免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 5339 | 回复: 3

MINIX3内核初始化过程 [复制链接]

论坛徽章:
0
发表于 2007-06-11 15:23 |显示全部楼层
MINIX3内核被成功加载到内存后,控制权会转移到kernel/mpx.s文件,根据机器字长为16位或32位来分别加载mpx88.s或者mpx386.s文件并执行。

此时,csds寄存器已经指向MINIX3内核的相应段,而ss寄存器仍指向boot的栈段,该栈中结构如下:
(栈底)
aout结构体的地址
boot参数的大小和地址
boot的CS和IP(16位),以便MINIX3结束后将控制权交还给boot
(栈顶)

对于32PC机,MINIX3内核会跳转到mpx386.s文件的MINIX标签处开始执行:
MINIX:
    jmp    over_flags        ! 跳过下面两个用于boot的数据
    .data2    CLICK_SHIFT
flags:
    .data2    0x01FD
    nop
over_flags:

! 设置boot栈,以便MINIX3结束后交还控制权
    movzx    esp, sp        ! 将boot栈扩展为32位
    push    ebp        ! 将栈顶地址传送给ebp
    mov    ebp, esp
    push    esi
    push    edi
    cmp    4(ebp), 0        ! 如果boot的cs和ip均为0则不可交还控制权
    jz    noret
    inc    (_mon_return)
noret:    mov    (_mon_sp), esp    ! 保存ESP的值

! 将boot的GDT拷贝到内核地址空间,并加载
    sgdt    (_gdt+GDT_SELECTOR)        ! 将GDT的描述结构拷贝到内存
                    ! 低2字节为字节长,高4字节为基址
    mov    esi, (_gdt+GDT_SELECTOR+2)    ! boot中GDT的基址
    mov    ebx, _gdt            ! 内核中GDT的基址
    mov    ecx, 8*8            ! 拷贝前8个描述符
copygdt:
eseg    movb    al, (esi)            ! eseg说明操作数的段寄存器为es
    movb    (ebx), al
    inc    esi
    inc    ebx
    loop    copygdt
    mov    eax, (_gdt+DS_SELECTOR+2)    ! 设置GDT基址
    and    eax, 0x00FFFFFF
    add    eax, _gdt
    mov    (_gdt+GDT_SELECTOR+2), eax
    lgdt    (_gdt+GDT_SELECTOR)        ! 加载GDT

! 设置内核段寄存器和栈
    mov    ebx, 8(ebp)    ! boot参数地址
    mov    edx, 12(ebp)    ! boot参数长度
    mov    eax, 16(ebp)    ! a.out头地址
    mov    (_aout), eax
    mov    ax, ds
    mov    es, ax
    mov    fs, ax
    mov    gs, ax
    mov    ss, ax
    mov    esp, k_stktop

! 调用start.c文件中的cstart函数来保存内核信息
    push    edx
    push    ebx
    push    SS_SELECTOR
    push    DS_SELECTOR
    push    CS_SELECTOR
    call    _cstart
    add    esp, 5*4

! 重新加载GDT和IDT
    lgdt    (_gdt+GDT_SELECTOR)
    lidt    (_gdt+IDT_SELECTOR)

    jmpf    CS_SELECTOR:csinit    ! 开始使用刚初始化好的表
csinit:
! 初始化段选择子
o16    mov    ax, DS_SELECTOR    ! o16说明操作数为16位
    mov    ds, ax
    mov    es, ax
    mov    fs, ax
    mov    gs, ax
    mov    ss, ax
o16    mov    ax, TSS_SELECTOR    ! 初始化任务状态段(TSS)
    ltr    ax
    push    0        ! 将eflags清0
    popf

! 调用main.c文件中的main函数运行boot image
    jmp    _main

其中,start.c文件中的cstart函数实现如下:
PUBLIC void cstart(cs, ds, mds, parmoff, parmsize)
U16_t cs, ds;        [color=#FF9900>// 内核代码段与数据段

U16_t mds;            [color=#FF9900>// boot数据段

U16_t parmoff, parmsize;    [color=#FF9900>// boot参数地址与长度

{
    char params[128*sizeof(char *)];
    register char *value;
    extern int etext, end;

#if _WORD_SIZE != 2
    [color=#FF9900>// 如果是386+则设置保护模式

    machine.protected = 1;   
#endif

    [color=#FF9900>// 保存内核代码段和数据地址与长度

    kinfo.code_base = seg2phys(cs);
    kinfo.code_size = (phys_bytes) &etext;
    kinfo.data_base = seg2phys(ds);
    kinfo.data_size = (phys_bytes) &end;

    [color=#FF9900>// 初始化保护模式下的描述符

    prot_init();

    [color=#FF9900>// 将boot参数拷贝到本地

    kinfo.params_base = seg2phys(mds) + parmoff;    [color=#FF9900>// boot参数地址

    kinfo.params_size = MIN(parmsize,sizeof(params)-2);    [color=#FF9900>// boot参数长度

    phys_copy(kinfo.params_base, vir2phys(params), kinfo.params_size);
                        [color=#FF9900>// 源 目的 长度


    [color=#FF9900>// 保存其他信息

    kinfo.nr_procs = NR_PROCS;
    kinfo.nr_tasks = NR_TASKS;
    strncpy(kinfo.release, OS_RELEASE, sizeof(kinfo.release));    [color=#FF9900>// 目的 源 长度

    kinfo.release[sizeof(kinfo.release)-1] = '\0';
    strncpy(kinfo.version, OS_VERSION, sizeof(kinfo.version));
    kinfo.version[sizeof(kinfo.version)-1] = '\0';
    kinfo.proc_addr = (vir_bytes) proc;
    kinfo.kmem_base = vir2phys(0);
    kinfo.kmem_size = (phys_bytes) &end;   

    [color=#FF9900>// 获取CPU类型

    machine.processor=atoi(get_value(params, "processor"));
#if _WORD_SIZE == 2
    machine.protected = machine.processor >= 286;        
#endif
    if (!machine.protected) mon_return = 0;

    [color=#FF9900>// 获取总线类型

    value = get_value(params, "bus");
    if (value == NIL_PTR || strcmp(value, "at") == 0) {
        machine.pc_at = TRUE;
    } else if (strcmp(value, "mca") == 0) {
        machine.pc_at = machine.ps_mca = TRUE;
    }

    [color=#FF9900>// 获取显示器类型

    value = get_value(params, "video");
    if (strcmp(value, "ega") == 0) machine.vdu_ega = TRUE;
    if (strcmp(value, "vga") == 0) machine.vdu_vga = machine.vdu_ega = TRUE;
}

论坛徽章:
0
发表于 2007-06-11 15:23 |显示全部楼层
main.c文件中的main函数实现如下:
PUBLIC void main()
{
    struct boot_image *ip;
    register struct proc *rp;
    register struct priv *sp;
    register int i, s;
    int hdrindex;
    phys_clicks text_base;
    vir_clicks text_clicks, data_clicks;
    reg_t ktsb;
    struct exec e_hdr;

    [color=#FF9900>// 初始化中断控制器

    intr_init(1);

    [color=#FF9900>// 初始化进程表与特权控制表

    for (rp = BEG_PROC_ADDR, i = -NR_TASKS; rp < END_PROC_ADDR; ++rp, ++i) {
        rp->p_rts_flags = SLOT_FREE;
        rp->p_nr = i; from ptr */
        (pproc_addr + NR_TASKS)[i] = rp;
    }
    for (sp = BEG_PRIV_ADDR, i = 0; sp < END_PRIV_ADDR; ++sp, ++i) {
        sp->s_proc_nr = NONE;
        sp->s_id = i;
        ppriv_addr[i] = sp;
    }

    [color=#FF9900>// 任务栈

    ktsb = (reg_t) t_stack;

    [color=#FF9900>// 初始化boot image中的进程

    for (i=0; i < NR_BOOT_PROCS; ++i) {
        ip = &image[i];
        rp = proc_addr(ip->proc_nr);        [color=#FF9900>// 进程描述符

        rp->p_max_priority = ip->priority;    [color=#FF9900>// 最大调度优先级

        rp->p_priority = ip->priority;        [color=#FF9900>// 当前调度优先级

        rp->p_quantum_size = ip->quantum;    [color=#FF9900>// 时间片

        rp->p_ticks_left = ip->quantum;        [color=#FF9900>// 当前剩余时间片

        strncpy(rp->p_name, ip->proc_name, P_NAME_LEN);    [color=#FF9900>// 进程名

        (void) get_priv(rp, (ip->flags & SYS_PROC));    [color=#FF9900>// 获取特权级

        priv(rp)->s_flags = ip->flags;        [color=#FF9900>// 设置进程特权级标识

        priv(rp)->s_trap_mask = ip->trap_mask;    [color=#FF9900>// 设置允许使用的系统调用

        priv(rp)->s_call_mask = ip->call_mask;    [color=#FF9900>// 设置允许使用的内核调用        priv(rp)->s_ipc_to.chunk[0] = ip->ipc_to;    // 可向其发送消息的进程

        if (iskerneln(proc_nr(rp))) {
            [color=#FF9900>// 如果是内核进程则设置栈保护

            if (ip->stksize > 0) {
                [color=#FF9900>// KERNEL进程的栈为0

                rp->p_priv->s_stack_guard = (reg_t *) ktsb;
                *rp->p_priv->s_stack_guard = STACK_GUARD;
            }
            ktsb += ip->stksize;    [color=#FF9900>// 栈底

            rp->p_reg.sp = ktsb;    [color=#FF9900>// 栈顶

            text_base = kinfo.code_base >> CLICK_SHIFT;
            hdrindex = 0;    [color=#FF9900>// 所有内核进程被编译成同一文件

        } else {
            hdrindex = 1 + i-NR_TASKS;
        }

        [color=#FF9900>// 从boot拷贝a.out头的exec结构体

        phys_copy(aout + hdrindex * A_MINHDR, vir2phys(&e_hdr),
                        (phys_bytes) A_MINHDR);
        [color=#FF9900>// 使用clicks表示地址

        text_base = e_hdr.a_syms >> CLICK_SHIFT;
        text_clicks = (e_hdr.a_text + CLICK_SIZE-1) >> CLICK_SHIFT;
        if (!(e_hdr.a_flags & A_SEP)) text_clicks = 0;
        data_clicks = (e_hdr.a_total + CLICK_SIZE-1) >> CLICK_SHIFT;
        [color=#FF9900>// 构建进程内存映像

        rp->p_memmap[T].mem_phys = text_base;
        rp->p_memmap[T].mem_len = text_clicks;
        rp->p_memmap[D].mem_phys = text_base + text_clicks;
        rp->p_memmap[D].mem_len = data_clicks;
        rp->p_memmap[S].mem_phys = text_base + text_clicks + data_clicks;
        rp->p_memmap[S].mem_vir = data_clicks;

        [color=#FF9900>// 设置初始化寄存器值

        rp->p_reg.pc = (reg_t) ip->initial_pc;
        rp->p_reg.psw = (iskernelp(rp)) ? INIT_TASK_PSW : INIT_PSW;

        if (isusern(proc_nr(rp))) {
            [color=#FF9900>// 初始化服务器栈指针(向下移动一个字),使其能接收argc参数

            rp->p_reg.sp = (rp->p_memmap[S].mem_vir +
                rp->p_memmap[S].mem_len) << CLICK_SHIFT;
            rp->p_reg.sp -= sizeof(reg_t);
        }

        if (rp->p_nr != HARDWARE) {
            [color=#FF9900>// 将进程设置为等待运行(ready)状态

            rp->p_rts_flags = 0;
            lock_enqueue(rp);
        } else {
            [color=#FF9900>// 阻止KERNEL(HARDWARE)进程运行

            rp->p_rts_flags = NO_MAP;
        }

        [color=#FF9900>// 为进程分配代码段和数据段

        alloc_segments(rp);
    } [color=#FF9900>// for循环结束


#if ENABLE_BOOTDEV
    [color=#FF9900>// 加载boot设备

    hdrindex++;
    phys_copy(aout + hdrindex * A_MINHDR,vir2phys(&e_hdr),(phys_bytes) A_MINHDR);
    if (e_hdr.a_flags & A_IMG) {
        kinfo.bootdev_base = e_hdr.a_syms;
        kinfo.bootdev_size = e_hdr.a_data;
    }
#endif

    bill_ptr = proc_addr(IDLE);    [color=#FF9900>// 设置IDLE为第一个运行的进程

    announce();        [color=#FF9900>// 显示欢迎信息


    [color=#FF9900>// 调用mpx386.s文件中的_restart函数开始进程调度等

    restart();
}


到此为止,MINIX3的内核就完成了全部的初始化工作,并开始通过进程调度来运行所有的进程。

论坛徽章:
0
发表于 2007-07-09 23:15 |显示全部楼层
多谢楼主分享,十分感谢,收藏了!

论坛徽章:
0
发表于 2007-07-10 10:31 |显示全部楼层
加油!加油!
对于我等,为你加油才是硬道理!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP