操作系統實現----多進程(無特權級轉移)

在上一篇的基礎之上,寫一個多進程程序:程序切換原理:在定時中斷的時候將當前進程的現場保存在當前進程的堆棧中(中斷時並自動壓入eflag,cs,eip,因爲沒有特權級的轉變,所以也沒有堆棧的切換),然後將棧設爲目標進程的堆棧,並彈出該進程現場,中斷最後的iret指令會將程序接着目標進程的eip運行。

kernel.s因爲將所有的段偏移設爲0,所以編程時地址的處理非常容易。

boot.s

%define KERNEL_SEG 0x1000	;內核開始運行的段位置
%define KERNEL_LEN 20		;內核扇區所佔數目


org 0x7c00
	
	mov ax,cs
	mov ds,ax
	mov es,ax
	
	mov ax,msg
	mov bp,ax
	mov ax,0x1301
	mov bx,0x000c
	mov cx,msgLen
	mov dx,0x0000
	int 10h;
	
load:
	mov ax,KERNEL_SEG
	mov es,ax
	mov ah,02			
	mov al,KERNEL_LEN
	xor bx,bx		
	mov ch,0
	mov cl,2
	mov dh,0
	mov dl,0
	int 13h

movKernel:
	mov ax,KERNEL_SEG
	mov ds,ax
	xor ax,ax
	mov es,ax
	xor si,si
	xor di,di
	mov cx,KERNEL_LEN * 512
	cli
copy:
	mov al,[ds:si]
	mov	[es:di],al
	inc si
	inc di
	loop copy

	jmp 0:0


msg: db "Loading kernel"
msgLen equ $-msg

times 510-($-$$) db 0
	dw 0xaa55



kernel.s:

%define STACK_LEN 1024		;內核棧空間、進程棧空間大小

	jmp start
;gdt
gdt:	db 0,0,0,0,0,0,0,0
gdt_cs:	db 0xff,0x7,0,0,0,0x9a,0xc0,0
gdt_ds:	db 0xff,0x7,0,0,0,0x92,0xc0,0
gdt_gs:	db 0x02,0,0,0x80,0x0b,0x92,0xc0,0

gdtLen equ $-gdt

selector_cs equ gdt_cs - gdt
selector_ds equ gdt_ds - gdt
selector_gs equ gdt_gs - gdt


gdtPtr:
	dw	gdtLen - 1
	dd	gdt

idt:
%rep 8
	dw 	intHandler	;offset
	dw	selector_cs	;selector
	dw	0x8e00		;property
	dw	0			;offset
%endrep	
	dw 	timeInt		;offset
	dw	selector_cs	;selector
	dw	0x8e00		;property
	dw	0			;offset
%rep 256-9
	dw 	intHandler	;offset
	dw	selector_cs	;selector
	dw	0x8e00		;property
	dw	0			;offset
%endrep	

idtLen	equ $ - idt

idtPtr:
	dw	idtLen - 1
	dd	idt
	
start:
	mov ax,cs
	mov ds,ax
	mov es,ax

	lgdt [gdtPtr]
	
	cli

	lidt [idtPtr]

	in al,0x92
	or al,00000010b
	out 0x92,al

	mov eax,cr0
	or eax,1
	mov cr0,eax
	
	jmp selector_cs:start_32

[bits 32]
start_32:
	mov ax,selector_ds
	mov ds,ax			;數據段初始化
	mov ss,ax			;堆棧段初始化
	mov esp,stack_top	;堆棧棧底

	mov ah,0x0c
	mov al,'P'
	call dis_str
	int 0x80
	int 0x80


	mov al,0x36		;控制字:通道0工作方式3、計數初值採用二進制
	out 0x43,al
	mov ax,11930	;頻率爲100hz
	out 0x40,al		;低位
	mov al,ah
	out 0x40,al		;高位

	sti

	;初始化兩個進程,主要是進程對應的堆棧區,事先要壓入現場信息
	;初始化進程1的堆棧
	mov ax,selector_ds
	mov ss,ax			;堆棧段初始化
	mov esp,stack_top_1	;堆棧棧底

	pushf
	push dword selector_cs		;push cs
	push dword task_1			;push eip

	push ds
	push es
	push fs
	push gs	
	pushad	
	mov [stack_1],esp		;保存進程1現在的棧指針

	;初始化進程0的堆棧
	mov ax,selector_ds
	mov ss,ax			;堆棧段初始化
	mov esp,stack_top_0	;堆棧棧底

	mov [stack_0],esp	;進程0的堆棧指針
	
	pushf
	push dword selector_cs		;push cs
	push dword task_0			;push eip

	iret		;啓動進程0

	jmp $		;等待時鐘中斷去切換執行兩個任務
	;
dis_str:
	push ebx
	mov bx,selector_gs
	mov gs,bx
	mov bx,selector_ds
	mov ds,bx
	mov ebx,[cursor_i]
	shl ebx,1
	mov [gs:ebx],ax
	shr ebx,1
	inc ebx
	cmp ebx,80*25
	jne .1
	mov ebx,0
.1:	mov [cursor_i],ebx
	pop ebx
	ret

intHandler:
	iret
	mov ah,0x0c
	mov al,'I'
	call dis_str
	mov al,'n'
	call dis_str
	mov al,'t'
	call dis_str
;	mov al,0x20			;發送EOI 	;爲什麼這些中斷不需要寫這句話?
;	out 0x20,al			;中斷處理結束,要是沒有這一句的話,只能響應中斷一次
	iret

timeInt:
	push ds
	push es
	push fs
	push gs	
	pushad
	;由於已經保存了現場,這些寄存器可以使用了
	;切換到內核空間
	mov	ax,selector_ds
	mov ds,ax
	mov al,[process_now]
	cmp al,0

	jne	j1	
	;如果是進程0	
	mov [stack_0],esp	;保存進程0的堆棧指針
	mov al,1
	mov [process_now],al
	mov ax,selector_ds
	mov ss,ax			;切換到進程1的堆棧
	mov esp,[stack_1]	;
	jmp j2
j1:
	;如果是進程1
	mov [stack_1],esp
	mov al,0
	mov [process_now],al
	mov ax,selector_ds
	mov ss,ax			;
	mov esp,[stack_0]	;切換到進程0的堆棧,
j2:	mov al,0x20			;發送EOI 	
	out 0x20,al			;中斷處理結束,要是沒有這一句的話,只能響應中斷一次
	
	;將目標進程的現場回覆
	popad	;popad 將通用寄存器彈出(除了esp)
	pop gs
	pop fs
	pop es
	pop ds	

	iret

task_0:
loo:
	mov ah,0x0c
	mov al,'A'
	call dis_str
	mov ecx,0xfffff		;delay
	loop $
	jmp loo
task_1:
loo2:
	mov ah,0x0b
	mov al,'B'
	call dis_str
	mov ecx,0xfffff		;delay
	loop $
	jmp loo2


data_label:
	cursor_i dd 80		;顯示屏幕位置	
	process_now	db	0	;當前運行進程
	stack_0	dd	0		;0進程的棧指針
	stack_1	dd	0		;1進程的棧指針

	times STACK_LEN db 0
stack_top:		;內核棧底
	times STACK_LEN db 0
stack_top_0:	;進程0使用的堆棧
	times STACK_LEN db 0
stack_top_1:	;進程1使用的堆棧

	times (512*20 - ($-$$)) db 0


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章