第六章 :进程,妈的终于推进到进程了。
我们需要一个数据结构记录进程的状态,在进程要被挂起的时候,进程信息就被写入这个数据结构,等到进程重新启动的时候,这个信息重新被读出来。
诱发进程切换的原因不只一种,比较典型的是发生了时钟中断。当时钟中断时,中断处理程序将会把控制权交给进程调度模块。
这时如果系统认为应该进行进程切换,进程切换就发生了,当前进程的状态就会被保存起来,队列中的下一个进程将被恢复执行。
最简单的进程
一个进程正在运行,这时候时钟中断发生了,特权级从ring1跳到ring0,开始执行时钟中断处理程序,中断处理程序这时调用进程调度模块,指定下一个应该运行的进程,当中断处理程序结束时,下一个进程准备就绪并开始运行,特权级从ring0跳回到ring1.
流程是这样的:
1、进程A在运行中
2、时钟中断发生,ring1-ring0,时钟中断处理程序启动。
3、进程调度,下一个应运行的进程(假设为进程B)被指定。
4、进程B被恢复,ring0--ring1 //进程B也不是新的,只是被挂起了
5、进程B运行中
想要实现这些功能,我们必须完成的如下几项任务:
时钟中断处理程序
进程调度模块
两个进程
---------------------------------------------------------------------------------------------------------
实现的关键技术:
1、进程的那些状态需要被保存
所有的寄存器
2、进程的状态需要何时以及怎样被保存
在进程刚刚被挂起时保存所有寄存器的值,使用pushad命令
这些代码我们应该写在时钟中断例程的最顶端,以便中断发生时马上执行。
3、如何恢复进程B的状态
使用pop指令,等所有寄存器的值都被恢复,执行指令iretd,就回到了进程B
4进程表的引人
定义一个进程结构体称为进程表,并形成一个进程表数组。
进程表是描述进程的,必须独立与进程之外,当我们把寄存器值压入进程表时,已经处在进程管理模块中了。
--------------------------------------------------------------------------------------------------------
进程栈和内核栈
当寄存器的值被保存到进程表,进程调度模块就开始执行了。
我们在进程调度模块会用到堆栈,而寄存器被压到进程表之后,esp是指向进程表的某个位置的。这是不能再压堆栈,因为会破坏压入的寄存器的数据。
这是我们让esp指向内核堆栈,在进程调度中使用内核堆栈。这样就不会破坏数据了。
进程栈--进程运行时自身的堆栈
进程表--存储进程状态信息的数据结构。
内核栈--进程调度模块运行时使用的堆栈
-----------------------------------------------------------------------------------------------------------
特权级变换:ring1----ring0
特权级转换,如果从外层向内层转换,需要从当前TSS中取得内层ss和esp作为目标代码的ss和esp。所以我们必须事先准备好TSS,
由于每个进程相对独立,我们把涉及到的描述符放在局部描述符表LDT中,所以我们还需要为每个进程准备IDT。
---------------------------------------------------------------------------------------------------------------
特权级变化:ring0--ring1
当我们准备开始第一个进程时,面临从ring0---ring1的转移,并启动进程A。所以我们可以在准备就绪之后跳转到中断处理程序的后半部分假装发生了一次时钟中断来启动进程A,利用ireted来实现ring0--ring1的转移。
这是因为ireted这个指令使中断处理程序(ring0级)跳转到进程(ring1级)
----------------------------------------------------------------------------------------------------------------