[自制简单操作系统] 7、多任务(二)——任务管理自动化&任务休眠


 

前言

>_<" 这里仿照窗口管理的方式将任务管理也修改成相应的管理模式,这样可以灵活的添加多个任务,而不必每次都要修改任务切换函数;此外还在任务休眠做了尝试,通过将任务挂起和唤醒从而加快运行速度~

 

一、任务管理自动化

>_<" 为了仿照窗口管理模式对任务进行管理,于是在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

 

三、效果展示

>_<" 可见比上一节讲的多任务要快很多,这主要是采用休眠的的结果~

四、相关链接

 

 

 

 

 

 

 

 

 

 

  

 





本文转自beautifulzzzz博客园博客,原文链接:http://www.cnblogs.com/zjutlitao/p/3994405.html ,如需转载请自行联系原作者
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值