#define _GNU_SOURCE
#include<sched.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
static char child_stack[2048];
int x = 10;
static int child_fn() {
printf("Pid: %ld
", (long) getpid());
return 0;
}
int main() {
printf("before= %d
", x);
errno = 0;
pid_t child_pid = clone(&child_fn, (void *) child_stack+1024, CLONE_NEWPID | SIGCHLD, NULL);
if(child_pid == -1) {
printf("%s
", strerror(errno));
return 0;
} else {
printf("clone()= %ld
", (long) child_pid);
printf("after= %d
", x);
while(waitpid(-1, NULL, 0) < 0 && errno == EINTR) {
printf("waiting
");
continue;
}
return 0;
}
}
我得到的输出是:
before= 10
clone()= 16
after= 10
这意味着child_fn
确实分配了一个Pid,即child_pid
。但是printf
中的child_fn
是不是没有执行,或者可能是在其他stdout范围内打印?无论哪种方式,这可能是什么原因,以及如何对其进行调试。我只是从名称空间开始,所以对它们了解不多。
如果我使用gdb
运行原始代码,则会发现它失败并显示SIGSEGV
:
$ gcc -o clone clone.c
$ sudo gdb ./clone
(gdb) set follow-fork-mode child
(gdb) run
Starting program: /home/lars/tmp/clone
before= 10
[New process 10768]
clone()= 10768
after= 10
Thread 2.1 "clone" received signal SIGSEGV, Segmentation fault.
[Switching to process 10768]
0x00007ffff7a5e9de in vfprintf () from /lib64/libc.so.6
我怀疑您的child_stack
太小。您分配了2048个字节,但只使用了一半。如果我将clone
调用修改为如下所示:
pid_t child_pid = clone(&child_fn, (void *) child_stack+2048, CLONE_NEWPID | SIGCHLD, NULL);
然后在我的系统上似乎可以正常运行:
$ sudo gdb ./clone
(gdb) set follow-fork-mode child
(gdb) run
Starting program: /home/lars/tmp/clone
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.24-9.fc25.x86_64
before= 10
[New process 10807]
clone()= 10807
after= 10
Pid: 1
[Inferior 2 (process 10807) exited normally]
root@cloud:/nsexec# gcc simple.c -o simple root@cloud:/nsexec# ./simple clone() = 58961 root@cloud:/nsexec# gdb ./clone GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-git Copyright (C) 2018 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "aarch64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from ./clone...(no debugging symbols found)...done. (gdb) set follow-fork-mode child (gdb) run Starting program: /nsexec/clone [New process 58985] clone() = 58985 Thread 2.1 "clone" received signal SIGBUS, Bus error. [Switching to process 58985] 0x0000aaaaaaaaa8d8 in child_func () (gdb)
From man 2 clone
:
The
child_stack
argument specifies the location of the stack used by the child process. Since the child and calling process may share memory, it is not possible for the child process to execute in the same stack as the calling process. The calling process must therefore set up memory space for the child stack and pass a pointer to this space toclone()
. Stacks grow downward on all processors that run Linux (except the HP PA processors), sochild_stack
usually points to the topmost address of the memory space set up for the child stack.
Your child is running into segmentation fault because the stack grows down and you are passing a pointer to the start of a newly allocated memory area, when you should pass a pointer to the end of such area. This only happens when you add another function call (getpid()
) because without that call your child process is not using as much stack.
Correct call would be:
pid_t child_pid = clone(child_func, ((uint8_t*)malloc(4096)) + 4095, SIGCHLD, NULL);
PS: I'm guessing that inline call to malloc()
was just to simplify the example, but you should check the return value of malloc()
before passing it to the child.