第16天 多任务(2)
16.1 任务管理自动化
上一节已实现两个进程间的连续切换,为试用于现阶段的多进程切换,遂仿照struct SHTCTL写出了struct TASKCTL。
/* bootpack.h */
#define MAX_TASKS 1000 /*最大任务数量*/
#define TASK_GDT0 3 /*定义从GDT的几号开始分配给TSS */
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的编号*/
struct TSS32 tss;
};
struct TASKCTL{
int running;/*正在运行的任务数量*/
int now;/*记录当前正在运行的是哪个任务*/
struct TASK *tasks[MAX_TASKS];
struct TASK tasks0[MAX_TASKS];
};
上述结构体以构建完成,下面将对其进行初始化等操作。
/* matsk.c */
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;
taskctl = (struct TASKCTL *)memman_alloc_4k(memman, sizeof(struct TASKCTL));
for(i = 0; i < MAX_TASKS; i++){
taskctl->tasks0[i].flags = 0;
taskctl->tasks0[i].sel = (TASK_GDT0 + i) * 8;
set_segmdesc(gdt + TASK_GDT0 + i, 103, (int) &taskctl->tasks0[i].tss, AR_TSS32);
}
task = task_alloc();
task->flags = 2;/*活动中标志*/
taskctl->running = 1;
taskctl->now = 0;
taskctl->tasks[0] = task;
load_tr(task->sel);
task_timer = timer_alloc();
timer_settime(task_timer, 2);
return task;
}
// 在taskctl中找到一个未被使用的任务,将其初始化并返回
struct TASK *task_alloc(void){
int i;
struct TASK *task;
for(i = 0; i < MAX_TASKS; i++){
if(taskctl->tasks0[i].flags == 0){
task = &taskctl->tasks0[i];
task->flags=1;/*正在使用的标志*/
task->tss.eflags = 0x00000202; /* IF = 1; */
task->tss.eax = 0; /*这里先置为0*/
task->tss.ecx = 0;
task->tss.edx = 0;
task->tss.ebx = 0;
task->tss.ebp = 0;
task->tss.esi = 0;
task->tss.edi = 0;
task->tss.es = 0;
task->tss.ds = 0;
task->tss.fs = 0;
task->tss.gs = 0;
task->tss.ldtr = 0;
task->tss.iomap = 0x40000000;
return task;
}
}
return 0; /*全部正在使用*/
}
//运行某个任务,将其添加至taskctl中,并更新running
void task_run(struct TASK *task){
task->flags = 2;/*活动中标志*/
taskctl->tasks[taskctl->running] = task;
taskctl->running++;
return;
}
//进程转换
void task_switch(void){
timer_settime(task_timer, 2);
if(taskctl->running >= 2){
taskctl->now++;
if(taskctl->now == taskctl->running)
taskctl->now=0;
farjmp(0, taskctl->tasks[taskctl->now]->sel);
}
}
按照上述在mtask.c中进行的操作,变更timer.c中相关操作,最终在bootpack.c中的进行调用上述函数。
void HariMain(void)
{
(中略)
/* 创建任务 */
struct TASK *task_b;
(中略)
task_init(memman);
task_b = task_alloc();
task_b->tss.esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024 - 8;
task_b->tss.eip = (int) &task_b_main;
task_b->tss.es = 1 * 8;
task_b->tss.cs = 2 * 8;
task_b->tss.ss = 1 * 8;
task_b->tss.ds = 1 * 8;
task_b->tss.fs = 1 * 8;
task_b->tss.gs = 1 * 8;
*((int *) (task_b->tss.esp + 4)) = (int) sht_back;
task_run(task_b);
for (;;) {
(中略)
}
}
}
16.2 让任务休眠
如果把任务
A
从tasks 中删掉,只保留任务
B
,那任务
B就可以全力以赴工作了。像这样将一个任务从 tasks
中删除的操作,用多任务中的术语来说叫做
“
休眠
”
(
sleep
)。
//task休眠:检查是否为当前正在运行的进程,如果是则还额外需要执行任务转换操作,
//否则需要考虑在当前运行的进程前面还是后面
void task_sleep(struct TASK *task){
int i;
char ts = 0;//记录当前task的状态
if(task->flags == 2){/*该任务处于唤醒状态*/
if(task == taskctl->tasks[taskctl->now])
ts = 1; /*让自己休眠的话,稍后需要进行任务切换*/
}
for(i = 0; i < taskctl->running; i++){//寻找task所在的位置
if(taskctl->tasks[i] == task)
break;
}
taskctl->running--;
if(i < taskctl->now)
taskctl->now--; /*需要移动成员,要相应地处理*/
for(; i < taskctl->running; i++)
taskctl->tasks[i] = taskctl->tasks[i+1];
task->flags = 1; /*不工作的状态*/
if(ts != 0){/*任务切换*/
if(taskctl->now >= taskctl->running)/*如果now的值出现异常,则进行修正*/
taskctl->now = 0;
farjmp(0, taskctl->tasks[taskctl->now]->sel);
}
return;
}
接下来是当
FIFO中写入数据的时候将任务唤醒的功能。首先,要在FIFO的结构定义中,添加用于记录要唤醒任务的信息的成员。然后改写fifo32_init,让它可以在参数中指定一个任务。如果不想使用任务自动唤醒功能的话,只要将task置为0即可。
接着,实现当向
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->buf = buf;
fifo->p = 0; /* 下一个写入地址 */
fifo->q = 0; /* 下一个读出地址 */
fifo->size = size;/* 缓冲区大小 */
fifo->free = size;/* 可用缓冲区大小 */
fifo->flags = 0;
fifo->task = task;/*这里*/
return;
}
/* 向fifo中写入data */
int fifo32_put(struct FIFO32 *fifo, int data){
if(fifo->free==0){/* 没有可用的缓冲区,设置flags并返回-1 */
fifo->flags|=FLAGS_OVERRUN;
return -1;
}
fifo->free--;
fifo->buf[fifo->p]=data;
fifo->p++;
if(fifo->p == fifo->size)fifo->p=0;
if(fifo->task != 0){/*从这里开始*/
if(fifo->task->flags != 2)/*如果任务处于休眠状态*/
task_run(fifo->task);/*将任务唤醒*/
}/*结束*/
return 0;
}
16.3 增加窗口数量
新建多个窗口,
形成任务
A
、任务
B0
、任务
B1
和任务
B2的格局。任务
B0
~
B2
各自拥有自己的窗口,它们的功能都一样,即进行计数。
/* bootpack */
#include "bootpack.h"
#include <stdio.h>
void make_window8(unsigned char *buf, int xsize, int ysize, char *title, char act);//根据act进行调整各窗口的显示颜色
void putfonts8_asc_sht(struct SHEET *sht, int x, int y, int c, int b, char *s, int l);
void make_textbox8(struct SHEET *sht, int x0, int y0, int sx, int sy, int c);
void task_b_main(struct SHEET *sht_win_b);
void HariMain(void)
{
(中略)
unsigned char *buf_back, buf_mouse[256], *buf_win, *buf_win_b;
struct SHEET *sht_back, *sht_mouse, *sht_win, *sht_win_b[3];
struct TASK *task_a, *task_b[3];
struct TIMER *timer;
(中略)
init_palette();
shtctl = shtctl_init(memman, binfo->vram, binfo->scrnx, binfo->scrny);
task_a = task_init(memman);
fifo.task = task_a;
/* sht_back */
sht_back = sheet_alloc(shtctl);/* 分配图层 */
buf_back = (unsigned char *) memman_alloc_4k(memman, binfo->scrnx * binfo->scrny);/* 分配内存 */
sheet_setbuf(sht_back, buf_back, binfo->scrnx, binfo->scrny, -1); /* 设置图层缓冲区 */
init_screen (buf_back, binfo->scrnx, binfo->scrny);/* 绘制背景 */
/* sht_win_b */
for (i = 0; i < 3; i++) {
sht_win_b[i] = sheet_alloc(shtctl);/* 分配图层 */
buf_win_b = (unsigned char *) memman_alloc_4k(memman, 144 * 52);/* 分配内存 */
sheet_setbuf(sht_win_b[i], buf_win_b, 144, 52, -1); /* 设置图层缓冲区 */
sprintf(s, "task_b%d", i);
make_window8(buf_win_b, 144, 52, s, 0);/* 绘制窗口 */
task_b[i] = task_alloc();
task_b[i]->tss.esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024 - 8;
task_b[i]->tss.eip = (int) &task_b_main;
task_b[i]->tss.es = 1 * 8;
task_b[i]->tss.cs = 2 * 8;
task_b[i]->tss.ss = 1 * 8;
task_b[i]->tss.ds = 1 * 8;
task_b[i]->tss.fs = 1 * 8;
task_b[i]->tss.gs = 1 * 8;
*((int *) (task_b[i]->tss.esp + 4)) = (int) sht_win_b[i];
task_run(task_b[i]);
}
/* sht_win */
sht_win = sheet_alloc(shtctl);
buf_win = (unsigned char *) memman_alloc_4k(memman, 160 * 52);/* 分配内存 */
sheet_setbuf(sht_win, buf_win, 160, 52, -1); /* 透明色 设置图层缓冲区 */
make_window8(buf_win, 160, 52, "task_a", 1);/* 绘制窗口 */
make_textbox8(sht_win, 8, 28, 144, 16, COL8_FFFFFF);/*输入字符*/
cursor_x = 8;
cursor_c = COL8_FFFFFF;
timer = timer_alloc();
timer_init(timer, &fifo, 1);
timer_settime(timer, 50);
/* sht_mouse */
sht_mouse = sheet_alloc(shtctl);
sheet_setbuf(sht_mouse, buf_mouse, 16, 16, 99);
init_mouse_cursor8(buf_mouse, 99);
mx = (binfo->scrnx - 16) / 2; /* 屏幕中心坐标 */
my = (binfo->scrny - 28 - 16) / 2;
/* 调整背景、鼠标和窗口的显示位置 */
sheet_slide(sht_back, 0, 0);
sheet_slide(sht_win_b[0], 168, 56);
sheet_slide(sht_win_b[1], 8, 116);
sheet_slide(sht_win_b[2], 168, 116);
sheet_slide(sht_win, 8, 56);
sheet_slide(sht_mouse, mx, my);
/* 调整背景、鼠标和窗口的图层高度 */
sheet_updown(sht_back, 0);
sheet_updown(sht_win_b[0], 1);
sheet_updown(sht_win_b[1], 2);
sheet_updown(sht_win_b[2], 3);
sheet_updown(sht_win, 4);
sheet_updown(sht_mouse, 5);
sprintf(s, "(%3d, %3d)", mx, my);
putfonts8_asc_sht(sht_back, 0, 0, COL8_FFFFFF, COL8_008484, s, 10);
sprintf(s, "memory %dMB free : %dKB",
memtotal / (1024 * 1024), memman_total(memman) / 1024);
putfonts8_asc_sht(sht_back, 0, 32, COL8_FFFFFF, COL8_008484, s, 40);
for (;;) {
io_cli();
if (fifo32_status(&fifo) == 0) {
task_sleep(task_a);
io_sti();
} else {
i = fifo32_get(&fifo);
io_sti();
if (256 <= i && i <= 511) {/* 键盘数据 */
(中略)
} else if (512 <= i && i <= 767) {/* 鼠标数据 */
(中略)
} else if (i <= 1) {/* 光标用计时器 */
if( i != 0){
timer_init(timer, &fifo, 0); /* 设定为0 */
cursor_c = COL8_000000;
}else{
timer_init(timer, &fifo, 1); /* 设定为1 */
cursor_c = COL8_FFFFFF;
}
timer_settime(timer, 50);
boxfill8(sht_win->buf, sht_win->bxsize, cursor_c, cursor_x, 28, cursor_x + 7, 43);
sheet_refresh(sht_win, cursor_x, 28, cursor_x + 8, 44);
}
}
}
}
void make_window8(unsigned char *buf, int xsize, int ysize, char *title, char act)
{
(中略)
char c, tc, tbc;
if (act != 0) {
tc = COL8_FFFFFF;
tbc = COL8_000084;
} else {
tc = COL8_C6C6C6;
tbc = COL8_848484;
}
(中略)
boxfill8(buf, xsize, tbc, 3, 3, xsize - 4, 20 );/*这里*/
(中略)
putfonts8_asc(buf, xsize, 24, 4, tc, title);/*这里*/
(中略)
return;
}
void task_b_main(struct SHEET *sht_win_b){
(中略)
fifo32_init(&fifo, 128, fifobuf, 0);// FIFO结构体初始化
timer_ls = timer_alloc();// 分配一个计时器
timer_init(timer_ls, &fifo, 100);// 计时器初始化
timer_settime(timer_ls, 100);// 设置计时器中断时长
for(;;){
count++;
io_cli();
if(fifo32_status(&fifo) == 0)
io_sti();
else{
i = fifo32_get(&fifo);
io_sti();
if(i == 100){
sprintf(s, "%11d", count - count0);/* 统计间隔1s内该函数运行的次数 */
putfonts8_asc_sht(sht_win_b, 24, 28, COL8_000000, COL8_C6C6C6, s, 11);
count0 = count;
timer_settime(timer_ls, 100);/* 重新设定计时器 */
}
}
}
}
16.4 设定任务优先级(1)
任务
B0
~
B2以同样的速度运行,从公平竞争的角度来说确实不错,不过在某些情 况下,我们需要提升或者降低某个应用程序的优先级。
/* bootpack.h */
struct TASK{
/*flags 0:未被使用状态 1:正在被使用状态 2:唤醒状态*/
int sel, flags;/* sel用来存放GDT的编号*/
int priority;/*任务优先级*/ //这里!
struct TSS32 tss;
};
/* mtask.c */
struct TASK *task_init(struct MEMMAN *memman){
(中略)
task = task_alloc();
task->flags = 2;/*活动中标志*/
task->priority = 2;
(中略)
timer_settime(task_timer, task->priority);
return task;
}
//根据priority调整task的优先级并运行(将其添加至taskctl中,并更新running)
void task_run(struct TASK *task, int priority){
if(priority > 0)
task->priority = priority;
if(task->flags != 2){
task->flags = 2;/*活动中标志*/
taskctl->tasks[taskctl->running] = task;
taskctl->running++;
}
return;
}
//进程转换
void task_switch(void){
struct TASK *task;
taskctl->now++;
if(taskctl->now == taskctl->running)
taskctl->now=0;
task = taskctl->tasks[taskctl->now];
timer_settime(task_timer, task->priority);
if(taskctl->running >= 2){
farjmp(0, task->sel);
}
return;
}
凡是调用task_run函数的位置,都需要添加一个输入参数(priority),在这就不一一说明。
16.5 设定任务优先级(2)
/* bootpack.h */
#define MAX_TASKS_LV 100 /*创建10个等级,每个等级中可有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{
/*flags 0:未被使用状态 1:休眠状态 2:唤醒状态*/
int sel, flags;/* sel用来存放GDT的编号*/
int level, priority;/*level任务等级 priority任务优先级*/
struct TSS32 tss;
};
struct TASKLEVEL{
int running;/*正在运行的任务数量*/
int now;/*记录当前正在运行的是哪个任务*/
struct TASK *tasks[MAX_TASKS_LV];
};
struct TASKCTL{
int now_lv; /*现在活动中的LEVEL */
char lv_change; /*在下次任务切换时是否需要改变LEVEL */
struct TASKLEVEL level[MAX_TASKLEVELS];
struct TASK tasks0[MAX_TASKS];
};
/* mtask.c */
//返回现在活动中的struct TASK的内存地址
struct TASK *task_now(void){
struct TASKLEVEL *tl = &taskctl->level[taskctl->now_lv];
return tl->tasks[tl->now];
}
//向struct TASKLEVEL中添加一个任务
void task_add(struct TASK *task){
struct TASKLEVEL *tl = &taskctl->level[task->level];
tl->tasks[tl->running] = task;
tl->running++;
task->flags = 2;//唤醒状态
return;
}
//从struct TASKLEVEL中删除一个任务
void task_remove(struct TASK *task){
int i;
struct TASKLEVEL *tl = &taskctl->level[task->level];
for(i = 0; i < tl->running; i++){/*寻找task所在的位置*/
if(tl->tasks[i] == task)
break;
}
tl->running--;
if(i < tl->now)/*需要移动成员,要相应地处理 */
tl->now--;
if(tl->now >= tl->running)/*如果now的值出现异常,则进行修正*/
tl->now = 0;
task->flags = 1; /* 休眠中 */
for(; i < tl->running; i++)
tl->tasks[i] = tl->tasks[i + 1];
return;
}
//在任务切换时决定接下来切换到哪个LEVEL
void task_switchsub(void){
int i;
/*寻找最上层的LEVEL */
for(i = 0; i < MAX_TASKLEVELS; i++){
if(taskctl->level[i].running > 0)
break;
}
taskctl->now_lv = i;
taskctl->lv_change = 0;
return;
}
//在memman中分配所有任务内存,并对任务进行初始化
struct TASK *task_init(struct MEMMAN *memman){
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 = 0; i < MAX_TASKS; i++) {
taskctl->tasks0[i].flags = 0;
taskctl->tasks0[i].sel = (TASK_GDT0 + i) * 8;
set_segmdesc(gdt + TASK_GDT0 + i, 103, (int) &taskctl->tasks0[i].tss, AR_TSS32);
}
for(i = 0; i < MAX_TASKLEVELS; i++){
taskctl->level[i].running = 0;
taskctl->level[i].now = 0;
}
task = task_alloc();
task->flags = 2;/*活动中标志*/
task->priority = 2;
task->level = 0;/*最高LEVEL */
task_add(task);
task_switchsub();/* LEVEL设置*/
load_tr(task->sel);
task_timer = timer_alloc();
timer_settime(task_timer, task->priority);
return task;
}
// 在taskctl中找到一个未被使用的任务,将其初始化并返回
struct TASK *task_alloc(void){
int i;
struct TASK *task;
for(i = 0; i < MAX_TASKS; i++){
if(taskctl->tasks0[i].flags == 0){
task = &taskctl->tasks0[i];
task->flags=1;/*正在使用的标志*/
task->tss.eflags = 0x00000202; /* IF = 1; */
task->tss.eax = 0; /*这里先置为0*/
task->tss.ecx = 0;
task->tss.edx = 0;
task->tss.ebx = 0;
task->tss.ebp = 0;
task->tss.esi = 0;
task->tss.edi = 0;
task->tss.es = 0;
task->tss.ds = 0;
task->tss.fs = 0;
task->tss.gs = 0;
task->tss.ldtr = 0;
task->tss.iomap = 0x40000000;
return task;
}
}
return 0; /*全部正在使用*/
}
//根据priority调整task的优先级并运行(将其添加至taskctl中,并更新running)
void task_run(struct TASK *task, int level, int priority){
if(level < 0)/*不改变LEVEL */
level = task->level;
if(priority > 0)
task->priority = priority;
if(task->flags == 2 && task->level != level)/*改变活动中的LEVEL */
task_remove(task); /*这里执行之后flag的值会变为1,于是下面的if语句块也会被执行*/
if(task->flags != 2){/*从休眠状态唤醒的情形*/
task->level = level;/*活动中标志*/
task_add(task);
}
taskctl->lv_change = 1; /*下次任务切换时检查LEVEL */
return;
}
//进程转换
void task_switch(void){
struct TASKLEVEL *tl = &taskctl->level[taskctl->now_lv];
struct TASK *new_task, *now_task = tl->tasks[tl->now];
tl->now++;
if(tl->now == tl->running)
tl->now = 0;
if(taskctl->lv_change != 0){/*修改任务等级*/
task_switchsub();
tl = &taskctl->level[taskctl->now_lv];
}
new_task = tl->tasks[tl->now];
timer_settime(task_timer, new_task->priority);
if(new_task != now_task)
farjmp(0, new_task->sel);
return;
}
//task休眠:检查是否为当前正在运行的进程,如果是则还额外需要执行任务转换操作,否则需要考虑在当前运行的进程前面还是后面
void task_sleep(struct TASK *task){
struct TASK *now_task;
if(task->flags == 2){//处于唤醒状态
now_task = task_now();
task_remove(now_task);
if(task == now_task){
task_switchsub();
now_task = task_now();
farjmp(0, now_task->sel);
}
}
return;
}
fifo.c
也需要改写一下,不过和上一节一样,只是将唤醒休眠任务的
task_run稍稍修 改一下而已。优先级和
LEVEL
都不需要改变,只要维持原状将任务唤醒即可。
暂且将任务
A
设为LEVEL 1
,任务
B0
~
B2
设为
LEVEL 2。
void HariMain(void)
{
(中略)
init_palette();
shtctl = shtctl_init(memman, binfo->vram, binfo->scrnx, binfo->scrny);
task_a = task_init(memman);
fifo.task = task_a;
task_run(task_a, 1, 0); /*这里! */
(中略)
/* sht_win_b */
for (i = 0; i < 3; i++) {
(中略)
task_run(task_b[i], 2, i + 1); /*这里! */
}
(中略)
}