Skip to content

day15多任务

任务切换

任务状态段TSS, 有16位和32位两个版本, 也是内存的一部分, 在定义了GDT之后使用

c
struct TSS32 {
    //这一行的保存任务设置相关的信息
	int backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3;
	//32位的寄存器, epi记录执行到的位置
    int eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;
	//16位的寄存器
    int es, cs, ss, ds, fs, gs;
    //任务设置相关的, 设置为0和0x40000000
	int ldtr, iomap;
};

实际上切换任务就是使用JMP改变EIP, 有两种, 只改变EIP的叫做near模式, 同时改变CS的叫做far模式

JMP跳转的位置是TSS的话就会进行任务切换

c
struct TSS32 tss_a, tss_b;
tss_a.ldtr = 0;
tss_a.iomap = 0x40000000;
tss_b.ldtr = 0;
tss_b.iomap = 0x40000000;
c
#define AR_TSS32		0x0089
struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT;

//设置两个段
set_segmdesc(gdt + 3, 103, (int) &tss_a, AR_TSS32);
set_segmdesc(gdt + 4, 103, (int) &tss_b, AR_TSS32);
load_tr(3 * 8);
assembly
_load_tr:		; void load_tr(int tr);
		LTR		[ESP+4]			; tr
		RET

这个是向task register任务寄存器TR写入GDT的值, 需要乘8, 这时候切换任务会自动记录当前的值

c
_taskswitch4:	; void taskswitch4(void);
		JMP		4*8:0
		RET

RET这个是在任务结束之后可能会返回, 之后返回C的主函数

c
//申请一个栈	
task_b_esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024; //这里直接设置为栈的尾部
//这个是返回的位置
tss_b.eip = (int) &task_b_main;
tss_b.eflags = 0x00000202; /* IF = 1; */
tss_b.eax = 0;
tss_b.ecx = 0;
tss_b.edx = 0;
tss_b.ebx = 0;
tss_b.esp = task_b_esp;
tss_b.ebp = 0;
tss_b.esi = 0;
tss_b.edi = 0;
tss_b.es = 1 * 8;
tss_b.cs = 2 * 8;
tss_b.ss = 1 * 8;
tss_b.ds = 1 * 8;
tss_b.fs = 1 * 8;
tss_b.gs = 1 * 8;

在这里设置的段和bootpack.c相同

之后设置切换回去

c
void task_b_main(void)
{
	struct FIFO32 fifo;
	struct TIMER *timer;
	int i, fifobuf[128];

	fifo32_init(&fifo, 128, fifobuf);
	timer = timer_alloc();
	timer_init(timer, &fifo, 1);
	timer_settime(timer, 500);

	for (;;) {
		io_cli();
		if (fifo32_status(&fifo) == 0) {
			io_sti();
			io_hlt();
		} else {
			i = fifo32_get(&fifo);
			io_sti();
			if (i == 1) { /* 5秒タイムアウト */
				taskswitch3(); /* タスクAにもどる */
			}
		}
	}
}
c
_taskswitch3:	; void taskswitch3(void);
		JMP		3*8:0
		RET

抽象切换任务

c
_farjmp:		; void farjmp(int eip, int cs);
		JMP		FAR	[ESP+4]				; eip, cs
		RET
c
if (i == 2) {
    farjmp(0, 4 * 8);
    timer_settime(timer_ts, 2);
}

时间到就进行切换, 切换回来之后设置另一个定时器

c
void task_b_main(void)
{
	struct FIFO32 fifo;
	struct TIMER *timer_ts;
	int i, fifobuf[128];

	fifo32_init(&fifo, 128, fifobuf);
	timer_ts = timer_alloc();
	timer_init(timer_ts, &fifo, 1);
	timer_settime(timer_ts, 2);

	for (;;) {
		io_cli();
		if (fifo32_status(&fifo) == 0) {
			io_sti();
			io_hlt();
		} else {
			i = fifo32_get(&fifo);
			io_sti();
			if (i == 1) { /* 时间到了 */
				farjmp(0, 3 * 8);
				timer_settime(timer_ts, 2);
			}
		}
	}
}

实现在任务中打印

需要传递图层的结构体

c
//把图层的信息存储在这个位置
*((int *) 0x0fec) = (int) sht_back;


void task_b_main(void)
{
	struct FIFO32 fifo;
	struct TIMER *timer_ts;
	int i, fifobuf[128], count = 0;
	char s[11];
	struct SHEET *sht_back;

	fifo32_init(&fifo, 128, fifobuf);
	timer_ts = timer_alloc();
	timer_init(timer_ts, &fifo, 1);
	timer_settime(timer_ts, 2);
    //获取图层
	sht_back = (struct SHEET *) *((int *) 0x0fec);

	for (;;) {
		count++;
		sprintf(s, "%10d", count);
		putfonts8_asc_sht(sht_back, 0, 144, COL8_FFFFFF, COL8_008484, s, 10);
		io_cli();
		if (fifo32_status(&fifo) == 0) {
			io_sti();
		} else {
			i = fifo32_get(&fifo);
			io_sti();
			if (i == 1) { /* 任务的时间到了 */
				farjmp(0, 3 * 8);
				timer_settime(timer_ts, 2);
			}
		}
	}
}

减少不必要的打印

c
void task_b_main(struct SHEET *sht_back)
{
	struct FIFO32 fifo;
	struct TIMER *timer_ts, *timer_put;
	int i, fifobuf[128], count = 0;
	char s[12];

	fifo32_init(&fifo, 128, fifobuf);
	timer_ts = timer_alloc();
	timer_init(timer_ts, &fifo, 2);
	timer_settime(timer_ts, 2);
	timer_put = timer_alloc();
	timer_init(timer_put, &fifo, 1);
	timer_settime(timer_put, 1);

	for (;;) {
		count++;
		io_cli();
		if (fifo32_status(&fifo) == 0) {
			io_sti();
		} else {
			i = fifo32_get(&fifo);
			io_sti();
			if (i == 1) {
				sprintf(s, "%11d", count);
				putfonts8_asc_sht(sht_back, 0, 144, COL8_FFFFFF, COL8_008484, s, 11);
				timer_settime(timer_put, 1);
			} else if (i == 2) {
				farjmp(0, 3 * 8);
				timer_settime(timer_ts, 2);
			}
		}
	}

改变传递的方式

在Intel架构里面传递的信息是在ESP+4的位置, 所以可以使用这个方式进行传递, ESP记录的是返回的地址

c
	task_b_esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024 - 8;
	tss_b.eip = (int) &task_b_main;
	tss_b.eflags = 0x00000202; /* IF = 1; */
	tss_b.eax = 0;
	tss_b.ecx = 0;
	tss_b.edx = 0;
	tss_b.ebx = 0;
	tss_b.esp = task_b_esp;
	tss_b.ebp = 0;
	tss_b.esi = 0;
	tss_b.edi = 0;
	tss_b.es = 1 * 8;
	tss_b.cs = 2 * 8;
	tss_b.ss = 1 * 8;
	tss_b.ds = 1 * 8;
	tss_b.fs = 1 * 8;
	tss_b.gs = 1 * 8;
//在这里设置传入的数据
	*((int *) (task_b_esp + 4)) = (int) sht_back;

如果在最初的时候减的是4会在最后导致溢出, 因为这里的内存计算是从0开始的

c
int mt_tr;

void mt_init(void)
{
    //初始化一个计数器
	mt_timer = timer_alloc();
	/* timer_init设置一个时间 */
	timer_settime(mt_timer, 2);
    // 初始化主任务
	mt_tr = 3 * 8;
	return;
}
//切换的函数
void mt_taskswitch(void)
{
	if (mt_tr == 3 * 8) {
		mt_tr = 4 * 8;
	} else {
		mt_tr = 3 * 8;
	}
	timer_settime(mt_timer, 2);
	farjmp(0, mt_tr);
	return;
}

进阶任务切换

c
void inthandler20(int *esp)
{
	struct TIMER *timer;
	char ts = 0;
	io_out8(PIC0_OCW2, 0x60);	/* IRQ-00受付完了をPICに通知 */
	timerctl.count++;
	if (timerctl.next > timerctl.count) {
		return;
	}
	timer = timerctl.t0; /* とりあえず先頭の番地をtimerに代入 */
	for (;;) {
		/* timers循环找到 */
		if (timer->timeout > timerctl.count) {
			break;
		}
		/* タイムアウト */
		timer->flags = TIMER_FLAGS_ALLOC;
		if (timer != mt_timer) {
			fifo32_put(timer->fifo, timer->data);
		} else {
			ts = 1; /* 这个是任务切换的时间到了 */
		}
		timer = timer->next; /* 更新下一个是时钟 */
	}
	timerctl.t0 = timer;
	timerctl.next = timer->timeout;
	if (ts != 0) {
		mt_taskswitch();
	}
	return;
}