大家好。
调用进程和子进程必须在同一内存空间。 因此,我使用 CLONE_VM 标志。 但子进程没有启动。 看起来堆栈内存分配有问题。 能解释一下原因吗?
#define _DEFAULT_SOURCE /* syscall() */
#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64 /* getrlimit() */
#include <sched.h> /* CLONE_* constants */
#include <linux/sched.h> /* struct clone_args */
#include <sys/syscall.h> /* SYS_* constants */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h> /* uintptr_t */
#include <err.h> /* err() */
#include <sys/resource.h>
#include <sys/mman.h>
#include <signal.h>
void spawn(void)
{
struct rlimit rlim;
struct clone_args cl_args = {0};
uint64_t stack_size;
void *stackBot;
void *stackTop;
pid_t pid;
if (getrlimit(RLIMIT_STACK, &rlim) == -1)
err(EXIT_FAILURE, "getrlimit");
stack_size = rlim.rlim_cur;
stackBot = mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN |
MAP_STACK, -1, 0);
if (stackBot == MAP_FAILED)
err(EXIT_FAILURE, "mmap");
long count = stack_size / sizeof(uint64_t);
stackTop = (uint64_t *)stackBot + count - 1;
printf("stack_size: %lu\n", stack_size);
printf("stack_size/2: %lu\n", stack_size / 2);
printf("stack_size/4: %lu\n", stack_size / 4);
printf("stack_size/8: %lu\n", stack_size / 8);
printf("stack_size/16: %lu\n", stack_size / 16);
printf("stack_size/4096: %lu\n", stack_size / 4096); /* PAGESIZE */
printf("stackBotAddr: %p\n", (void *)stackBot);
printf("stackTopAddr: %p\n", (void *)stackTop);
printf("stackBotVal: %lu\n", *(uint64_t *)(uintptr_t)stackBot);
printf("stackTopVal: %lu\n", *(uint64_t *)(uintptr_t)stackTop);
printf("stackTopAddr-stackBotAddr: %lu\n", (void *)stackTop - stackBot);
printf("stackBotCast: %lu\n", (uint64_t)(uintptr_t)stackBot);
printf("stackTopCast: %lu\n", (uint64_t)(uintptr_t)stackTop);
cl_args.flags = CLONE_FILES | CLONE_IO | CLONE_VM;
cl_args.exit_signal = SIGCHLD;
cl_args.stack = (uint64_t)(uintptr_t)stackBot;
cl_args.stack_size = (void *)stackTop - stackBot;
pid = syscall(SYS_clone3, &cl_args, sizeof(cl_args));
switch(pid) {
case -1:
munmap(stackBot, stack_size);
err(EXIT_FAILURE, "syscall");
case 0: /* Child */
printf("Hello from child. Child's pid: %d\n", getpid());
munmap(stackBot, stack_size);
break;
default: /* Parent */
printf("Hello from parent. Parent's pid: %d\n", getpid());
break;
}
}
int
main(int argc, char *argv[])
{
spawn();
printf("Before last while\n");
while(1) {
}
return EXIT_SUCCESS;
}
使用
gdb
进行调试会导致
[Detaching after fork from child process 16095]
attach process-id
通过 gdb
通向
Attaching to process 16095
warning: process 16095 is a zombie - the process has already terminated
ptrace: Operation not permitted.
在使用clone3()之前,我尝试通过fork()和clone()函数来解决问题。 我拒绝fork(),因为父进程和子进程必须有相同的内存空间。 clone() 函数不起作用,因为文件描述符应该传递给子进程,但如果存在 CLONE_VM 标志,文件描述符会自发更改。我还发现了关于这个问题的两个有用的链接:
但是子进程永远不会启动。
大家
通过实验发现,创建线程时,当调用进程和子进程处于同一地址空间(CLONE_VM标志)时,无法从高级语言(C)调用clone和clone3系统调用使用包装函数 syscall(2)。在这种情况下,syscall(2) 启动子进程时没有堆栈帧和返回地址,因此它将无法返回。克隆(2)手册页上的描述,建议使用
创建一个线程(CLONE_VM标志)long syscall(SYS_clone3, struct clone_args *cl_args, size_t size);
,是一个文档错误。该问题的解决方案在文章Linux 上实用的 libc-free 线程 中有详细描述。也非常感谢本文作者的个人评论。