前言
>_<" 上一节已经实现了简单的多任务,而且还写了任务挂起函数,用来加快处理~这一节在上面的基础上增加多个窗口,然后又在优先级上面设计了一种分级的优先级模式~
一、效果展示
>_<" 如图产生了4个窗口,除了task_a窗口另外窗口是显示1s中的数数。
>_<" 这里的优先级模式是:有优先级较高的层里面有任务时,优先级低的层内的所有任务将会被屏蔽,只有该优先级高的层里面的任务才能进行任务切换~,像本例程是 把任务A是LEVEL1,优先级2;其他3个B任务分别是LEVEL2,优先级分别为1,2,3.这里的优先级是中断时间,优先级1即该任务的中断时间为 0.01s
二、代码说明
>_<" 在bootpack.h里有保存任务状态段信息的结构体TSS32,然后有任务结构体TASK,接下来是LEVEL的结构体,最后一个结构体是负责管理所有的任务~
1 /* mtask.c 任务切换相关*/ 2 #define MAX_TASKS 1000 /* 最大任务数量 */ 3 #define TASK_GDT0 3 /* 定义从GDT的几号开始分配给TSS */ 4 #define MAX_TASKS_LV 100 //每个level最多100个任务 5 #define MAX_TASKLEVELS 10 //最多10个level 6 7 struct TSS32 {//task status segment 任务状态段 8 int backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3;//保存的不是寄存器的数据,而是与任务设置相关的信息,在执行任务切换的时候这些成员不会被写入(backlink除外,某些情况下会被写入) 9 int eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;//32位寄存器 10 int es, cs, ss, ds, fs, gs;//16位寄存器 11 int ldtr, iomap;//有关任务设置部分 12 }; 13 struct TASK { 14 int sel, flags; /* sel用来存放GDT的编号 */ 15 int level, priority; 16 struct TSS32 tss; 17 }; 18 struct TASKLEVEL { 19 int running; /* 正在运行的任务量数 */ 20 int now; /* 这个变量用来记录当前正在运行的任务是哪一个 */ 21 struct TASK *tasks[MAX_TASKS_LV]; 22 }; 23 struct TASKCTL { 24 int now_lv; /* 正在运行的level */ 25 char lv_change; /* 在下次任务切换时是否需要改变LEVEL */ 26 struct TASKLEVEL level[MAX_TASKLEVELS];//最多10个level 27 struct TASK tasks0[MAX_TASKS]; 28 }; 29 extern struct TIMER *task_timer; 30 struct TASK *task_init(struct MEMMAN *memman);//初始化任务控制 31 struct TASK *task_alloc(void);//分配一个任务 32 void task_run(struct TASK *task, int level, int priority);//设置一个任务的LEVEL和优先级 33 void task_switch(void);//如果LEVEL改变了要改变当前的LEVEL然后从当前的LEVEL中找到当前任务进行切换 34 void task_sleep(struct TASK *task);//删除任务的时候要考虑LEVEL的变化~
>_<" 在初始化里面主要多了把所有LEVEL的running(即:正在运行的任务数)置0,now(即:当前正在执行的任务的标号)置0,然后分配一个任务设置level和优先级,并加入level。其中第10行是用来设置任务切换时决定接下来切换到哪个LEVEL。接下来,分配一个时间并给它设置~
1 for (i = 0; i < MAX_TASKLEVELS; i++) {//把每一层的level全都设置为0 2 taskctl->level[i].running = 0; 3 taskctl->level[i].now = 0; 4 } 5 task = task_alloc(); 6 task->flags = 2; /* 活动中标志 */ 7 task->priority = 2; //任务优先级//0.02s定时器 8 task->level = 0; /* 第0层 */ 9 task_add(task); //加入level中 10 task_switchsub(); /* 用来任务切换时决定接下来切换到哪个LEVEL */ 11 load_tr(task->sel); 12 //向TR寄存器写入这个值,因为刚才把当前运行任务的GDT定义为3号,TR寄存器是让CPU记住当前正在运行哪一个任务 13 //每当进行任务切换时,TR寄存器的值也会自动变换,task register 14 //每次给TR赋值的时候,必须把GDT的编号乘以8 15 task_timer = timer_alloc(); 16 timer_settime(task_timer, task->priority);
>_<" 在没有改变之前,task_run中下一个要切换的任务是固定不变的,不过现在就不同了,如果本次task_run启动一个比当前活动中的任务LEVEL更高的任务,那么下次任务切换时,就得无条件的切换到更高优先级的LEVEL中。此外,如果当前任务中的LEVEL被下调,那么就得把其他LEVEL的有先任务放在前面。综上所述:我们需要再下次切换时先检查LEVEL,因此将lv_change置为1。
1 void task_run(struct TASK *task, int level, int priority) 2 { 3 if (level < 0) { 4 level = task->level; /* 不改变level */ 5 } 6 if (priority > 0) { 7 task->priority = priority; 8 } 9 10 if (task->flags == 2 && task->level != level) { /* 改变活动中的LEVEL */ 11 task_remove(task); /* 这里执行之后flag的值会变为1,于是下面的语句也会被执行 */ 12 } 13 if (task->flags != 2) { 14 /* 从休眠状态唤醒 */ 15 task->level = level; 16 task_add(task); 17 } 18 19 taskctl->lv_change = 1; /* 下次切换任务时检查LEVEL */ 20 return; 21 }
>_<" 所以在任务切换里,如果上面的LEVEL变化了,就要把第10~11行进行当前level的设置,下面就是很正常的切换了~
1 void task_switch(void) 2 { 3 struct TASKLEVEL *tl = &taskctl->level[taskctl->now_lv];//当前任务的LEVEL 4 struct TASK *new_task, *now_task = tl->tasks[tl->now]; 5 tl->now++; 6 if (tl->now == tl->running) { 7 tl->now = 0; 8 } 9 if (taskctl->lv_change != 0) {//LEVEL之间变换了 10 task_switchsub();//用来在任务时决定接下来切换到哪个LEVEL,直接从头开始找,找到第一个LEVEL中有任务的返回 11 tl = &taskctl->level[taskctl->now_lv]; 12 } 13 new_task = tl->tasks[tl->now]; 14 timer_settime(task_timer, new_task->priority); 15 if (new_task != now_task) { 16 farjmp(0, new_task->sel); 17 } 18 return; 19 }
>_<" 下面的任务挂起也比较简单了~
1 void task_sleep(struct TASK *task) 2 { 3 struct TASK *now_task; 4 if (task->flags == 2) { 5 /* 如果处于活动状态 */ 6 now_task = task_now(); 7 task_remove(task); /* 执行此语句的话,flags将被置为1 remove其实就是从数组中删除一个*/ 8 if (task == now_task) { 9 /* 如果让自己休眠需要进行任务切换 */ 10 task_switchsub(); 11 now_task = task_now(); /* 在设定后获取当前任务的值 */ 12 farjmp(0, now_task->sel); 13 } 14 } 15 return; 16 }
>_<" 在main函数里分别对4个窗口的任务做如下设置
1 task_a = task_init(memman);//初始化任务a//初始化任务管理器,task_init会返回自己的构造地址,我们将这个地址存入fifo.task 2 fifo.task = task_a;//可以自动管理,待唤醒的task(//记录休眠任务名) 3 task_run(task_a, 1, 2);//将任务A的LEVEL设置为1,优先级2,B的3个任务LEVEL是2,所以优先级低
1 task_run(task_b[i], 2, i + 1);;//启动任务,LEVEL2,优先级1,2,3
三、相关链接
没有加入优先级,只是用平均分配的模式的多窗口链接13c:http://pan.baidu.com/s/1nt4w6RB
加入LEVEL模式的优先级时的多窗口链接13e:http://pan.baidu.com/s/1eQw3sQY