fork()在内部调用clone()是真的吗?

Don*_*ild 38 c linux operating-system system-calls

我读到这里clone()系统调用,用于在Linux中创建一个线程.现在,语法clone()是这样的,就需要启动程序/函数地址传递给它.

但是在这个页面上写的内部fork()调用clone().所以我的问题是如何通过fork()开始运行fork()调用后的代码部分来创建子进程,即它如何不需要函数作为起点?

如果我提供的链接信息不正确,请指导我一些更好的链接/资源.

谢谢

Die*_*Epp 67

对于这样的问题,请始终阅读源代码.

从glibc的公司nptl/sysdeps/unix/sysv/linux/fork.c(GitHub上)(nptl=原生POSIX线程对于Linux),我们可以找到的实现fork(),这绝对不是一个系统调用,我们可以看到,魔术里面发生的ARCH_FORK宏,它被定义为内嵌调用clone()nptl/sysdeps/unix/sysv/linux/x86_64/fork.c(GitHub上) .但是等等,没有函数或堆栈指针传递给这个版本clone()!那么,这里发生了什么?

我们来看看clone()glibc 的实现.它在sysdeps/unix/sysv/linux/x86_64/clone.S(GitHub).您可以看到它的作用是将函数指针保存在子堆栈上,调用克隆系统调用,然后新进程将读取弹出该函数的堆栈然后调用它.

它的工作原理如下:

clone(void (*fn)(void *), void *stack_pointer)
{
    push fn onto stack_pointer
    syscall_clone()
    if (child) {
        pop fn off of stack
        fn();
        exit();
    }
}
Run Code Online (Sandbox Code Playgroud)

而且fork()......

fork()
{
    ...
    syscall_clone();
    ...
}
Run Code Online (Sandbox Code Playgroud)

摘要

实际的clone()系统调用不接受函数参数,它只是从返回点继续,就像fork().因此clone(),fork() 函数库函数都是clone()系统调用的包装器.

文档

对于clone()既是库函数又是系统调用这一事实,我的手册副本更为贴切.但是,我确实发现它有点误导,clone()见于第2节,而不是第2节和第3节.从手册页:

#include <sched.h>

int clone(int (*fn)(void *), void *child_stack,
          int flags, void *arg, ...
          /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );

/* Prototype for the raw system call */

long clone(unsigned long flags, void *child_stack,
          void *ptid, void *ctid,
          struct pt_regs *regs);
Run Code Online (Sandbox Code Playgroud)

和,

该页面描述了glibc clone() 包装器函数和它所基于的底层系统调用.正文描述了包装函数; 原始系统调用的差异在本页末尾描述.

最后,

原始clone()系统调用更紧密地对应fork(2)于子节点中的执行从呼叫点继续.因此,clone()省略了包装函数的fn和arg参数.此外,参数顺序也会改变.

  • exacly:`fork()`不调用`clone()`,两者都是使用系统调用`clone`的函数. (2认同)

Cir*_*sta 9

@Dietrich通过查看实施情况做了很好的解释.棒极了!无论如何,还有另一种发现方式:通过查看调用strace"嗅探".

我们可以准备一个非常简单的程序,使用fork(2)然后检查我们的假设(即,没有fork系统调用真正发生).

#define WRITE(__fd, __msg) write(__fd, __msg, strlen(__msg))

int main(int argc, char *argv[])
{
  pid_t pid;

  switch (pid = fork()) {
    case -1:
      perror("fork:");
      exit(EXIT_FAILURE);
      break;
    case 0:
      WRITE(STDOUT_FILENO, "Hi, i'm the child");
      exit(EXIT_SUCCESS);
    default:
      WRITE(STDERR_FILENO, "Heey, parent here!");
      exit(EXIT_SUCCESS);
  }

  return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

现在,编译该代码(clang -Wall -g fork.c -o fork.out)然后执行它strace:

strace -Cfo ./fork.strace.log ./fork.out
Run Code Online (Sandbox Code Playgroud)

这将拦截我们的进程调用的系统调用(-f我们也拦截子调用),然后将这些调用放入./fork.trace.log; -c选项在最后提供了摘要).我的机器(Ubuntu 14.04,x86_64 Linux 3.16)的结果是(汇总):

6915  arch_prctl(ARCH_SET_FS, 0x7fa001a93740) = 0
6915  mprotect(0x7fa00188c000, 16384, PROT_READ) = 0
6915  mprotect(0x600000, 4096, PROT_READ) = 0
6915  mprotect(0x7fa001ab9000, 4096, PROT_READ) = 0
6915  munmap(0x7fa001a96000, 133089)    = 0
6915  clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fa001a93a10) = 6916
6915  write(2, "Heey, parent here!", 18) = 18
6916  write(1, "Hi, i'm the child", 17 <unfinished ...>
6915  exit_group(0)                     = ?
6916  <... write resumed> )             = 17
6916  exit_group(0)                     = ?
6915  +++ exited with 0 +++
6916  +++ exited with 0 +++
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 24.58    0.000029           4         7           mmap
 17.80    0.000021           5         4           mprotect
 14.41    0.000017           9         2           write
 11.02    0.000013          13         1           munmap
 11.02    0.000013           4         3         3 access
 10.17    0.000012           6         2           open
  2.54    0.000003           2         2           fstat
  2.54    0.000003           3         1           brk
  1.69    0.000002           2         1           read
  1.69    0.000002           1         2           close
  0.85    0.000001           1         1           clone
  0.85    0.000001           1         1           execve
  0.85    0.000001           1         1           arch_prctl
------ ----------- ----------- --------- --------- ----------------
100.00    0.000118                    28         3 total
Run Code Online (Sandbox Code Playgroud)

不出所料,没有fork电话.只是原始的clone系统调用及其标志,子堆栈等正确设置.

  • 在某些体系结构上,fork()系统调用发生,但fork()仍然在内核模式内部调用clone(). (2认同)