30天自制操作系统(第16天)

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); /*这里! */
    }
    (中略)
}
  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值