使用clone3系统调用创建进程及waitid调用异常问题求助
问题分析与解决方案
你的代码存在三个核心问题,导致子进程无法执行、waitid随机报错:
1. clone3 参数错误:未指定栈空间且flags组合不当
你设置了CLONE_VM标志,这会让子进程共享父进程的地址空间(本质是创建线程而非独立进程),但线程需要独立的栈空间,代码中args.stack和args.stack_size均未初始化,子进程执行时会直接破坏父进程栈结构,导致子进程代码还没运行就崩溃。
如果你的目标是创建独立进程,需要去掉CLONE_VM标志;如果是创建线程,必须手动分配栈内存并设置到clone_args中。
2. waitid 系统调用实现错误
你手动实现的sys_waitid存在两个致命问题:
- 硬编码系统调用号
247:不同内核版本、架构的系统调用号可能不同,应使用标准定义的__NR_waitid(需确保_GNU_SOURCE已定义)。 - 参数顺序与系统调用不匹配:Linux内核的
waitid系统调用仅接收4个参数:type, id, infop, options,没有rusage参数。libc的waitid封装是通过额外逻辑实现rusage获取的,你多传的usage参数会导致参数栈错位,options被随机值覆盖,这就是随机出现EINVAL错误的原因。
3. 输出缓冲问题
子进程中的printf("Value changed!")没有换行符,导致输出被缓冲,即使子进程正常执行,你也可能看不到输出,误以为代码没运行。
修正后的代码
#ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include <bits/types/struct_rusage.h> #include <errno.h> #include <linux/sched.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/syscall.h> #include <sys/wait.h> #include <unistd.h> #ifndef CLONE_PIDFD #define CLONE_PIDFD 0x00001000 #endif #ifndef __NR_clone3 #define __NR_clone3 -1 struct clone_args { __aligned_u64 flags; __aligned_u64 pidfd; __aligned_u64 child_tid; __aligned_u64 parent_tid; __aligned_u64 exit_signal; __aligned_u64 stack; __aligned_u64 stack_size; __aligned_u64 tls; }; #endif // 正确的clone3系统调用封装 static pid_t sys_clone3(struct clone_args *args) { return syscall(__NR_clone3, args, sizeof(struct clone_args)); } // 正确的waitid系统调用封装(内核层面无rusage参数) static int sys_waitid(idtype_t type, id_t id, siginfo_t* info, int options) { return syscall(__NR_waitid, type, id, info, options); } int value = 10; int main(int argc, char *argv[]) { struct clone_args args = { // 去掉CLONE_VM,创建独立进程;若要创建线程,需添加CLONE_VM+CLONE_THREAD,并设置栈 .flags = 0, .exit_signal = SIGCHLD, .pidfd = 0, .child_tid = 0, .parent_tid = 0, .stack = 0, .stack_size = 0, .tls = 0, }; int pid = sys_clone3(&args); if (pid < 0) { fprintf(stderr, "%s - Failed to create new process\n", strerror(errno)); exit(EXIT_FAILURE); } if (pid == 0) { printf("Child process with pid %d\n", getpid()); value = 20; printf("Value changed!\n"); // 添加换行符刷新缓冲 exit(EXIT_SUCCESS); } printf("Parent process received child's pid %d\n", pid); siginfo_t info; // 使用系统调用层面的WEXITED | WALL选项,而非libc封装的__WALL int wait_status = sys_waitid(P_ALL, 0, &info, WEXITED | WALL); if (wait_status == -1) { fprintf(stderr, "Failed to wait on child process (%d): %s\n", errno, strerror(errno)); exit(EXIT_FAILURE); } printf("The value is: %d\n", value); return 0; }
关键注意事项
- 若要创建线程(共享地址空间),需:
- 添加
CLONE_VM | CLONE_THREAD | CLONE_SIGHAND等线程相关标志 - 用
mmap或malloc分配至少PTHREAD_STACK_MIN大小的栈空间,设置到args.stack(栈指针要指向栈顶,因为栈是向下生长的,所以要分配后加栈大小)
- 添加
- 系统调用的参数顺序、数量必须严格匹配内核定义,不能直接照搬libc封装的函数签名
- 如需获取rusage信息,可在waitid成功后,调用
getrusage(RUSAGE_CHILDREN, &usage)实现
内容的提问来源于stack exchange,提问作者rempas
相关产品推荐
相关产品推荐

