《30天自制操作系统》学习笔记——第十五天

       十五天的内容开始啦!

        本书的进度大概已经到一半,今天要开始讲一个非常重要同时也挺难的东西,那就是多任务!

                                                     

       多任务我们都很清楚,但一步步实现多任务的过程实在不容易,请跟紧思路哦!

       首先,我们考虑电脑是如何完成多任务这样的功能呢?对了,就是把时间分成一片片,再分给一个个任务,让其执行。而分时间片的工作交给之前一直在研究的timer来实现。就像这样:

                                                                      

       好的,现在我们有了时间片,那接下来呢?如何在一个程序的timer超时后,切换到另一个程序呢?

       讲切换任务的时候,我们引入TSS“任务状态段”,即Task Status Segment。TSS中的内容包括两部分,第一部分是与任务设置相关的信息,第二部分是各种段寄存器,通用寄存器。为什么要保存寄存器的值呢?在任务切换的时候,我们应该像调用子程序时一样,应该先保存断点。而这里,因为是任务之间的切换,所以大部分寄存器都要重新保存。

       那么有了TSS之后呢?TSS不过是记录了一些设置信息和断点信息,如何记性任务切换呢?

       要进行任务切换,我们执行JMP 指令。如果JMP后面的地址是far地址,并指向TSS的话,CPU就不会执行通常的改写EIP和CS的操作,而是将此指令理解为任务切换。而为了实现这样的功能CPU在每次执行带有段地址的指令时,都回去确认一下GDT中的设置来判断是正常的JMP,还是任务切换。

       这里还要提到一个寄存器,TR,“task register”,专门来存当前执行的task的TSS段址,它能让CPU记住现在正在运行哪一个任务。而TR里面的值是TSS在GDT中的编号*8。

       TR寄存器的内容书上介绍的不是很仔细,这里引用CSDN上一个博主的文章:

       任务寄存器tr保存 16 位的段选择子、32 位基地址、16 位段界限和当前任务的 TSS属性。它引用 GDT 中的 TSS 描述符。基地址指明 TSS 的第一个字节(字节 0)的线性地址,段界限确定 TSS 的字节个数。TR寄存器包含了当前正在CPU运行的进程的TSSD(任务段描述符)选择符。也包含了两个隐藏的非编程域:TSSD的base 和limit域。通过这种方式处理器就能直接对TSS寻址,而不用从GDT中索引TSS的地址

        TR寄存器---->GDT中的TSS描述符---->硬件上下文的具体数据。任务切换中

                               

       cpu会把当前寄存器的数据保存到当前(旧的)tr寄存器所指向的tss数据结构里,然后把新的tss数据复制到当前寄存器里。这些操作是通过cpu的硬件实现的


        引用结束。好了,现在我们知道了TS的作用,继续回到我们的多任务的内容中来。

        接下来,作者尝试编写第一个多任务的程序。这里只有两个任务,A是主线的,负责鼠标,键盘输入等,B基本什么都不做。关键代码如下:

void HariMain(void)
{
	
	struct TSS32 tss_a, tss_b;
	

	for (;;) {
		io_cli();
		if (fifo32_status(&fifo) == 0) {
			io_stihlt();
		} else {
			i = fifo32_get(&fifo);
			io_sti();
			if (i == 2) {
				farjmp(0, 4 * 8);
				timer_settime(timer_ts, 2);
			} 
		}
	}
}

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

       上面的程序基本上完成了任务切换,但是这种多任务不是真正的多任务,因为切换任务时的farjmp还要自己指定。但真正的多任务,是要做到在程序本身不知道的情况下进行任务切换。接下来,作者就进行了一些修改,使程序变得更像一个真正的多任务了。其实关键修改就是改变了timer的中断处理程序。我们知道,设定好timer为10ms之后,每过10ms就会引发一次定时器中断。此时原来的中断处理程序是判断timer是否超时,超时则记下data。为什么不直接在中断处理程序里判断是什么timer呢?因为我们要尽可能地缩短中断处理程序,使系统更流畅。但是这里,我们必须要在中断处理程序中加入判断是否是多任务timer的语句。若是多任务timer超时,则直接执行任务切换。只有这样,才能做到程序本身不知道的情况下进行任务切换。

       具体的源代码就不附上了。明天的多任务会引进更多内容,准备好接受挑战吧!


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值