【读书笔记-《30天自制操作系统》-15】Day16

本篇内容继续多任务的讲解。上一篇中实现了两个任务之间的自动切换,但还不够通用,这里将其优化为多个任务之间的切换。接着引入了任务休眠的概念与休眠的程序实现。最后介绍了任务的优先级,一种用切换时间的长短来衡量,一种用Task Level来衡量。

在这里插入图片描述

1. 多任务管理程序优化

对多任务管理程序进行一些优化,首先修改结构体定义。

#define MAX_TASKS		1000	/* 最大任务数量 */
#define TASK_GDT0		3		/* 定义GDT从3号开始分配给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];
};

仿照前面的SHEETCTL修改了当前的TASKCTL。tasks用于存放当前运行的任务指针,初始化函数task_init如下:

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;
}

初始化函数中给每个任务分配了GDT编号,有了自己的TSS。task_init函数最终会返回一个内存地址,表示当前运行的程序已经作为一个任务了。

接下来还需要初始化一个任务结构的函数task_alloc,这里随意地设置了变量的初始值:

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; /* 无空闲  */
}

task_run函数,将task添加到tasks的末尾,并将running变量增加1。这样就将任务添加到了任务列表中,在任务切换时可以被切换到。

void task_run(struct TASK *task)
{
	task->flags = 2; /* 
	task->flags = 2; /* 任务活动中标志 */
	taskctl->tasks[taskctl->running] = task;
	taskctl->running++;
	return;
}

然后是重点,任务切换函数task_switch,此函数实现对任务列表中的任务进行依次切换:

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);
	}
	return;
}

此函数中设置了0.02s的超时时间。当任务里列表中有2个以上任务时,在达到超时时间时就依次进行切换。如果当前是列表中最后一个任务在运行,则下一次切换又会切换到列表的首个任务。

切换相关的函数完成,相应地修改主程序:

……
	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);
	……

超时中断函数inthandler20,在超时处理中调用了task_switch函数实现切换:

void inthandler20(int *esp)
{
	struct TIMER *timer;
	char ts = 0;
	io_out8(PIC0_OCW2, 0x60);	
	timerctl.count++;
	if (timerctl.next > timerctl.count) {
		return;
	}
	timer = timerctl.t0; 
	for (;;) {
		if (timer->timeout > timerctl.count) {
			break;
		}
		timer->flags = TIMER_FLAGS_ALLOC;
		if (timer != task_timer) {
			fifo32_put(timer->fifo, timer->data);
		} else {
			ts = 1;
		}
		timer = timer->next; 
	}
	timerctl.t0 = timer;
	timerctl.next = timer->timeout;
	if (ts != 0) {
		task_switch();
	}
	return;
}

2. 任务休眠

有些任务实际运行的时间可能并不长,但是需要及时地响应,比如移动鼠标,如果长时间不同鼠标,分配给鼠标监控任务的时间里,该任务基本都处于无事可做的状态,这样就浪费了本可以分配给其他任务的时间,降低了CPU的使用效率。为了改善这种情况,引入了任务休眠的概念。

实现起来也比较简单,只要将该任务从任务列表中删除即可。但同时如果触发了鼠标事件,又要及时将任务唤醒。
首先创建函数task_sleep:

{
	int i;
	char ts = 0;
	if (task->flags == 2) {		/* 如果指定任务处于唤醒状态 */
		if (task == taskctl->tasks[taskctl->now]) {
			ts = 1; /* 让当前运行中的任务休眠,稍后需要进行任务切换 */
		}
		/* 寻找task所在位置 */
		for (i = 0; i < taskctl->running; i++) {
			if (taskctl->tasks[i] == task) {
				break;
			}
		}
		taskctl->running--;
		if (i < taskctl->now) {
			taskctl->now--; /* 需要移动tasks数组成员 */
		}
		/* 移动成员 */
		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;
}

如果需要休眠的任务不是当前运行的任务,使该任务休眠只需要将该任务从任务列表中删除,将tasks列表中后面的任务移位即可;如果需要休眠的任务是当前运行的任务,则处理结束后还必须马上切换到下一个任务。

还要实现在FIFO中写入数据时将任务唤醒的功能。修改FIFO结构体定义,加入用于记录要唤醒任务的信息:

struct FIFO32 {
	int *buf;
	int p, q, size, free, flags;
	struct TASK *task;
};

改写fifo32_init,使其可以在参数中指定一个任务。在不使用唤醒功能时,该参数被设置为了0。

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 = 0;
	fifo->p = 0; 
	fifo->q = 0; 
	fifo->task = task; /* 有数据写入时需要唤醒的任务 */
	return;
}

实现唤醒功能:

int fifo32_put(struct FIFO32 *fifo, int data)
/* FIFO写入数据并积累起来 */
{
	if (fifo->free == 0) {
		/* 没有剩余空间则溢出 */
		fifo->flags |= FLAGS_OVERRUN;
		return -1;
	}
	fifo->buf[fifo->p] = data;
	fifo->p++;
	if (fifo->p == fifo->size) {
		fifo->p = 0;
	}
	fifo->free--;
	if (fifo->task != 0) {
		if (fifo->task->flags != 2) { /* 如果任务处于休眠状态 */
			task_run(fifo->task); /* 通过task_run让任务重新回到任务列表 */
		}
	}
	return 0;
}

相比任务A未休眠的情况,B任务得到了更多运行时间,提高了运行效率。

下面通过增加多个窗口来直观感受多任务的运行情况。

……
	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_screen8(buf_back, binfo->scrnx, binfo->scrny);

	/* sht_win_b */
	//创建3个窗口
	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); /* �����F�Ȃ� */
		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, 144, 52, -1); /* �����F�Ȃ� */
	make_window8(buf_win, 144, 52, "task_a", 1);
	make_textbox8(sht_win, 8, 28, 128, 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) { 
				sprintf(s, "%02X", i - 256);
				putfonts8_asc_sht(sht_back, 0, 16, COL8_FFFFFF, COL8_008484, s, 2);
				if (i < 0x54 + 256) {
					if (keytable[i - 256] != 0 && cursor_x < 128) {
						s[0] = keytable[i - 256];
						s[1] = 0;
						putfonts8_asc_sht(sht_win, cursor_x, 28, COL8_000000, COL8_FFFFFF, s, 1);
						cursor_x += 8;
					}
				}
				if (i == 256 + 0x0e && cursor_x > 8) { 
					putfonts8_asc_sht(sht_win, cursor_x, 28, COL8_000000, COL8_FFFFFF, " ", 1);
					cursor_x -= 8;
				}
				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);
			} else if (512 <= i && i <= 767) { /* �}�E�X�f�[�^ */
				if (mouse_decode(&mdec, i - 512) != 0) {
					sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);
					if ((mdec.btn & 0x01) != 0) {
						s[1] = 'L';
					}
					if ((mdec.btn & 0x02) != 0) {
						s[3] = 'R';
					}
					if ((mdec.btn & 0x04) != 0) {
						s[2] = 'C';
					}
					putfonts8_asc_sht(sht_back, 32, 16, COL8_FFFFFF, COL8_008484, s, 15);
					mx += mdec.x;
					my += mdec.y;
					if (mx < 0) {
						mx = 0;
					}
					if (my < 0) {
						my = 0;
					}
					if (mx > binfo->scrnx - 1) {
						mx = binfo->scrnx - 1;
					}
					if (my > binfo->scrny - 1) {
						my = binfo->scrny - 1;
					}
					sprintf(s, "(%3d, %3d)", mx, my);
					putfonts8_asc_sht(sht_back, 0, 0, COL8_FFFFFF, COL8_008484, s, 10);
					sheet_slide(sht_mouse, mx, my);
					if ((mdec.btn & 0x01) != 0) {
						sheet_slide(sht_win, mx - 80, my - 8);
					}
				}
			} else if (i <= 1) { 
				if (i != 0) {
					timer_init(timer, &fifo, 0); 
					cursor_c = COL8_000000;
				} else {
					timer_init(timer, &fifo, 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);
			}
		}
	}
}

任务A用于监控鼠标移动与键盘输入,任务B0-B2则用于其他3个窗口的显示,可以看到多个窗口同时运行的现象。
在这里插入图片描述
3. 设置任务优先级-切换时间

以上完成的内容中,各个任务都被分配了相同的切换时间。实际上通过给任务分配不同的切换时间,也是调整任务优先级的一种方式。通过设置不同的任务切换时实现了任务优先级的差异。

struct TASK {
	int sel, flags;
	int priority;
	struct TSS32 tss;
};

在task结构体中增加了priority变量,设置从1-10的优先级,对应切换时间0.01s-0.1s。
相应修改任务切换的函数:

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; 
	task->priority = 2; /* 切换时间为0.02s */
	taskctl->running = 1;
	taskctl->now = 0;
	taskctl->tasks[0] = task;
	load_tr(task->sel);
	task_timer = timer_alloc();
	timer_settime(task_timer, task->priority);
	return task;
}

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;
}

int fifo32_put(struct FIFO32 *fifo, int data)
{
	if (fifo->free == 0) {
		fifo->flags |= FLAGS_OVERRUN;
		return -1;
	}
	fifo->buf[fifo->p] = data;
	fifo->p++;
	if (fifo->p == fifo->size) {
		fifo->p = 0;
	}
	fifo->free--;
	if (fifo->task != 0) {
		if (fifo->task->flags != 2) { 
			task_run(fifo->task, 0); /* 唤醒时不改变优先级,设置为0 */
		}
	}
	return 0;
}

修改内容还是比较容易理解的。增加了priority参数供task_run设置超时时间时进行应用,实现不同任务使用不同的切换时间。

4. 设置任务优先级-Level

通过设置切换时间,确实可以使某些任务获得更多的运行时间,但是仍然要等待其他任务到达切换时间之后才能进行切换。像键盘与鼠标监控这种对于实时性要求很高的任务,如果不能及时切换,会给用户带来卡顿的使用体验。同样的还有网卡,如果不能及时接收,网络数据就可能丢失。因此这种情况下需要牺牲其他任务,优先保证这些任务的运行。

为了解决这个问题,设计了一种Level模式。将任务从高到低分为几个不同的Level,如果高Level中有任务在运行,就永远不会切换到低Level中的任务,只在高Level中选择任务进行切换。这样就可以确保Level高的任务始终会得到优先处理。

修改TASKCTL:

#define MAX_TASKS_LV	100
#define MAX_TASKLEVELS	10

struct TASK {
	int sel, flags; 
	int level, priority;
	struct TSS32 tss;
};
struct TASKLEVEL {
	int running; /* 这一Level中运行的任务数量 */
	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];
};

在TASKCTL中,我们增加了levle数组,当前设置3个Level。每个数组元素都是TASKLEVEL结构体,其中保存着每一Level的任务信息。此外还增加了lv_change变量,用于记录下一次任务切换时是否需要改变Level。

为了完成优先级Level的相关功能,首先要编写操作TASKLEVEL结构体的函数。

struct TASK *task_now(void)
{
	struct TASKLEVEL *tl = &taskctl->level[taskctl->now_lv];
	return tl->tasks[tl->now];
}

void task_add(struct TASK *task)
{
	struct TASKLEVEL *tl = &taskctl->level[task->level];
	tl->tasks[tl->running] = task;
	tl->running++;
	task->flags = 2;
	return;
}

void task_remove(struct TASK *task)
{
	int i;
	struct TASKLEVEL *tl = &taskctl->level[task->level];

	/* 寻找task所在位置*/
	for (i = 0; i < tl->running; i++) {
		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;
}

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;
}

task_now用于获取当前运行中的任务地址,需要首先根据当前活动的Level获取TASKLEVEL结构体,再获取该Level中正在运行的任务。

task_add用于向TASKLEVEL结构体中添加一个任务。调用task_add函数时,传入的参数task中已经设置好了需要添加到哪个Level的信息。通过task中的Level信息找到该Level的指针,并将该任务添加到此Level的任务列表中。

task_remove用于从某一Level的列表中删除任务。同样显示通过Level信息找到TASKLEVEL的指针,然后在该Level的任务列表中找到此任务进行移除,将此任务设置为休眠。移除之后,需要将列表中其他的任务进行移位。

task_switchsub用于确定任务切换时切换到哪个Level。

完成了操作TASKLEVEL结构体相关的函数,可以进入其他程序的修改了。
task_init与task_run修改如下:

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; /* 0.02s */
	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;
}

void task_run(struct TASK *task, int level, int priority)
{
	if (level < 0) {
		level = task->level; /* 不改变Level */
	}
	if (priority > 0) {
		task->priority = priority;
	}

	if (task->flags == 2 && task->level != level) { /* 改变活动中的Level */
		task_remove(task); /* 从旧Level的任务列表中移除 */
	}
	if (task->flags != 2) {
		/* 加入新Level的任务列表 */
		task->level = level;
		task_add(task);
	}

	taskctl->lv_change = 1; /* 下次任务切换时检查Level */
	return;
}

改写task_sleep与task_switch:

void task_sleep(struct TASK *task)
{
	struct TASK *now_task;
	if (task->flags == 2) {
		/* 当前处于活动状态 */
		now_task = task_now();
		task_remove(task); /* 将任务从当前Level任务列表中移除 */
		if (task == now_task) {
			/* 当前运行的任务休眠,需要进行切换 */
			task_switchsub();
			now_task = task_now(); /* 经过task_switchsub获取Level后,再获取需要切换的任务 */
			farjmp(0, now_task->sel);
		}
	}
	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;
}

在fifo与主程序中也需要相应修改,只需在调用task_run函数时加入相应的Level参数即可。这里将A设置为Level1,将B0-B2设置为Level2,这样在A任务处于活动状态时就不会切换到B0-B2。运行效果相同,但如果频繁移动鼠标,可以发现鼠标的响应相比修改之前会有很大改善。

到这里多任务的主要内容就基本完成了。在逐步完善程序的过程中,对于之前理解模糊的优先级的概念也有了更深入的理解。下一篇的主要内容转向交互,敬请期待。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值