进程与进程调度
进程切换
特权级变换:ring1-ringo
在以前的代码中,还没有使用过除ring0之外的其他特权级。对于有特权级变换的转移,如果由外层向内层转移时,需要从 TSS中取得从当前TSS中取出内层ss和esp作为目标代码的ss和esp。所以,我们必须事先准备好TSS。由于每个进程相对独立,我们把涉及到的描述符放在局部描述符表LDT中,所以,我们还需要为每个进程准备LDT。
在刚才的分析过程中,假设的初始状态是“进程A运行中”。到目前为止我们的代码完全运行在ring0。所以,可以预见,当准备开始第一个进程时,面临一个从 ring0到ring1的转移,并启动进程A。
关键代码:
代码实现了以下的部分:
时钟中断处理实现,用以实现ring0到ring1的跳转;
全新的进程表、进程体、TSS结构和更新过的GDT结构;
准备一个小的进程体;
对进程表、TSS进行初始化……
在一切准备完毕后,我们便可通过restart跳入进程,最终可以看到运行起来的进程。
而回顾整个程序,我们第一个进程的启动过程主要分为以下四步:
- 第一步,准备好进程体(本例中为TestA());
- 第二步,初始化GDT中的TSS和LDT两个描述符,并且初始化TSS(在init_port()中完成);
- 第三步,准备进程表(在kernel_main()中完成);
- 第四步,完成跳转,实现ring0->ring1.开始进程(kernel.asm中的restart)。
借助Makefile可以轻松的完成调试,只需要输入make final,即可由GNU Make自动完成编译链接工作,只需要输入bochs即可开始调试。
结果:
时钟中断:
丰富中断处理程序
代码及分析
;打开时钟中断
out_byte(INT_M_CTLMASK, 0xFE); // Master 8259, OCW1.
out_byte(INT_S_CTLMASK, 0xFF); // Slave 8259, OCW1.
;设置EOI
hwint00: ; Interrupt routine for irq 0 (the clock).
mov al, EOI ; `. reenable
out INT_M_CTL, al ; / master 8259
Iretd
EOI和INT_M_CTL
INT_M_CTL equ 0x20 ; I/O port for interrupt controller <Master>
INT_M_CTLMASK equ 0x21 ; setting bits in this port disables ints <Master>
INT_S_CTL equ 0xA0 ; I/O port for second interrupt controller <Slave>
INT_S_CTLMASK equ 0xA1 ; setting bits in this port disables ints <Slave>
EOI equ 0x20
时钟中断处理程序
extern disp_str
...
[SECTION .data]
clock_int_msg db "^", 0
...
ALIGN 16
hwint00: ; Interrupt routine for irq 0 (the clock).
sub esp, 4
pushad ; `.
push ds ; |
push es ; | 保存原寄存器值
push fs ; |
push gs ; /
mov dx, ss
mov ds, dx
mov es, dx
mov esp, StackTop ; 切到内核栈
inc byte [gs:0] ; 改变屏幕第 0 行, 第 0 列的字符
mov al, EOI ; `. reenable
out INT_M_CTL, al ; / master 8259
push clock_int_msg
call disp_str
add esp, 4
mov esp, [p_proc_ready] ; 离开内核栈
lea eax, [esp + P_STACKTOP]
mov dword [tss + TSS3_S_SP0], eax
pop gs ; `.
pop fs ; |
pop es ; | 恢复原寄存器值
pop ds ; |
popad ; /
add esp, 4
iretd
运行结果:
解决中断重入问题
关键代码分析
修改后的时间中断:
hwint00: ; Interrupt routine for irq 0 (the clock).
sub esp, 4
pushad ; `.
push ds ; |
push es ; | 保存原寄存器值
push fs ; |
push gs ; /
mov dx, ss
mov ds, dx
mov es, dx
inc byte [gs:0] ; 改变屏幕第 0 行, 第 0 列的字符
mov al, EOI ; `. reenable
out INT_M_CTL, al ; / master 8259
inc dword [k_reenter]
cmp dword [k_reenter], 0
jne .re_enter
mov esp, StackTop ; 切到内核栈
sti
push clock_int_msg
call disp_str
add esp, 4
;;; push 1
;;; call delay
;;; add esp, 4
cli
mov esp, [p_proc_ready] ; 离开内核栈
lea eax, [esp + P_STACKTOP]
mov dword [tss + TSS3_S_SP0], eax
.re_enter: ; 如果(k_reenter != 0),会跳转到这里
dec dword [k_reenter]
pop gs ; `.
pop fs ; |
pop es ; | 恢复原寄存器值
pop ds ; |
popad ; /
add esp, 4
Iretd
运行结果: