由于Linux0.11的内核态进程切换使用的方式是用ljmp来进行TSS的跳转,效率较低,因此考虑对其进行优化,改为后面版本使用的kernel stack栈的切换
需要做的任务
1.重写schedule,switch_to函数
2.将修改过的函数接在一起
3.修改fork函数
目前 Linux 0.11 中工作的 schedule() 函数是首先找到下一个进程的数组位置 next,而这个 next 就是 GDT 中的 n,所以这个 next 是用来找到切换后目标 TSS 段的段描述符的,一旦获得了这个 next 值,直接调用上面剖析的那个宏展开 switch_to(next);就能完成如图 TSS 切换所示的切换了。
现在,我们不用 TSS 进行切换,而是采用切换内核栈的方式来完成进程切换,所以在新的 switch_to 中将用到当前进程的 PCB、目标进程的 PCB、当前进程的内核栈、目标进程的内核栈等信息。由于 Linux 0.11 进程的内核栈和该进程的 PCB 在同一页内存上(一块 4KB 大小的内存),其中 PCB 位于这页内存的低地址,栈位于这页内存的高地址;另外,由于当前进程的 PCB 是用一个全局变量 current 指向的,所以只要告诉新 switch_to()函数一个指向目标进程 PCB 的指针就可以了。同时还要将 next 也传递进去,虽然 TSS(next)不再需要了,但是 LDT(next)仍然是需要的,也就是说,现在每个进程不用有自己的 TSS 了,因为已经不采用 TSS 进程切换了,但是每个进程需要有自己的 LDT,地址分离地址还是必须要有的,而进程切换必然要涉及到 LDT 的切换。
对应的switch_to源码应该修改下:
if ((*p)->state == TASK_RUNNING && (*p)->counter > c) c = (*p)->counter, next = i; //...... switch_to(next);
改为
if ((*p)->state == TASK_RUNNING && (*p)->counter > c) c = (*p)->counter, next = i, pnext = *p; //....... switch_to(pnext, LDT(next));
然后再修改switch_to函数
switch_to: !处理栈帧 pushl %ebp movl %esp,%ebp !寄存器压栈处理 pushl %ecx pushl %ebx pushl %eax !ebx,current指向下一个pcb:8(%ebp)表示ebp上两格:因为switch_to有两个参数pnext和LDT(next),所以向上两格刚好是pnext movl 8(%ebp),%ebx cmpl %ebx,current je 1f ! 切换PCB ! ... ! TSS中的内核栈指针的重写 ! ... ! 切换内核栈 ! ... ! 切换LDT ! ... movl $0x17,%ecx mov %cx,%fs ! 和后面的 clts 配合来处理协处理器,由于和主题关系不大,此处不做论述 cmpl %eax,last_task_used_math jne 1f clts 1: popl %eax popl %ebx popl %ecx popl %ebp ret
原文地址:https://www.cnblogs.com/zsben991126/p/12038747.html