为什么glibc的fork实现没有使用sys_fork?

时间:2016-05-11 14:14:32

标签: c linux fork glibc

在eglibc nptl/sysdeps/unix/sysv/linux/i386/fork.c中有一个定义:

#define ARCH_FORK() \
  INLINE_SYSCALL (clone, 5,                           \
          CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, 0,     \
          NULL, NULL, &THREAD_SELF->tid)

在实际__libc_fork()中用作实施的核心。但是例如在Linux arch/x86/entry/syscalls/syscall_32.tbl中存在sys_fork条目以及syscalls_64.tbl条目。显然Linux确实有fork的特殊系统调用。

所以我现在想知道:为什么glibc在fork()方面实现clone,如果内核已经提供了fork系统调用?

2 个答案:

答案 0 :(得分:6)

我查看了Ulrich Drepper将该代码添加到glibc的提交,并且在提交日志(或其他地方)中没有任何解释。

看看Linux fork的实现,但是:

return _do_fork(SIGCHLD, 0, 0, NULL, NULL, 0);

这是clone

return _do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr, tls);
显然,它们几乎完全相同。唯一的区别是,在调用clone时,您可以设置各种标志,可以为新进程指定堆栈大小等。fork不接受任何参数。

查看Drepper的代码,clone标记为CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD。如果使用fork,则唯一的标记为SIGCHLD

以下是关于这些额外标志的clone联机帮助页:

CLONE_CHILD_CLEARTID (since Linux 2.5.49)
          Erase child thread ID at location ctid in child memory when  the  child
          exits,  and  do  a  wakeup  on  the futex at that address.  The address
          involved may be changed by the set_tid_address(2) system call.  This is
          used by threading libraries.

CLONE_CHILD_SETTID (since Linux 2.5.49)
          Store child thread ID at location ctid in child memory.

...你可以看到他确实传递了一个指针,指向内核应该首先存储子线程的线程ID,然后再进行futex唤醒。 glibc做某个futex在那个地址等吗?我不知道。如果是这样,那就解释了为什么Drepper选择使用clone

(如果没有,它只是我们心爱的glibc的极端积累的另一个例子!如果你想找到一些漂亮,干净,维护良好的代码,只需继续前进并去看看在musl libc!)

答案 1 :(得分:1)

简而言之:为什么不呢?

你有一个确保在所有平台上存在的系统调用(你确实知道英特尔不是那里唯一的平台,对吗?),另一个被弃用,因为它是不必要的。它们都带有完全相同的语义。当您只调用保证存在的代码时,您的代码会更加紧凑。

我将详细阐述一下。

Fork由Posix定义,而clone是特定于Linux的。但是,Linux有时会将Posix定义为“系统调用”并在用户空间中实现它们。 fork(和vfork和pthread_create)就是这种情况。它们都是通过调用“clone”在用户空间中实现的。

因此,在内核级别认为fork是不必要的。如果瘦用户空间包装器可以实现它,内核就可以了。因此,在Linux上,克隆保证在所有平台上都存在,而fork可能存在也可能不存在,具体取决于特定平台。