zoukankan      html  css  js  c++  java
  • mit 6.828学习笔记不知道几--lab4 partc

    exercise 13:

    在inc/trap.h  给出了几种 hardware IRQ numbers

    #define IRQ_OFFSET    32    // IRQ 0 corresponds to int IRQ_OFFSET
    
    // Hardware IRQ numbers. We receive these as (IRQ_OFFSET+IRQ_WHATEVER)
    #define IRQ_TIMER        0
    #define IRQ_KBD          1
    #define IRQ_SERIAL       4
    #define IRQ_SPURIOUS     7
    #define IRQ_IDE         14
    #define IRQ_ERROR       19

    在idt中的偏移量由  idt[IRQ_OFFSET+IRQNumber]来决定

    在inc/mmu.h中定义了SETGATE:

    // Set up a normal interrupt/trap gate descriptor.
    // - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate.
        //   see section 9.6.1.3 of the i386 reference: "The difference between
        //   an interrupt gate and a trap gate is in the effect on IF (the
        //   interrupt-enable flag). An interrupt that vectors through an
        //   interrupt gate resets IF, thereby preventing other interrupts from
        //   interfering with the current interrupt handler. A subsequent IRET
        //   instruction restores IF to the value in the EFLAGS image on the
        //   stack. An interrupt through a trap gate does not change IF."
    // - sel: Code segment selector for interrupt/trap handler
    // - off: Offset in code segment for interrupt/trap handler
    // - dpl: Descriptor Privilege Level -
    //      the privilege level required for software to invoke
    //      this interrupt/trap gate explicitly using an int instruction.
    #define SETGATE(gate, istrap, sel, off, dpl)            
    {                                
        (gate).gd_off_15_0 = (uint32_t) (off) & 0xffff;        
        (gate).gd_sel = (sel);                    
        (gate).gd_args = 0;                    
        (gate).gd_rsv1 = 0;                    
        (gate).gd_type = (istrap) ? STS_TG32 : STS_IG32;    
        (gate).gd_s = 0;                    
        (gate).gd_dpl = (dpl);                    
        (gate).gd_p = 1;                    
        (gate).gd_off_31_16 = (uint32_t) (off) >> 16;        
    }

    注册外部中断与lab3中注册trap是一样的。

    在kern/traentry.S中添加 entry points

    TRAPHANDLER_NOEC(irq_error_handler, IRQ_OFFSET+IRQ_ERROR);
    TRAPHANDLER_NOEC(irq_ide_handler, IRQ_OFFSET+IRQ_IDE);
    TRAPHANDLER_NOEC(irq_kbd_handler, IRQ_OFFSET+IRQ_KBD);
    TRAPHANDLER_NOEC(irq_serial_handler, IRQ_OFFSET+IRQ_SERIAL);
    TRAPHANDLER_NOEC(irq_spurious_handler, IRQ_OFFSET+IRQ_SPURIOUS);
    TRAPHANDLER_NOEC(irq_timer_handler, IRQ_OFFSET+IRQ_TIMER);

    在inc/trap.h中添加函数定义:

    void irq_error_handler();
    void irq_kbd_handler();
    void irq_ide_handler();
    void irq_timer_handler();
    void irq_spurious_handler();
    void irq_serial_handler();

    在kern/trap.c中添加以下代码:

       //在inc/trap.h中给出了Hardware IRQ numbers.
        //外部设备中断在内核中禁用的,在用户态下启用,因此dpl应该是3
        SETGATE(idt[IRQ_OFFSET + IRQ_ERROR], 0, GD_KT, irq_error_handler, 3);
        SETGATE(idt[IRQ_OFFSET + IRQ_IDE], 0, GD_KT, irq_ide_handler, 3);
        SETGATE(idt[IRQ_OFFSET + IRQ_KBD], 0, GD_KT, irq_kbd_handler, 3);
        SETGATE(idt[IRQ_OFFSET + IRQ_SERIAL], 0, GD_KT, irq_serial_handler, 3);
        SETGATE(idt[IRQ_OFFSET + IRQ_SPURIOUS], 0, GD_KT, irq_spurious_handler, 3); 
        SETGATE(idt[IRQ_OFFSET + IRQ_TIMER], 0, GD_KT, irq_timer_handler, 3);

    修改kern/env.c中的env_alloc()确保用户进程在开中断状态下运行.

    外部中断由eflags寄存器的FL_IF标志位控制(inc/mmu.h),当该位被设置时,打开外部中断。

    //在文件inc/mmu.h中Eflags registers
    #define FL_IF        0x00000200    // Interrupt Flag

    因此我们要设置这个标志位:

    //在kern/env.c
         // Enable interrupts while in user mode.
          // LAB 4: Your code here.
          e->env_tf.tf_eflags |= FL_IF;

    取消对sched_halt()中sti指令的注释.这样空闲的cpu也不会屏蔽中断

    // Reset stack pointer, enable interrupts and then halt.
        asm volatile (
            "movl $0, %%ebp
    "
            "movl %0, %%esp
    "
            "pushl $0
    "
            "pushl $0
    "
            // Uncomment the following line after completing exercise 13
            "sti
    "
            "1:
    "
            "hlt
    "
            "jmp 1b
    "
        : : "a" (thiscpu->cpu_ts.ts_esp0));

    在终端运行一个测试程序:

    make run-spin

    得到结果:

    SMP: CPU 0 found 1 CPU(s)
    enabled interrupts: 1 2
    [00000000] new env 00001000
    I am the parent.  Forking the child...
    [00001000] new env 00001001
    I am the parent.  Running the child...
    I am the child.  Spinning...
    I am the parent.  Killing the child...
    [00001000] destroying 00001001
    [00001000] free env 00001001
    [00001000] exiting gracefully
    [00001000] free env 00001000
    No runnable environments in the system!

    exercise 13 完成

    Exercise 14:

    首先看看 lapic_eoi,这个函数位于kern/lpic.c中

    // Acknowledge interrupt.
    void
    lapic_eoi(void)
    {
        if (lapic)
            lapicw(EOI, 0);
    }

    修改trap_dispatch(在文件kern/trap.c中):

    else if(tf->tf_trapno == IRQ_OFFSET + IRQ_TIMER){
            lapic_eoi(); 
            sched_yield();
            return;
        }

    再次运行spin应该出现以下结果

    SMP: CPU 0 found 1 CPU(s)
    enabled interrupts: 1 2
    [00000000] new env 00001000
    I am the parent.  Forking the child...
    [00001000] new env 00001001
    I am the parent.  Running the child...
    I am the child.  Spinning...
    I am the parent.  Killing the child...
    [00001000] destroying 00001001
    [00001000] free env 00001001
    [00001000] exiting gracefully
    [00001000] free env 00001000
    No runnable environments in the system!

    exercise 15:

    ----在kern / syscall.c中实现sys_ipc_recv和sys_ipc_try_send。调用envid2env时,应将checkperm标志设置为0,这意味着允许任何进程将IPC消息发送到任何其他进程,并且除了验证目标envid合法之外,内核不执行任何特殊权限检查。

    
    
    static int
    sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm)
    {
        
    
    

    int r;
    pte_t *pte;
    struct PageInfo *pp;
    struct Env *env;

    
    
    
    
    
    

    if ((r = envid2env(envid, &env, 0)) < 0)
    return -E_BAD_ENV;
    if (env->env_ipc_recving != true || env->env_ipc_from != 0)
    return -E_IPC_NOT_RECV;
    if (srcva < (void *)UTOP && PGOFF(srcva))
    return -E_INVAL;
    if (srcva < (void *)UTOP) {
    if ((perm & PTE_P) == 0 || (perm & PTE_U) == 0)
    return -E_INVAL;
    if ((perm & ~(PTE_P | PTE_U | PTE_W | PTE_AVAIL)) != 0)
    return -E_INVAL;
    }
    if (srcva < (void *)UTOP && (pp = page_lookup(curenv->env_pgdir, srcva, &pte)) == NULL)
    return -E_INVAL;
    if (srcva < (void *)UTOP && (perm & PTE_W) != 0 && (*pte & PTE_W) == 0)
    return -E_INVAL;
    if (srcva < (void *)UTOP && env->env_ipc_dstva != 0) {
    if ((r = page_insert(env->env_pgdir, pp, env->env_ipc_dstva, perm)) < 0)
    return -E_NO_MEM;
    env->env_ipc_perm = perm;
    }

    
    
    
    
    
    

    env->env_ipc_from = curenv->env_id;
    env->env_ipc_recving = false;
    env->env_ipc_value = value;
    env->env_status = ENV_RUNNABLE;
    env->env_tf.tf_regs.reg_eax = 0;
    return 0;

    
    
    
    }
     
    static int
    sys_ipc_recv(void* dstva)
    {
        // LAB 4: Your code here.
        //-E_INVAL if dstva < UTOP but dstva is not page-aligned.
        if ((uintptr_t)dstva < UTOP) {
            if ((uintptr_t)dstva % PGSIZE)
                return -E_INVAL;
        }
        curenv->env_ipc_dstva = dstva;
        curenv->env_ipc_from = 0; //证明现在还没有收到任何信息
        curenv->env_ipc_recving = 1; //1-block, 0-unblock
        // mark yourself not runnable, and then give up the CPU.
        curenv->env_status = ENV_NOT_RUNNABLE;
        //不需要自己unblock,sys_ipc_try_send成功会把这设0
        sched_yield();
        //panic("sys_ipc_recv not implemented");
        //return 0;
    }

    ----在lib / ipc.c中实现ipc_recv和ipc_send函数。

    void
    ipc_send(envid_t to_env, uint32_t val, void *pg, int perm)
    {
        // LAB 4: Your code here.
        int r;
        while(1){
            if(pg)
                r=sys_ipc_try_send(to_env, val, pg, perm);
            else
                r=sys_ipc_try_send(to_env, val, (void *)UTOP, perm);
            if(r!=0){
                if(r!=-E_IPC_NOT_RECV)
                    panic("ipc send fault:%e",r);
                else
                    sys_yield();
            }else
                return;
        }
        panic("ipc_send not implemented");
        
    
    }
    int32_t
    ipc_recv(envid_t *from_env_store, void *pg, int *perm_store)
    {
        // LAB 4: Your code here.
        //// If 'pg' is nonnull, then any page sent by the sender will be mapped at
    //    that address.
        int r;
        if (pg != NULL)
            r = sys_ipc_recv(pg);
        else//?????
            r = sys_ipc_recv((void*)UTOP);
        // If 'from_env_store' is nonnull, then store the IPC sender's envid in
        //    *from_env_store.
        if (from_env_store != NULL)
            *from_env_store = thisenv->env_ipc_from;
        // If 'perm_store' is nonnull, then store the IPC sender's page permission
        //    in *perm_store (this is nonzero iff a page was successfully
        //    transferred to 'pg').
        if (perm_store != NULL)
            *perm_store = thisenv->env_ipc_perm;
        // If the system call fails, then store 0 in *fromenv and *perm (if
        //    they're nonnull) and return the error.
        if (from_env_store != NULL && perm_store != NULL && r < 0) {
            *from_env_store = 0;
            *perm_store = 0;
            return r;
        }
        if (r < 0)
            return r;
        return thisenv->env_ipc_value;
        //panic("ipc_recv not implemented");
        //return 0;
    
        
    }

    在syscall.c中进行设置

    case (SYS_ipc_recv):
                return sys_ipc_recv((void *)a1);
    case (SYS_ipc_try_send):
                return sys_ipc_try_send(a1, a2, (void *)a3, a4);

    结束

  • 相关阅读:
    bootstrap学习笔记一: bootstrap初认识,hello bootstrap(下)
    bootstrap学习笔记一: bootstrap初认识,hello bootstrap(上)
    AutoCompleteTextView的使用
    常用的android弹出对话框
    PopupWindow的使用
    linux udev、mdev 介绍
    linux 守护进程编程
    linux 下的文件目录操作之遍历目录
    linux 下查找图片文件方法
    linux 内核 zImage 生成过程分析
  • 原文地址:https://www.cnblogs.com/luo-he/p/13996855.html
Copyright © 2011-2022 走看看