发布于 

30天自制操作系统(16)

DAY16_多任务(2)

1.任务管理自动化

  • 充分做好多任务机制的基础上,再利用多任务逐步完善操作系统本身。
  • 如果我们想要运行三个任务的话,就必须改写mt_taskswitch的代码。如果能像当初定时器和窗口背景的做法一样更好。
struct TASKCTL *taskctl; 
struct TIMER *task_timer;
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;
}
  • 调用task_init,会返回一个内存地址,意思是==现在正在运行的这个程序,已经变成一个任务了==。可能大家不是很能理解这个说法,在调用init之后,所有程序的运行都会被当成任务来进行管理,而调用init的这个程序,我们也要让它所属于某个任务,这样一来,通过调用任务的设置函数,就可以对任务进行各种控制,比如说修改优先级等。

2.让任务休眠

  • 给每个任务==分配不同的时间==——休眠+唤醒
  • 休眠:

    void task_sleep(struct TASK *task) 
    {
    int i;
    char ts = 0;
    if (task->flags == 2) { /*如果指定任务处于唤醒状态*/
    if (task == taskctl->tasks[taskctl->now]) {
    ts = 1; /*让自己休眠的话,稍后需要进行任务切换*/
    }
    /*寻找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中添加用于记录要唤醒任务的信息的成员。
      struct FIFO32 { 
      int *buf;
      int p, q, size, free, flags;
      struct TASK *task;
      };

      3.增加窗口数量

  • 形成任务A、任务B0、任务B1和任务B2的格局。
  • 任务B0~B2各自拥有自己的窗口,它们的功能都一样,即进行计数,这有点像在Windows中启动了一个应用程序及其2个副本的感觉。

4.设定任务优先级(1)

  • 改写mtask.c、task_switch
    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;
    }

5.设定任务优先级(2)

  • 在操作系统中有一些处理,即使牺牲其他任务的性能也必须要尽快完成,否则会引起用户的不满,就比如这次对鼠标的处理。对于这类任务,我们可以让它在处理结束后马上休眠,而优先级则可以设置得非常高。
  • 我们需要设计一种架构,使得即便高优先级的任务同时运行,也能够区分哪个更加优先。
    16.1
  • 这种架构的工作原理是,最上层的LEVEL 0中只要存在哪怕一个任务,则完全忽略LEVEL 1和LEVEL 2中的任务,只在LEVEL 0的任务中进行任务切换。当LEVEL 0中的任务全部休眠,或者全部降到下层LEVEL,也就是当LEVEL 0中没有任何任务的时候,接下来开始轮到LEVEL 1中的任务进行任务切换。当LEVEL 0和LEVEL 1中都没有任务时,那就该轮到LEVEL2 出场了。
    在这种架构下,只要把音乐播放任务设置在LEVEL 0中,就可以保证获得比鼠标更高的优先级。

  • task_now 用于返回现在活动中的struct TASK的内存地址
    struct TASK *task_now(void) 
    {
    struct TASKLEVEL *tl = &taskctl->level[taskctl->now_lv];
    return tl->tasks[tl->now];
    }
  • task_add,用来向struct TASKLEVEL中添加一个任务
    void task_add(struct TASK *task) 
    {
    struct TASKLEVEL *tl = &taskctl->level[task->level];
    tl->tasks[tl->running] = task;
    tl->running++;
    task->flags = 2; /*活动中*/
    return;
    }

  • task_remove

    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;
    }
  • task_switchsub,用来在任务切换时决定接下来切换到哪个LEVEL

    void task_switchsub(void) 
    {
    int i;
    /*寻找最上层的LEVEL */
    for (i = 0; i < MAX_TASKLEVELS; i++) {
    if (taskctl->level[i].running > 0) {
    break; /*找到了*/
    }
    }
    taskctl->now_lv = i;
    taskctl->lv_change = 0;
    return;
    }