zoukankan      html  css  js  c++  java
  • linux0.11学习笔记技术铺垫简单AB任务切换程序(5)实现三个任务切换

    上篇我们实现了时钟中断的响应,以及输出简单的字符功能。本篇我们实现系列文章的最终目的:任务切换。任务切换在x86上,硬件给提供了硬件支持,简单可行。

    涉及到tss切换,并自动实现了ldt切换。非常简单。

    1. 看代码head.s

    SCRN_SEL = 0x18
    TSS0_SEL    = 0x20
    LDT0_SEL    = 0x28
    TSS1_SEL    = 0X30
    LDT1_SEL    = 0x38
    TSS2_SEL    = 0X40
    LDT2_SEL    = 0x48

    .globl startup_32
    .text
    startup_32:
        movl $0x10,%eax
        mov %ax,%ds
        mov %ax,%es
        mov %ax,%gs
        mov %ax,%fs

        lss init_stack,%esp

        call setup_idt
        call setup_gdt

        # init again
        movl $0x10,%eax
        mov %ax,%ds
        mov %ax,%es
        mov %ax,%gs
        mov %ax,%fs
        lss init_stack,%esp

    # setup timer & system call interrupt descriptors.
        movl $0x00080000, %eax   
        movw $timer_interrupt, %ax
        movw $0x8E00, %dx
        movl $0x08, %ecx
        lea idt(,%ecx,8), %esi
        movl %eax,(%esi)
        movl %edx,4(%esi)
        movw $system_interrupt, %ax
        movw $0xef00, %dx
        movl $0x80, %ecx
        lea idt(,%ecx,8), %esi
        movl %eax,(%esi)
        movl %edx,4(%esi)

    # Move to user mode (task 0)
        pushfl
        andl $0xffffbfff, (%esp)
        popfl
        movl $TSS0_SEL, %eax
        ltr %ax
        movl $LDT0_SEL, %eax
        lldt %ax
        movl $0, current
        sti
        pushl $0x17
        pushl $usr_stk0
        pushfl
        pushl $0x0f
        pushl $task0
        iret

    die:
        jmp die

    .align 2
    ignore_int:
        push %ds
        pushl %eax
        movl $0x10, %eax
        mov %ax, %ds
        mov $0x0c98, %ax            /* print 'C' */
        call write_char
        popl %eax
        pop %ds
        iret

    write_char:
        push %gs
        pushl %ebx

        mov $SCRN_SEL,%ebx
        mov %bx,%gs

        mov src_loc,%bx
        shl $1,%ebx
        mov %ax,%gs:(%ebx)
        shr $1,%ebx
        incl %ebx
        cmpl $2000,%ebx
        jb 1f
        movl $0,%ebx
    1:
        movl %ebx,src_loc

        popl %ebx
        pop %gs
        ret

    .align 2
    timer_interrupt:
        push %ds
        pushl %edx
        pushl %ecx
        pushl %ebx
        pushl %eax
        movl $0x10, %eax
        mov %ax, %ds
        movb $0x20, %al
        outb %al, $0x20
        // is 1
        movl $1, %eax
        cmpl %eax, current
        je 3f
        // is 2
        movl $2, %eax
        cmpl %eax, current
        je 1f
        // is 0
        movl $0, %eax
        cmpl %eax, current
        je 2f
    1:    movl $0, current
        ljmp $TSS0_SEL, $0
        jmp 4f
    2:    movl $1, current
        ljmp $TSS1_SEL, $0
        jmp 4f
    3:    movl $2, current
        ljmp $TSS2_SEL, $0
        jmp 4f
    4:    popl %eax
        popl %ebx
        popl %ecx
        popl %edx
        pop %ds
        iret

    /* system call handler */
    .align 2
    system_interrupt:
        push %ds
        pushl %edx
        pushl %ecx
        pushl %ebx
        pushl %eax
        movl $0x10, %edx
        mov %dx, %ds
        call write_char
        popl %eax
        popl %ebx
        popl %ecx
        popl %edx
        pop %ds
        iret

    /*********************************************/
    current:
        .long 0

    setup_idt:
        lea ignore_int,%edx
        movl $0x00080000,%eax
        movw %dx,%ax        /* selector = 0x0008 = cs */
        movw $0x8E00,%dx    /* interrupt gate - dpl=0, present */
        lea idt,%edi
        mov $256,%ecx
    rp_sidt:
        movl %eax,(%edi)
        movl %edx,4(%edi)
        addl $8,%edi
        dec %ecx
        jne rp_sidt
        lidt lidt_opcode
        ret

    setup_gdt:
        lgdt lgdt_opcode
        ret

    .align 2
    lidt_opcode:
        .word 256*8-1
        .long idt
    lgdt_opcode:
        .word (end_gdt-gdt)-1
        .long gdt

    src_loc:
        .long 0

    .align 2
    idt:
        .fill 256,8,0

    gdt:
        .quad 0x0000000000000000
        .quad 0x00c09a00000007ff
        .quad 0x00c09200000007ff
        .quad 0x00c0920b80000002

        .word 0x68,tss0,0xe900,0x0
        .word 0x40,ldt0,0xe200,0x0
        .word 0x68,tss1,0xe900,0x0
        .word 0x40,ldt1,0xe200,0x0
        .word 0x68,tss2,0xe900,0x0
        .word 0x40,ldt2,0xe200,0x0
    end_gdt:

        .fill 128,4,0
    init_stack:
        .long init_stack
        .word 0x10

    .align 2
    ldt0:   
        .quad 0x0000000000000000
        .quad 0x00c0fa00000003ff    # 0x0f, base = 0x10000
        .quad 0x00c0f200000003ff    # 0x17
    tss0:
        .long 0             /* back link */
        .long stack0_krn_ptr, 0x10    /* esp0, ss0 */
        .long 0, 0            /* esp1, ss1 */
        .long 0, 0            /* esp2, ss2 */
        .long 0                /* cr3 */
        .long task0            /* eip */
        .long 0x200            /* eflags */
        .long 0, 0, 0, 0        /* eax, ecx, edx, ebx */
        .long usr_stk0, 0, 0, 0    /* esp, ebp, esi, edi */
        .long 0x17,0x0f,0x17,0x17,0x17,0x17 /* es, cs, ss, ds, fs, gs */
        .long LDT0_SEL            /* ldt */
        .long 0x8000000            /* trace bitmap */

        .fill 128,4,0
    stack0_krn_ptr:
        .long 0

    /************************************/
    .align 2
    ldt1:   
        .quad 0x0000000000000000
        .quad 0x00c0fa00000003ff    # 0x0f, base = 0x10000
        .quad 0x00c0f200000003ff    # 0x17
    tss1:
        .long 0             /* back link */
        .long stack1_krn_ptr, 0x10    /* esp0, ss0 */
        .long 0, 0            /* esp1, ss1 */
        .long 0, 0            /* esp2, ss2 */
        .long 0                /* cr3 */
        .long task1            /* eip */
        .long 0x200            /* eflags */
        .long 0, 0, 0, 0        /* eax, ecx, edx, ebx */
        .long usr_stk1, 0, 0, 0    /* esp, ebp, esi, edi */
        .long 0x17,0x0f,0x17,0x17,0x17,0x17 /* es, cs, ss, ds, fs, gs */
        .long LDT1_SEL            /* ldt */
        .long 0x8000000            /* trace bitmap */

        .fill 128,4,0
    stack1_krn_ptr:
        .long 0
    /************************************/
    .align 2
    ldt2:   
        .quad 0x0000000000000000
        .quad 0x00c0fa00000003ff    # 0x0f, base = 0x10000
        .quad 0x00c0f200000003ff    # 0x17
    tss2:
        .long 0             /* back link */
        .long stack2_krn_ptr, 0x10    /* esp0, ss0 */
        .long 0, 0            /* esp1, ss1 */
        .long 0, 0            /* esp2, ss2 */
        .long 0                /* cr3 */
        .long task2            /* eip */
        .long 0x200            /* eflags */
        .long 0, 0, 0, 0        /* eax, ecx, edx, ebx */
        .long usr_stk2, 0, 0, 0    /* esp, ebp, esi, edi */
        .long 0x17,0x0f,0x17,0x17,0x17,0x17 /* es, cs, ss, ds, fs, gs */
        .long LDT2_SEL            /* ldt */
        .long 0x8000000            /* trace bitmap */

        .fill 128,4,0
    stack2_krn_ptr:
        .long 0

    .align 2
    task0:
        movw $0x0c61, %ax              /* print 'a' */
        int $0x80
        movl $0xefff, %ecx
    1:    loop 1b
        jmp task0

        .fill 128,4,0
    usr_stk0:
        .long 0

    task1:
        movw $0x0d62, %ax              /* print 'b' */
        int $0x80
        movl $0xffff, %ecx
    1:    loop 1b
        jmp task1

        .fill 128,4,0
    usr_stk1:
        .long 0

    task2:
        movw $0x0e63, %ax              /* print 'c' */
        int $0x80
        movl $0xfff, %ecx
    1:    loop 1b
        jmp task2

        .fill 128,4,0
    usr_stk2:
        .long 0

    2. 代码分析

    SCRN_SEL = 0x18
    TSS0_SEL    = 0x20
    LDT0_SEL    = 0x28
    TSS1_SEL    = 0X30
    LDT1_SEL    = 0x38
    TSS2_SEL    = 0X40
    LDT2_SEL    = 0x48

    .globl startup_32
    .text
    startup_32:
        movl $0x10,%eax
        mov %ax,%ds
        mov %ax,%es
        mov %ax,%gs
        mov %ax,%fs

        lss init_stack,%esp

        call setup_idt
        call setup_gdt

        # init again
        movl $0x10,%eax
        mov %ax,%ds
        mov %ax,%es
        mov %ax,%gs
        mov %ax,%fs
        lss init_stack,%esp

    以上设置环境,分析见以前文章,此处略过。

        movl $0x00080000, %eax   
        movw $timer_interrupt, %ax
        movw $0x8E00, %dx
        movl $0x08, %ecx
        lea idt(,%ecx,8), %esi
        movl %eax,(%esi)
        movl %edx,4(%esi)
        movw $system_interrupt, %ax
        movw $0xef00, %dx
        movl $0x80, %ecx
        lea idt(,%ecx,8), %esi
        movl %eax,(%esi)
        movl %edx,4(%esi)

    以上设置了时钟中断和系统调用中断服务程序,时钟中断默认为0x08,系统调用中断默认为0x80.因为任务运行在特权级3,所以不能直接访问显存,需要通过中断门切换到特权级0完成输出字符功能。

    src_loc:
        .long 0

    .align 2
    idt:
        .fill 256,8,0

    gdt:
        .quad 0x0000000000000000
        .quad 0x00c09a00000007ff
        .quad 0x00c09200000007ff
        .quad 0x00c0920b80000002
        .word 0x68,tss0,0xe900,0x0
        .word 0x40,ldt0,0xe200,0x0
        .word 0x68,tss1,0xe900,0x0
        .word 0x40,ldt1,0xe200,0x0
        .word 0x68,tss2,0xe900,0x0
        .word 0x40,ldt2,0xe200,0x0
    end_gdt:

    以上设置了gdt表,分别是系统代码,系统数据,系统显存,任务0 tss,任务0 ldt,任务1 tss,任务1 ldt,任务2 tss,任务2 ldt。相关属性参照相关书籍即可。

    .align 2
    system_interrupt:
        push %ds
        pushl %edx
        pushl %ecx
        pushl %ebx
        pushl %eax
        movl $0x10, %edx
        mov %dx, %ds
        call write_char
        popl %eax
        popl %ebx
        popl %ecx
        popl %edx
        pop %ds
        iret

    以上设置了系统调用中断服务程序,调用write_char输出一个字符,字符数据在ax中,由任务自己定义。

    .align 2
    timer_interrupt:
        push %ds
        pushl %edx
        pushl %ecx
        pushl %ebx
        pushl %eax
        movl $0x10, %eax
        mov %ax, %ds
        movb $0x20, %al
        outb %al, $0x20
        // is 1
        movl $1, %eax
        cmpl %eax, current
        je 3f
        // is 2
        movl $2, %eax
        cmpl %eax, current
        je 1f
        // is 0
        movl $0, %eax
        cmpl %eax, current
        je 2f
    1:    movl $0, current
        ljmp $TSS0_SEL, $0
        jmp 4f
    2:    movl $1, current
        ljmp $TSS1_SEL, $0
        jmp 4f
    3:    movl $2, current
        ljmp $TSS2_SEL, $0
        jmp 4f
    4:    popl %eax
        popl %ebx
        popl %ecx
        popl %edx
        pop %ds
        iret

    以上是时钟服务程序,比较了当前的任务,如果是0,则用ljmp $TSS1_SEL, $0跳到任务1,此句完成了任务切换,相当简单。如果是任务1,则跳到任务2,如果是任务2,则跳到任务0.

    .align 2
    ldt0:   
        .quad 0x0000000000000000
        .quad 0x00c0fa00000003ff    # 0x0f, base = 0x10000
        .quad 0x00c0f200000003ff    # 0x17
    tss0:
        .long 0             /* back link */
        .long stack0_krn_ptr, 0x10    /* esp0, ss0 */
        .long 0, 0            /* esp1, ss1 */
        .long 0, 0            /* esp2, ss2 */
        .long 0                /* cr3 */
        .long task0            /* eip */
        .long 0x200            /* eflags */
        .long 0, 0, 0, 0        /* eax, ecx, edx, ebx */
        .long usr_stk0, 0, 0, 0    /* esp, ebp, esi, edi */
        .long 0x17,0x0f,0x17,0x17,0x17,0x17 /* es, cs, ss, ds, fs, gs */
        .long LDT0_SEL            /* ldt */
        .long 0x8000000            /* trace bitmap */

        .fill 128,4,0
    stack0_krn_ptr:
        .long 0

    以上代码定义了任务0的tss和ldt,ldt描述了任务0的代码和数据段信息,tss定义了任务的各种属性,入口,用户堆栈,ldt,寄存器内容,内核栈等。

    ljmp tss段选择符这样的语句会造成任务切换,cpu自动保存当前任务tss内容,并恢复需要切换到的任务的tss内容,造成任务切换。任务1和任务2的描述内容与任务0类似。

    .align 2
    task0:
        movw $0x0c61, %ax              /* print 'a' */
        int $0x80
        movl $0xefff, %ecx
    1:    loop 1b
        jmp task0

        .fill 128,4,0
    usr_stk0:
        .long 0

    以上是任务0的代码,设置参数ax为0x0c61,并调用系统调用int 0x80,后边的语句实现延时,并循环执行代码。

        pushfl
        andl $0xffffbfff, (%esp)
        popfl
        movl $TSS0_SEL, %eax
        ltr %ax
        movl $LDT0_SEL, %eax
        lldt %ax
        movl $0, current
        sti
        pushl $0x17
        pushl $usr_stk0
        pushfl
        pushl $0x0f
        pushl $task0
        iret

    以上代码比较关键,为转移到任务0做准备,分别准备堆栈,加载任务0的内容进cpu,开中断,最后通过iret指令,让cpu觉得是中断返回,这样造成cpu特权级的切换,切换到了任务0中执行。

     

    3. 编译执行

    编过过程不再叙述,看运行截图:

     

    通过一些列的文章,我们阐述了如何在保护模式下切换任务。同时知识包括:gdt,idt,ldt,tss,时钟中断服务,特权级切换,显存编程,boot和loader功能,bios调用等等。详细知识还要在实践中摸索学习,希望大家一起进步。

     

    本文完.

  • 相关阅读:
    VisionPro 各控件的C#中类库 CogAcqFifoTool(2)
    VisionPro 各控件的C#中类库 CogAcqFifoTool(1)
    C# new的三种用法
    C# as用法
    C# 基类之Environment类
    C#开发的软件在Windows7中出现对路径的访问被拒绝异常
    IDEA创建springboot项目【mas-service】
    .Net Core-ObjectPool
    中介者模式的实现-MediatR
    .NET Core分布式事件总线、分布式事务解决方案:CAP
  • 原文地址:https://www.cnblogs.com/linucos/p/2447054.html
Copyright © 2011-2022 走看看