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

Linux下C语言sigqueue传递struct指针至子进程异常排查

问题

我正在学习Linux下C语言的信号相关知识,想通过sigqueue函数,借助信号处理函数的siginfo_t结构体参数,把一个struct Data整数结构体的指针从父进程传给fork出的子进程(信号处理函数用sigaction定义,原型是void handler(int, siginfo_t*, void*))。

父进程定义了union sigval,把sival_ptr字段设为指向struct Data的指针(结构体包含两个整数),然后调用sigqueue(child_pid, SIGUSR1, my_sigval)发送信号。

子进程通过sigaction设置了SIGUSR1的处理函数,启用了SA_SIGINFO标志。在信号处理函数里,我定义了struct Data*类型指针data,执行data = (struct Data*) (si->si_value.sival_ptr)强转,然后尝试打印父进程发送的数据。

但实际运行时,子进程会在打印struct Data内的值前退出,而该语句之前的代码都能正常执行输出。请问哪里出错了?是父进程传递指针的方式不对,还是子进程处理函数里的强转有问题?

父进程代码

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

#define MY_SIGNAL SIGUSR1

// 定义包含两个整数的结构体
struct Data {
    int value1;
    int value2;
};

int main() {
    // 准备数据结构
    struct Data* data = (struct Data*) malloc(sizeof(struct Data));
    data->value1 = 10;
    data->value2 = 20;

    // 创建子进程
    pid_t pid = fork();

    if (pid == -1) {
        perror("fork failed");
        return 1;
    } else if (pid == 0) { // 子进程
        // 子进程逻辑
        execlp("./c2","c2",  NULL);
    } else { // 父进程
        // 发送携带数据结构体指针的信号
        union sigval value;
        value.sival_ptr = (void*) data;
//        value.sival_int = 7;
        sleep(1);
        sigqueue(pid, MY_SIGNAL, value);
        sleep(1);
    }

    wait(NULL);
    return 0;
}

子进程代码

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#define MY_SIGNAL SIGUSR1

// 定义包含两个整数的结构体
struct Data {
    int value1;
    int value2;
};

// 信号处理函数
void handler(int sig, siginfo_t *si, void *unused) {
    printf("Received signal: %d\n", sig);
//    printf("Received signal value: %d\n", si->si_value.sival_int);
    struct Data* data;
    data = (struct Data*)(si->si_value.sival_ptr);
    printf("Received values: %d, %d\n", data->value1, data->value2);
    // 将接收的数据复制到分配的内存中
}

int main() {
    // 设置信号处理
    struct sigaction sa;
    sa.sa_sigaction = handler;
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    sigaction(MY_SIGNAL, &sa, NULL);

    
    pause();

    return 0;
}
错误原因分析

核心问题是进程地址空间隔离:

  • 父进程和你用execlp替换镜像后的子进程,拥有完全独立的虚拟地址空间。父进程中malloc出来的struct Data指针,仅在父进程地址空间内有效,子进程地址空间里这个指针指向的是无效内存区域。
  • 当子进程尝试通过该指针访问内存时,会触发段错误(SIGSEGV),导致进程直接退出,因此printf("Received values: ...")语句根本没机会执行。
  • 你看到的“前面代码正常输出”,是因为段错误发生在访问data->value1的瞬间,之前的printf("Received signal: %d\n", sig)已经完成输出。
修复方案

不能直接跨进程传递指针,要传递数据本身或者使用进程间共享内存的方式,以下是两种可行方案:

方案1:用sival_int传递小数据

如果结构体数据量小,可以把两个整数打包到sival_int中(通过位操作实现),关键修改如下:

父进程修改部分

// 把value1存在高16位,value2存在低16位
union sigval value;
value.sival_int = (data->value1 << 16) | (data->value2 & 0xFFFF);
sleep(1);
sigqueue(pid, MY_SIGNAL, value);
sleep(1);

子进程处理函数修改部分

void handler(int sig, siginfo_t *si, void *unused) {
    printf("Received signal: %d\n", sig);
    int value1 = (si->si_value.sival_int >> 16) & 0xFFFF;
    int value2 = si->si_value.sival_int & 0xFFFF;
    printf("Received values: %d, %d\n", value1, value2);
}

方案2:使用共享内存传递大数据

如果结构体数据量较大,用共享内存让父子进程访问同一块物理内存:

父进程修改部分

#include <sys/shm.h> // 新增头文件

int main() {
    // 创建共享内存段
    int shmid = shmget(IPC_PRIVATE, sizeof(struct Data), IPC_CREAT | 0666);
    if (shmid == -1) {
        perror("shmget failed");
        return 1;
    }
    // 映射共享内存到父进程地址空间
    struct Data* data = (struct Data*) shmat(shmid, NULL, 0);
    if (data == (void*)-1) {
        perror("shmat failed");
        return 1;
    }
    data->value1 = 10;
    data->value2 = 20;

    pid_t pid = fork();

    if (pid == -1) {
        perror("fork failed");
        return 1;
    } else if (pid == 0) { // 子进程
        // 把共享内存ID作为参数传给子进程
        char shmid_str[10];
        snprintf(shmid_str, sizeof(shmid_str), "%d", shmid);
        execlp("./c2","c2", shmid_str, NULL);
    } else { // 父进程
        sleep(1);
        // 发送信号通知子进程读取共享内存
        union sigval value;
        sigqueue(pid, MY_SIGNAL, value);
        sleep(1);
        // 解除映射并删除共享内存
        shmdt(data);
        shmctl(shmid, IPC_RMID, NULL);
    }

    wait(NULL);
    return 0;
}

子进程修改部分

#include <sys/shm.h> // 新增头文件

int shmid; // 全局变量存储共享内存ID

void handler(int sig, siginfo_t *si, void *unused) {
    printf("Received signal: %d\n", sig);
    struct Data* data = (struct Data*) shmat(shmid, NULL, 0);
    if (data == (void*)-1) {
        perror("shmat failed");
        return;
    }
    printf("Received values: %d, %d\n", data->value1, data->value2);
    shmdt(data);
}

int main(int argc, char* argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <shmid>\n", argv[0]);
        exit(1);
    }
    shmid = atoi(argv[1]);

    // 设置信号处理
    struct sigaction sa;
    sa.sa_sigaction = handler;
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    sigaction(MY_SIGNAL, &sa, NULL);

    pause();

    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 16:39:53