多任务并行执行(上)

问题

如何并行执行多个任务?(如何在多个任务之间切换执行?)

方案思路

在中断服务程序中改变 gCTaskAddr 的值

  • 注:gCtaskAddr 指向当前执行任务中的 Task 结构体

课程目标:创建两个任务并行执行

1. 启动时钟中断

2. 启动 TaskA 并打开中断开关

3. 在时钟中断服务程序中使得 gCTaskAddr 指向 TaskB

4. TaskB 执行 (中断开关已打开)

5. 在时钟中断服务程序中使得 gCTaskAddr 指向 TaskA

6. 。。。

任务并行方案

再再论TSS。。。

目前的设计中,使用TSS唯一的意义仅是转入高特权级时获取栈的位置 (潜台词:仅仅在中断发生时,将 ss0 和 esp0 指定的内存作为初始内核栈)。

因此,所有任务共享一个TSS结构体;在切换任务执行时,重新指定 TSS 结构体中的 ss0 和 esp0;注:每个任务的 LDT 依然是私有的,不可共享。

多任务并行执行

kmain.c

#include "kernel.h"
#include "screen.h"
#include "global.h"

void (* const InitInterrupt)() = NULL;
void (* const EnableTimer)() = NULL;
void (* const SendEOI)() = NULL;

Task t = {0};
Task p = {0};

volatile Task* gCTaskAddr = NULL;

TSS gTss = {0};

extern void TimerHandlerEntry();

void ChangeTask()
{
	gCTaskAddr = (gCTaskAddr == &p) ? &t : &p;
	
	gTss.ss0 = GDT_DATA32_FLAT_SELECTOR;
    gTss.esp0 = (uint)&gCTaskAddr->rv.gs + sizeof(RegValue);
	
	SetDescValue(&gGdtInfo.entry[GDT_TASK_LDT_INDEX], (uint)&gCTaskAddr->ldt, sizeof(gCTaskAddr->ldt) - 1, DA_LDT + DA_DPL0);
	
	LoadTask(gCTaskAddr);
}

void TimerHandler()
{
	static uint i = 0;
	
	i = (i + 1) % 5;
	
	if(i == 0)
	{
		ChangeTask();
	}
	
	SendEOI(MASTER_EOI_PORT);
	
}

Delay(int n)
{
	while(n > 0)
	{
		int i = 0, j = 0;
		
		for(i = 0; i < 1000; i++)
		{
			for(j = 0; j < 1000; j++)
			{
				asm volatile("nop\n");
			}
		}
		
		n--;
	}
}

void TaskA()
{
	int i = 0;
	
	SetPrintPos(0, 12);
	PrintString("TaskA: ");
	
	while(1)
	{
		SetPrintPos(8, 12);
		PrintChar('A' + i);
		i = (i + 1) % 26;
		Delay(1);
	}
}

void TaskB()
{
	int i = 0;
	
	SetPrintPos(0, 13);
	PrintString("TaskB: ");
	
	while(1)
	{
		SetPrintPos(8, 13);
		PrintChar('0' + i);
		i = (i + 1) % 10;
		Delay(1);
	}
}

void InitTask(Task* pt, void (* entry) ())
{
	pt->rv.gs = LDT_VIDEO_SELECTOR;
	pt->rv.cs = LDT_CODE32_SELECTOR;
	pt->rv.ds = LDT_DATA32_SELECTOR;
	pt->rv.es = LDT_DATA32_SELECTOR;
	pt->rv.fs = LDT_DATA32_SELECTOR;
	pt->rv.ss = LDT_DATA32_SELECTOR;
	
	pt->rv.esp = (uint)pt->stack + sizeof(pt->stack);
	pt->rv.eip = (uint)entry;
	pt->rv.eflags = 0x3202;

	gTss.ss0 = GDT_DATA32_FLAT_SELECTOR;
	gTss.esp0 = (uint)&pt->rv.gs + sizeof(pt->rv);
	gTss.iomb = sizeof(TSS);
	
	SetDescValue(pt->ldt + LDT_VIDEO_INDEX,  0xB8000, 0X07FFF, DA_DRWA + DA_32 + DA_DPL3);
	SetDescValue(pt->ldt + LDT_CODE32_INDEX, 0x00,    0XFFFFF, DA_C + DA_32 + DA_DPL3);
	SetDescValue(pt->ldt + LDT_DATA32_INDEX, 0x00,    0XFFFFF, DA_DRW + DA_32 + DA_DPL3);

	pt->ldtSelector = GDT_TASK_LDT_SELECTOR;
	pt->tssSelector = GDT_TASK_TSS_SELECTOR;
	
	SetDescValue(&gGdtInfo.entry[GDT_TASK_LDT_INDEX], (uint)&pt->ldt, sizeof(pt->ldt) - 1, DA_LDT + DA_DPL0);
	SetDescValue(&gGdtInfo.entry[GDT_TASK_TSS_INDEX], (uint)&gTss, sizeof(gTss) - 1, DA_386TSS + DA_DPL0);
}

void KMain()
{
	PrintString("D.T.OS\n");
	
	uint base = 0;
	uint limit = 0;
	uint attr = 0;
	int i = 0;
	
	PrintString("Gdt Entry: ");
	PrintIntHex((uint)gGdtInfo.entry);
	PrintChar('\n');
	
	PrintString("Gdt Size: ");
	PrintIntDec((uint)gGdtInfo.size);
	PrintChar('\n');

	PrintString("Idt Entry: ");
	PrintIntHex((uint)gIdtInfo.entry);
	PrintChar('\n');
	
	PrintString("Idt Size: ");
	PrintIntDec((uint)gIdtInfo.size);
	PrintChar('\n');
	
	InitTask(&p, TaskB);
	InitTask(&t, TaskA);	
	
	SetIntHandler(gIdtInfo.entry + 0x20, (uint)TimerHandlerEntry);
	
	PrintString("Stack Bottom: ");
    PrintIntHex((uint)t.stack);
    PrintString("    Stack Top: ");
    PrintIntHex((uint)t.stack + sizeof(t.stack));
    
    gCTaskAddr = &t;
    
    InitInterrupt();
    EnableTimer();
	
	RunTask(gCTaskAddr);
}

kentry.asm


%include "common.asm"

global _start
global TimerHandlerEntry

extern gCTaskAddr
extern TimerHandler
extern KMain
extern ClearScreen
extern gGdtInfo
extern gIdtInfo
extern RunTask
extern InitInterrupt
extern EnableTimer
extern SendEOI
extern LoadTask

%macro BeginISR 0
	sub esp, 4
	
	pushad
	
	push ds
	push es
	push fs
	push gs
	
	mov dx, ss
	mov ds, dx
	mov es, dx
	
	mov esp, BaseOfLoader	
%endmacro

%macro EndISR 0
	mov esp, [gCTaskAddr]
	
	pop gs
	pop fs
	pop es
	pop ds
	
	popad
	
	add esp, 4
	
	iret
%endmacro

[section .text]
[bits 32]
_start:
    mov ebp, 0
    
    call InitGlobal
    call ClearScreen
    
    call KMain
    
    
    jmp $
    
;
;
InitGlobal:
	push ebp
	mov ebp, esp
	
	mov eax, dword [GdtEntry]
	mov dword [gGdtInfo], eax
	
	mov eax, dword [GdtSize]
	mov dword [gGdtInfo + 4], eax
	
	mov eax, dword [IdtEntry]
	mov dword [gIdtInfo], eax
	
	mov eax, dword [IdtSize]
	mov dword [gIdtInfo + 4], eax
	
	mov eax, dword [RunTaskEntry]
	mov dword [RunTask], eax
	
	mov eax, dword [InitInterruptEntry]
	mov dword [InitInterrupt], eax
	
	mov eax, dword [EnableTimerEntry]
	mov dword [EnableTimer], eax
	
	mov eax, dword[SendEOIEntry]
	mov dword [SendEOI], eax
	
	mov eax, dword [LoadTaskEntry]
	mov dword [LoadTask], eax
	
	leave
	
	ret
	
;
;
TimerHandlerEntry:
BeginISR
	call TimerHandler
EndISR
	
	

loader.asm


%include "blfunc.asm"
%include "common.asm"

org BaseOfLoader

interface:
    BaseOfStack     equ    BaseOfLoader	
	BaseOfTarget    equ    BaseOfKernel
	Target  db "KERNEL     "
    TarLen          equ   ($-Target)

[section .gdt]
; GDT definition
;                                     段基址,       段界限,       段属性
GDT_ENTRY		    : Descriptor		0, 			  0,		   0
CODE32_DESC		    : Descriptor		0, 	   Code32SegLen - 1,  DA_32 + DA_C + DA_DPL0
VIDEO_DESC		    : Descriptor	 0xB8000, 	  0x07FFF,        DA_32 + DA_DRWA + DA_DPL0
CODE32_FLAT_DESC	: Descriptor		0, 	      0xFFFFF,        DA_32 + DA_C + DA_DPL0
DATA32_FLAT_DESC	: Descriptor		0, 	      0xFFFFF,        DA_32 + DA_DRW + DA_DPL0
TASK_LDT_DESC       : Descriptor		0, 			  0,		   0
TASK_TSS_DESC       : Descriptor		0, 			  0,		   0
; GDT end  

GdtLen	  equ	   $ - GDT_ENTRY
GdtPtr:    
		dw	GdtLen - 1
		dd	0

; GDT Selector
Code32Selector	    equ    (0x0001 << 3) + SA_TIG + SA_RPL0
VideoSelector	    equ    (0x0002 << 3) + SA_TIG + SA_RPL0
Code32FlatSelector	equ    (0x0003 << 3) + SA_TIG + SA_RPL0
Data32FlatSelector	equ    (0x0004 << 3) + SA_TIG + SA_RPL0
; end of [section .gdt]


[section .idt]
align 32
[bits 32]
IDT_ENTRY:
; IDT definition
;                           Selector,           Offset,            DCount,           Attribut
%rep 256
             Gate        Code32FlatSelector,   DefaultHandler,        0,             DA_386IGate + DA_DPL0
%endrep
; IDT end

IdtLen      equ     $ - IDT_ENTRY
IdtPtr:
        dw  IdtLen - 1
        dd  0

[section .s16]
[bits 16]
BLMain:
	mov ax, cs
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, SPInitValue

	; initialize GDT for 32 bits code segment
	mov esi, CODE32_SEGMENT
	mov edi, CODE32_DESC
	call InitDescItem

	; initalize GDT pointer struct
	mov eax, 0
	mov ax, ds
	shl eax, 4
	add eax, GDT_ENTRY
	mov dword [GdtPtr + 2], eax
	
	; initialize IDT pointer struct
	mov eax, 0
	mov ax, ds
	shl eax, 4
	add eax, IDT_ENTRY
	mov dword [IdtPtr + 2], eax
	
	call LoadTarget
	cmp dx, 0
	jz Output	

	call StoreGlobal

	; 1. load GDT
    lgdt [GdtPtr]
    
    ; 2. close interrupt
    ;    load IDT
    ;    set IOPL = 3
    cli 
    
    lidt [IdtPtr]
    
    pushf
    pop eax
    
    or eax, 0x3000
    
    push eax
    popf
    
    ; 3. open A20
    in al, 0x92
    or al, 00000010b
    out 0x92, al
    
    ; 4. enter protect mode
    mov eax, cr0
    or eax, 0x01
    mov cr0, eax
    
    ; 5. jump to 32 bits code
    jmp dword Code32Selector : 0


Output:
	mov bp, ErrStr
	mov cx, ErrLen
	call Print
	
	jmp $

; esi    --> code segment labelBACK_ENTRY_SEGMENT
; edi    --> descriptor label
InitDescItem:
	push eax

	mov eax, 0
	mov ax, cs
	shl eax, 4
	add eax, esi
	mov word [edi + 2], ax
	shr eax, 16
	mov byte [edi + 4], al
	mov byte [edi + 7], ah
	
	pop eax

	ret

;
;
StoreGlobal:
	push eax	
	
	mov dword [RunTaskEntry], RunTask
	
	mov dword [LoadTaskEntry], LoadTask
	
	mov dword [InitInterruptEntry], InitInterrupt
	
	mov dword [EnableTimerEntry], EnableTimer 
	
	mov dword [SendEOIEntry], SendEOI
	
	mov eax, dword [GdtPtr + 2]
	mov dword [GdtEntry], eax
	
	mov dword [GdtSize], GdtLen / 8
	
	mov eax, dword [IdtPtr + 2]
	mov dword [IdtEntry], eax
	
	mov dword [IdtSize], IdtLen / 8
	
	pop eax
	
	ret

[section .sfunc]
[bits 32]

DefaultHandlerFunc:

    iret
    
DefaultHandler      equ     DefaultHandlerFunc - $$

; void InitInterrupt()
;
InitInterrupt:
	push ebp
	mov ebp, esp
	
    push ax
    push dx
    
    call Init8259A
    
    sti
    
    mov al, 0xFF
    mov dx, MASTER_IMR_PORT
    call WriteIMR
    
    mov al, 0xFF
    mov dx, SLAVE_IMR_PORT
    call WriteIMR
    
    pop dx
    pop ax
    
    leave
    
    ret
    
; void EnableTimer()
;
EnableTimer:
	push ebp
	mov ebp, esp
	
	push ax
    push dx
    
    mov dx, MASTER_IMR_PORT
    call ReadIMR
    and ax, 0xFE
    call WriteIMR
    
    pop ax
    pop dx
	
	leave
	
	ret

; SendEOI(uint port) 
;
SendEOI:
	push ebp
	mov ebp, esp
	
	mov edx, [esp + 8];
	mov al, 0x20
	out dx, al
	call Delay
	
	leave
	
	ret

;
;
Delay:
    %rep 5
    nop
    %endrep
    
    ret
    
;
;
Init8259A:
    push ax
    
    ; Master
    ; ICW1
    mov al, 00010001B
    out MASTER_ICW1_PORT, al
    call Delay
    
    ; ICW2
    mov al, 0x20
    out MASTER_ICW2_PORT, al
    call Delay
    
    ; ICW3
    mov al, 00000100B
    out MASTER_ICW3_PORT, al
    call Delay
    
    ; ICW4
    mov al, 00010001B
    out MASTER_ICW4_PORT, al
    call Delay
    
    ; Slave
    ; ICW1
    mov al, 00010001B
    out SLAVE_ICW1_PORT, al
    call Delay
    
    ; ICW2
    mov al, 0x28
    out SLAVE_ICW2_PORT, al
    call Delay
    
    ; ICW3
    mov al, 00000010B
    out SLAVE_ICW3_PORT, al
    call Delay
    
    ; ICW4
    mov al, 00000001B
    out SLAVE_ICW4_PORT, al
    call Delay
    
    pop ax
    
    ret

;al  --> IMR register value
;dx  --> 8259A port
WriteIMR:
    out dx, al
    call Delay
    
    ret
    
; dx  --> 8259A port
; return:
;    al  --> IMR register value
ReadIMR:
    in al, dx
    call Delay
    
    ret

; 
; dx  --> 8259A port
WriteEOI:
    push ax
    
    mov al, 0x20
    out dx, al
    call Delay
    
    pop ax
    
    ret



[section .gfunc]
[bits 32]
;
; parameter  ===> Task* pt
RunTask:
	push ebp
	mov ebp, esp
	
	mov esp, [ebp + 8]
	
	lldt word [esp + 200]
	ltr  word [esp + 202] 
	
	pop gs
	pop fs
	pop es
	pop ds
	
	popad
	
	add esp, 4
	
	iret
	
; void LoadTask(Task* pt)
;
LoadTask:
	push ebp
	mov ebp, esp
	
	mov eax, [esp + 8]
	
	lldt word [eax + 200]
	
	leave
	
	ret
	

[section .s32]
[bits 32]
CODE32_SEGMENT:
	mov ax, VideoSelector
	mov gs, ax
	
	mov ax, Data32FlatSelector
	mov ds, ax
	mov es, ax
	mov fs, ax
	
	mov ax, Data32FlatSelector
	mov ss, ax
	mov esp, BaseOfLoader
	
	jmp dword Code32FlatSelector : BaseOfKernel

Code32SegLen	equ    $ - CODE32_SEGMENT


ErrStr db  "No Kernel" 
ErrLen equ ($-ErrStr)

Buffer:
    db  0x00

本次任务需要定义两个任务 t 和 p,两个任务入口函数,TaskA 函数用于打印字母 A - Z,TaskB 函数用于打印数字 1 - 9。

由于我们现在使用 TSS 的唯一目的是:仅仅在中断发生时,将 ss0 和 esp0 指定的内存作为初始内核栈。因此,我们定义了一个全局的 TSS,所有任务共享这个全局 TSS。

InitTask 函数用于初始化一个任务,并设置这个任务执行的入口地址。

初始化任务 t 和 p 后,打开中断并使能时钟中断,然后开始 t 任务的执行;当前的esp0指向的是 t 的 RegValue 的底部,LDT 也是 t 任务结构内部定义的 ldt。

RunTask(gCTaskAddr) 执行后,首先是加载任务 t 的 LDT 和 TSS,然后恢复 t 任务的一系列寄存器,通过 iret 指令,转去执行 TaskA;每过1个时钟中断,都会保护 t 的任务上下文,去执行中断服务程序,然后恢复上下文;当每过5个时钟中断时,首先会保存 t 的上下文,让gCTaskAddr 指向任务 p,改变TSS,让 esp0 指向 p 的 RegValue 的底部 (以便下一次中断保存的是任务 p 的上下文),改变全局段描述符LDT;然后去执行 LoadTask。

LoadTask 里,让 esp 指向 gCTaskAddr (任务结构 p),加载任务 p 里的 ldt,然后再执行EndISR,恢复 p 任务的上下文,就开始执行 p 任务了。

如此往复。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值