武汉大学21级网安操作系统大作业 任务一
1.?问题介绍与思路摘要
?1.1 问题介绍
在已有实验代码基础上,将1-8章节(可以包含第9章)进行功能综合,形成你自己的一个简易OS
功能要求:
??可以考虑使用软盘或者硬盘,启动该mini-OS。
??能够实现你在前面章节所实现的,内存分配与释放,
??能够实现你在前面章节所实现的多进程管理与调度,
??所有代码需用目录树结构管理,并添加完整的makefile编译,以及文档
?1.2 思路摘要
?根据老师点拨,法一决定在chapter10的基础上做,法二是在chapter6的基础上做的。
在本学期“内核雏形”一节的实验中,我们利用makefile初步形成了一个内核雏形,其中已经添加了在屏幕上画一个喜欢的ASCII图案、内存分配与释放、自己设计的键盘中断;在本学期“多进程与进程调度”一节的实验中,我们实现了多级反馈队列调度算法。
基于此,将之前完成的功能:启动代码,在引导过程中在屏幕上画一个喜欢的ASCII图案;内存分配与释放;自己设计的键盘中断;多级反馈队列调度算法等集成。
2.?具体思路及其实现(法一)
2.1?在屏幕上画一个喜欢的ASCII图案
?在start.c中加入printself函数,内容自定义,如
PUBLIC?void?printDP()?{??
????/*?通过打印空格的方式清空屏幕的前17行,并把?disp_pos?清零?*/??
????int?i;??
????disp_pos?=?0;??
????for(i=0;i<80*17;i++){??
????????disp_str("?");??
????}??
????disp_pos?=?0;??
??
????disp_str(??
????????"Welcom?to?my?test!\n"??
//自己可以自定义打印内容
????);??
}??
?然后,在kernel.asm引入并调用
效果如下?
?
2.2?内存分配与释放
之前的实验中,将函数写在了一个文件中,即page.asm文件,直接复制到mm文件夹下,这是其中较为重要的几个函数。?
DispTransfer:??
????;?保存现有的寄存器状态??
????xchg?bx,bx??
????pusha??
??
????;?打印base_address??
????call?DispReturn??
????push?szBASE??
????call?DispStr??
????add?esp,?4??
??
????;?打印虚拟地址??
????mov?eax,?VirtualAddr??
????push?eax??
????call?DispInt??
????add?esp,?4??
??
????;?打印PDE_base_address??
????call?DispReturn??
????push?szPDE??
????call?DispStr??
????add?esp,?4??
??
????;?计算并打印页目录项??
????;?mov?ax,?SelectorFlatRW??
????;?mov?es,?ax??
????mov?eax,?VirtualAddr??
????shr?eax,?22??
????cmp?eax,?[PageTableNumber]??
????;?jg?.error??
????mov?ebx,?4??
????mul?ebx??
????mov?edx,?cr3??
????add?eax,?edx??
????mov?ecx,?dword?[es:eax]??
????xor?ecx,?PG_P?|?PG_USU?|PG_RWW??
????and?ecx,?0xfffffc00??
????push?dword?[es:eax]??
????call?DispInt??
????add?esp,?4??
??
????;?打印PDE_base_address??
????call?DispReturn??
????push?szPTE??
????call?DispStr??
????add?esp,?4??
??
????;?计算打印页表项??
????mov?eax,?VirtualAddr??
????shr?eax,?12??
????and?eax,?0x3FF??
????mov?ebx,?4??
????mul?ebx??
????add?eax,?ecx??
????mov?ecx,?dword?[es:eax]??
????xor?ecx,?PG_P?|?PG_USU?|?PG_RWW??
????and?ecx,?0xfffff000??
????push?dword?[es:eax]??
????call?DispInt??
????add?esp,?4??
??
????;?打印Final_address??
????call?DispReturn??
????push?szFINAL??
????call?DispStr??
????add?esp,?4??
??
????;计算打印最终地址??
????mov?eax,?VirtualAddr??
????and?eax,?0xFFF??
????add?eax,?ecx??
????push?eax??
????call?DispInt??
????add?esp,?4??
??
????;?恢复寄存器并返回??
????popa??
????ret??
??
.error:??
????;?打印错误消息??
????push?szAddressNotPresent??
????call?DispStr??
????add?esp,?4??
??
????;?恢复寄存器并返回??
????popa??
alloc_pages:??
??
????;save?register??
????;?push?ds??
????;?push?es??
????;?;function?implement??
????;?mov?bx,?SelectorFlatRW???
????;?mov?ds,bx??
??
????;?mov?bx,SelectorData??
????;?mov?es,bx??
??
????mov?ecx,eax??
??
????;计算总共需要分配的物理内存大小,并将其存储在?ebx?寄存器中。??
????mov?ebx,4096????;page?number?*?page?size??
????mul?ebx??
??
????mov?ebx,[es:AvaLinearAddress]??
????add?[es:AvaLinearAddress],eax??
????;ebx存的是AvaLinearAddress?指定的线性地址对应的地址??
????push?ebx??
????mov?eax,ebx??
????mov?ebx,cr3??
????and?ebx,0xfffff000??
????and?eax,0xffc00000??
????shr?eax,20??
????add?ebx,eax?????;ebx中是PDE中的地址??
????mov?edx,ebx?????;edx->missing?PDE?address页目录项(PDE)的地址??
????mov?ebx,[ebx]???;PTE?地址???
????;test?p?flag?检查?PDE?中的?P?标志位??
????test?ebx,0x0000_0001??
????jnz?.pde_exist??
????;page?table?is?not?exist??
????mov?ebx,cr3??
????mov?ebx,[ebx]???;first?page?table?address??
????and?ebx,0xfffff000??
????shl?eax,10??
????add?ebx,eax?????;这里会直接变为页表的位置??
????or?ebx,0x0000_0007??
????mov?[edx],ebx??
??
.pde_exist:??
????mov?eax,[esp]??
??
????and?ebx,0xfffff000??
????and?eax,0x003ff000??
????shr?eax,10??
????add?ebx,eax??
??
.change_pte:??
????call?alloc_a_4k_page??
????test?eax,eax??
????jz?.allocation_failed???;分配失败??
??
????or?eax,0x0000_0007??
????mov?[ebx],eax??
????add?ebx,4??
????loop?.change_pte??
.allocation_failed:??
????pop?ebx??
????;?pop?es??
????;?pop?ds??
????ret??
??
??
alloc_a_4k_page:??
????????????????????????;return?eax:?physical?address??
????;保存寄存器的值??
????push?ds??
????push?es??
????;function?implement,设置合适的段寄存器的值,以确保访问正确的内存段??
????xor?eax,eax??
????;?mov?ax,SelectorFlatRW??
????;?mov?es,ax??
????;?mov?ax,SelectorData??
????;?mov?ds,ax??
??
.search:??
????bts?[BitMap],eax??
??
????jnc?.find??
????inc?eax??
????cmp?eax,BitMapLen*8??
????jl?.search??
??
????;no?available?physical?space,执行?hlt?指令来暂停?CPU?的执行,表示没有可用的物理空间。??
????;hlt;在分页分配中,使用?hlt?来表示分配失败并暂停?CPU?的执行是一种不常见的方式。??
????mov?eax,0??
????ret??
??
.find:??
??
????shl?eax,12??
??
????pop?es??
????pop?ds??
????ret??
free_pages:??
????;?push?ds??
????;?push?es??
????push?ebx?????????;?save?eax?and?ebx??
????push?eax??
??
????;?mov?bx,?SelectorFlatRW??????
????;?mov?ds,?bx??
????;?mov?bx,?SelectorData??
????;?mov?es,?bx???????;?normal?init??
??????????
????;?find?the?pde?and?pte???
????mov?ebx,?cr3??
????and?ebx,?0xfffff000??
????and?eax,?0xffc00000???
????shr?eax,?20??????;?20?=?22?-2???
????add?ebx,?eax??
????mov?edx,?[ebx]??
????and?edx,?0xfffffff8??
????mov?[ebx],?edx??
??????
????mov?ebx,?[ebx]??
??????????????
????mov?eax,?[esp]??
????add?esp,?4???
????and?ebx,0xfffff000??
????and?eax,0x003ff000??
????shr?eax,10??
????add?ebx,?eax??
??
??
????mov?ecx,?[esp]????????????
????add?esp,4??
.free_pte:??
????mov?eax,?[ebx]??
????and?eax,?0xfffffff8???
????mov?edx,?eax????????????????;?eax:映射的物理地址??
????shr?edx,?12???????????????;edx:第edx个页面(由于物理地址开始为0x00000000)??
????btr?[BitMap],?edx?????????;改变位图,设置为0,空闲??
????mov?[ebx],?eax??
????add?ebx,32??
????loop?.free_pte??
??
????;?pop?es??
????;?pop?ds??
????ret???
然后在?kernel.asm中引入需要的函数
再添加?AllocAndFree函数并声明、调用
AllocAndFree:??
????xchg?bx,bx??
????mov?eax,4??
????call?alloc_pages??
?????
????xchg?bx,bx??
????mov?eax,ebx??
????mov?ebx,4??
?????
????call?free_pages??
????xchg?bx,bx??
????ret??
最后再在Makefile中?添加相关指令
?代码效果如下图
?这是DispTransfer(打马赛克的区域是个人信息,见谅)
?这是alloc free
?
?2.3?自己设计的键盘中断
?在hwint01中添加自己设计的键盘中断代码,下面是示例代码
ALIGN???16??
hwint01:????????????????;?Interrupt?routine?for?irq?1?(keyboard)??
????????inc?????byte?[gs:((80?*?17?+?50)?*?2)]??
????????in??????al,?0x60??
????????movzx?????ecx,?al??
????????;mov?????ecx,?8??
??
????????cmp?????ecx,50??
????????jg??????iretd_label??
???????
????????lea?????ebx,[array+ecx]??
????????mov?????al,[ebx]??
????????mov?????ah,0ch??
????????mov?????[gs:((80?*?17?+?40)*2)],ax??
??
iretd_label:??
????????mov?????al,20h??
????????out?????20h,al??
????????iretd??
然后在一些代码地方添加能引发键盘中断的指令,运行后即可得到下图?
?2.4?多级反馈队列调度算法(仅展示关键代码)
首先,在sys/proc.h中定义多级反馈队列的三个队列?
?在kernel/global.c中初始化多级反馈队列
?然后,在include/sys/global.h中声明,作为全局可用,这样在任何地方都有queue指向第一个队列
?然后,在include/sys/proc.h中为每个进程添加参数inqueue(是否在队列中)、runtime(当前剩余时间)、queuenum(所在队列号)等参数。
再在kernel/proc.c中将入队和出队封装成两个函数inqueue和outqueue?
int?inqueue(struct?proc*?p)?{??//?把p进队??
????QUEUE*?tempqueue;??
????tempqueue?=?queue?+?p->queuenum;??//?将当前程序放到队尾??
????tempqueue->taskqueue[tempqueue->rear]?=?p;??
????tempqueue->rear?=?(tempqueue->rear?+?1)?%?QUEUE_LEN;??
????p->inqueue?=?1;??
????tempqueue->len?+=?1;??
void?outqueue(struct?proc*?p)?{??//?把p所在队的队首出队??
????QUEUE*?tempqueue;??
????//?__asm__?__volatile__("xchg?%bx,%bx");??
????tempqueue?=?queue?+?p->queuenum;??
????tempqueue->front?=?(tempqueue->front?+?1)?%?QUEUE_LEN;??
????p->inqueue?=?0;??
????tempqueue->len?-=?1;??//?队首就绪,?出队??
}??
接下来是重头戏,即多级队列调度算法?
?首先仿造原schedule中的方法, 判断是否有新进程入队, 若一个进程当前为可执行状态, 并且不在队中, 则将其入队. 因为每次执行进程都会使进程出队, 所以在最开始初始化保证了每次调用该函数都能拿到进程。
struct?proc*?get_one_proc()?{??//?每次能拿到一个进程?,?且不可能拿不到??
????struct?proc*?p;??
????QUEUE*?tempqueue;??
????int?now_queue_num?=?0;??
????for?(p?=?&FIRST_PROC;?p?<=?&LAST_PROC;?p++)?{??
????????if?(p->p_flags?==?0?&&?p->inqueue?==?0?&&??
????????????(p?!=?p_proc_ready?||?queue->len?==?0))?{??
????????????//?__asm__?__volatile__("xchg?%bx,?%bx");makei?e??
????????????p->ticks?=?queue->timep;??//?进队进行初始化??
????????????p->queuenum?=?0;??
????????????p->runtime?=?100;??
????????????inqueue(p);??
????????????//?disp_int(p?-?&FIRST_PROC);??
????????}??
????}??
????now_queue_num?=?0;??
????while?(now_queue_num?<=?2)?{??//?在三个队列中找能运行的程序??
????????tempqueue?=?queue?+?now_queue_num;??
????????int?length?=?0;??
????????int?point?=?tempqueue->front;??
????????while?(tempqueue->taskqueue[point]->p_flags?!=?0?&&??
???????????????length?<?tempqueue->len)?{??
????????????outqueue(tempqueue->taskqueue[point]);??
????????????inqueue(tempqueue->taskqueue[point]);??
????????????//?如果是阻塞的则先出队再入队??
????????????length?+=?1;??
????????????point?=?(point?+?1)?%?QUEUE_LEN;??
????????}??
????????if?(length?<?tempqueue->len)?{??//?找到一个??
????????????return?tempqueue->taskqueue[point];??
????????}??
????????now_queue_num++;??
????}??
}??
然后编写调度函数,?需要考虑,?是否发生抢占??若没发生,?当前时间片是否用完,?当前程序运行时间是否结束??基于以上分支,?编写调度函数如下所示?
void?myschedule()?{??
????struct?proc?*p,?*next;??
????next?=?get_one_proc();??
??
????if?(p_proc_ready->ticks?&&?p_proc_ready->runtime?&&??
????????next->queuenum?>=?p_proc_ready->queuenum)?{??//当前进程有时间片和运行时间,且没有优先级更高的,继续
????????return;??
????}??
??
????if?(p_proc_ready->runtime?==?0)?{??//?运行时间用完,换下一个
????????p_proc_ready?=?next;??
????????outqueue(next);??
????????return;??
????}??
??
????if?(p_proc_ready->ticks?==?0)?{??//?当前时间片用完,?进队,且选下一个??
????????if?(p_proc_ready->queuenum?<?2)?{??
????????????p_proc_ready->queuenum?+=?1;??//?进到下一队列??
????????}??
????????p_proc_ready->ticks?=?(queue?+?p_proc_ready->queuenum)->timep;??
??
????????inqueue(p_proc_ready);??
????????p_proc_ready?=?next;??
????????outqueue(next);??
????????return;??
????}??
??
????if?(next->queuenum?<?p_proc_ready->queuenum)?{??//?抢占??有更高的优先级
????????inqueue(p_proc_ready);??
????????p_proc_ready?=?next;??
????????outqueue(next);??
????????return;??
????}??
}??
然后,在include/sys/proto.h声明myschdule,因为后面别的文件要调用。?
此处还存在一个地方对进程的调度存在控制, 因为此处存储在进程的阻塞, 而程序阻塞后需要选出下一个执行的程序, 否则就会发生死锁, 所以需要在下面的block函数中也进行相应的修改。?
PRIVATE?void?block(struct?proc*?p)?{??
????assert(p->p_flags);??
????inqueue(p);??
????struct?proc*?next?=?get_one_proc();??
????p_proc_ready?=?next;??
????outqueue(next);??
????//?disp_int(p_proc_ready?-?&FIRST_PROC);??
}??
最后,修改clock.c中的clock_handler函数让当前进程的时间片和运行时间减少。?
PUBLIC?void?clock_handler(int?irq)?{??
????if?(++ticks?>=?MAX_TICKS)??
????????ticks?=?0;??
??
????if?(key_pressed)??
????????inform_int(TASK_TTY);??
??
????if?(k_reenter?!=?0)?{??
????????return;??
????}??
??
????//?if?(p_proc_ready->ticks?>?0)?{??
????//?????return;??
????//?}??
????if?(p_proc_ready->ticks)??
????????p_proc_ready->ticks--;??
??
????if?(p_proc_ready->runtime)??
????????p_proc_ready->runtime--;??
??
????myschedule();??
??
????__asm__?__volatile__("xchg?%bx,%bx");??
????switch?((p_proc_ready?-?&FIRST_PROC))??
????{??
????case?0x6:??
????????disp_str("A,");??
????????break;??
????case?0x7:??
????????disp_str("B,");??
????????break;??
????case?0x8:??
????????disp_str("C,");??
????????break;??
????default:??
????????break;??
????}??
?????disp_str("");??
}??
编译运行后,效果如下图?
但是,可以看到,存在问题?
不知道为啥,这里一开始少了一个A。另外,达到5个之后,队列以C、A、B的顺序从5到8到9才到10。
?2.5 Makefile集成
在目录下执行tree指令,得到如下目录树。Makefile中的修改不多,只有在内存与分配中修改了一小部分,详细可看对应小节,此处略去。?
.
├── 80m.img
├── a.img
├── bochsrc
├── boot
│?? ├── boot.asm
│?? ├── boot.bin
│?? ├── include
│?? │?? ├── fat12hdr.inc
│?? │?? ├── load.inc
│?? │?? └── pm.inc
│?? ├── loader.asm
│?? └── loader.bin
├── command
│?? ├── echo.c
│?? ├── Makefile
│?? ├── pwd.c
│?? └── start.asm
├── fs
│?? ├── disklog.c
│?? ├── link.c
│?? ├── main.c
│?? ├── misc.c
│?? ├── open.c
│?? └── read_write.c
├── include
│?? ├── stdio.h
│?? ├── string.h
│?? ├── sys
│?? │?? ├── config.h
│?? │?? ├── console.h
│?? │?? ├── const.h
│?? │?? ├── fs.h
│?? │?? ├── global.h
│?? │?? ├── hd.h
│?? │?? ├── keyboard.h
│?? │?? ├── keymap.h
│?? │?? ├── proc.h
│?? │?? ├── protect.h
│?? │?? ├── proto.h
│?? │?? ├── sconst.inc
│?? │?? └── tty.h
│?? └── type.h
├── kernel
│?? ├── clock.c
│?? ├── console.c
│?? ├── global.c
│?? ├── hd.c
│?? ├── i8259.c
│?? ├── kernel.asm
│?? ├── keyboard.c
│?? ├── kliba.asm
│?? ├── klib.c
│?? ├── main.c
│?? ├── proc.c
│?? ├── protect.c
│?? ├── start.c
│?? ├── systask.c
│?? └── tty.c
├── kernel.bin
├── krnl.map
├── lib
│?? ├── close.c
│?? ├── exec.c
│?? ├── exit.c
│?? ├── fork.c
│?? ├── getpid.c
│?? ├── misc.c
│?? ├── open.c
│?? ├── orangescrt.a
│?? ├── printf.c
│?? ├── read.c
│?? ├── stat.c
│?? ├── string.asm
│?? ├── syscall.asm
│?? ├── syslog.c
│?? ├── unlink.c
│?? ├── vsprintf.c
│?? ├── wait.c
│?? └── write.c
├── Makefile
├── mm
│?? ├── exec.c
│?? ├── forkexit.c
│?? ├── main.c
│?? └── page.asm
└── scripts
├── genlog
└── splitgraphs
10 directories, 78 files
?至此,任务一第一种方法完成。
3.?具体思路及其实现(法二)
?在第六章代码基础上完成,前面的流程与法一都一样,只有多级队列调度部分的代码不一样。(仅展示重要函数)
?先是在main.c中修改kernel_main函数
PUBLIC?int?kernel_main()??
{??
????disp_str("-----\"kernel_main\"?begins-----\n");??
??
????int?j;??
????disp_pos?=?0;??
????for(j=0;j<80*25;j++){??
????????????disp_str("?");??
????????}??
????disp_pos?=?0;??
??
????TASK*???????p_task??????=?task_table;??
????PROCESS*????p_proc??????=?proc_table;??
????char*???????p_task_stack????=?task_stack?+?STACK_SIZE_TOTAL;??
????u16?????selector_ldt????=?SELECTOR_LDT_FIRST;??
????int?i;??
????for?(i?=?0;?i?<?NR_TASKS;?i++)?{??
????????strcpy(p_proc->p_name,?p_task->name);?//?name?of?the?process??
????????p_proc->pid?=?i;?????????//?pid??
??
????????p_proc->ldt_sel?=?selector_ldt;??
??
????????memcpy(&p_proc->ldts[0],?&gdt[SELECTOR_KERNEL_CS?>>?3],??
???????????????sizeof(DESCRIPTOR));??
????????p_proc->ldts[0].attr1?=?DA_C?|?PRIVILEGE_TASK?<<?5;??
????????memcpy(&p_proc->ldts[1],?&gdt[SELECTOR_KERNEL_DS?>>?3],??
???????????????sizeof(DESCRIPTOR));??
????????p_proc->ldts[1].attr1?=?DA_DRW?|?PRIVILEGE_TASK?<<?5;??
????????p_proc->regs.cs??=?((8?*?0)?&?SA_RPL_MASK?&?SA_TI_MASK)??
????????????|?SA_TIL?|?RPL_TASK;??
????????p_proc->regs.ds??=?((8?*?1)?&?SA_RPL_MASK?&?SA_TI_MASK)??
????????????|?SA_TIL?|?RPL_TASK;??
????????p_proc->regs.es??=?((8?*?1)?&?SA_RPL_MASK?&?SA_TI_MASK)??
????????????|?SA_TIL?|?RPL_TASK;??
????????p_proc->regs.fs??=?((8?*?1)?&?SA_RPL_MASK?&?SA_TI_MASK)??
????????????|?SA_TIL?|?RPL_TASK;??
????????p_proc->regs.ss??=?((8?*?1)?&?SA_RPL_MASK?&?SA_TI_MASK)??
????????????|?SA_TIL?|?RPL_TASK;??
????????p_proc->regs.gs??=?(SELECTOR_KERNEL_GS?&?SA_RPL_MASK)??
????????????|?RPL_TASK;??
??
????????p_proc->regs.eip?=?(u32)p_task->initial_eip;??
????????p_proc->regs.esp?=?(u32)p_task_stack;??
????????p_proc->regs.eflags?=?0x1202;?/*?IF=1,?IOPL=1?*/??
??
????????p_task_stack?-=?p_task->stacksize;??
????????p_proc++;??
????????p_task++;??
????????selector_ldt?+=?1?<<?3;??
????}??
??
????time_slice?=?5;//初始化??
????process_num?=?0;??
??
????//?for?(?i?=?0;?i?<?NR_TASKS;?i++)??
????//?{??
????//??proc_table[i].priority?=?1;??
????//??proc_table[i].ticks?=?time_slice;??
????//?}??
??????
????proc_table[0].ticks?=?4;???
????proc_table[0].priority?=?1;??
????proc_table[1].ticks?=?8;??
????proc_table[1].priority?=?2;??
????proc_table[2].ticks?=?16;??
????proc_table[2].priority?=?3;??
??
????k_reenter?=?0;??
????ticks?=?0;??
??????
??
????p_proc_ready????=?proc_table;??
??
????????/*?初始化?8253?PIT?*/??
????????out_byte(TIMER_MODE,?RATE_GENERATOR);??
????????out_byte(TIMER0,?(u8)?(TIMER_FREQ/HZ)?);??
????????out_byte(TIMER0,?(u8)?((TIMER_FREQ/HZ)?>>?8));??
??
????????put_irq_handler(CLOCK_IRQ,?clock_handler);?/*?设定时钟中断处理程序?*/??
????????enable_irq(CLOCK_IRQ);?????????????????????/*?让8259A可以接收时钟中断?*/??
??
????restart();??
??
????while(1){}??
}??
然后修改clock.c中的clock_handler函数?
PUBLIC?void?clock_handler(int?irq)??
{??
????ticks++;??
????p_proc_ready->ticks--;??
????PROCESS*?p;??
??
????//disp_int(p_proc_ready->pid);??
????//if?(k_reenter?!=?0)?{??
????//?return;??
????//}??
??
????if(p_proc_ready->priority?==?1)??
????{??
????????if(p_proc_ready->ticks?==?0)??
????????{??
????????????p_proc_ready->priority?=?2;??
????????????p_proc_ready->ticks?=?time_slice?*?2;??
????????????schedule();??
????????}??
????}else?if(p_proc_ready->priority?==?2){??
????????if(p_proc_ready->ticks?!=?0)??
????????{??
????????????for(p?=?proc_table;?p?<?proc_table?+?NR_TASKS;?p++)??
????????????{??
????????????????if?(p->priority?==?1)??
????????????????{??
????????????????????p_proc_ready?=?p;??
????????????????????return;??
????????????????}??
????????????}??
????????????for(p?=?proc_table;?p?<?proc_table?+?NR_TASKS;?p++)??
????????????{??
????????????????if?(p->priority?==?2)??
????????????????{??
????????????????????p_proc_ready?=?p;??
????????????????????return;??
????????????????}??
????????????}??
????????}else{??
????????????p_proc_ready->priority?=?3;??
????????????p_proc_ready->ticks?=?time_slice?*?3;??
????????????schedule();??
????????}??
????}else?if(p_proc_ready->priority?==?3){??
????????if(p_proc_ready->ticks?!=?0)??
????????{??
????????????for(p?=?proc_table;?p?<?proc_table?+?NR_TASKS;?p++)??
????????????{??
????????????????if?(p->priority?==?1)??
????????????????{??
????????????????????p_proc_ready?=?p;??
????????????????????return;??
????????????????}??
????????????}??
????????????for(p?=?proc_table;?p?<?proc_table?+?NR_TASKS;?p++)??
????????????{??
????????????????if?(p->priority?==?2)??
????????????????{??
????????????????????p_proc_ready?=?p;??
????????????????????return;??
????????????????}??
????????????}??
????????}else{??
????????????p_proc_ready->ticks?=?time_slice?*?3;??
????????????schedule();??
????????????if(p_proc_ready->priority?==?3)??
????????????{??
????????????????while(1){??
????????????????????process_num?=?(process_num?+?1)?%?NR_TASKS;??
????????????????????p_proc_ready?=?proc_table?+?process_num;??
????????????????????if(p_proc_ready->priority?==?3)?break;??????
????????????????}??
????????????}??
????????}??
?????}??
?????return;??
??????
?????//if?(p_proc_ready->ticks?>?0)?{??
?????//?return;??
?????//}??
??????
?????//schedule();??
??????
????}??
?还有schedule函数
PUBLIC?void?schedule()??
{??
????PROCESS*?p;??
????int??inf?=?0xfffffff;??
????int??greatest_priority?=?0xfffffff;??
??
????while?(greatest_priority?==?inf)?{??
????????for?(p?=?proc_table;?p?<?proc_table+NR_TASKS;?p++)?{??
????????????if?(p->priority?<?greatest_priority)?{??
????????????????greatest_priority?=?p->priority;??
????????????????p_proc_ready?=?p;??
????????????}??
????????}??
??
????????//?if?(!greatest_ticks)?{??
????????//??for?(p?=?proc_table;?p?<?proc_table+NR_TASKS;?p++)?{??
????????//??????p->ticks?=?p->priority;??
????????//??}??
????????//?}??
????}??
}??
编译运行后,结果如下?
?可以看到,显示根据时间片和优先级输出,最后优先级一致后,则进行轮询,故实验成功。