大家一起写操作系统(4)-简单的任务切换

下面这段程序定义了两个任务,每个任务都有自己的TSS,LDT。任务0打印字符‘a’,任务1打印字符'b'.任务切换通过时钟中断处理过程完成。时钟到达时,判断当前任务号,切换到另一个任务,并用jmp tss:0的方式实现任务切换。任务中通过0x80系统调用打印字符。任务切换时,cpu会自动把当前任务的上下文(主要是各种寄存器,ldt)等保存当当前任务的TSS段中,并加载目标任务的TSS段相关信息,切换到新任务。


LATCH equ 11930 ;时钟中断
VIDEO_DS equ 0x18 ;显存段
TSS0_CS equ 0x20  ;任务0 TSS段
TSS0_LDT equ 0x28 ;任务0 LDT段
TSS1_CS equ 0x30  ;任务1 TSS段
TSS1_LDT equ 0x38 ;任务1 LDT段

[SECTION text]
[BITS 32]
startup_32:
    mov eax,0x10
    mov ds,ax
    lss esp,[init_stack] ;mem low->reg,mem high->ss
    call setup_idt ;设置默认IDT
    call setup_gdt ;设置GDT
    mov  eax,0x10  ;设置完GDT后,重新加载ds,es,fs,gs,ss,esp
    mov  ds,ax
    mov  es,ax
    mov  fs,ax
    mov  gs,ax
    lss esp,[init_stack]
    
;设置8253定时芯片,把计数器通道0设置成每隔10ms向中断控制器发送一个中断请求
    mov al,0x36
    mov edx,0x43
    out dx,al
    mov eax,LATCH
    mov edx,0x40
    out dx,al
    mov al,ah
    out dx,al
    
;在IDT表第8和0x80项处分别设置定时中断门和系统调用陷阱门描述符
;    31 ------------------------- 16 ---------------------8 --------- 0  中断门描述符格式
;      过程入口点偏移高位       P DPL 01110                高32位
;      段 选 择  符             过程入口点偏移低位      低32位

    mov eax,0x00080000    ;eax高位为内核代码段选择符,低位为时钟中断程序偏移
    mov ax,time_interrupt
    mov dx,0x8E00         ;中断门类型;
    mov ecx,0x08          ;中断向量号,BIOS设置的时钟中断为8,暂用它
    lea esi,[idt+ecx*8]   ;取第8号中断向量地址
    mov dword [esi],eax         ;设置低32位
    mov dword [esi+4],edx       ;设置高32位

;    31 ----------------------- 16 ---------------------8 --------- 0  陷阱门描述符格式
;      过程入口点偏移高位         P DPL 01111                   高32位
;      段 选 择  符             过程入口点偏移低位      低32位
    mov eax,0x00080000
    mov ax,system_interrupt ;过程入口点偏移
    mov dx,0xEF00           ;DPL=3,特权级3可执行
    mov ecx,0x80
    lea esi,[idt+ecx*8]     ;0x80中断向量描述符地址
    mov dword [esi],eax     ;设置低32位
    mov dword [esi+4],edx   ;设置高32位
;END 设置IDT 0x08和0x80

;移动到任务0
    pushf
    and dword [esp],0xffffbfff
    popf
    mov eax,TSS0_CS
    ltr ax   ;加载任务0 TR
    mov eax,TSS0_LDT
    lldt ax  ;加载任务LDT
    mov dword [current],0
    sti   ;开中断
    ;jmp $
    push  dword 0x17  ;任务0局部数据段选择符
    push  dword init_stack ;堆栈指针
    pushf  ;标志寄存器
    push  dword 0x0f  ;任务0局部代码段
    push  dword task0    ;任务0 eip
    iret             ;中断返回指令,切换到特权级3任务0执行
 
setup_gdt:
    lgdt [lgdt_opcode]
    ret
    
;begin setup_idt fun
setup_idt:
    lea edx,[ignore_int]  ;中断入口偏移放入edx
    mov eax,0x00080000  ;eax高位为内核代码段选择子
    mov ax,dx           ;eax低位为中断入口偏移低位
    mov dx,0x8E00       ;edx低位为中断门定义.DPL=0
    lea edi,[idt]         ;得到idt表位置,准备初始化
    mov ecx,256         ;初始化256 IDT描述符
rp_idt:
    mov dword [edi],eax
    mov dword [edi+4],edx
    add edi,8
    dec ecx
    jne rp_idt
    lidt [lidt_opcode]
    ret
;end setup_idt fun

;默认中断处理函数
ignore_int: ;默认中断处理函数
    push ds
    push eax
    mov  eax,0x10 ;内核数据段
    mov  ds,ax
    mov  eax,'C' ;设置显示'c'字符
    call write_char
    pop eax
    pop ds
    iret
;END 默认中断处理函数

;时钟中断处理函数
align 2
time_interrupt:
    push ds
    push eax
    mov  eax,0x10 ;ds->内核数据段
    mov  ds,ax
    mov al,0x20
    out 0x20,al ;立即设置允许其它中断,向8259A发送EOI命令
    mov al,'S'
    call write_char
    mov eax,1
    cmp eax,dword [current]
    je  goto_tss0   ;当前任务号为1,切换任务0
    mov dword [current],eax
    jmp TSS1_CS:0
    jmp  exit_time
goto_tss0:
    mov dword [current],0
    jmp TSS0_CS:0
exit_time:
    pop eax
    pop ds
    iret
;END 时钟中断处理程序
    
;系统调用0x80,显示字符串
system_interrupt:
    push ds
    push edx
    push ecx
    push ebx
    push eax
    mov  edx,0x10 ;内核数据段
    mov  ds,edx
    call write_char
    pop  eax
    pop  ebx
    pop  ecx
    pop  edx
    pop  ds
    iret      ;软中断返回
;END 系统调用

;Begin write_char
write_char:
    push gs
    push ebx
    mov  ebx,VIDEO_DS
    mov  gs,bx
    mov  ebx,dword [scr_loc]
    shl  ebx,1
    mov  [gs:ebx],al
    shr  ebx,1
    inc ebx
    cmp ebx,2000
    jb  exit
    mov ebx,0
exit:
    mov dword [scr_loc],ebx
    pop  ebx
    pop  gs
    ret
;End write_char

;内存控制变量
color:   dw 0 ;显示颜色    
current: dd 0 ;当前执行的任务号
scr_loc: dd 0 ;屏幕位置

;BEGIN lidt    
align 2                   
lidt_opcode:
    dw 256*8-1               ;长度
    dd idt                 ;线性基址
;END lidt    
lgdt_opcode:
    dw (end_gdt - gdt)-1
    dd gdt

align 3
    gdt:
    dd 0x00000000 ;NULL
    dd 0x00000000
    dd 0x000007ff
    dd 0x00c09a00 ;内核代码段0x08
    dd 0x000007ff
    dd 0x00c09200 ;内核数据段0x10
    dd 0x80000002
    dd 0x00c0920b ;显存数据段描述符0x18
;    31 ----------------24------------19 ---------------- 16 ---------11-------8 ---------------- 0  系统段描述符之TSS,LDT
;       基地址31...24     G D 0 AVL      段限长19...16      P DPL 0     TYPE    基地址23...16
;                                                                         1001 tss
;                                                                         0010 ldt
;       基地址                         15......0           段限长  15.....0

    dw 0x68,tss0,0xe900,0x0 ;TSS0段选择符0x20
    dw 0x40,ldt0,0xe200,0x0 ;LDT0 0x28
    dw 0x68,tss1,0xe900,0x0 ;TSS1段选择符 0x30
    dw 0x40,ldt1,0xe200,0x0 ;LDT1 0x38
end_gdt:

idt:
    times 512 dd 0 ;初始化中断描述表
    
kernel_stack:
    times 128 dw 0    ;512B内核堆栈
init_stack:
    dd init_stack ;堆栈段偏移位置
    dw 0x10 ;内核数据段选择符
    
ldt0:    
    dd 0x00000000,0x00000000
    dd 0x000003ff,0x00c0fa00 ;局部代码段描述符 0x0f
    dd 0x000003ff,0x00c0f200 ;局部数据段描述符 0x17
    
tss0:
    dd 0             ;back link
    dd krn_stk0,0x10 ;内核esp0,ss0
    dd 0,0,0,0,0 ;esp1,ss1,esp2,ss2,cr3
    dd 0,0,0,0,0 ;eip,eflags,eax,ecx,edx
    dd 0,0,0,0,0 ;ebx,esp,ebp,esi,edi
    dd 0,0,0,0,0,0 ;es,cs,ss,ds,fs,gs
    dd TSS0_LDT,0x80000000 ;ldt,trace bitmap
    
    times 128 dd 0
krn_stk0:           ;任务0内核栈esp

ldt1:
    dd 0x00000000,0x00000000
    dd 0x000003ff,0x00c0fa00 ;局部代码段描述符 0x0f
    dd 0x000003ff,0x00c0f200 ;局部数据段描述符 0x17

tss1:
    dd 0             ;back link
    dd krn_stk1,0x10 ;内核esp0,ss0
    dd 0,0,0,0,0 ;esp1,ss1,esp2,ss2,cr3
    dd task1,0x200,0,0,0 ;eip,eflags,eax,ecx,edx
    dd 0,usr_stk1,0,0,0 ;ebx,esp,ebp,esi,edi
    dd 0x17,0x0f,0x17,0x17,0x17,0x17 ;es,cs,ss,ds,fs,gs
    dd TSS1_LDT,0x80000000 ;ldt,trace bitmap

    times 128 dd 0
krn_stk1:            ;任务1内核栈

task0:
    mov eax,0x17
    mov ds,ax ;用户数据段
    mov al,65
    int 0x80 ;软中断,系统调用显示字符'A'
    mov ecx,0xffff
    loop $
    jmp task0

task1:
    mov al,66
    int 0x80 ;软中断,系统调用显示字符'A'
    mov ecx,0xffff
    loop $
    jmp task1
    
    times 128 dd 0
usr_stk1:



你可能感兴趣的:(大家一起写操作系统(4)-简单的任务切换)