MIT6.828 Part B: Copy-on-Write Fork

MIT6.828_LAB4_PartB_Copy-on-Write Fork Part B: Copy-on-Write Fork 正如先前所提到的,Unix提供了fork()系统调用作为创建进程原语,fork()会将父进程的地址空间复制来创建一个子进程。 xv6 Unix通过将父进程所有页的数据复制到新分配给子进程的页上实现了fork(),实际上dumbfork()中也采用了相同的方法,复制父进程的地址空间到子进程是fork()操作中开销最大的部分。 但是,在子进程中fork()之后通常紧接着是对exec()的调用,新程序会替换掉子进程的内存。 这就是shell程序通常所做的事情。 阅读详情

  子进程在被创建之后很可能立刻执行exec()了,之前做的一系列的拷贝就是无用功了。
  所以说,当创建一个新的子进程的时候,只需要拷贝父进程的内存映射(页表)就可以了,而且将父进程所有的内存映射页都标记为只读的,这样,当子进程或者父进程尝试去读的时候是安全的,而当尝试去写的时候,就会出发page fault,而在page fault处理例程中,单独将被写入的页(比如说栈)拷贝一份,修改掉发出写行为的进程的页表相应的映射就可以了。

User-level page fault handling
  1个用户级写时拷贝的fork函数需要在写保护页时触发page fault,所以我们第一步应该先规定或者确立一个page fault处理例程,每个进程需要向内核注册这个处理例程,只需要传递一个函数指针即可,sys_env_set_pgfault_upcall函数将当前进程的page fault处理例程设置为func指向的函数。
  Exercise 8:
  实现sys_env_set_pgfault_upcall函数。
  回答:

static int
sys_env_set_pgfault_upcall(envid_t envid, void *func)
{
        struct Env *env;

        if (envid2env(envid, &env, 1) < 0)
                return -E_BAD_ENV;
        env->env_pgfault_upcall = func;
        return 0;
}

Normal and Exception Stacks in User Environments
  在正常运行期间,用户进程运行在用户栈上,栈顶寄存器ESP指向USTACKTOP处,堆栈数据位于USTACKTOP-PGSIZE 与USTACKTOP-1之间的页。当在用户模式发生1个page fault时,内核将在专门处理page fault的用户异常栈上重新启动进程。
  而异常栈则是为了上面设置的异常处理例程设立的。当异常发生时,而且该用户进程注册了该异常的处理例程,那么就会转到异常栈上,运行异常处理例程。
  到目前位置出现了三个栈:
  [KSTACKTOP, KSTACKTOP-KSTKSIZE]
  内核态系统栈

  [UXSTACKTOP, UXSTACKTOP - PGSIZE]
  用户态错误处理栈

  [USTACKTOP, UTEXT]
  用户态运行栈

  内核态系统栈是运行内核相关程序的栈,在有中断被触发之后,CPU会将栈自动切换到内核栈上来,而内核栈的设置是在kern/trap.c的trap_init_percpu()中设置的。

void
trap_init_percpu(void)
{
        // Setup a TSS so that we get the right stack
        // when we trap to the kernel.
        thiscpu->cpu_ts.ts_esp0 = KSTACKTOP - cpunum() * (KSTKGAP + KSTKSIZE);
        thiscpu->cpu_ts.ts_ss0 = GD_KD;

        // Initialize the TSS slot of the gdt.
        gdt[(GD_TSS0 >> 3) + cpunum()] = SEG16(STS_T32A, (uint32_t) (&thiscpu->cpu_ts),
                                        sizeof(struct Taskstate) - 1, 0);
        gdt[(GD_TSS0 >> 3) + cpunum()].sd_s = 0;

        // Load the TSS selector (like other segment selectors, the
        // bottom three bits are special; we leave them 0)
        ltr(GD_TSS0 + sizeof(struct Segdesc) * cpunum());

        // Load the IDT
        lidt(&idt_pd);
}

  用户定义注册了自己的中断处理程序之后,相应的例程运行时的栈,整个过程如下:
  首先陷入到内核,栈位置从用户运行栈切换到内核栈,进入到trap中,进行中断处理分发,进入到page_fault_handler()
当确认是用户程序触发的page fault的时候(内核触发的直接panic了),为其在用户错误栈里分配一个UTrapframe的大小
把栈切换到用户错误栈,运行响应的用户中断处理程序中断处理程序可能会触发另外一个同类型的中断,这个时候就会产生递归式的处理。处理完成之后,返回到用户运行栈。
Invoking the user page fault handle
  可以将用户自己定义的用户处理进程当作是一次函数调用看待,当错误发生的时候,调用一个函数,但实际上还是当前这个进程,并没有发生变化。所以当切换到异常栈的时候,依然运行当前进程,但只是运行的中断处理函数,所以说此时的栈指针发生了变化,而且程序计数器eip也发生了变化,同时还需要知道的是引发错误的地址在哪。这些都是要在切换到异常栈的时候需要传递的信息。和之前从用户栈切换到内核栈一样,这里是通过在栈上构造结构体,传递指针完成的。
  这里新定义了一个结构体用来记录出现用户定义错误时候的信息Utrapframe:

struct UTrapframe {
        /* information about the fault */
        uint32_t utf_fault_va;  /* va for T_PGFLT, 0 otherwise */
        uint32_t utf_err;
        /* trap-time return state */
        struct PushRegs utf_regs;
        uintptr_t utf_eip;
        uint32_t utf_eflags;
        /* the trap-time stack to return to */
        uintptr_t utf_esp;
} __attribute__((packed));

  相比于UTrapframe,这里多了utf_fault_va,因为要记录触发错误的内存地址,同时还少了es,ds,ss等。因为从用户态栈切换到异常栈,或者从异常栈再切换回去,实际上都是一个用户进程,所以不涉及到段的切换,不用记录。在实际使用中,Trapframe是作为记录进程完整状态的结构体存在的,也作为函数参数进行传递;而UTrapframe只在处理用户定义错误的时候用。到
  整体上讲,当正常执行过程中发生了页错误,那么栈的切换是
  用户运行栈—>内核栈—>异常栈
  而如果在异常处理程序中发生了也错误,那么栈的切换是
  异常栈—>内核栈—>异常栈
  Exercise 9:
  实现page_fault_handler函数来分发page fault到用户模式的处理函数。
  回答:
  如果当前已经在用户错误栈上了,那么需要留出4个字节,否则不需要,具体和跳转机制有关系。简单说就是在当前的错误栈顶的位置向下留出保存UTrapframe的空间,然后将tf中的参数复制过来。修改当前进程的程序计数器和栈指针,然后重启这个进程,此时就会在用户错误栈上运行中断处理程序了。当然,中断处理程序运行结束之后,需要再回到用户运行栈中,这个就是异常处理程序需要做的了。

void
page_fault_handler(struct Trapframe *tf)
{
        uint32_t fault_va;

        // Read processor's CR2 register to find the faulting address
        fault_va = rcr2();

        // Handle kernel-mode page faults.
        if (tf->tf_cs == GD_KT)
                panic("page_fault in kernel mode, fault address: %d\n", fault_va);

        struct UTrapframe *utf;

        if (curenv->env_pgfault_upcall) {
                if (UXSTACKTOP - PGSIZE <= tf->tf_esp && tf->tf_esp <= UXSTACKTOP - 1)
                        utf = (struct UTrapframe *)(tf->tf_esp - sizeof(struct UTrapframe) - 4);
                else
                        utf = (struct UTrapframe *)(UXSTACKTOP - sizeof(struct UTrapframe));
                user_mem_assert(curenv, (void *)utf, sizeof(struct UTrapframe), PTE_U | PTE_W);

                utf->utf_fault_va = fault_va;
                utf->utf_err = tf->tf_trapno;
                utf->utf_eip = tf->tf_eip;
                utf->utf_eflags = tf->tf_eflags;
                utf->utf_esp = tf->tf_esp;
                utf->utf_regs = tf->tf_regs;
                tf->tf_eip = (uint32_t)curenv->env_pgfault_upcall;
                tf->tf_esp = (uint32_t)utf;
                env_run(curenv);
        }

        // Destroy the environment that caused the fault.
        cprintf("[%08x] user fault va %08x ip %08x\n",
                curenv->env_id, fault_va, tf->tf_eip);
        print_trapframe(tf);
        env_destroy(curenv);
}

  如果异常栈发生了overflow怎么办?看一下memlayout.h就知道了。用户异常栈就一页的大小,一旦溢出,访问的就是内核都没有访问权限的空间,会发生内核空间中的page fault,此时会直接panic,不会造成更严重的后果。
  
User-mode Page Fault Entrypoint
  接下来,就需要汇编实现功能:当从用户定义的处理函数返回之后,如何从用户错误栈直接返回到用户运行栈。
  Exercise 10:
  实现在lib/pfentry.S中的_pgfault_upcall调用。
  回答:
  _pgfault_upcall是所有用户页错误处理程序的入口,在这里调用用户自定义的处理程序,并在处理完成后,从错误栈中保存的UTrapframe中恢复相应信息,然后跳回到发生错误之前的指令,恢复原来的进程运行。

.text
.globl _pgfault_upcall
_pgfault_upcall:
        // Call the C page fault handler.
        pushl %esp                      // function argument: pointer to UTF
        movl _pgfault_handler, %eax
        call *%eax
        addl $4, %esp                   // pop function argument

        movl 48(%esp), %ebp
        subl $4, %ebp
        movl %ebp, 48(%esp)
        movl 40(%esp), %eax
        movl %eax, (%ebp)

        // Restore the trap-time registers.  After you do this, you can no longer modify any general-purpose registers.
        addl $8, %esp
        popal

        // Restore eflags from the stack.  After you do this, you can
        // no longer use arithmetic operations or anything else that
        // modifies eflags.
        addl $4, %esp
        popfl

        // Switch back to the adjusted trap-time stack.

        popl %esp

        // Return to re-execute the instruction that faulted.
        ret

  分析:
  重点是调用_pgfault_handler返回时的操作,此时的异常栈结构如下:
  这里写图片描述
  这里trap-time esp上的空间有1个4字节的保留空间,是做为中断递归的情形。
  然后将栈中的trap-time esp取出减去4,再存回栈中。此时如果是中断递归中,esp-4即是保留的4字节地址;如果不是则是用户运行栈的栈顶。
  再将原来出错程序的EIP(即trap-time eip)取出放入保留的4字节,以便后来恢复运行。此时的异常栈布局如下:
  这里写图片描述
  紧接着恢复通用寄存器和EFLAG标志寄存器,此时的异常栈结构如下:
  这里写图片描述
  然后pop esp切换为原来出错程序的运行栈,最后使用ret返回出错程序。
  
  Exercise 11:
  实现C库用户态的page fault处理函数。
  回答:
  进程在运行前注册自己的页错误处理程序,重点是申请用户异常栈空间,最后添加上系统调用号。

void
set_pgfault_handler(void (*handler)(struct UTrapframe *utf))
{
        int r;

        if (_pgfault_handler == 0) {
                // First time through!
                if ((r = sys_page_alloc(thisenv->env_id, (void *)(UXSTACKTOP - PGSIZE), PTE_P | PTE_W | PTE_U)) < 0)
                        panic("set_pgfault_handler: %e", r);
                sys_env_set_pgfault_upcall(thisenv->env_id, _pgfault_upcall);

        }

        // Save handler pointer for assembly to call.
        _pgfault_handler = handler;
}

Implementing Copy-on-Write Fork
  接下来就是最重要的部分:实现copy-on-write fork。
  与之前的dumbfork不同,fork出一个子进程之后,首先要进行的就是将父进程的页表的全部映射拷贝到子进程的地址空间中去。这个时候物理页会被两个进程同时映射,但是在写的时候是应该隔离的。采取的方法是在子进程映射的时候,将父进程空间中所有可以写的页表的部分全部标记为可读且COW。而当父进程或者子进程任意一个发生了写的时候,因为页表现在都是不可写的,所以会触发异常,进入到我们设定的page fault处理例程,当检测到是对COW页的写操作的情况下,就可以将要写入的页的内容全部拷贝一份,重新映射。
  Exercise 12:
  实现在lib/fork.c的fork,duppage和pgfault。
  回答:
  首先是pgfault处理page fault时的写时拷贝。

static void
pgfault(struct UTrapframe *utf)
{
        int r;
        void *addr = (void *) utf->utf_fault_va;
        uint32_t err = utf->utf_err;

        if ((err & FEC_WR) == 0 || (uvpt[PGNUM(addr)] & PTE_COW) == 0)
                panic("pgfault: it's not writable or attempt to access a non-cow page!");
        // Allocate a new page, map it at a temporary location (PFTEMP),
        // copy the data from the old page to the new page, then move the new
        // page to the old page's address.

        envid_t envid = sys_getenvid();
        if ((r = sys_page_alloc(envid, (void *)PFTEMP, PTE_P | PTE_W | PTE_U)) < 0)
                panic("pgfault: page allocation failed %e", r);

        addr = ROUNDDOWN(addr, PGSIZE);
        memmove(PFTEMP, addr, PGSIZE);
        if ((r = sys_page_unmap(envid, addr)) < 0)
                panic("pgfault: page unmap failed %e", r);
        if ((r = sys_page_map(envid, PFTEMP, envid, addr, PTE_P | PTE_W |PTE_U)) < 0)
                panic("pgfault: page map failed %e", r);
        if ((r = sys_page_unmap(envid, PFTEMP)) < 0)
                panic("pgfault: page unmap failed %e", r);
}

  在pgfault函数中先判断是否页错误是由写时拷贝造成的,如果不是则panic。借用了一个一定不会被用到的位置PFTEMP,专门用来发生page fault的时候拷贝内容用的。先解除addr原先的页映射关系,然后将addr映射到PFTEMP映射的页,最后解除PFTEMP的页映射关系。
  接下来是duppage函数,负责进行COW方式的页复制,将当前进程的第pn页对应的物理页的映射到envid的第pn页上去,同时将这一页都标记为COW。

static int
duppage(envid_t envid, unsigned pn)
{
        int r;

        void *addr;
        pte_t pte;
        int perm;

        addr = (void *)((uint32_t)pn * PGSIZE);
        pte = uvpt[pn];
        perm = PTE_P | PTE_U;
        if ((pte & PTE_W) || (pte & PTE_COW))
                perm |= PTE_COW;
        if ((r = sys_page_map(thisenv->env_id, addr, envid, addr, perm)) < 0) {
                panic("duppage: page remapping failed %e", r);
                return r;
        }
        if (perm & PTE_COW) {
                if ((r = sys_page_map(thisenv->env_id, addr, thisenv->env_id, addr, perm)) < 0) {
                        panic("duppage: page remapping failed %e", r);
                        return r;
                }
        }

        return 0;
}

  最后是fork函数,将页映射拷贝过去,这里需要考虑的地址范围就是从UTEXT到UXSTACKTOP为止,而在此之上的范围因为都是相同的,在env_alloc的时候已经设置好了。

envid_t
fork(void)
{
        uint32_t addr;
        int i, j, pn, r;
        extern void _pgfault_upcall(void);
        if ((envid = sys_exofork()) < 0) {
                panic("sys_exofork failed: %e", envid);
                return envid;
        }
        if (envid == 0) {
                thisenv = &envs[ENVX(sys_getenvid())];
                return 0;
        }

        for (i = PDX(UTEXT); i < PDX(UXSTACKTOP); i++) {
                if (uvpd[i] & PTE_P) {
                        for (j = 0; j < NPTENTRIES; j++) {
                                pn = PGNUM(PGADDR(i, j, 0));
                                if (pn == PGNUM(UXSTACKTOP - PGSIZE))
                                        break;
                                if (uvpt[pn] & PTE_P)
                                        duppage(envid, pn);
                        }
                }

        }

        if ((r = sys_page_alloc(envid, (void *)(UXSTACKTOP - PGSIZE), PTE_P | PTE_U | PTE_W)) < 0) {
                panic("fork: page alloc failed %e", r);
                return r;
        }
        if ((r = sys_page_map(envid, (void *)(UXSTACKTOP - PGSIZE), thisenv->env_id, PFTEMP, PTE_P | PTE_U | PTE_W)) < 0) {
                panic("fork: page map failed %e", r);
                return r;
        }
        memmove((void *)(UXSTACKTOP - PGSIZE), PFTEMP, PGSIZE);
        if ((r = sys_page_unmap(thisenv->env_id, PFTEMP)) < 0) {
                panic("fork: page unmap failed %e", r);
                return r;
        }
        sys_env_set_pgfault_upcall(envid, _pgfault_upcall);
        if ((r = sys_env_set_status(envid, ENV_RUNNABLE)) < 0) {
                panic("fork: set child env status failed %e", r);
                return r;
        }

        return envid;
}

  首先需要为父进程设定错误处理例程。这里调用set_pgfault_handler函数是因为当前并不知道父进程是否已经建立了异常栈,没有的话就会建立一个,而sys_env_set_pgfault_upcall则不会建立异常栈。
  调用sys_exofork准备出一个和父进程状态相同的子进程,状态暂时设置为ENV_NOT_RUNNABLE。然后进行拷贝映射的部分,在当前进程的页表中所有标记为PTE_P的页的映射都需要拷贝到子进程空间中去。但是有一个例外,是必须要新申请一页来拷贝内容的,就是用户异常栈。因为copy-on-write就是依靠用户异常栈实现的,所以说这个栈要在fork完成的时候每个进程都有一个,要硬拷贝过来。
  主要流程就是:
  1、申请新的物理页,映射到子进程的(UXSTACKTOP-PGSIZE)位置上去。
  2、父进程的PFTEMP位置也映射到子进程新申请的物理页上去,这样父进程也可以访问这一页。
  3、在父进程空间中,将用户错误栈全部拷贝到子进程的错误栈上去,也就是刚刚申请的那一页。
  4、然后父进程解除对PFTEMP的映射。
  5、最后把子进程的状态设置为可运行。
  至此,Lab4的part B的写时拷贝就完成了。
  

MIT JOS LAB4学习笔记 Lab4 Part A: 多处理器支持和协作式多任务 练习 1 :实现在 kern/pmap.c 中的 mmio_map_region 方法。你可以看看 kern/lapic.c 中的 lapic_init 开头部分,了解一下它是如何被调用的。你还需要完成接下来的练习,你的 mmio_map_region 才能够正常运行。 lapic_init()函数的一开始就调用了该函数,将从lapica... 阅读详情

相关推荐

2021 XV6 5:Copy-on-Write Fork

XV6 操作系统

weixin_46266058的博客 1152

MIT6.828 Lab5 Copy-on-Write Fork for xv6

【代码】MIT6.828 Lab5 Copy-on-Write Fork for xv6。

QQQ的博客 781

MIT 6.828 学习笔记6 Lab4实验报告

Lab4实验报告 Execrise 1 Implement mmio_map_region in kern/pmap.c. // mmio_map_region() uintptr_t ret = base; size = ROUNDUP(size, PGSIZE); base = base + size; if (base >= MMIOLIM) { panic("larg

丶Hiroshi 9554

[MIT6.828] LAB4 Part B: Copy-on-Write Fork

<br />Exercise 3. Implement the sys_env_set_pgfault_upcall system call. Be sure to enable permission checking when looking up the environment ID of the target environment, since this is a "dangerous" system call. <br /><br /> 修改kern/syscall.c文件,添加注册页故障处理函

系统中国 1915

MIT-6.s081-OS lab cow: Copy-on-Write Fork for xv6

本实验仍然是虚拟内存,所以暂且跳过了中间的内容(指lec和课本) 分析 copy on write fork : 在之前的code walk through(https://blog.csdn.net/RedemptionC/article/details/107709618)中提到了,fork的实现里,有一个uvmcopy的调用,作用是将父进程的内存复制到子进程中,这个复制包括申请物理内存,设置pte 但是在下列情况下: 父进程和子进程都不修改某些内存页 fork之后子进程马上调用exec,而

RedemptionC的博客 2319

从TL431光耦到集成隔离器:3种反激电源反馈方案全对比,你的项目到底该选哪个?

本文深入对比了反激电源设计中TL431光耦、集成隔离器和原边反馈三种主流反馈方案的优缺点,帮助开发者在电源设计中选择最适合的方案。从精度、成本、隔离等级等多维度分析,为反激式(Flyback)拓扑的应用提供实用选型指南。

weixin_30536513的博客 399

MIT6.828 LAB4 PartB

JOS LAB4 PartB   PartB主要讲述了fork的写时复制的实现,为了满足实现的要求,需要page_fault的实现,即页面异常处理。          这里主要实现几个系统调用函数,以供后面的fork实现调用。这些系统调用都比较简单。这里就看下 sys_exofork的实现。     sys_exofork这个函数只是fork函数的一个雏形,他的功能就是新建立一个进程,

fang92的专栏 2245

保姆级教程:手把手带你复现APFNet论文的RGBT目标跟踪模型(附完整代码与数据集配置)

本文提供了一份详细的APFNet论文复现指南,涵盖从环境搭建到模型测试的全流程。通过解析多模态融合模块和渐进式训练流程,帮助读者高效完成RGBT目标跟踪模型的复现工作,特别针对训练和测试中的常见问题提供实用解决方案。

weixin_29056781的博客 342

【转载】COW奶牛!Copy On Write机制了解一下

整理Redis知识时,对于cow机制感兴趣,百度了下,感觉大神 Java3y 的讲解超精致容易看懂。忍不住分享下,以下正文: 在读《Redis设计与实现》关于哈希表扩容的时候,发现这么一段话: 执行BGSAVE命令或者BGREWRITEAOF命令的过程中,Redis需要创建当前服务器进程的子进程,而大多数操作系统都采用写时复制(copy-on-write)来优化子进程的使用效率,所以在子...

老张的便利贴 531

DSP28335与Vofa+上位机通信实战:SCIb配置避坑指南(附完整代码)

本文提供DSP28335与Vofa+上位机通信的实战指南,重点解析SCIb模块配置的常见问题与解决方案。针对研旭开发板的硬件连接、波特率精确计算、FIFO高效使用以及FireWater协议封装进行了详细说明,并附有完整代码,帮助开发者快速实现实时数据可视化与波形分析,提升调试效率。

weixin_29172963的博客 150

华三(H3C)网络设备配置命令大全与学习指南

本文全面介绍 H3C 网络设备的配置命令,涵盖路由器、交换机、ACL、NAT、VTY、SSH、本地用户、日志、STP、防火墙、QoS 等模块,适合网络工程初学者与运维人员参考。

DreamCollector的博客 6565

MIT6.828学习之Lab4_Part B: Copy-on-Write Fork

如之前提到的,Unix提供了fork()系统调用作为其主要的进程创建原语。fork()系统调用将调用者进程(父进程)的地址空间复制到一个新创建的进程(子进程)。 xv6 Unix 通过复制父进程物理页所有数据到分配给子进程的物理页。dumbfork()也是这么做的。将父地址空间复制到子地址空间是fork()操作中开销最大的部分。 然而,调用fork()后几乎里面会在子进程中调用一个exec(),它...

a747979985的博客 1308

手把手教你配置C6678的SPI启动:从NorFlash烧写到多核加载的完整流程

本文详细介绍了TMS320C6678多核DSP的SPI启动全流程,包括硬件准备、镜像文件生成与转换、NorFlash烧写与验证、多核启动同步机制以及常见问题排查。通过实战演示,帮助开发者高效配置SPI启动模式,解决启动失败、多核同步异常等问题,提升嵌入式系统开发效率。

weixin_27076351的博客 151

MIT_6.828_Lab4 Part B

此次的实验是实现一个写时复制的fork()。很好的实验,能够加深对fork()的理解。

salvete的博客 502

NFS共享速度慢?麒麟系统性能调优5个关键参数实测对比

本文针对银河麒麟服务器系统上NFS共享速度慢的问题,通过实测对比分析了五个关键调优参数。重点探讨了rsize/wsize传输块大小、TCP/UDP协议选择、sync/async模式权衡等,并结合麒麟系统特性提供了具体的优化建议与配置示例,旨在帮助用户有效提升国产化环境下的文件共享性能。

weixin_29254029的博客 335

如何选择汽车域控制器MCU?一文看透7大关键维度[特殊字符](含芯片推荐、踩坑预警)

MCU选型 Checklist(实用表格)维度你需要考虑的问题Flash/RAM我的任务栈够吗?是否支持OTA双Bank?安全等级这功能出故障会不会危及人命?接口资源够几个CAN?支持CAN FD?ETH是否支持TSN?主频我的软件响应周期是多少?是否带Cache?多核架构是SMP/AMP?是否带锁步核?跑多个任务会不会互相干扰?功耗与封装睡眠电流够低?有外扩DDR空间?BGA能焊?供应链有无国产替代?交期是否稳定?支持AUTOSAR吗?

weixin_43199439的博客 662

[操作系统实验lab4]实验报告

实验概况 在开始实验之前,先对实验整体有个大概的了解,这样能让我们更好地进行实验。 我们本次实验需要补充的内容包括一整套以sys开头的系统调用函数,其中包括了进程间通信需要的一些系统调用如sys_ipc_can_recv等,以及补充完成fork.c函数,当然也不能少填写syscall_wrap.S. 系统调用 关于系统调用,我们主要是以以下流程来进行的: 用户调用syscall特权指令触发异...

weixin_30555125的博客 510

手把手教你学PCIE--固件与BIOS配置 - BIOS设置对PCIe性能的影响

本文详细分析了BIOS设置对PCIe性能的影响,并提供了优化建议。通过合理调整链路参数、功耗管理策略和高级功能配置,可以显著提升系统的性能和稳定性。本文将进一步分析BIOS设置如何影响PCIe设备的性能,并提供优化建议。BIOS(或UEFI)是系统启动时的第一个控制层,其设置直接决定了PCIe设备的工作状态和性能表现。BIOS中的功耗管理选项(如C-States、P-States)会影响PCIe链路的低功耗模式(L0s/L1)。MSI/MSI-X中断具有更高的性能和更好的可扩展性,应作为首选方案。

MHD0815的博客 995

Mit6.S081-实验6-Copy-on-Write Fork for xv6

Mit6.S081-实验6-Copy-on-Write Fork for xv6一、前言二、The problem三、The solution四、Implement copy-on write1,实验要求2,进攻计划3,一些提示4,具体实现5,执行效果 一、前言 虚拟内存提供一定程度的重定向:kernel可以通过标记PTEs无效、只读(导致page faults)来中断内存引用; kernel也可以通过改变地址含义(通过更改PTES)。 在电脑系统中有个说法:系统问题可以通过一定程度的重定向解决。 lazy

code a better life~ 33万+

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Linux写时拷贝技术(copy-on-write)

源于网上资料 COW技术初窥:       在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,linux中引入了“写时复制“技术,也就是只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。       那么子进程的物理空间没有代码,怎么去取指令执行exec系统调用呢?       在fork之后exe...

weixin_33923148的博客 1109

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Lab6: Copy-on-Write Fork for xv6 详解

Lab6: Copy-on-Write Fork for xv6 问题 xv6中的fork()系统调用将父进程的所有用户空间内存复制到子进程中。如果父进程较大,则复制可能需要很长时间。更糟糕的是,这项工作经常造成大量浪费;例如,子进程中的fork()后跟exec()将导致子进程丢弃复制的内存,而其中的大部分可能都从未使用过。另一方面,如果父子进程都使用一个页面,并且其中一个或两个对该页面有写操作,则确实需要复制。 解决方案 copy-on-write (COW) fork()的目标是推迟到子进程实际需要物

qq_53111905的博客 6942

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

MIT 6.S081 lab 6:Copy-on-Write Fork

1 Copy-on-Write Fork Lab 实现xv6 4.6节中的COW技术。 起始工作 // 1.增加标志位PTE_C的宏,表示是否为COW页 // riscv.h #define PTE_C (1L << 8) // COW page flag // 2.增添全局变量 pageCount 来对所有分配的物理页引用计数。 // kalloc.c uint *pageCount[32]; 初始化pageCount 需要用32个页去计数从 KERBASE- PHYSTOP(这

weixin_43174477的博客 845

北航操作系统实验lab4总结

syscall流程图(以writef())为例 syscall_env_alloc 是系统调用函数,最终执行的特定函数为sys_env_alloc() int r; struct Env *e; //给当前进程申请一个儿子进程 if ((r=env_alloc(&e, curenv -> env_id)) < 0) { ...

piaopu0120的博客 1619

Linux -- fork() 写时拷贝(copy-on-write)

参考:《Linux内核设计与实现》 Linux 的fork()使用写时拷贝页来实现! 写时拷贝:是一种推迟或者免除拷贝的技术。 内核fork()时并不复制整个进程地址空间,而是让父子进程共享一个地址空间---》只有在需要写入时,数据才会被复制,从而使各个进程拥有各自的拷贝数据。 也就是说,只有在需要写入的时候才复制资源,在此之前,以只读方式共享。 比如:父进程fork()后,子进程立即e...

DXT的博客 1075

copy-on-write 写时复制

最近在读一些关于虚拟化的论文,里面大都提到了一个copy-on-write的技术。 Copy-on-Write 简单来说是,复制一个对象时,不是真正的在内存中开辟一块新的地址,将原来的对象复制到新地址,而是在新对象的内存映射表(Translation Table)中指向同原对象相同的位置,并且把那块内存的copy-on-write位设置为1,在对这个对象执行读操作的时候,内存数据没有变动,直接执

0X011954 2858
上一篇: MIT6.828 Lab4 Part A: Multiprocessor Support and Cooperative Multitasking
下一篇: MIT6.828 LAB4 Part C: Preemptive Multitasking and Inter-Process communication (IPC)
bysui
博客等级 码龄14年 199粉丝 21原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值