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参数.此外,参数顺序也会改变.
@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系统调用及其标志,子堆栈等正确设置.