[自制简单操作系统] 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 ,如需转载请自行联系原作者
相关文章
|
4月前
|
Shell Linux
shell 脚本常用于自动化执行文件备份与压缩的任务
shell 脚本常用于自动化执行文件备份与压缩的任务
31 1
|
7月前
|
运维 Cloud Native Go
Ansible自动化:简化你的运维任务
Ansible自动化:简化你的运维任务
62 0
|
4月前
|
Unix Shell Linux
在Unix/Linux操作系统中,Shell脚本广泛用于自动化任务
在Unix/Linux操作系统中,Shell脚本广泛用于自动化任务
26 2
|
7天前
|
IDE 安全 数据管理
Visual Basic for Applications (VBA):自动化Office任务
【4月更文挑战第27天】**Visual Basic for Applications (VBA)** 是Microsoft Office中的宏语言,用于自动化Excel、Word、Outlook等应用的任务。VBA基于Visual Basic,通过编写代码控制应用行为,提升效率。文章介绍了VBA环境、基础语法,展示了在Excel(数据处理)、Word(文档管理)和Outlook(邮件自动化)中的应用。强调安全性和调试重要性,学习VBA能增强Office软件的功能,实现高效自动化工作流程。
|
2月前
|
运维 监控 Linux
linux脚本自动化运维任务
Linux自动化运维通过脚本提升效率,涵盖服务管理(启停服务、异常恢复)、系统监控(资源警报)、日志管理(清理分析)、备份恢复、补丁更新、自动化部署(如Ansible)、网络管理、定时任务(cron)和故障排查。结合shell、Python及工具,形成高效运维体系。
28 3
|
2月前
|
存储 Unix Shell
【简化Cmake编译过程 】编写通用的bash脚本:简化和构建cmake高效自动化任务
【简化Cmake编译过程 】编写通用的bash脚本:简化和构建cmake高效自动化任务
45 0
|
7月前
|
Linux Shell 调度
(三)Linux命令行工具和脚本编程:自动化任务和提高效率
Linux命令行工具和脚本编程是系统管理员和开发人员必备的技能。这些技能不仅可以自动化日常任务,还可以提高工作效率。本文将介绍如何使用Linux命令行工具和Shell脚本编程来自动化任务,并提供一些实用的技巧和示例。
114 1
|
3月前
|
Ubuntu Linux Shell
【Linux操作系统】探秘Linux奥秘:进程与任务管理的解密与实战
【Linux操作系统】探秘Linux奥秘:进程与任务管理的解密与实战
38 0
|
4月前
|
存储 Unix Shell
Python 自动化指南(繁琐工作自动化)第二版:十七、计时、安排任务和启动程序
Python 自动化指南(繁琐工作自动化)第二版:十七、计时、安排任务和启动程序
62 1

热门文章

最新文章