我对分叉和克隆有些困惑。我看到了:
fork用于进程,克隆用于线程
fork只调用clone,clone用于所有进程和线程
这些准确吗?这两个使用2.6 Linux内核的系统调用之间有什么区别?
我对分叉和克隆有些困惑。我看到了:
fork用于进程,克隆用于线程
fork只调用clone,clone用于所有进程和线程
这些准确吗?这两个使用2.6 Linux内核的系统调用之间有什么区别?
Answers:
fork()
是原始的UNIX系统调用。它只能用于创建新进程,而不能用于线程。而且,它是便携式的。
在Linux中,clone()
是一个新的通用系统调用,可用于创建新的执行线程。根据传递的选项,新的执行线程可以遵循UNIX进程,POSIX线程,介于两者之间或完全不同的事物(例如不同的容器)的语义。您可以指定各种选项,以决定是共享还是复制内存,文件描述符,各种命名空间,信号处理程序等等。
由于clone()
是超集系统调用,因此fork()
glibc 中的系统调用包装器的实现实际上是调用clone()
,但这是程序员不需要了解的实现细节。fork()
出于向后兼容性的原因,实际的实际系统调用仍存在于Linux内核中,尽管它已经变得多余,因为使用旧版本的libc或glibc以外的其他libc的程序可能会使用它。
clone()
还用于实现pthread_create()
POSIX函数以创建线程。
移植的程序应该调用fork()
和pthread_create()
,没有clone()
。
看来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()
都是正确的。但是,涉及的“克隆”是不同的。
man 2 clone
这种方式表达了这句话,我认为这使问题感到困惑,并阻止发问者获得一个好的答案。
clone
从由基础系统调用接受参数列表库函数基本上是不同的。具体来说,系统调用总是在同一个堆栈上返回两次,这与传统方法fork
一样。与子堆栈有关的所有参数都严格在用户空间中处理。例如,见sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/...