北航操作系统实验lab4总结


时间越来越不够了,无法详解,仅记录一些梳理过程。

(一)syscall流程图

(以writef())为例
syscall流程图

(二)fork区分进程

(newenvid = syscall_env_alloc()) == 0
这个进程可以分为三个部分:

①sys_env_alloc()->eax(v0)
②eax(v0)->newenvid
③if(newenvid==0)

父进程在①触发了异常,会在syscall.S/lib.c中修改父进程的env_tf.cp0_epc,使它记录的是②的位置。
sys_env_alloc()中,父进程做了:

  • 申请一个子进程e;
  • 设置e的状态为ENV_NOT_RUNNABLE
  • 将环境(①发生之前)复制给e;
  • e -> env_tf.pc = e -> env_tf.cp0_epc;让子进程被设置为ENV_NOT_RUNNABLE 的时候从②开始执行;
  • e -> env_tf.regs[2] = 0;将子进程的v0寄存器设置为0,即子进程的eax(v0)被设置为0;
  • 设置e的优先级/时间片
  • return e->env_id;将父进程的v0寄存器即eax(v0)被设置为0.

当父进程从异常返回时或子进程刚被调用时,它们都是从②开始执行,但是获得了不同的eax(v0)。所以便能有效区分了。

(三)缺页中断

1.所有缺页流程

在这里插入图片描述

2.cow类型缺页处理函数流程

在fork产生亲自关系后,需要在子进程runnable之前设置写时复制机制。

  • 为父进程注册pgfault_handler
  • 复制父进程的页给子进程,并修改双方的权限(按情况增加COW)
  • 为子进程申请一页(映射到UXSTACKTOP下面那个BY2PG)作为异常处理栈。权限是PTE_V|PTE_R,有效且只读。
  • 为子进程注册pgfault_handler

这里设计了很多长得很像的函数,注意辨析!

(1)注册缺页处理函数

目的:为目标进程设置两个参数env -> env_pgfault_handlerenv -> env_xstacktop,使得发生COW类型缺页中断时可以在正确的栈里调用正确的处理函数。

①父子不同的注册方式

set_pgfault_handler(pgfault)syscall_set_pgfault_handler(newenvid, __asm_pgfault_handler, UXSTACKTOP)
前者是父进程注册方式

  • set_pgfault_handler在lib/pgfault.c里
  • 初次设置:
    • 在UXSTACKTOP下第一个BY2PG处申请了一个异常处理栈
    • 调用syscall_set_pgfault_handler(0, __asm_pgfault_handler, UXSTACKTOP)
    • __pgfault_handler = pgfault;
  • 再次设置:
    • __pgfault_handler = fn;

后者是子进程注册方式

  • syscall_set_pgfault_handler(newenvid, __asm_pgfault_handler, UXSTACKTOP)

可以发现子进程的异常处理栈是在fork.c申请的,父进程是在注册时申请的。除此之外,父进程在注册过程中多进行的一步就是设置变量__pgfault_handler

②__pgfault_handler

函数指针,保存着当前进程的COW类缺页处理函数

③ __asm_pgfault_handler

从内核返回后,此时的栈指针是由内核设置的在异常处理栈的栈指针,而且指向一个由内核复制好的Trapframe 结构体的底部。通过宏TF_BADVADDR 用lw 指令取得了Trapframe 中的cp0_badvaddr 字段的值,这个值也正是发生缺页中断的虚拟地址。将这个地址作为第一个参数去调用了__pgfault_handler 这个字内存储的函数,不难看出这个函数是真正进行处理的函数。函数返回后就是一段类似于恢复现场的汇编,最后非常巧妙地利用了MIPS 的延时槽特性跳转的同时恢复了栈指针(延迟槽里恢复的)。

    lw 		a0, TF_BADVADDR(sp)
	lw	 	t1, __pgfault_handler//重点,跳转到处理函数
	jalr	t1

nop//异常处理结束,恢复现场
		lw		v1,TF_LO(sp)                                       
		mtlo	v1                               
		lw		v0,TF_HI(sp)                                         
		lw		v1,TF_EPC(sp)                    
		mthi	v0                               
		mtc0	v1,CP0_EPC                                             
		lw	$31,TF_REG31(sp)                 
		lw	$30,TF_REG30(sp)                 
		lw	$28,TF_REG28(sp)                 
		lw	$25,TF_REG25(sp)                 
		lw	$24,TF_REG24(sp)
		//……剩下的挨着存
		lw	$1,TF_REG1(sp) 
		lw	k0,TF_EPC(sp) 	//atomic operation needed 
		jr	k0			//
		lw	sp,TF_REG29(sp)  /* Deallocate stack */        
④sys_set_pgfault_handler

把异常处理栈和处理函数设置到目标进程的属性里。

env -> env_pgfault_handler = func;
env -> env_xstacktop = xstacktop;
(2)遇到COW类型缺页中断

在第一个图里,我们知道遇到COW类型缺页中断会被分发进入page_fault_handler\trap.c\lib

page_fault_handler(struct Trapframe *tf)
{
        u_int va;
        u_int *tos, d;
        struct Trapframe PgTrapFrame;
        extern struct Env * curenv;
//printf("^^^^cp0_BadVAddress:%x\n",tf->cp0_badvaddr);
        //支持异常处理的重入(处理缺页异常时又发生缺页异常)
        bcopy(tf, &PgTrapFrame,sizeof(struct Trapframe));
        if(tf->regs[29] >= (curenv->env_xstacktop - BY2PG) && tf->regs[29] <= (curenv->env_xstacktop - 1))
        {
                //panic("fork can't nest!!");
                tf->regs[29] = tf->regs[29] - sizeof(struct  Trapframe);
                bcopy(&PgTrapFrame, tf->regs[29], sizeof(struct Trapframe));
        }
        else
        {

                tf->regs[29] = curenv->env_xstacktop - sizeof(struct  Trapframe);
//              printf("page_fault_handler(): bcopy(): src:%x\tdes:%x\n",(int)&PgTrapFrame,(int)(curenv->env_xstacktop - sizeof(struct  Trapframe)));
                bcopy(&PgTrapFrame, curenv->env_xstacktop - sizeof(struct  Trapframe), sizeof(struct Trapframe));
        }
//      printf("^^^^cp0_epc:%x\tcurenv->env_pgfault_handler:%x\n",tf->cp0_epc,curenv->env_pgfault_handler);

        //从处理缺页异常的地址开始执行
        tf->cp0_epc = curenv->env_pgfault_handler;

        return;
}
①流程

在handle_mod里有

    NESTED(handle_\exception, TF_SIZE, sp)  
	//省略了很多
	move	a0, sp

所以我们可以明确第一个参数$a0,即这里的tf,应该是当前的运行现场TF

  • 将当前的运行现场复制到异常处理栈(与curenv->env_xstacktop 有关)
  • 设置代码重新开始执行的地址(与curenv->env_pgfault_handler有关)
②具体处理函数pgfault()

思路:将va(父子同一个)对应的物理页框,复制完全相同的内容到另一个物理页框,将想修改的父/子的va映射到新的物理页框。

static void
pgfault(u_int va)
{
    u_int *temp;
    //按页搬运,所以直接对齐
    va = ROUNDDOWN(va,BY2PG);
    //随便找一个不会冲突的临时虚拟地址
    temp = UXSTACKTOP-2*BY2PG;
    u_int perm = (*vpt)[VPN(va)]& 0xfff;
    if(perm & PTE_COW){
        //temp-->new_pa(null)
        if(syscall_mem_alloc(syscall_getenvid(),temp,perm & (~PTE_COW))<0)
        {
            user_panic("sys_mem_alloc error.\n");
        }
        //temp-->new_pa(old_pa)
        user_bcopy((void *)va,(void *)temp,BY2PG);
        //temp-->new_pa(old_pa) va-->new_pa(old_pa)
	 if(syscall_mem_map(syscall_getenvid(),temp,syscall_getenvid(),va,perm & (~PTE_COW))<0)
        {
            user_panic("sys_mem_map error.\n");
        }
        //temp-->null va-->new_pa(old_pa)
        if(syscall_mem_unmap(syscall_getenvid(),temp)<0)
        {
            user_panic("sys_mem_unmap error.\n");
        }
    }
    else
    {
        user_panic("Maximum Limit for ENV Exceeded\n");
    }
}
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值