操作系统Lab笔记——为什么sys_exofork()必须内联?

本文内容写于2023年12月18日,为JOS Lab的分析笔记。今正式公开。

如果sys_exofork()不内联,在ret指令前,%esp0xeebfdf34%ebp0xeebfdf70(实际上是fork()%ebp,因为sys_exofork()没有push %ebpmov %esp, %ebp)。

新建的子环境会卡在ret之前,等待iret从系统调用返回。而父环境继续fork()。在调用sys_page_map()进行COW映射之前,%esp会降至%0xeebfdf28,即父环境会覆盖子环境的返回地址(因为此时两者公用栈)。仔细来说,是在调用sys_page_map()之前push %edi传入参数时。这个参数是dstva

而如果sys_exo_fork()是内联的,就不会有以上问题。fork()调用的叶函数不会修改fork()的返回地址,而fork()完成后,已经完成了栈的COW映射,父环境的其他过程不会修改子环境fork()的返回地址。

事实上,只考虑测试样例的话,我已经设计出了非内联版本的sys_exofork()。原理很简单:把返回地址搬到一个不会被修改的地方。

c
static envid_t __attribute__((noinline))
sys_exofork(void)
{
    envid_t ret;
    asm volatile("movl (%esp), %eax\n"
                 "movl %eax, -0x80(%esp)\n");
    asm volatile("int %2" : "=a"(ret) : "a"(SYS_exofork), "i"(T_SYSCALL));
    asm volatile("push %eax\n"
                 "movl -0x7c(%esp), %eax\n"
                 "movl %eax, 0x4(%esp)\n"
                 "pop %eax\n");
    return ret;
}

或者:

c
asm(".type sys_exofork, @function\n"
    "sys_exofork:\n"
    "\tmovl (%esp), %eax\n"
    "\tmovl %eax, -0x80(%esp)\n"
    "\tmovl $7, %eax\n"
    "\tint $48\n"
    "\tpush %eax\n"
    "\tmovl -0x7c(%esp), %eax\n"
    "\tmovl %eax, 0x4(%esp)\n"
    "\tpop %eax\n"
    "\tret\n");
Todo List
Valaxy v0.28.4 驱动|主题-Yunv0.28.4