一个操作系统的实现(6)


进程与进程调度

进程切换

特权级变换: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

运行结果:


文章作者: Aiaa
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Aiaa !
  目录