《30天自制操作系统》16_day_学习笔记
harib13a:
今天我们要继续折腾多任务,任务的高效管理是操作系统的一个重要的任务。在今天,我们将为系统创建更加完善的任务管理系统,其中包括优先级,任务等级等。
1、任务管理结构体
#define MAX_TASKS 1000 /* 最大任务数量 */
#define TASK_GDT0 3 /* 任务块在GDT中的初始位置:从GDT的3号段开始 */
struct TSS32 {//任务状态段,这个在前面已经提到过,这里再介绍一下
// 26个int成员,104字节
int backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3; //与任务设置相关的信息(任务切换时,除backlink,都不会被写入)
int eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;//32位寄存器;eip任务返回时,找到返回的地址
int es, cs, ss, ds, fs, gs;//16位寄存器
int ldtr, iomap; //有关任务设置的信息。任务切换时CPU不写;ldtr = 0; iomap = 0x4000_0000
};
struct TASK {//任务结构体
//sel用来存储GDT中的编号
//flag表示任务的状态
//tss是任务状态段
int sel, flags;
struct TSS32 tss;
};
struct TASKCTL { //任务管理结构体
int running; /* 表示正在运行中的任务数量 */
int now; /* 记录当前正在运行的是哪一个任务 */
struct TASK *tasks[MAX_TASKS];//记录正在运行中的任务的地址
struct TASK tasks0[MAX_TASKS];//1000个任务
};
2、初始化任务管理结构体
//初始化TASKCTL;初始化一个TASK为flag=2(正在运行)
//返回:返回正在运行这个任务的地址
struct TASKCTL *taskctl;
struct TIMER *task_timer;
struct TASK *task_init(struct MEMMAN *memman)
{
int i;
struct TASK *task; //临时任务变量
struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT; //初始化任务段描述的GDT
taskctl = (struct TASKCTL *) memman_alloc_4k(memman, sizeof (struct TASKCTL));//分配任务管理结构体
for (i = ; i < MAX_TASKS; i++) {
taskctl->tasks0[i].flags = ; //所有任务的flag置0 (未使用)
taskctl->tasks0[i].sel = (TASK_GDT0 + i) * ;//任务在GDT中的编号
set_segmdesc(gdt + TASK_GDT0 + i, , (int) &taskctl->tasks0[i].tss, AR_TSS32);//1000个任务按照编号写入GDT中
}
task = task_alloc(); //临时任务分配
task->flags = ; /* 临时任务为正在运行中flag=2 */
taskctl->running = ; //正在运行中任务数为1
taskctl->now = ; //当前运行任务编号为0(数组从0开始的)
taskctl->tasks[] = task; //赋值给TASKCTK的第一个任务
load_tr(task->sel); //加载到TR寄存器中,让该任务运行
task_timer = timer_alloc();//定时器设置
timer_settime(task_timer, );
return task;
}
3、创建初始化一个任务结构的函数
/* 这个函数做的事,就是把TASKCTL中的任务数组task0[]
的下一个没有使用的任务的任务状态段赋值,初始化,并置flag=1正在使用*/
struct TASK *task_alloc(void)
{
int i;
struct TASK *task;//临时的任务结构体
for (i = ; i < MAX_TASKS; i++) { //按顺序查找taskctl中的所有任务
if (taskctl->tasks0[i].flags == ) {//找到第一个没有被分配的任务
task = &taskctl->tasks0[i]; //赋值给临时变量task
task->flags = ; /* 正在使用的标识 */
task->tss.eflags = 0x00000202; /* IF = 1; */
task->tss.eax = ; /* 其他的值都初始化为0 */
task->tss.ecx = ;
task->tss.edx = ;
task->tss.ebx = ;
task->tss.ebp = ;
task->tss.esi = ;
task->tss.edi = ;
task->tss.es = ;
task->tss.ds = ;
task->tss.fs = ;
task->tss.gs = ;
task->tss.ldtr = ;
task->tss.iomap = 0x40000000;
return task;
}
}
return ; /* 初始化成功 */
}
4、任务运行函数task_run
//这个函数做的事:将参数task的flag=2运行中,再将改任务的地址放到TASKCTL的tasks[]中
void task_run(struct TASK *task)
{
task->flags = ; /* task的flag=2 正在运行 */
taskctl->tasks[taskctl->running] = task; //task的地址放到ctl的tasks[]中
taskctl->running++; //运行中任务计数+1
return;
}
5、最后,任务切换task_switch()
//将now指向的运行中的任务向后移动一个
void task_switch(void)
{
timer_settime(task_timer, );//任务切换的定时器
if (taskctl->running >= ) { //运行中的任务数>2
taskctl->now++; //now指向下一个运行中的任务
if (taskctl->now == taskctl->running) {
taskctl->now = ; //到达了尾部,又回到开始
}
farjmp(, taskctl->tasks[taskctl->now]->sel);//JMP到now指向的新的任务的GDT
}
return;
}
harib13b:
前面我们已经完成了任务管理系统的任务初始化,创建等工作。下面我们继续完善,添加任务删除(休眠)操作task_sleep.
//mtask.c
void task_sleep(struct TASK *task)
{
int i;
char ts = ;
if (task->flags == ) { /* 如果指定的任务处于唤醒状态 */
if (task == taskctl->tasks[taskctl->now]) {//要休眠的是正在运行的任务
ts = ; /* 让自己休眠,稍后需要进行任务切换 */
}
for (i = ; i < taskctl->running; i++) {/*寻找task所在的位置 */
if (taskctl->tasks[i] == task) {
/* task的位置找到了 */
break;
}
}
taskctl->running--;
if (i < taskctl->now) {
taskctl->now--; /* 正在运行的前移一个单位 */
}
for (; i < taskctl->running; i++) {//所有活跃的任务迁移一个单位
taskctl->tasks[i] = taskctl->tasks[i + ];
}
task->flags = ; /* 不工作的状态 */
if (ts != ) { //需要休眠的是正在运行的任务(自己)进行任务切换
if (taskctl->now >= taskctl->running) {
/* now的值到了最后一个,跳到最前面的。 */
taskctl->now = ;
}
//跳到GDT相应的段号。
farjmp(, taskctl->tasks[taskctl->now]->sel);
}
}
return;
}
接下来是FIFO中写入数据时将任务唤醒的功能,添加用于记录唤醒任务的信息成员。
注 意:这里是有数据写入的时候就唤醒任务。
//bootpack.h
struct FIFO32 {
int *buf;
int p, q, size, free, flags;
struct TASK *task;//写入数据时,需要唤醒的任务。
};
//fifo.c
void fifo32_init(struct FIFO32 *fifo, int size, int *buf, struct TASK *task)
/* FIFO缓冲区的初始化 */
{
fifo->size = size;
fifo->buf = buf;
fifo->free = size;
fifo->flags = ;
fifo->p = ; /* 写入的位置 */
fifo->q = ; /* 读取的位置 */
fifo->task = task; /* 写入数据是需要唤醒的任务 */
return;
} int fifo32_put(struct FIFO32 *fifo, int data)
/* FIFO中写入数据 */
{
if (fifo->free == ) {//没有空闲的空间了
fifo->flags |= FLAGS_OVERRUN;
return -; //返回一个错误
}
fifo->buf[fifo->p] = data;
fifo->p++;
if (fifo->p == fifo->size) {
fifo->p = ;
}
fifo->free--;
if (fifo->task != ) { //有要唤醒的任务
if (fifo->task->flags != ) { /* 任务不是活跃状态 */
task_run(fifo->task); /* task_run()唤醒 */
}
}
return ;
}
harib13c:
增加窗口的数量,增加了B0、B1、B2三个任务窗口,它们的设定相同。
void HariMain(void)
{ //....
unsigned char *buf_back, buf_mouse[], *buf_win, *buf_win_b;
struct SHEET *sht_back, *sht_mouse, *sht_win, *sht_win_b[];
struct TASK *task_a, *task_b[];
//....
/* sht_win_b */
for (i = ; i < ; i++) {
sht_win_b[i] = sheet_alloc(shtctl);
buf_win_b = (unsigned char *) memman_alloc_4k(memman, * );
sheet_setbuf(sht_win_b[i], buf_win_b, , , -);
sprintf(s, "task_b%d", i);
make_window8(buf_win_b, , , s, );
task_b[i] = task_alloc();
task_b[i]->tss.esp = memman_alloc_4k(memman, * ) + * - ;
task_b[i]->tss.eip = (int) &task_b_main;
task_b[i]->tss.es = * ;
task_b[i]->tss.cs = * ;
task_b[i]->tss.ss = * ;
task_b[i]->tss.ds = * ;
task_b[i]->tss.fs = * ;
task_b[i]->tss.gs = * ;
*((int *) (task_b[i]->tss.esp + )) = (int) sht_win_b[i];
task_run(task_b[i]);
}
//....
}
harib13d:
到这里,任务是以相同的速度运行的,任务切换时间都为0.02s,下面我们通过设定不同的任务切换间隔来设定任务的优先级。
1、修改任务结构体TASK
//bootpack.h
struct TASK {
int sel, flags; /* sel偼GDT偺斣崋偺偙偲 */
int priority; //这里的优先级实际上是设定了任务切换的时间。
struct TSS32 tss;
};
2、任务的相应的函数mtask.c
/* mtask.c节选 */
#include "bootpack.h" struct TASKCTL *taskctl;
struct TIMER *task_timer;
struct TASK *task_init(struct MEMMAN *memman)
{ //...
task->priority = ; /* 0.02s */
//...
task_timer = timer_alloc();//设定任务切换的时间的定时器
timer_settime(task_timer, task->priority);
return task;
} void task_run(struct TASK *task, int priority)
{
if (priority > ) {
//初始化任务的时候,加入了设定优先级的参数。
task->priority = priority;
}
if (task->flags != ) {
task->flags = ; /* 摦嶌拞儅乕僋 */
taskctl->tasks[taskctl->running] = task;
taskctl->running++;
}
return;
} void task_switch(void)
{ //.....
//定时器时间超时才进行任务的切换
timer_settime(task_timer, task->priority);
if (taskctl->running >= ) {
farjmp(, task->sel);
}
return;
}
3、改写fifo.c中的任务唤醒
int fifo32_put(struct FIFO32 *fifo, int data)
/* FIFO傊僨乕僞傪憲傝崬傫偱拁偊傞 */
{ //.....
fifo->free--;
if (fifo->task != ) {
if (fifo->task->flags != ) { /* 需要唤醒的任务处于非活跃状态 */
task_run(fifo->task, ); /* 唤醒任务,定时器为0 */
}
}
return ;
}
harib13e:
我们继续来折腾优先级吧(看上图),我们将任务的优先级设计成上面的结构,最上层的LEVEL0中只要存在哪怕一个任务,则完全忽略LEVEL1和LEVEL2中的任务,只在LEVEL0中进行任务的切换。当LEVEL0中的人物全部休眠,或者全部降到下层的LEVEL,才轮到LEVEL1的任务进行切换。当LEVLE0和LEVEL1中都没有任务的时候才轮到LEVEL2出场。下面我们来看看具体的做法。
1、修改任务结构体的定义
//bootpack.h
//对于每个LEVEL我们最多允许创建100个任务,一共10个LEVEL
#define MAX_TASKS 1000 /* 任务总数 */
#define TASK_GDT0 3 /* TSS放到GDT的3号段 */
#define MAX_TASKS_LV 100 //任务指针
#define MAX_TASKLEVELS 10 //任务级别数量
struct TSS32 {
int backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3;
int eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;
int es, cs, ss, ds, fs, gs;
int ldtr, iomap;
};
struct TASK {
int sel, flags; /* sel用来存放GDT的编号*/
int level, priority;
struct TSS32 tss;
};
struct TASKLEVEL {
int running; /*正在运行的任务的数量 */
int now; /* 记录当前运行的任务*/
struct TASK *tasks[MAX_TASKS_LV];//100个任务指针
};
struct TASKCTL {
int now_lv; /* 当前活动中的LEVEL */
char lv_change; /* 标志:下次切换任务时,是否需要改变LEVEL */
struct TASKLEVEL level[MAX_TASKLEVELS];//LEVEL一共10级
struct TASK tasks0[MAX_TASKS]; //任务总数
};
2、我们来写几个用于操作struct TASKLEVEL的函数
//mtask.c
struct TASK *task_now(void)
{ //用来返回现在活动中的TASK的地址
struct TASKLEVEL *tl = &taskctl->level[taskctl->now_lv];
return tl->tasks[tl->now];
}
void task_add(struct TASK *task)
{ //向TASKLEVEL中添加一个任务
struct TASKLEVEL *tl = &taskctl->level[task->level];//当前任务的TASKLEVEL
tl->tasks[tl->running] = task;//把任务task添加到task后面
tl->running++; //正在运行的数量+1
task->flags = ; /* 活动中的标识 */
return;
}
void task_remove(struct TASK *task)
{ //从TASKLEVLE中删除一个任务
int i;
struct TASKLEVEL *tl = &taskctl->level[task->level];//获取要删除task的TASKLEVEL /* task的位置 */
for (i = ; i < tl->running; i++) {
if (tl->tasks[i] == task) {
/* 找到了task出去 */
break;
}
}
tl->running--;
if (i < tl->now) {
tl->now--; /* 需要删除的task在now的前面 */
}
if (tl->now >= tl->running) {
/* now到了任务的最后一个,又跳到头部 */
tl->now = ;
}
task->flags = ; /* 任务标识=1 */
/* 都往前移动一个 */
for (; i < tl->running; i++) {
tl->tasks[i] = tl->tasks[i + ];
}
return;
}
void task_switchsub(void)
{ //用来在任务切换时,决定接下来切换到那个LEVEL
int i;
/* 寻找最上层的LEVEL */
for (i = ; i < MAX_TASKLEVELS; i++) {
if (taskctl->level[i].running > ) {
break; /* 找到了 */
}
}
taskctl->now_lv = i;
taskctl->lv_change = ;
return;
}
3、改写任务TASK操作函数
struct TASK *task_init(struct MEMMAN *memman)
{ //初始化函数,开始只有LEVEL0中有任务
int i;
struct TASK *task;
struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT;
taskctl = (struct TASKCTL *) memman_alloc_4k(memman, sizeof (struct TASKCTL));
for (i = ; i < MAX_TASKS; i++) { //初始化TASKCTL中的每一个任务,并写到GDT相应的段号
taskctl->tasks0[i].flags = ;
taskctl->tasks0[i].sel = (TASK_GDT0 + i) * ;//GDT段号
set_segmdesc(gdt + TASK_GDT0 + i, , (int) &taskctl->tasks0[i].tss, AR_TSS32);
}
for (i = ; i < MAX_TASKLEVELS; i++) { //初始化LEVEL
taskctl->level[i].running = ;
taskctl->level[i].now = ;
}
task = task_alloc();//在LEVLE0中初始化一个任务
task->flags = ; /* 活动中 */
task->priority = ; /* 0.02s */
task->level = ; /* LEVEL的值 */
task_add(task); //添加到TASKLEVEL中
task_switchsub(); /* 初始化LEVLE */
load_tr(task->sel); //初始化TR寄存器
task_timer = timer_alloc();//定时器
timer_settime(task_timer, task->priority);
return task;
}
void task_run(struct TASK *task, int level, int priority)
{ //在参数中指定LEVEL的值
if (level < ) {
level = task->level; /* 不改变LEVEL(不能为负) */
}
if (priority > ) { //优先级设置
task->priority = priority;
}
if (task->flags == && task->level != level) { /* 改变活动中的LEVEL */
task_remove(task); /* 将TASK移除,flag=1执行下面的任务 */
}
if (task->flags != ) {
/* 唤醒任务 */
task->level = level;
task_add(task); //把任务添加到活动中
}
taskctl->lv_change = ; /* 下次任务切换时检查LEVEL */
return;
}
void task_sleep(struct TASK *task)
{ //调用task_remove简化代码
struct TASK *now_task;
if (task->flags == ) {
/* 如果处于活动状态 */
now_task = task_now();//当前运行的任务地址
task_remove(task); /* 执行后FLAG=1 */
if (task == now_task) {
/* 让自己休眠,需要进行切换 */
task_switchsub();
now_task = task_now(); /* 设定后,获取当前的任务 */
farjmp(, now_task->sel);//任务切换,GDT号
}
}
return;
}
4、最后修改fifo.c任务唤醒
int fifo32_put(struct FIFO32 *fifo, int data)
/* FIFO接收到数据时,将任务唤醒 */
{ //....
fifo->free--;
if (fifo->task != ) { //fifo中有需要唤醒的任务
if (fifo->task->flags != ) { /* 唤醒到活跃状态 */
task_run(fifo->task, -, ); /* 修改了RUN的参数传递调用 */
}
}
return ;
}
《30天自制操作系统》16_day_学习笔记的更多相关文章
- 《30天自制操作系统》学习笔记--Mac下工具的使用
现在来介绍官网上下的工具怎么用首先是官网地址,书上有个注释上有:hrb.osask.jp 翻译成中文大概是这个样子滴. 上面有两个文件可以下载,一个是工具,一个是工具的源代码,很好的学习资料 下面把工 ...
- 《30天自制操作系统》学习笔记--Mac环境搭建
弄了三天了,终于弄好了,先说结果,就是作者在网站上放了os x的工具(hrb.osask.jp,也有linux下的工具,可以自己去下载),也就是说我白忙活了三天... 再说一下这几天都干啥了,主要是想 ...
- 《30天自制操作系统》学习笔记--番外篇之Mac环境下的工具介绍
这几天又有点不务正业了,书也没看,一直在搞这个破环境,尝试各种做法,网上各种垃圾信息,浪费了很多时间,说的基本都是废话,不过还是找到了一些,赶紧写下来,不然这个过几天又忘了 首先是环境,我用的是Max ...
- 《30天自制操作系统》读书笔记(5) GDT&IDT
梳理项目结构 项目做到现在, 前头的好多东西都忘了, 还是通过Makefile重新理解一下整个项目是如何编译的: 现在我们拥有这么9个文件: ipl10.nas InitialProgramLo ...
- 《30天自制操作系统》读书笔记(3) 引入C语言
这一次的学习相当曲折, 主要是因为粗心, Makefile里面的错误导致了文件生成出现各种奇奇怪怪的问题, 弄得心力交瘁, 因此制作过程还是尽量按着作者的路子来吧. 作者提供的源码的注释在中文系统下是 ...
- 《30天自制操作系统》读书笔记(2)hello, world
让系统跑起来 要写一个操作系统,我们首先要有一个储存系统的介质,原版书似乎是06年出版的,可惜那时候没有电脑,没想到作者用的还是软盘,现在的电脑谁有软驱?不得已我使用一张128M的SD卡来代替,而事实 ...
- 30天自制操作系统第九天学习笔记(u盘软盘双启动版本)
暑假学习小日本的那本书:30天自制操作系统 qq交流群:122358078 ,更多学习中的问题.资料,群里分享 environment:开发环境:ubuntu 第九天的课程已学完,确实有点不想写 ...
- 从你的u盘启动:30天自制操作系统第四天u盘启动学习笔记
暑假学习小日本的那本书:30天自制操作系统 qq交流群:122358078 ,更多学习中的问题.资料,群里分享 developing environment:ubuntu 关于u盘启动自己做的操 ...
- 30天自制操作系统第八天学习笔记(u盘软盘双启动版本)
暑假学习小日本的那本书:30天自制操作系统 qq交流群:122358078 ,更多学习中的问题.资料,群里分享 environment:开发环境:ubuntu 第八天的学习思考: 关于鼠标是怎么 ...
- 《30天自制操作系统》笔记(03)——使用Vmware
<30天自制操作系统>笔记(03)——使用Vmware 进度回顾 在上一篇,实现了用IPL加载OS程序到内存,然后JMP到OS程序这一功能:并且总结出下一步的OS开发结构.但是遇到了真机测 ...
随机推荐
- BZOJ 1045 题解
1045: [HAOI2008] 糖果传递 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 3502 Solved: 1623[Submit][Sta ...
- Code[VS] 2152 滑雪题解
Code[VS] 2152 滑雪题解 题目描述 Description trs喜欢滑雪.他来到了一个滑雪场,这个滑雪场是一个矩形,为了简便,我们用r行c列的矩阵来表示每块地形.为了得到更快的速度,滑行 ...
- wind.onload和$(document).ready()的区别例示
例子: <html> <script type="text/javascript" src="jquery-1.7.1.min.js"> ...
- 你用java的swing可以做出这么炫的mp3播放器吗?
这个mp3播放器是基于java的swing编写的,我认为界面还是可以拿出来和大家看一看评一评. 先说说创作的初衷,由于前段时间工作不是很忙,与其闲着,还不如找一些东西来给自己捣腾捣腾,在 之前写的 j ...
- linux下获取本机IP
转载:http://blog.chinaunix.net/uid-20593763-id-1620213.html 源代码级Unix/Linux 通用网卡IP地址获取方法 在Unix和Linux系统下 ...
- 3Sum
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all un ...
- nginx基于IP的虚拟主机
知识点: server的语法: upstream语法: upstream中192.168.100.1不是ip只是个标识,只要和下面的proxy_pass 对应即可. 基于IP的虚拟主机: listen ...
- Js练笔——用循环和递归实现追踪对象深度(循环引用关系不考虑)
function reobs(obj){ //返回对象中对象属性组成的数组 var a=[]; var b=[]; for(it in obj){ a.push(it); } for(var i=0; ...
- WIN8 MTK驱动不能安装解决办法
1.把鼠标移动到桌面最右下角的位置会出来一个侧边栏,按那个齿轮就是“设置”,会出来个菜单,选择最下边的“更多电脑设置” 注:也可以按快捷键“WIN+I” 2.选择“常规”→“高级启动”→”立即重启“ ...
- Maven3下的java web项目
咱们使用Maven3构建一个j2ee项目,项目的成果是一个war包,只需把它部署在服务器上,就可以使用浏览器访问. 具体详细信息 参考 http://www.mossle.com/docs/mave ...