前言
>_<" 这里仿照窗口管理的方式将任务管理也修改成相应的管理模式,这样可以灵活的添加多个任务,而不必每次都要修改任务切换函数;此外还在任务休眠做了尝试,通过将任务挂起和唤醒从而加快运行速度~
一、任务管理自动化
>_<" 为了仿照窗口管理模式对任务进行管理,于是在bootpack.h里做如下定义:
1 /* mtask.c 任务切换相关*/ 2 #define MAX_TASKS 1000 /* 最大任务数量 */ 3 #define TASK_GDT0 3 /* 定义从GDT的几号开始分配给TSS */ 4 5 struct TSS32 {//task status segment 任务状态段 6 int backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3;//保存的不是寄存器的数据,而是与任务设置相关的信息,在执行任务切换的时候这些成员不会被写入(backlink除外,某些情况下会被写入) 7 int eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;//32位寄存器 8 int es, cs, ss, ds, fs, gs;//16位寄存器 9 int ldtr, iomap;//有关任务设置部分 10 }; 11 struct TASK { 12 int sel, flags; /* sel用来存放GDT的编号 */ 13 struct TSS32 tss; 14 }; 15 struct TASKCTL { 16 int running; /* 正在运行的任务量数 */ 17 int now; /* 这个变量用来记录当前正在运行的任务是哪一个 */ 18 struct TASK *tasks[MAX_TASKS]; 19 struct TASK tasks0[MAX_TASKS]; 20 }; 21 extern struct TIMER *task_timer; 22 struct TASK *task_init(struct MEMMAN *memman);//初始化任务控制 23 struct TASK *task_alloc(void);//分配一个任务 24 void task_run(struct TASK *task);//将task添加到tasks的末尾,然后running加1 25 void task_switch(void);//running为1的时候不用进行任务切换,函数直接结束,当running大于2的时候,先把now加1 26 //再把now代表的任务切换成当前的任务,最后再将末尾的任务移到开头
PS:可以看出和窗口管理很相似,TASK是一个任务,TASKCTL是任务管理结构体
>_<" 下面是对应的任务管理.c文件,其中task_init函数是初始化任务控制,task_alloc是分配一个任务函数,task_run其实就相当于唤醒,task_switch任务切换
1 /* 任务管理相关程序 */ 2 3 #include "bootpack.h" 4 5 struct TASKCTL *taskctl; 6 struct TIMER *task_timer; 7 8 ///////////////////////////////////////////////////////////////////////////////////// 9 //功能:初始化任务控制 10 //参数: 11 //返回:返回一个内存地址,意思是现在正在运行这个程序,已经变成一个任务 12 struct TASK *task_init(struct MEMMAN *memman) 13 { 14 int i; 15 struct TASK *task; 16 struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT; 17 taskctl = (struct TASKCTL *) memman_alloc_4k(memman, sizeof (struct TASKCTL));//TASKCTL是个很大的结构体,所以要申请一个内存空间 18 for (i = 0; i < MAX_TASKS; i++) { 19 taskctl->tasks0[i].flags = 0; 20 taskctl->tasks0[i].sel = (TASK_GDT0 + i) * 8; 21 set_segmdesc(gdt + TASK_GDT0 + i, 103, (int) &taskctl->tasks0[i].tss, AR_TSS32);//定义在gdt的号,段长限制为103字节 22 } 23 task = task_alloc(); 24 task->flags = 2; /* 活动中标志 */ 25 taskctl->running = 1; 26 taskctl->now = 0; 27 taskctl->tasks[0] = task; 28 load_tr(task->sel); 29 //向TR寄存器写入这个值,因为刚才把当前运行任务的GDT定义为3号,TR寄存器是让CPU记住当前正在运行哪一个任务 30 //每当进行任务切换时,TR寄存器的值也会自动变换,task register 31 //每次给TR赋值的时候,必须把GDT的编号乘以8 32 task_timer = timer_alloc(); 33 timer_settime(task_timer, 2); 34 return task; 35 } 36 ///////////////////////////////////////////////////////////////////////////////////// 37 //功能:任务分配[遍历所有的任务,发现任务处于空闲状态的进行初始化] 38 //参数: 39 struct TASK *task_alloc(void) 40 { 41 int i; 42 struct TASK *task; 43 for (i = 0; i < MAX_TASKS; i++) { 44 if (taskctl->tasks0[i].flags == 0) { 45 task = &taskctl->tasks0[i]; 46 task->flags = 1; /* 正在使用标志 */ 47 task->tss.eflags = 0x00000202; /* IF = 1; */ 48 task->tss.eax = 0; /* 这里先设置为0 */ 49 task->tss.ecx = 0; 50 task->tss.edx = 0; 51 task->tss.ebx = 0; 52 task->tss.ebp = 0; 53 task->tss.esi = 0; 54 task->tss.edi = 0; 55 task->tss.es = 0; 56 task->tss.ds = 0; 57 task->tss.fs = 0; 58 task->tss.gs = 0; 59 task->tss.ldtr = 0;//先这样设置 60 task->tss.iomap = 0x40000000; 61 return task; 62 } 63 } 64 return 0; /* 全部都正在使用 */ 65 } 66 ///////////////////////////////////////////////////////////////////////////////////// 67 //功能:将task添加到tasks的末尾,然后running加1 68 //参数: 69 void task_run(struct TASK *task) 70 { 71 task->flags = 2; /* 活动中标志 */ 72 taskctl->tasks[taskctl->running] = task; 73 taskctl->running++; 74 return; 75 } 76 ///////////////////////////////////////////////////////////////////////////////////// 77 //功能:running为1的时候不用进行任务切换,函数直接结束,当running大于2的时候,先把now加1 78 //再把now代表的任务切换成当前的任务,最后再将末尾的任务移到开头 79 //参数: 80 void task_switch(void) 81 { 82 timer_settime(task_timer, 2); 83 if (taskctl->running >= 2) { 84 taskctl->now++; 85 if (taskctl->now == taskctl->running) { 86 taskctl->now = 0; 87 } 88 farjmp(0, taskctl->tasks[taskctl->now]->sel); 89 } 90 return; 91 }
- 第17行,因为任务管理结构体很大,所以要提前分配内存
- 第18~22行,是初始化所有任务的flags,sel,以及定义每个任务的GDT
- 第23~34行,是生成一个基础的任务,并进行任务切换时钟设置
- 第39~65行,是遍历所有的任务,发现当前有没有使用的任务对其进行初始化并返回,实现任务分配的功能
- 第69~75行,是任务唤醒函数
- 第80~91行,是任务切换函数,当running=1时不进行切换,当running>2时,把now+1,然后把now所代表的当前任务进行切换,其中第85~87行的判断是当now跑到末尾时,让其跑到开头
二、任务休眠
>_<" 如果仅仅是采用上述方式,只能实现每个任务分配大约相同的时间,这样会导致过于平均而不是很优的策略~与其让一个任务空闲着不如直接让其挂起,将自己多出的时间都分配给另一些需要大量时间的任务来执行。这里就要用到休眠:即,将一个任务从tasks中删除。不过,当一个任务休眠时,当FIFO有数据传过来时还要让其唤醒,使其再具有数据处理能力~下面是mtask.c中的任务休眠函数:
1 ///////////////////////////////////////////////////////////////////////////////////// 2 //功能:任务休眠,从任务数组中删除该任务,如果处于正在运行的任务,就让其休眠 3 //参数: 4 void task_sleep(struct TASK *task) 5 { 6 int i; 7 char ts = 0; 8 if (task->flags == 2) { /* 如果指定任务处于唤醒状态 */ 9 if (task == taskctl->tasks[taskctl->now]) { 10 ts = 1; /* 让自己休眠的话,稍后需要进行任务切换 */ 11 } 12 /* 寻找task所在的位置 */ 13 for (i = 0; i < taskctl->running; i++) { 14 if (taskctl->tasks[i] == task) { 15 break; 16 } 17 } 18 taskctl->running--;//当前正在运行的任务数量减1 19 if (i < taskctl->now) {//欲休眠的任务在当前任务前,因为想删除该任务,所以当前任务标号要减1 20 taskctl->now--; /* 需要移动成员,所以做相应的处理 */ 21 } 22 /* 移动成员 */ 23 for (; i < taskctl->running; i++) { 24 taskctl->tasks[i] = taskctl->tasks[i + 1]; 25 } 26 task->flags = 1; /* 不做工作的状态 */ 27 if (ts != 0) { 28 /* 任务切换 */ 29 if (taskctl->now >= taskctl->running) { 30 /* now值越界进行让其变为开始 */ 31 taskctl->now = 0; 32 } 33 farjmp(0, taskctl->tasks[taskctl->now]->sel); 34 } 35 } 36 return; 37 }
PS: 整个过程就类似于从数组中删除一个数据~就这么简单
>_<" 要实现唤醒功能,就要在FIFO结构体中加入用于记录唤醒任务的成员信息,如下:bootpack.h里的FIFO结构体
1 /* fifo.c */ 2 struct FIFO32 {//FIFO缓冲区数据结构 3 int *buf;//缓冲区 4 int p, q, size, free, flags;//下一个数据的写入地址,下一个数据的读出地址,缓冲区的大小,free是缓冲区没有数据的字节数,flag是是否溢出 5 struct TASK *task;//当FIFO中写数据的时候将任务唤醒,用于记录要唤醒任务的信息 6 }; 7 8 void fifo32_init(struct FIFO32 *fifo, int size, int *buf, struct TASK *task);//缓冲区结构体指针,大小,缓冲区开始位置,有数据写入的时候要唤醒任务的任务 9 int fifo32_put(struct FIFO32 *fifo, int data);//往缓冲区内插入一个数据,当有任务处于休眠的时候要唤醒S 10 int fifo32_get(struct FIFO32 *fifo); 11 int fifo32_status(struct FIFO32 *fifo);
>_<" 然后还要修改fifo32_init函数,其中最后一个参数就是指定的一个任务,如果不想使用任务自动唤醒功能,就将task置为0即可!
>_<" 接着要修改fifo32_put函数,实现向FIFO中写数据时,唤醒某个任务的功能~
1 int fifo32_put(struct FIFO32 *fifo, int data) 2 { 3 if (fifo->free == 0) {//溢出 4 fifo->flags |= FLAGS_OVERRUN; 5 return -1; 6 } 7 fifo->buf[fifo->p] = data; 8 fifo->p++; 9 if (fifo->p == fifo->size) {//当插入位置到达最后时再返回第一个位置 10 fifo->p = 0; 11 } 12 fifo->free--; 13 if(fifo->task!=0){//如果设置了有唤醒任务就唤醒 14 if(fifo->task->flags!=2){//如果处于休眠状态 15 task_run(fifo->task);//将任务唤醒 16 } 17 } 18 return 0; 19 }
本次bootpack.c
三、效果展示
>_<" 可见比上一节讲的多任务要快很多,这主要是采用休眠的的结果~
四、相关链接
- 任务管理自动化链接13a:http://pan.baidu.com/s/1gdkpZsn
- 任务休眠链接13b:http://pan.baidu.com/s/1gd3mMrh
分类:
核心_操作系统
本文转自beautifulzzzz博客园博客,原文链接:http://www.cnblogs.com/zjutlitao/p/3994405.html
,如需转载请自行联系原作者