您现在的位置是:首页 > 服务器相关

操作系统设计之进程调度代码篇(1)

batsom2021-10-24服务器相关

简介代码学习篇

在我们的操作系统中,已经存在的进程是运行在ring1上的,它们已经不能任意地使用某些指令,不能访问某些权限更高的内存区域,但如果一项任务需要这些使用指令或者内存区域时,只能通过系统调用来实现,它是应用程序和操作系统之间的桥梁。
所以,一件事情就可能是应用程序做一部分,操作系统做一部分。这样,问题就又涉及特权级变换。
很明显,这已经难不倒我们了,因为进程的切换就是不停地在重复这么一个特权级变换的过程。在那里,触发变换的是外部中断,我们把这个诱因换一下就可以了,变成"int nnn",一切就都解决了。

前期准备:
1.准备进程代码和中断代码,系统调用代码!
2.对进程初始化代码
3.执行进程代码

一些基本概念:
结构体:是C语言中一种重要的数据类型,该数据类型由一组称为成员(或称为域,或称为元素)的不同数据组成,其中每个成员可以具有不同的类型。结构体通常用来表示类型不同但是又相关的若干数据。


进程调度代码说明:

1)PIT计数器代码:

kernel/main.c
        out_byte( TIMER_MODE,RATE_GENERATOR );
        out_byte( TIMER0,( u8 )( TIMER_FREQ/HZ ) );
        out_byte( TIMER0,( u8 )( ( TIMER_FREQ/HZ )>>8 ) );

相关函数部分:
include/proto.h
        out_byte(u16 port, u8 value)

相关变量:
include/const.h
        #define TIMER0      0x40
        #define TIMER_MODE  0x43
        #define RATE_GENERATOR 0x34
        #define TIMER_FREQ   1193182L
        #define HZ          100

解说:相关变量是为了更改中断触发周期!系统中断是不停发生的

2)系统调用部分
kernel/syscall.asm
    _NR_get_ticks       equ 0 ; 要跟 global.c 中 sys_call_table 的定义相对应!
    INT_VECTOR_SYS_CALL equ 0x90
    global    get_ticks ; 导出符号

    bits 32
    [section .text]

    get_ticks:
        mov    eax, _NR_get_ticks
        int    INT_VECTOR_SYS_CALL  
        ret

中断门
kernel/protect.c
init_idt_desc( INT_VECTOR_SYS_CALL ,DA_386IGate,sys_call,           PRIVILEGE_USER);


kernel/kernel.asm
sys_call:
        call save                        /*调用save*/
        sti                              /*允许中断发生*/
        call [sys_call_table + eax*4]   /*调用的是sys_call_table[eax]*/
        mov [esi+EAXREG-P_STACKBASE],eax  /*eax是把函数sys_call_table[eax]的返回值放在进程表中eax的位置,以便进程P被恢复执行时eax中装的是正确的返回值。*/
        cli                              /*禁止中断发生*/
        ret

global.c
PUBLIC system_call      sys_call_table[ NR_SYS_CALL ]={ sys_get_ticks };

include/type.h
typedef void*    system_call;

kernel/proc.c
PUBLIC int sys_get_ticks()
{
    return ticks;
}

解说:程序调用int 90软中断,中断门调用sys_call,开中断,调用sys_call_table[eax],返回ticks,关闭中断!由于系统总是在发生,所以ticks是在不停变化的!

3)进程部分
PUBLIC void clock_handler( int irq ){
     ticks++;     
     p_proc_ready->ticks--;
     
     if( k_reenter != 0 ){
          return;
          
     }
     if( p_proc_ready->ticks>0 ){
          return;
          
     }
     schedule(  );
     
}


延迟函数:
kernel/clock.c
PUBLIC void milli_delay( int milli_sec ){
     int t = get_ticks();
     while(( ( get_ticks(  ) - t ) * 1000 / HZ ) < milli_sec  ){
          
     }
}
解说:如果两次获取值小于延迟值,着等待!

kernel/main.c
void TestA()
{
    int i = 0;

    while(1){
     disp_color_str( "A.",BRIGHT|MASK_COLOR( BLACK,RED ) ); /*打印*/
     milli_delay(10); /*10毫秒*/
    }
}



初始化进程表:
    memcpy(&p_proc->ldts[0], &gdt[SELECTOR_KERNEL_CS >> 3], sizeof(DESCRIPTOR));  //将SELECTOR_KERNEL_CS所指的描述符拷贝到进程PCB的ldts[0]处
    p_proc->ldts[0].attr1 = DA_C | PRIVILEGE_TASK << 5;                              // change the DPL
    memcpy(&p_proc->ldts[1], &gdt[SELECTOR_KERNEL_DS >> 3], sizeof(DESCRIPTOR)); //将SELECTOR_KERNEL_DS所指的描述符拷贝到进程PCB的ldts[1]处
    p_proc->ldts[1].attr1 = DA_DRW | PRIVILEGE_TASK << 5;                          // change the DPL

    //LDT中共有两个描述符,分别被初始化为内核代码段和内核数据段,只是改变了一下DPL,以让其运行在低特权级下(2)
    p_proc->regs.cs  = ((8 * 0) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;    //8*0和8*1是选择子,而且是进程PCB中的LDT描述符的选择子。
 //cs指向LDT中的第一个描述符 (3) 通过cs中的这个描述符跳转到内核代码段
 //LDT选择子是从0开始的。一个描述符相隔8个字节,所以要乘以8
    p_proc->regs.ds  = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;    //根据选择子的属性确定是全局选择子还是LDT选择子。
    p_proc->regs.es  = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;    //本例会根据cs中的选择子自动到进程PCB中读取LDT描述符
    p_proc->regs.fs  = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;    //本例比较特殊的地方是进程LDT描述符在进程PCB中,没有固定的LDT段
    p_proc->regs.ss  = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;    //ds,es,fs,ss指向LDT中的第二个描述符
    p_proc->regs.gs  = (SELECTOR_KERNEL_GS & SA_RPL_MASK) | RPL_TASK;  //gs指向显存,只是其RPL发生改变
    p_proc->regs.eip= (u32)p_task->initial_eip;       //eip指向TestA,这表明进程将从TestA的入口地址开始运行
    p_proc->regs.esp= (u32)p_task_stack;             //esp指向单独的堆栈,堆栈大小为STACK_SIZE_TOTAL
    p_proc->regs.eflags = 0x1202;   //eflags=0x1202,恰巧设置了IF位,并把IOPL设为1,这样,进程就可以使用I/O指令,并且中断会在iretd执行时,被打开。

启动进程
p_proc_ready    = proc_table;
put_irq_handler( CLOCK_IRQ,clock_handler );
enable_irq( CLOCK_IRQ );
restart();

函数部分:
lib/kliba.asm
         void disp_color_str(char * info, int color);

结构体
include/proc.h
    typedef struct s_stackframe {
        u32    gs;        /* \                                    */
        u32    fs;        /* |                                    */
        u32    es;        /* |                                    */
        u32    ds;        /* |                                    */
        u32    edi;        /* |                                    */
        u32    esi;        /* | pushed by save()                   */
        u32    ebp;        /* |                                    */
        u32    kernel_esp;    /* <- 'popad' will ignore it            */
        u32    ebx;        /* |                                    */
        u32    edx;        /* |                                    */
        u32    ecx;        /* |                                    */
        u32    eax;        /* /                                    */
        u32    retaddr;    /* return addr for kernel.asm::save()   */
        u32    eip;        /* \                                    */
        u32    cs;        /* |                                    */
        u32    eflags;        /* | pushed by CPU during interrupt     */
        u32    esp;        /* |                                    */
        u32    ss;        /* /                                    */
    }STACK_FRAME;

typedef struct s_proc {
        STACK_FRAME regs;          
        u16 ldt_sel;               
        DESCRIPTOR ldts[LDT_SIZE];   
      int ticks ;
      int priority;  
        u32 pid;                   
        char p_name[16];           
    }PROCESS;

宏定义部分:
include/protect.h
    /* 描述符索引 */
    #define    INDEX_DUMMY        0    
    #define    INDEX_FLAT_C        1    
    #define    INDEX_FLAT_RW        2    
    #define    INDEX_VIDEO        3    
    #define    INDEX_TSS        4
    #define    INDEX_LDT_FIRST        5
    /* 选择子 */
    #define    SELECTOR_DUMMY           0    
    #define    SELECTOR_FLAT_C        0x08    
    #define    SELECTOR_FLAT_RW    0x10    
    #define    SELECTOR_VIDEO        (0x18+3)
    #define    SELECTOR_TSS        0x20    
    #define SELECTOR_LDT_FIRST    0x28

    #define    SELECTOR_KERNEL_CS    SELECTOR_FLAT_C
    #define    SELECTOR_KERNEL_DS    SELECTOR_FLAT_RW
    #define    SELECTOR_KERNEL_GS    SELECTOR_VIDEO

    /* 每个任务有一个单独的 LDT, 每个 LDT 中的描述符个数: */
    #define LDT_SIZE        2

    /* 选择子类型值说明 */
    /* 其中, SA_ : Selector Attribute */
    #define    SA_RPL_MASK    0xFFFC
    #define    SA_RPL0        0
    #define    SA_RPL1        1
    #define    SA_RPL2        2
    #define    SA_RPL3        3

    #define    SA_TI_MASK    0xFFFB
    #define    SA_TIG        0
    #define    SA_TIL        4

/* 线性地址 → 物理地址 */
#define vir2phys(seg_base, vir)    (u32)(((u32)seg_base) + (u32)(vir))

kernel/protect.c
进程LDT描述符:
    init_descriptor(&gdt[INDEX_TSS],
            vir2phys(seg2phys(SELECTOR_KERNEL_DS), &tss),
            sizeof(tss) - 1,
            DA_386TSS);

PRIVATE void init_descriptor(DESCRIPTOR *p_desc,u32 base,u32 limit,u16 attribute)
{
    p_desc->limit_low    = limit & 0x0FFFF;
    p_desc->base_low    = base & 0x0FFFF;
    p_desc->base_mid    = (base >> 16) & 0x0FF;
    p_desc->attr1        = attribute & 0xFF;
    p_desc->limit_high_attr2= ((limit>>16) & 0x0F) | (attribute>>8) & 0xF0;
    p_desc->base_high    = (base >> 24) & 0x0FF;
}

由段名求绝对地址
PUBLIC u32 seg2phys(u16 seg)
{
    DESCRIPTOR* p_dest = &gdt[seg >> 3];
    return (p_dest->base_high<<24) | (p_dest->base_mid<<16) | (p_dest->base_low);
}


/* 填充 GDT 中 TSS 这个描述符 */
memset(&tss, 0, sizeof(tss));
tss.ss0 = SELECTOR_KERNEL_DS;
init_descriptor(&gdt[INDEX_TSS],                                 //设置填充地址位置(GDT中的LDT描述符 )
        vir2phys(seg2phys(SELECTOR_KERNEL_DS), &tss),    //LDT基址,这个GDT中的LDT基址指向proc_table[0].ldts
        sizeof(tss) - 1,                                 //LDT大小
        DA_386TSS);                  
tss.iobase = sizeof(tss); /* 没有I/O许可位图 */

kernel/kernel.asm
加载ltr
    xor    eax, eax
    mov    ax, SELECTOR_TSS
    ltr    ax

restart:
    mov    esp, [p_proc_ready]
    lldt    [esp + P_LDT_SEL]
    lea    eax, [esp + P_STACKTOP]
    mov    dword [tss + TSS3_S_SP0], eax

restart_reenter:
  dec dword [k_reenter]
    pop    gs
    pop    fs
    pop    es
    pop    ds
    popad

    add    esp, 4

    iretd





 

郑重声明:

本站所有活动均为互联网所得,如有侵权请联系本站删除处理

随便看看

文章排行

本栏推荐

栏目更新