使用进程实现生产者-消费者模型时遇SIGUSR1错误求助
首先,你遇到的User defined signal 1: 30错误(对应SIGUSR1信号异常),根源在于代码里的多个逻辑和语法问题,我帮你逐一拆解并修正:
主要问题点
信号处理函数不符合标准原型:
系统调用signal()要求信号处理函数必须接收一个int类型的参数(表示触发的信号编号),但你的producer()和consumer()函数没有参数,这会导致信号触发时栈帧异常,直接引发未定义行为,这是你看到信号错误的核心原因之一。进程间PID传递错误:
全局变量child和parent在fork后是父子进程各自独立的副本:- 父进程里的
child从未被正确赋值(子进程里的child = getpid()只在子进程生效,父进程看不到),导致父进程执行kill(child, SIGUSR1)时,实际是向PID为0的进程组发信号,而非目标子进程。 - 子进程里的
parent变量也未被正确初始化(父进程设置parent=getpid()的分支子进程不会执行),导致子进程执行kill(parent, SIGUSR2)同样是无效操作。
- 父进程里的
循环逻辑错误:
你把fork()放在for循环外,但父子进程都会进入同一个for循环,这会导致双方各自独立执行10次循环,完全不符合"生产者创建文件→发信号→消费者删除→发信号→重复"的交替逻辑。初始文件创建多余且可能引发冲突:
程序开头的system("touch file")没必要,而且可能导致消费者第一次删除时文件已存在,但后续逻辑会重复创建,反而容易引发混乱。
修正后的代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> // 全局变量存储父子进程PID,确保双方都能拿到正确值 pid_t child_pid; pid_t parent_pid; // 符合标准的信号处理函数,接收信号编号参数 void producer_handler(int signo) { // 生产者创建文件 system("touch file"); printf("Producer: File was created.\n"); // 通知消费者可以删除 kill(child_pid, SIGUSR1); } void consumer_handler(int signo) { // 消费者删除文件 system("rm file"); printf("Consumer: File was deleted.\n"); // 通知生产者可以再次创建 kill(parent_pid, SIGUSR2); } int main(void) { parent_pid = getpid(); pid_t pid = fork(); if (pid < 0) { perror("fork() failed"); return EXIT_FAILURE; } else if (pid == 0) { // 子进程(消费者)逻辑 child_pid = getpid(); // 注册SIGUSR1信号处理函数,触发时删除文件 signal(SIGUSR1, consumer_handler); // 先通知父进程可以开始第一次创建 kill(parent_pid, SIGUSR2); // 子进程进入等待信号的循环,执行10次 for (int i = 0; i < 10; ++i) { pause(); } } else { // 父进程(生产者)逻辑 child_pid = pid; // 直接用fork返回的子进程PID,这是最可靠的方式 // 注册SIGUSR2信号处理函数,触发时创建文件 signal(SIGUSR2, producer_handler); // 父进程进入循环等待信号,执行10次创建 for (int i = 0; i < 10; ++i) { pause(); } } return EXIT_SUCCESS; }
关键修正说明
信号处理函数标准化:
给producer_handler和consumer_handler添加了int signo参数,符合signal()的要求,避免栈异常。正确传递PID:
父进程直接使用fork()返回的子进程PID赋值给child_pid,这是最可靠的方式;子进程则继承父进程在fork前就赋值好的parent_pid,确保双方都能正确找到目标进程。修正循环与等待逻辑:
父子进程各自进入自己的循环,通过pause()等待对方的信号,实现严格的交替执行:- 子进程启动后先主动发SIGUSR2给父进程,触发第一次文件创建
- 父进程创建文件后发SIGUSR1给子进程,触发删除
- 子进程删除后发SIGUSR2给父进程,如此循环10次
移除多余的初始文件创建:
去掉了开头的touch file,让第一次创建由生产者逻辑触发,避免初始状态混乱。
运行说明
编译运行后,你会看到类似如下的交替输出:
Producer: File was created. Consumer: File was deleted. Producer: File was created. Consumer: File was deleted. ...(重复10次)
这样就能实现你想要的"同一时间只能存在一个文件"的需求,也不会再出现信号相关的错误了。
内容的提问来源于stack exchange,提问作者sziko

