30天自制操作系统日志第15天

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

操作系统实验日志学号20160810520姓名甘昆禄专业年级班级智能1601实验日期2018.01.02实验项目第15、16天:多任务一、实验主要内容终于等到第十五天-多进程的实现,在《30天自制操作系统》中介绍了多任务的实现,在此理清一下思路。1.首先一个很重要的概念:在我们平常windows系统上我们可以同时做很多事(下载,听音乐,看视频等),如果电脑是单核(一个CPU),那么这些事并不是这个CPU在同时在做,而是CPU使用了一种障眼法(其实不是CPU使用的,而是我们写的程序使用了障眼法,我们写的程序让CPU以一个极短的时间轮流执行着这些事,极短的时间让肉眼看不见)。CPU在每个时刻只能执行一件事。2.那么写多进程的思路是什么?首先我们先执行两个进程(不讨论多进程),进程其实也就是一段可执行的程序,这段程序要在内存中才能执行,那么放在内存的什么地方呢?CPU又是怎么找到这段程序呢?从这开始下手:①在32位的保护模式下,我们通过段选择子+全局描述符表进行内存的寻址(非常好的文章介绍内存寻址的文章)那么我们就要在全局描述符表中选择一段来写入我们即将执行的进程的首地址。#defineADR_GDT0x00270000//全局描述符表的开始位置#defineAR_TSS320x0089structSEGMENT_DESCRIPTOR*gdt=(structSEGMENT_DESCRIPTOR*)ADR_GDT;set_segmdesc(gdt+3,103,(int)&tss_a,AR_TSS32);//第一个进程,这个地方gdt+3其实是bootpack.c的程序的位置set_segmdesc(gdt+4,103,(int)&tss_b,AR_TSS32);//这个是第二个进程,tss_a和tss_b可以先想成程序地址通过这个设置,就将这两个进程的位置设置好,那么CPU执行进程二的时候,先到GDT表中找到位置,如果执行指令JMPFAR0:3*8(CS置0,EIP置为3*8,那么段基址+段内偏移=0+&tss_b),那么CPU会找到程序的位置&tss_b。CPU也是同样找到进程一的地址,那么至此程序的地址找到了。如果这样想就错了为什么错了?仔细想想如果进程一执行到一半,找到进程二的地址,切到进程二去执行了,那么再切回到进程一,凭什么CPU就知道到执行上次进程一切换的地方接着执行呢?那么这就涉及到上面写入全局描述符表里的tss_a和tss_b了,这两个并不是进程的入口地址,而是为了切换进程时保存当前进程的所有进程的状态的。TSS全称为taskstatesegment,是指在操作系统进程管理的过程中,进程切换时的任务现场信息。下面是网上的资料:X86体系从硬件上支持任务间的切换。为此目的,它增设了一个新段:任务状态段(TSS),它和数据段、代码段一样也是一种段,记录了任务的状态信息。与其它段一样,TSS也有描述它的结构:TSS描述符表,它记录了一个TSS的信息,同时还有一个TR寄存器,它指向当前任务的TSS。任务切换的时候,CPU会将原寄存器的内容写出到相应的TSS,同时将新TSS的内容填到寄存器中,这样就实现了任务的切换。TSS在任务切换过程中起着重要作用,通过它实现任务的挂起和恢复。所谓任务切换是指挂起当前正在执行的任务,恢复或启动执行另一个任务。Linux任务切换是通过switch_to这个宏来实现的,它利用长跳指令,当长跳指令的操作数是TSS描述符的时候,就会引起CPU的任务的切换,此时,CPU将所有寄存器的状态保存到当前任务寄存器TR所指向的TSS段中,然后利用长跳指令的操作数(TSS描述符)找到新任务的TSS段,并将其中的内容填写到各个寄存器中,最后,将新任务的TSS选择符更新到TR中。这样系统就开始运行新切换的任务了。由此可见,通过在TSS中保存任务现场各寄存器状态的完整映象,实现了任务的切换。task_struct中的tss成员就是记录TSS段内容的。当进程被切换前,该进程用tss_struct保存处理器的所有寄存器的当前值。当进程重新执行时,CPU利用tss恢复寄存器状态。上面提到的TR寄存器是用来记住当前正在运行的任务,当任务切换时,TR的值会自动变化。我们可以通过LTR汇编指令来向TR寄存器写入值。TSS的结构:structTSS32{intbacklink,esp0,ss0,esp1,ss1,esp2,ss2,cr3;inteip,eflags,eax,ecx,edx,ebx,esp,ebp,esi,edi;intes,cs,ss,ds,fs,gs;intldtr,iomap;};设置到全局描述符中的tss_a和tss_b还没有初始化。因为上述的进程一其实就是第一个开始的进程,因此tss_a在切换的时候会自动保存数据到tss_a,因此不用初始化;现在对tss_b进行初始化,tss_b_esp=(int)memman_alloc_4k(memman,64*1024)+64*1024-8;tss.esp=task_b_esp;tss.eip=(int)&task_b_main;tss.cs=2*8;tss.ds=1*8;tss.ss=1*8;tss.gs=1*8;tss.fs=1*8;tss.es=1*8;重点是esp,eip和cs寄存器,因为这几个寄存器关乎到程序的执行,因为第二个进程的代码写在bootpack.c中,二bootpack.c的代码段是在GDT中的第三段,因此CS为2*8,段内偏移就是进程二的地址&task_b_main。这样就会切换到第二个进程执行了。那么前面说的CPU会以极短的时间来轮流执行这些进程,这个结合定时器实现的,每个进程运行到定时器的一定时间,就自动切换到其他进程,这样每个进程执行一段时间。16天:任务自动化:要实现任务切换的自动化。最好能够有一个任务数组,每次定时器timeout就切换到下一个,这样就不用写很复杂的taskswitch()了。首先执行task_init(),这里创建TASKCTL结构,为task数组的每一个TASK在段表中注册。最后设置当前的任务,也就是把调用task_init()函数的这个程序变成一个任务。之后便可进行任务切换了。task_run()只是将任务加入running列表,真正的运行是在task_switch()切换到该任务时。让任务休眠如果任务主要是IO操作,没有IO的时候没事干,这时可以让它休眠。当IO中断时再把它叫醒。休眠的方法是将任务从running列表中删除,需要唤醒时再次task_run()即可。task_sleep():将指定的task移出running列表。如果要sleep的任务是当前正在执行的任务(taskctl-now),则立刻进行切换。具体的做法是:1.为FIFO结构添加task成员变量,表示当这个FIFO来数据时,唤醒哪一个task。2.在fifo32_put()中,当有数据来时,判断task是否处于休眠,是则调用task_run()。3.在主程序for循环中,判断fifo的状态。如果fifo中没有数据则让task休眠。实现任务的优先级之前的任务切换都是以0.02秒为固定间隔。要实现优先级,则应该能够以不同的时间间隔切换。为TASK结构添加priority成员变量。可以在task_init()中设置,也可以在task_run()中改变。在task_switch()中,通过timer_settime(),根据task的priority来设置定时器的timeout值,从而改变任务切换的间隔。除了时间长短不同,不同优先级的执行顺序也应该不一样。在这里使用了多级队列。处在高级队列中的全部任务休眠或降级时,低级队列才有机会执行。新增TASKLEVEL结构,每个结构包含一个TASK指针数组。在TASKCTL结构中加入一个TASKLEVEL结构的数组,保存所有TASK结构的数组只在TASKCTL中有一个。这样形成多级任务队列。添加task_add(),task_remove()函数,方便任务在多级间的移动。添加task_switchsub()函数,用于改变level。为实现多级队列,还是改了挺多地方。总的来说,首先将处理鼠标键盘的任务A放在LEVEL0(高优先级),其他任务放在LEVEL1。如果任务A休眠了,则LEVEL0空了,task_sleep()就会调用task_switchsub(),找到一个有任务的level。如果任务A要唤醒了,则task_run()会改变lv_change值,提醒切换任务时切换到更高级level中。二、遇到的问题及解决方法1、不明白任务A为什么不用设置寄存器的值,而B的要,还有任务B切换回A的时候是怎么切换的,感觉任务A的地址并没有在哪里表示呀?解决:未解决三、实验心得体会实验终于做完了……不过这一两天的内容多任务实在不知道具体作用是啥,我们在做任务的时候,真的感觉上就是同时在执行任务的呀,每次for循环,无论多少任务,不是都会去遍历的吗?缓冲区也都在一起,每次都会检测缓冲区的呀。

1 / 5
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功