[自制简单操作系统] 6、多任务(一)

简介:


 

#前言#

>_<" 这里主要是多任务的初探~比较简单,主要是尝试保存当前任务、任务切换、恢复当前任务,而真正的多任务要比这个复杂的多,因为包含互不干扰,甚至是高度优化的并行技术!

 

一、保存当前任务:

>_<" 当向CPU发出切换命令的时候,CPU会先把寄存器中的值全部写入内存中,这样当切换回来时,可以从中断的地方继续执行。接下来,为了运行下一个程序,CPU会把所有的寄存器中的值从内存中读取出来,这就完成了一次切换。

>_<" 这里的结构TSS32是task status segment,是32位任务状态段,其也是内存段的一种,需要再GDT中进行定以后使用。

1 struct TSS32 {//task status segment 任务状态段
2     int backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3;//保存的不是寄存器的数据,而是与任务设置相关的信息,在执行任务切换的时候这些成员不会被写入(backlink除外,某些情况下会被写入)
3     int eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;//32位寄存器
4     int es, cs, ss, ds, fs, gs;//16位寄存器
5     int ldtr, iomap;//有关任务设置部分
6 };

 

二、任务切换:从任务A到任务B[12a]

>_<" 接下来要在main函数中创建两个TSS:任务A的TSS和任务B的TSS,然后对其初始化,接着再在GDT中定义...见下面的main函数(有删减,这里只展示核心部分):

复制代码
 18 void HariMain(void)
 19 {
 25     int mx, my, i ,cursor_x, cursor_c, task_b_esp;//cursor_x是记录光标位置的变量,cursor_c表示光标现在的颜色,每隔0.5秒变化一次,任务B的栈
 41     struct TSS32 tss_a, tss_b;//首先建立2个TSS,任务a的TSS和任务b的TSS
110 tss_a.ldtr = 0;//先这样设置好 111 tss_a.iomap = 0x40000000; 112 tss_b.ldtr = 0; 113 tss_b.iomap = 0x40000000; 114 set_segmdesc(gdt + 3, 103, (int) &tss_a, AR_TSS32);//定义在gdt的3号,段长限制为103字节 115 set_segmdesc(gdt + 4, 103, (int) &tss_b, AR_TSS32); 116 //向TR寄存器写入这个值,因为刚才把当前运行任务的GDT定义为3号,TR寄存器是让CPU记住当前正在运行哪一个任务 117 //每当进行任务切换时,TR寄存器的值也会自动变换,task register 118 //每次给TR赋值的时候,必须把GDT的编号乘以8 119 load_tr(3 * 8); 120 task_b_esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024;//要为任务B专门分配栈,直接用任务A的栈就会乱成一团糟 121 tss_b.eip = (int) &task_b_main;//任务b的开始地址 122 tss_b.eflags = 0x00000202; /* IF = 1; */ 123 tss_b.eax = 0; 124 tss_b.ecx = 0; 125 tss_b.edx = 0; 126 tss_b.ebx = 0; 127 tss_b.esp = task_b_esp; 128 tss_b.ebp = 0; 129 tss_b.esi = 0; 130 tss_b.edi = 0; 131 tss_b.es = 1 * 8; 132 tss_b.cs = 2 * 8; 133 tss_b.ss = 1 * 8; 134 tss_b.ds = 1 * 8; 135 tss_b.fs = 1 * 8; 136 tss_b.gs = 1 * 8; 137 138 for (;;) {140 if (fifo32_status(&fifo) == 0) {142 } else {145 if (256 <= i && i <= 511) {//键盘数据165 }else if (512 <= i && i <= 767) {//鼠标数据202 }else if(i==10){//10s定时 204 taskswitch4();//程序10s以后进行任务切换 205 }else if (i == 3){}219 } 220 } 221 }300 ///////////////////////////////////////////////////////////////////////////////////// 301 //功能:任务b的函数 302 //参数: 303 void task_b_main(void) 304 { 305 for (;;) { io_hlt(); } 306 }
复制代码
  bootpack.c 全部代码
  • 第41行是建立2个TSS,分别是任务A和任务B的

  • 第110~113是对A和B的TSS任务的成员变量的初始化,这里不能乱赋值否则不能正常切换

  • 第114和第115两行是定义两个TSS的GDT,分别是段3和段4

  • 第119行load_tr(3 * 8);是用汇编写的函数,目的是给TR寄存器赋值,因为刚才设置任务a的GDT为3,而TR寄存器是让CPU记住当前执行那一条任务,所以,要给TR重新设置值,这里*8是和硬件有关~

  • 第120行是给任务B分配栈,否则和任务A混在一起就会出现混乱

  • 第121~136是对B的TSS的初始化设置,其中第一行指定任务B的入口地址,当任务切换到任务B时,就会转到第303行任务B的函数执行~

  • 第204行是任务切换,同样这里的函数也是用汇编写的,里面就是一个JMP语句:JMP 4*8:0 用来转到任务B所对应的段地址,我们可以看到这是在10s定时器中写的,所以每隔10s就进行任务切换了

  • @总的来说,这段代码的任务就是做一个10s后的任务切换工作,先运行A任务,然后任务切换进入B,一直处于进行hlt()~

 

三、任务切换:任务A和B的互相切换[12b]

>_<" 上面只是实现了从任务A切换到任务B,然后就一直处于hlt()了~这里我们只要简单的改一下任务B的函数就能实现任务的切回了:

复制代码
 1 void task_b_main(void)
 2 {
 3     struct FIFO32 fifo;
 4     struct TIMER *timer;
 5     int i,fifobuf[128];
 6     
 7     fifo32_init(&fifo,128,fifobuf);
 8     timer=timer_alloc();
 9     timer_init(timer,&fifo,1);
10     timer_settime(timer,500);
11     
12     for(;;){
13         io_cli();
14         if(fifo32_status(&fifo)==0){
15             io_stihlt();
16         }else{
17             i=fifo32_get(&fifo);
18             io_sti();
19             if(i==1){//超时时间为5s
20                 taskswitch3();//返回任务A
21             }
22         }
23     }
24 }
复制代码

PS: 这里在任务B的函数里写了和任务A中相同的变量名,其实是互不影响的~

 

四、多任务:每隔0.02s切换任务 & 变量传递[这里是窗口sht_back][12e]

>_<" 这里把汇编写的taskswitch3();和taskswitch4();合并为一个可以带参数的任务切换函数farjmp(),并声明一个定时器0.02s中断一次用于任务切换,注意这里每次切换后要重新设置定时器,以便下次仍然是0.02s切换一次~,对于参数传递采用的思路是:把sht_back在任务A的时候找一个内存存进去(*((int*)0x0fec)=(int) sht_back;),然后在任务B的时候再从这个内存中读出sht_back的值(sht_back=(struct SHEET *)*((int*)0x0fec);)

1 if (i==2){//每隔0.02s执行一次任务切换
2     farjmp(0,4*8);
3     timer_settime(timer_ts,2);
4 }
  bootpack.c 本次的~

 

五、任务自动切换[12g]

>_<" 真正的多任务是程序本身不知道的情况下进行任务切换!这里直接创建一个mtask.c专门用来处理多任务

复制代码
 1 /* 任务管理相关程序 */
 2 
 3 #include "bootpack.h"
 4 
 5 struct TIMER *mt_timer;
 6 int mt_tr;
 7 
 8 /////////////////////////////////////////////////////////////////////////////////////
 9 //功能:初始化mt_timer和mt_tr的值,并将计数器设定为0.02s,仅此而已
10 //参数:
11 //附加:变量mt_tr实际上代表了TR寄存器,而不需要timer_init是因为在超时的时候不需要向FIFO缓冲区写入数据
12 //接下来,mt_taskswitch函数的功能是按照当前的mt_tr变量的值计算下一个mt_tr的值,将计时器重新设定为0.02s之后并进行任务切换
13 void mt_init(void)
14 {
15     mt_timer = timer_alloc();
16     /* 这里没有必要使用timer_init */
17     timer_settime(mt_timer, 2);
18     mt_tr = 3 * 8;
19     return;
20 }
21 /////////////////////////////////////////////////////////////////////////////////////
22 //功能:按照当前的mt_tr变量的值计算下一个mt_tr的值,将计时器重新设定为0.02s之后并进行任务切换
23 //参数:
24 void mt_taskswitch(void)
25 {
26     if (mt_tr == 3 * 8) {
27         mt_tr = 4 * 8;
28     } else {
29         mt_tr = 3 * 8;
30     }
31     timer_settime(mt_timer, 2);
32     farjmp(0, mt_tr);
33     return;
34 }
复制代码
  • mt_init函数是初始化mt_timer和mt_tr的值,并将计数器设置为0.02s之后,mt_tr代表TR寄存器,而不需要使用timer_init()是因为发生超时时不需要向FIFO写数据~

  • mt_taskswitch是按照当前的mt_tr变量的值计算出下一个mt_tr的值,并设置计时器为0.02s,然后任务切换~

>_<" 这里就必须对timer.c里的定时器中断响应函数inthandler20进行响应的修改了:

复制代码
 1 void inthandler20(int *esp)
 2 {
 3     char ts=0;//标记mt_timer任务切换计时器是否超时,如果直接在else语句直接调用任务切换会出现错误,因为这个中断处理还没完成
 4     struct TIMER *timer;
 5     io_out8(PIC0_OCW2, 0x60);    /* 把IRQ-00信号接受完了的信息通知给PIC */
 6     timerctl.count++;
 7     if (timerctl.next > timerctl.count) {//如果下一个还没计数完毕就直接返回
 8         return;
 9     }
10     timer = timerctl.t0; //把最前面的地址赋址给timer
11     for (;;) {
12         if (timer->timeout > timerctl.count) {
13             break;
14         }//从前往后遍历,一旦发现有计时未完成的计时器就跳出循环
15         /*除了上面的情况,都是定时已达的定时器*/
16         timer->flags = TIMER_FLAGS_ALLOC;
17         if(timer!=mt_timer){
18             fifo32_put(timer->fifo, timer->data);
19         }else{
20             ts=1;/* mt_timer超时 */
21         }
22         timer = timer->next;//下一个定时器的地址赋址给timer
23     }
24     timerctl.t0 = timer;//新移位
25     timerctl.next = timer->timeout;//timectl.next设定
26     if(ts!=0){
27         mt_taskswitch();
28     }
29     return;
30 }
复制代码
  • 如果发生超时的是mt_timer的话,不向FIFO写数据,而是置ts为1,最后根据这个标志看是否要进行任务切换~(这里一定不能直接在第20行进行任务切换,因为中断还没进行完毕~)

 

六、效果展示:

>_<" 这里每隔1s就显示一下累计的数据:7370035,下面一行是总的数据~

 

七、代码链接:






本文转自beautifulzzzz博客园博客,原文链接:http://www.cnblogs.com/zjutlitao/p/3993403.html ,如需转载请自行联系原作者

 

相关文章
|
15天前
|
监控 Unix Linux
Linux操作系统调优相关工具(四)查看Network运行状态 和系统整体运行状态
Linux操作系统调优相关工具(四)查看Network运行状态 和系统整体运行状态
30 0