2.6 内核 Linux 上的分叉与克隆

2.6 内核 Linux 上的分叉与克隆

我对分叉和克隆有一些困惑。我已经看到:

  • fork用于进程,clone用于线程

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

这些是否准确?这 2 个系统调用与 2.6 Linux 内核有什么区别?

答案1

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

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

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

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

可移植程序应该调用fork()and pthread_create(),而不是clone()

答案2

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 */ );

这就是 do 描述的“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以查看系统调用 forking 实际执行的操作。我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手册页克隆具有非常不同的参数。child_stack=0因为第一个参数不同于int (*fn)(void *).

看起来fork(2)系统调用是按照以下方式实现的真实的 clone(),就像“库函数”的clone()实现一样。这真实的 clone()具有与手册页克隆不同的参数集。

fork()简单地说,您关于和的两个明显矛盾的陈述clone()都是正确的。不过,所涉及的“克隆”是不同的。

答案3

fork()只是系统调用的一组特定标志clone()clone()足够通用,可以创建“进程”或“线程”,甚至可以创建进程和线程之间的奇怪事物(例如,共享相同文件描述符表的不同“进程”)。

本质上,对于与内核中的执行上下文关联的每种“类型”信息,clone()您可以选择为该信息设置别名或复制它。线程对应于别名,进程对应于复制。通过将标志的中间组合指定为clone(),您可以创建非线程或进程的奇怪事物。你通常不应该这样做,我想在 Linux 内核的开发过程中,关于它是否应该允许像clone().

相关内容