[自制简单操作系统] 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 ,如需转载请自行联系原作者
相关文章
|
8天前
|
运维 监控 Python
自动化运维:使用Python脚本简化日常任务
【10月更文挑战第36天】在数字化时代,运维工作的效率和准确性成为企业竞争力的关键。本文将介绍如何通过编写Python脚本来自动化日常的运维任务,不仅提高工作效率,还能降低人为错误的风险。从基础的文件操作到进阶的网络管理,我们将一步步展示Python在自动化运维中的应用,并分享实用的代码示例,帮助读者快速掌握自动化运维的核心技能。
22 3
|
15天前
|
运维 监控 Linux
自动化运维:如何利用Python脚本优化日常任务##
【10月更文挑战第29天】在现代IT运维中,自动化已成为提升效率、减少人为错误的关键技术。本文将介绍如何通过Python脚本来简化和自动化日常的运维任务,从而让运维人员能够专注于更高层次的工作。从备份管理到系统监控,再到日志分析,我们将一步步展示如何编写实用的Python脚本来处理这些任务。 ##
|
1月前
|
运维 监控 网络安全
自动化运维的魔法:如何用Python简化日常任务
【10月更文挑战第9天】在数字时代的浪潮中,运维人员面临着日益增长的挑战。本文将揭示如何通过Python脚本实现自动化运维,从而提高效率、减少错误,并让运维工作变得更具创造性。我们将探索一些实用的代码示例,这些示例将展示如何自动化处理文件、监控系统性能以及管理服务器配置等常见运维任务。准备好让你的运维工作升级换代了吗?让我们开始吧!
|
1月前
|
运维 应用服务中间件 数据库
自动化运维:使用Ansible简化日常任务
【10月更文挑战第2天】在快速迭代的软件开发周期中,运维工作往往变得重复而繁琐。本文将介绍如何使用Ansible这一强大的自动化工具来简化日常任务,从而提升效率并减少人为错误。从基础配置到复杂部署,我们将一步步展示如何通过编写简单的Playbook来实现自动化管理。
68 3
|
1月前
|
存储 运维 监控
自动化运维:使用Shell脚本简化日常任务
【9月更文挑战第35天】在IT运维的日常工作中,重复性的任务往往消耗大量的时间。本文将介绍如何通过编写简单的Shell脚本来自动化这些日常任务,从而提升效率。我们将一起探索Shell脚本的基础语法,并通过实际案例展示如何应用这些知识来创建有用的自动化工具。无论你是新手还是有一定经验的运维人员,这篇文章都会为你提供新的视角和技巧,让你的工作更加轻松。
52 2
|
2月前
|
运维 Ubuntu 应用服务中间件
自动化运维:使用Ansible进行配置管理和任务自动化
【9月更文挑战第27天】在现代IT基础设施中,自动化运维是提高效率、减少人为错误和确保系统一致性的关键。本文将介绍如何使用Ansible,一个流行的开源IT自动化工具,来简化日常的运维任务。我们将探索Ansible的核心概念,包括它的架构、如何安装和使用它,以及一些实际的使用案例。无论你是新手还是有经验的运维专家,这篇文章都会提供有价值的见解和技巧,以帮助你更好地利用Ansible实现自动化。
|
2月前
|
运维 监控 Python
自动化运维:使用Python脚本简化日常任务
【9月更文挑战第23天】在本文中,我们将探索如何通过编写Python脚本来自动化常见的系统管理任务,从而提升效率并减少人为错误。文章将介绍基础的Python编程概念、实用的库函数,以及如何将这些知识应用于创建有用的自动化工具。无论你是新手还是有经验的系统管理员,这篇文章都将为你提供有价值的见解和技巧,帮助你在日常工作中实现自动化。
|
2月前
|
安全 Unix Linux
Unix是一个多用户、多任务的操作系统
Unix是一个多用户、多任务的操作系统
165 3
|
2月前
|
运维 监控 Python
自动化运维:使用Python脚本实现日常任务
【9月更文挑战第24天】在现代的软件开发周期中,运维工作扮演着至关重要的角色。本文将介绍如何利用Python编写简单的自动化脚本,来优化和简化日常的运维任务。从备份数据到系统监控,Python的易用性和强大的库支持使其成为自动化运维的首选工具。跟随这篇文章,你将学习如何使用Python编写自己的自动化脚本,提高运维效率,减少人为错误,并最终提升整个开发流程的质量。
|
2月前
|
存储 运维 监控
自动化运维的崛起:如何利用脚本简化日常任务
【9月更文挑战第21天】在快速发展的IT行业中,自动化运维不再是可选项,而是提升效率、减少人为错误的必由之路。本文将深入探讨自动化运维的重要性,并通过一个实际的脚本示例,展示如何将日常重复的运维任务自动化,从而释放运维人员的时间,让他们专注于更有价值的工作。