Day16 多任务(2)
文章目录
- 1. 任务管理自动化(harib13a)
- 2. 任务休眠(harib13b)
- 3. 增加窗口数量(harib13c)
- 4. 设定任务优先级(1)(harib13d)
- 5. 设定任务优先级(2)(harib13e)
1. 任务管理自动化(harib13a)
使用定时器和窗口那种处理方式处理多线程的切换。
// bootpack.h文件
#define ADR_GDT 0x00270000
#define MAX_TASKS 1000 /* 最大任务数量 */
#define TASK_GDT0 3 /* 定义从GDT的几号开始分配给TSS */
struct TSS32 {int backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3;int eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;int es, cs, ss, ds, fs, gs;int ldtr, iomap;
};
struct TASK {int sel, flags; /* sel存放GDT的编号 */struct TSS32 tss;
};
struct TASKCTL {int running; /* 正在运行的任务数量 */int now; /* 用来记录当前正在运行的是哪个任务 */struct TASK *tasks[MAX_TASKS]; // 参与到调度的taskstruct TASK tasks0[MAX_TASKS]; // 预留出最大task内存
};// mtask.c文件
struct TASK *task_init(struct MEMMAN *memman)
{int i;struct TASK *task;struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT;taskctl = (struct TASKCTL *) memman_alloc_4k(memman, sizeof (struct TASKCTL));for (i = 0; i < MAX_TASKS; i++) {taskctl->tasks0[i].flags = 0; /* 先标识为未使用 */taskctl->tasks0[i].sel = (TASK_GDT0 + i) * 8;set_segmdesc(gdt + TASK_GDT0 + i, 103, (int) &taskctl->tasks0[i].tss, AR_TSS32);}task = task_alloc();task->flags = 2; /* 标识为活动中 */taskctl->running = 1;taskctl->now = 0;taskctl->tasks[0] = task;load_tr(task->sel);task_timer = timer_alloc();timer_settime(task_timer, 2);return task;
}struct TASK *task_alloc(void)
{int i;struct TASK *task;for (i = 0; i < MAX_TASKS; i++) {if (taskctl->tasks0[i].flags == 0) {task = &taskctl->tasks0[i];task->flags = 1; /* 标识为正在使用 */task->tss.eflags = 0x00000202; /* IF = 1; */task->tss.eax = 0; /* 暂且先赋值为0 */task->tss.ecx = 0;task->tss.edx = 0;task->tss.ebx = 0;task->tss.ebp = 0;task->tss.esi = 0;task->tss.edi = 0;task->tss.es = 0;task->tss.ds = 0;task->tss.fs = 0;task->tss.gs = 0;task->tss.ldtr = 0;task->tss.iomap = 0x40000000;return task;}}return 0; /* 全部正在使用 */
}void task_run(struct TASK *task)
{task->flags = 2; /* 标识为活动中 */taskctl->tasks[taskctl->running] = task;taskctl->running++;return;
}void task_switch(void)
{timer_settime(task_timer, 2);if (taskctl->running >= 2) {taskctl->now++;if (taskctl->now == taskctl->running) {taskctl->now = 0;}farjmp(0, taskctl->tasks[taskctl->now]->sel);}return;
}
// bootpack.c
void HaribMain(void)
{struct TASK *task_b;task_init(memman);task_b = task_alloc();task_b->tss.esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024 - 8;task_b->tss.eip = (int) &task_b_main;task_b->tss.es = 1 * 8;task_b->tss.cs = 2 * 8;task_b->tss.ss = 1 * 8;task_b->tss.ds = 1 * 8;task_b->tss.fs = 1 * 8;task_b->tss.gs = 1 * 8;*((int *) (task_b->tss.esp + 4)) = (int) sht_back;task_run(task_b);/* 省略 */
}
需要增加task的时候只需要调用task_alloc和task_run就可以了。
2. 任务休眠(harib13b)
由于任务A中主要做鼠标,键盘,定时器的处理操作(打印),所以当没有这些动作时,任务A是空闲的(HLT),但是任务B却一直在工作。本节期望实现当任务A空闲的时候就使任务A休眠,当有对应的中断过来,再唤醒它。
// mtask.c
void task_sleep(struct TASK *task)
{int i;char ts = 0;if (task->flags == 2) { /* 如果该任务处于活动中 */if (task == taskctl->tasks[taskctl->now]) {ts = 1; /* 让本task休眠的话,稍后需要进行任务切换 */}/* 寻找task所在的位置,让该task休眠 */for (i = 0; i < taskctl->running; i++) {if (taskctl->tasks[i] == task) {break;}}taskctl->running--;if (i < taskctl->now) {taskctl->now--;}/* 移动成员 */for (; i < taskctl->running; i++) {taskctl->tasks[i] = taskctl->tasks[i + 1];}task->flags = 1; /* 不工作的状态 */if (ts != 0) {/* 本进程休眠,任务切换 */if (taskctl->now >= taskctl->running) {/* 如果now的值出现异常,则进行修正 */taskctl->now = 0;}farjmp(0, taskctl->tasks[taskctl->now]->sel);}}return;
}
修改FIFO定义,使缓冲区中有数据写入时将任务唤醒:
// bootpack.h
struct FIFO32 {int *buf;int p, q, size, free, flags;struct TASK *task;
};// fifo.c
void fifo32_init(struct FIFO32 *fifo, int size, int *buf, struct TASK *task)
{fifo->size = size;fifo->buf = buf;fifo->free = size; /* 剩余空间 */fifo->flags = 0;fifo->p = 0; /* 写入位置 */fifo->q = 0; /* 读取位置 */fifo->task = task; /* 新增 */return;
}int fifo32_put(struct FIFO32 *fifo, int data)
{if (fifo->free == 0) {/* 无剩余空间,则溢出 */fifo->flags |= FLAGS_OVERRUN;return -1;}fifo->buf[fifo->p] = data;fifo->p++;if (fifo->p == fifo->size) {fifo->p = 0;}fifo->free--;if (fifo->task != 0) {if (fifo->task->flags != 2) { /* 新增,先判断是否已经处于唤醒状态 */task_run(fifo->task); /* 如果任务处于休眠状态,则唤醒 */}}return 0;
}// bootpack.c
void HariMain(void)
{struct TASK *task_a, *task_b;fifo32_init(&fifo, 128, fifobuf, 0);task_a = task_init(memman);fifo.task = task_a;task_b = task_alloc();for (;;) {io_cli(); // 屏蔽中断请求if (fifo32_status(&fifo) == 0) {task_sleep(task_a); // 屏蔽中断请求的状态下进行休眠io_sti();}else{// 省略}}
}void task_b_main(struct SHEET *sht_back)
{fifo32_init(&fifo, 128, fifobuf, 0);
}
3. 增加窗口数量(harib13c)
增加新的任务,最终使共4个任务,并分别命名为task_a,task_b0,task_b1,task_b2。对每个任务分配一个窗口。
此外,还删除了task_a的3秒定时器和10秒定时器。
// bootpack.c
void HaribMain(void)
{unsigned char *buf_back, buf_mouse[256], *buf_win, *buf_win_b;struct SHEET *sht_back, *sht_mouse, *sht_win, *sht_win_b[3];struct TASK *task_a, *task_b[3];struct TIMER *timer;init_palette();shtctl = shtctl_init(memman, binfo->vram, binfo->scrnx, binfo->scrny);task_a = task_init(memman); // 主线程还是task_afifo.task = task_a;/* sht_back */sht_back = sheet_alloc(shtctl);buf_back = (unsigned char *) memman_alloc_4k(memman, binfo->scrnx * binfo->scrny);sheet_setbuf(sht_back, buf_back, binfo->scrnx, binfo->scrny, -1); /* 无透明色 */init_screen8(buf_back, binfo->scrnx, binfo->scrny);/* sht_win_b */for (i = 0; i < 3; i++) {sht_win_b[i] = sheet_alloc(shtctl);buf_win_b = (unsigned char *) memman_alloc_4k(memman, 144 * 52);sheet_setbuf(sht_win_b[i], buf_win_b, 144, 52, -1); /* 无透明色 */sprintf(s, "task_b%d", i);make_window8(buf_win_b, 144, 52, s, 0);task_b[i] = task_alloc();task_b[i]->tss.esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024 - 8;task_b[i]->tss.eip = (int) &task_b_main;task_b[i]->tss.es = 1 * 8;task_b[i]->tss.cs = 2 * 8;task_b[i]->tss.ss = 1 * 8;task_b[i]->tss.ds = 1 * 8;task_b[i]->tss.fs = 1 * 8;task_b[i]->tss.gs = 1 * 8;*((int *) (task_b[i]->tss.esp + 4)) = (int) sht_win_b[i];task_run(task_b[i]);}/* sht_win */sht_win = sheet_alloc(shtctl);buf_win = (unsigned char *) memman_alloc_4k(memman, 160 * 52);sheet_setbuf(sht_win, buf_win, 144, 52, -1); /* ���F��� */make_window8(buf_win, 144, 52, "task_a", 1);make_textbox8(sht_win, 8, 28, 128, 16, COL8_FFFFFF);cursor_x = 8;cursor_c = COL8_FFFFFF;timer = timer_alloc();timer_init(timer, &fifo, 1);timer_settime(timer, 50);/* sht_mouse */sht_mouse = sheet_alloc(shtctl);sheet_setbuf(sht_mouse, buf_mouse, 16, 16, 99);init_mouse_cursor8(buf_mouse, 99);mx = (binfo->scrnx - 16) / 2; /* 计算坐标,使其位于画面中央 */my = (binfo->scrny - 28 - 16) / 2;sheet_slide(sht_back, 0, 0);sheet_slide(sht_win_b[0], 168, 56);sheet_slide(sht_win_b[1], 8, 116);sheet_slide(sht_win_b[2], 168, 116);sheet_slide(sht_win, 8, 56);sheet_slide(sht_mouse, mx, my);sheet_updown(sht_back, 0);sheet_updown(sht_win_b[0], 1);sheet_updown(sht_win_b[1], 2);sheet_updown(sht_win_b[2], 3);sheet_updown(sht_win, 4);sheet_updown(sht_mouse, 5);sprintf(s, "(%3d, %3d)", mx, my);putfonts8_asc_sht(sht_back, 0, 0, COL8_FFFFFF, COL8_008484, s, 10);sprintf(s, "memory %dMB free : %dKB",memtotal / (1024 * 1024), memman_total(memman) / 1024);putfonts8_asc_sht(sht_back, 0, 32, COL8_FFFFFF, COL8_008484, s, 40);
}
4. 设定任务优先级(1)(harib13d)
当前的B0 ~ B2这三个任务,都是以0.02s的时间间隔进行任务切换,修改一下唉,使其可以在0.01s ~ 0.1s的时间范围内设定不同的任务切换。即优先级越高的任务,切换的频率越高,则调度到的次数越多。0.01s ~ 0.1s的差异可实现最大10倍的优先级差异。
struct TASK {int sel, flags;int priority; // 添加优先级字段struct TSS32 tss;
};struct TASK *task_init(struct MEMMAN *memman)
{int i;struct TASK *task;struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT;taskctl = (struct TASKCTL *) memman_alloc_4k(memman, sizeof (struct TASKCTL));for (i = 0; i < MAX_TASKS; i++) {taskctl->tasks0[i].flags = 0;taskctl->tasks0[i].sel = (TASK_GDT0 + i) * 8;set_segmdesc(gdt + TASK_GDT0 + i, 103, (int) &taskctl->tasks0[i].tss, AR_TSS32);}task = task_alloc();task->flags = 2; /* 标识为活动中 */task->priority = 2; /* 初始化为0.02s */taskctl->running = 1;taskctl->now = 0;taskctl->tasks[0] = task;load_tr(task->sel);task_timer = timer_alloc();timer_settime(task_timer, task->priority);return task;
}void task_run(struct TASK *task, int priority)
{ // 调用时传入优先级参数if (priority > 0) {task->priority = priority;}// 如果传入priority为0,则唤醒任务 if (task->flags != 2) {task->flags = 2; /* 标识为活动中 */taskctl->tasks[taskctl->running] = task;taskctl->running++;}return;
}void task_switch(void)
{struct TASK *task;taskctl->now++;if (taskctl->now == taskctl->running) {taskctl->now = 0;}task = taskctl->tasks[taskctl->now];timer_settime(task_timer, task->priority);if (taskctl->running >= 2) {farjmp(0, task->sel);}return;
}/* fifo.c */
int fifo32_put(struct FIFO32 *fifo, int data)
{if (fifo->free == 0) {fifo->flags |= FLAGS_OVERRUN;return -1;}fifo->buf[fifo->p] = data;fifo->p++;if (fifo->p == fifo->size) {fifo->p = 0;}fifo->free--;if (fifo->task != 0) {if (fifo->task->flags != 2) { /* 如果任务处于休眠 */task_run(fifo->task, 0); /* 唤醒 */}}return 0;
}
5. 设定任务优先级(2)(harib13e)
增加一个level结构体作为一类task的集合,本level中的task在活动中时,其他level的task就不应该被调度到。这样做是为了使即便等高优先级的任务同时运行,也可以分辨哪一个任务更加优先。
类似图中所示,最上层的level0中只要存在一个task,则完全忽略level1和level2中的task,只在level0的任务中进行切换。当level0中task全部休眠,或没有task时,就开始轮到level1中的task进行任务切换。
// bootpack.h
struct TASK {int sel, flags; /* sel用来存放GDT的编号 */int level, priority; // 增加一个level字段,标识task实例是哪个levelstruct TSS32 tss;
};
struct TASKLEVEL {int running; /* 正在运行的任务数量 */int now; /* 用来记录当前运行的是哪个任务 */struct TASK *tasks[MAX_TASKS_LV]; // 每个level里管理的task
};
struct TASKCTL {int now_lv; /* 当前活动中的是哪个level */char lv_change; /* 下次任务切换时是否需要改变level */struct TASKLEVEL level[MAX_TASKLEVELS];struct TASK tasks0[MAX_TASKS]; // 预留出所有task的空间
};
// mtask.c
struct TASKCTL *taskctl;
struct TIMER *task_timer;struct TASK *task_now(void)
{struct TASKLEVEL *tl = &taskctl->level[taskctl->now_lv];return tl->tasks[tl->now];
}void task_add(struct TASK *task)
{struct TASKLEVEL *tl = &taskctl->level[task->level];tl->tasks[tl->running] = task;tl->running++;task->flags = 2; /* 活动中 */return;
}void task_remove(struct TASK *task)
{int i;struct TASKLEVEL *tl = &taskctl->level[task->level];/* 寻找task所在的位置 */for (i = 0; i < tl->running; i++) {if (tl->tasks[i] == task) {break;}}tl->running--;if (i < tl->now) {tl->now--; /* 需要移动成员 */}if (tl->now >= tl->running) {/* 如果now的值出现异常,则进行修正 */tl->now = 0;}task->flags = 1; /* 休眠中 *//* 移动 */for (; i < tl->running; i++) {tl->tasks[i] = tl->tasks[i + 1];}return;
}void task_switchsub(void)
{ // 切换levelint i;/* 寻找最上层的level */for (i = 0; i < MAX_TASKLEVELS; i++) {if (taskctl->level[i].running > 0) {break; /* 找到 */}}taskctl->now_lv = i; // 标识当前活动task所在的leveltaskctl->lv_change = 0; // 不需要改变levelreturn;
}struct TASK *task_init(struct MEMMAN *memman)
{int i;struct TASK *task;struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT;taskctl = (struct TASKCTL *) memman_alloc_4k(memman, sizeof (struct TASKCTL));/* 省略 */for (i = 0; i < MAX_TASKLEVELS; i++) {taskctl->level[i].running = 0;taskctl->level[i].now = 0;}task = task_alloc();task->flags = 2; /* 活动中 */task->priority = 2; /* 0.02s的频率切换 */task->level = 0; /* level 0 */task_add(task); /* 把主任务添加到level0中 */task_switchsub(); /* 切换到level0 */load_tr(task->sel);task_timer = timer_alloc();timer_settime(task_timer, task->priority);return task;
}void task_run(struct TASK *task, int level, int priority)
{if (level < 0) {level = task->level; /* 主要用于唤醒的时候,不改变level */}if (priority > 0) {task->priority = priority;}if (task->flags == 2 && task->level != level) { /* 如果已经在运行,并且level不同 */task_remove(task); /* 先移除该task,期间会把flags赋值为1,即标识为未运行 */}if (task->flags != 2) {/* 再修改level,并标识为活动中(flags赋值为2) */task->level = level;task_add(task);}taskctl->lv_change = 1; /* 下次任务切换时检查level */return;
}void task_sleep(struct TASK *task)
{struct TASK *now_task;if (task->flags == 2) {/* 当处于活动状态时 */now_task = task_now(); // 查看当前tasktask_remove(task); /* 把该task移除 */if (task == now_task) {/* 如果刚才移除的就是正在运行的task */task_switchsub(); // 就要进行level切换now_task = task_now(); /* 切换后在查看当前task */farjmp(0, now_task->sel);}}return;
}void task_switch(void)
{struct TASKLEVEL *tl = &taskctl->level[taskctl->now_lv];struct TASK *new_task, *now_task = tl->tasks[tl->now];tl->now++;if (tl->now == tl->running) {tl->now = 0;}if (taskctl->lv_change != 0) {// 主要是增加了这个判断和相应的动作task_switchsub();tl = &taskctl->level[taskctl->now_lv];}new_task = tl->tasks[tl->now];timer_settime(task_timer, new_task->priority);if (new_task != now_task) {farjmp(0, new_task->sel);}return;
}
// fifo.c
int fifo32_put(struct FIFO32 *fifo, int data)
{/* 省略 */fifo->free--;if (fifo->task != 0) {if (fifo->task->flags != 2) { /* 如果处于休眠状态 */task_run(fifo->task, -1, 0); /* 唤醒 */}}return 0;
}
此外,在HariMain函数中,对task调用task_run(task_a, 1, 2);
和task_run(task_b[i], 2, i + 1);
。