You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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;
}

关键注意事项

  • 若要创建线程(共享地址空间),需:
    1. 添加CLONE_VM | CLONE_THREAD | CLONE_SIGHAND等线程相关标志
    2. 用mmap或malloc分配至少PTHREAD_STACK_MIN大小的栈空间,设置到args.stack(栈指针要指向栈顶,因为栈是向下生长的,所以要分配后加栈大小)
  • 系统调用的参数顺序、数量必须严格匹配内核定义,不能直接照搬libc封装的函数签名
  • 如需获取rusage信息,可在waitid成功后,调用getrusage(RUSAGE_CHILDREN, &usage)实现

内容的提问来源于stack exchange,提问作者rempas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 13:48:13