2.6内核Linux上的Fork vs Clone


37

我对分叉和克隆有些困惑。我看到了:

  • fork用于进程,克隆用于线程

  • fork只调用clone,clone用于所有进程和线程

这些准确吗?这两个使用2.6 Linux内核的系统调用之间有什么区别?

Answers:


52

fork()是原始的UNIX系统调用。它只能用于创建新进程,而不能用于线程。而且,它是便携式的。

在Linux中,clone()是一个新的通用系统调用,可用于创建新的执行线程。根据传递的选项,新的执行线程可以遵循UNIX进程,POSIX线程,介于两者之间或完全不同的事物(例如不同的容器)的语义。您可以指定各种选项,以决定是共享还是复制内存,文件描述符,各种命名空间,信号处理程序等等。

由于clone()是超集系统调用,因此fork()glibc 中的系统调用包装器的实现实际上是调用clone(),但这是程序员不需要了解的实现细节。fork()出于向后兼容性的原因,实际的实际系统调用仍存在于Linux内核中,尽管它已经变得多余,因为使用旧版本的libc或glibc以外的其他libc的程序可能会使用它。

clone()还用于实现pthread_create()POSIX函数以创建线程。

移植的程序应该调用fork()pthread_create(),没有clone()


2
posix_spawn是另一个相关的功能-在某些方面,它们比fork更具可移植性。
2015年

10

看来clone()在Linux 2.6 中有两件事

有一个系统调用:

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

这是doing描述的“ clone()” man 2 clone

如果您仔细阅读了该手册页,将会看到以下内容:

It is actually a library function layered on top of the
underlying clone() system call.

显然,您应该使用“库函数”实现线程,该函数位于令人困惑的同名系统调用上。

我写了一个简短的程序:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int
main(int ac, char **av)
{
    pid_t cpid;
    switch (cpid = fork()) {
    case 0:   // Child process
        break;
    case -1:  // Error
        break;
    default:  // parent process
        break;
    }
    return 0;
}

用:进行编译c99 -Wall -Wextra,然后运行它strace -f以查看系统调用分叉实际执行的操作。我strace在Linux 2.6.18机器(x86_64 CPU)上解决了这个问题:

20097 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x2b4ee9213770) = 20098
20097 exit_group(0)                     = ?
20098 exit_group(0)

输出中没有“ fork”调用strace。输出clone()中显示的调用的strace参数与man-page-clone完全不同。child_stack=0因为第一个论点与int (*fn)(void *)

看来fork(2)系统调用是根据real实现的 clone(),就像实现了“库函数”一样clone()。该真正 clone()拥有一套不同的从该名男子页克隆参数。

简而言之,您关于fork()和的两个显然矛盾的说法clone()都是正确的。但是,涉及的“克隆”是不同的。


9
“它实际上是一个在底层clone()系统调用之上的库函数。” —通常,这适用于每个系统调用。实际上,程序员实际上总是在libc中调用以系统调用命名的函数。这是因为直接从C进行实际的实际系统调用需要特定于平台的魔术(通常通过强制某种类型的CPU陷阱,取决于体系结构ABI)和最好委派给libc的机器代码。
Celada

1
@Celada-是的,同意。我只是以man 2 clone这种方式表达了这句话,我认为这使问题感到困惑,并阻止发问者获得一个好的答案。
Bruce Ediger

2
相信手册页装置,以指示该参数列表中的clone从由基础系统调用接受参数列表库函数基本上是不同的。具体来说,系统调用总是在同一个堆栈上返回两次,这与传统方法fork一样。与子堆栈有关的所有参数都严格在用户空间中处理。例如,见sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/...
zwol

1
我想给您的答案最好的答案,因为它摇摆不定,但是我被众人所吸引,并获得了第一个答案。她确实获得了响应时间点。感谢您的解释。
2015年

6

fork()只是系统调用的一组特殊标志clone()clone()通常足以创建“进程”或“线程”,甚至创建在进程和线程之间某个地方的怪异事物(例如,共享同一文件描述符表的不同“进程”)。

本质上,对于与内核中的执行上下文相关联的每种“类型”的信息,clone()您都可以选择对该信息进行别名或复制。线程对应于别名,进程对应于复制。通过指定标志的中间组合clone(),您可以创建不是线程或进程的怪异事物。通常,您不应该这样做,并且我想在Linux内核的开发过程中会发生一些关于是否应允许使用这样的通用机制的争论clone()

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.