【问题标题】:Fork implementation分叉实现
【发布时间】:2012-02-10 02:02:21
【问题描述】:

fork 系统调用代码是怎么写的。我想知道一个函数如何返回两个不同的值以及返回到两个不同的进程的一些细节。总之想知道fork系统调用是怎么实现的?

【问题讨论】:

  • 你可以试试看Linux内核源码...
  • 您对操作系统可以创建进程并可以选择将哪些内存区域映射到每个进程的地址空间这一想法感到满意吗?
  • 这有什么值得质疑的?

标签: c unix operating-system systems-programming


【解决方案1】:

卡尔的回答很棒。我想补充一点,在许多操作系统中,返回值是在其中一个寄存器中传递的。在 x86 架构中这个寄存器可能是 eax,在 ARM 架构中这个寄存器可能是 R0 等等。

每个进程还有一个进程控制块 (PCB),它存储在某些中断、系统调用或异常发生并将控制权传递给操作系统时的寄存器值。下一次调度进程时,寄存器的值会从 PCB 中恢复。

现在,当 fork() 发生时,操作系统可以这样做:

 child_process->PCB[return_value_register] = 0;
 parrent_process->PCB[return_value_register] = child_pid;

因此,当重新安排进程时,每个进程都会看到不同的返回值。

例如,您可以看到xv6's implementation of fork。在那里,父进程仍处于运行状态,因此它使用简单的 return 语句返回父进程的返回值。但是它将子进程的 EAX 寄存器的值设置为 0,因此当子进程被调度时,它会将 0 视为返回值:

// Clear %eax so that fork returns 0 in the child.
np->tf->eax = 0;

请注意,return 0 也将编译为“mov eax, 0”之类的内容。

更新:我刚刚为我正在做的一个爱好操作系统实现了 fork()。可以看源码here

【讨论】:

    【解决方案2】:

    您已经通过说这是一个系统调用来解释它。完成所有这些工作是操作系统的工作,并且操作系统几乎可以在您的程序上下文或您正在实现它的任何语言的规则之外做它想做的任何事情。这是一个简单的例子发生:

    1. 程序调用fork()系统调用
    2. 内核 fork 系统调用复制了运行程序的进程
    3. 内核为原始程序和副本的系统调用设置返回值(副本的PID分别为0)
    4. 内核将两个进程都放入调度程序队列中
    5. 随着每个进程的调度,内核“返回”到两个程序中的每一个。

    【讨论】:

      【解决方案3】:

      Unix V6 大学源代码手册中有一条注释,由 Ken Thompson 和 Dennis Ritchie 自己注释,描述了双重回报的实际工作原理。评论以以下句子结尾:

      你不应该理解这一点。

      【讨论】:

      • IIRC 指的是用于此的程序集,而不是返回两次自身的想法。
      【解决方案4】:

      以简单的方式,例如将进程克隆到 fork() 函数中,并移动 IP/EIP/RIP 寄存器以跳过函数中的某些指令,如下所示:

      return pid;
      return 0;
      

      第一个进程将执行第一条指令并从堆栈中弹出函数,第二个进程将开始但从第二条指令返回0。

      【讨论】:

      • 我认为这只是内核从每个进程的系统调用中返回不同的值。
      • 当你调用 fork() 时,两个进程从同一点开始。我不知道您所说的“第二个”过程(父母或孩子)是什么意思,但它不正确
      猜你喜欢
      • 2011-09-28
      • 1970-01-01
      • 2017-11-11
      • 2011-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-04
      相关资源
      最近更新 更多