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

Linux下C语言多进程信号程序异常:仅父进程输出问题排查

C语言多进程信号同步异常问题分析

需求与问题

需要实现的逻辑:

  • 创建两个子进程
  • 第二个子进程阻塞,直到接收父进程的SIGUSR1信号
  • 第一个子进程阻塞,直到接收第二个子进程的SIGUSR2信号(该信号终止它)
  • 父进程在所有子进程终止后再退出

实际执行异常:仅父进程的printf内容被输出,子进程的打印和信号逻辑未按预期执行。

用户提供的代码

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

void this(int sig) {
    printf("this is this");
}

int main() {
    int pid = fork();
    int pid2;

    if (pid < 0) {
        exit(-1);
    } else if (pid == 0) {
        printf("FIrst child is paused");
        pause();
        printf("ERror");
    } else {
        pid2 = fork();
        if (pid2 < 0) {
            exit(-2);
        } else if (pid2 == 0) {
            signal(SIGUSR1, &this);
            printf("Second child is paused");
            pause();
            kill(pid,SIGUSR2);
            printf("signal sent to first child");
        } else {
            printf("this is the parent");
            kill(pid2, SIGUSR1);
            printf("signal sent to second child");
            wait(NULL);
            exit(-3);
        }
    }
}

问题根源分析

1. 标准输出缓冲未刷新

printf默认采用行缓冲模式,当输出内容没有换行符\n时,数据会暂存在进程的输出缓冲区中,不会立即打印到终端。子进程可能在缓冲区未刷新时就进入阻塞或终止状态,导致内容无法显示。

2. 信号发送与处理函数注册的竞争条件

父进程在创建第二个子进程后,立即调用kill(pid2, SIGUSR1)发送信号,但此时第二个子进程可能还没来得及执行signal(SIGUSR1, &this)注册信号处理函数。SIGUSR1的默认处理动作是终止进程,这会导致第二个子进程直接退出,后续向第一个子进程发送SIGUSR2的逻辑完全无法执行,第一个子进程会一直卡在pause()。

3. 父进程未回收所有子进程

父进程只调用了一次wait(NULL),只能回收一个子进程,另一个子进程会变成僵尸进程,直到父进程退出后才被系统回收。

修正后的代码

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

void sigusr1_handler(int sig) {
    printf("this is sigusr1 handler\n");
}

int main() {
    int pid = fork();
    int pid2;

    if (pid < 0) {
        perror("fork first child failed");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        printf("First child is paused\n");
        pause(); // SIGUSR2默认会终止进程,无需额外处理
        printf("Error: First child shouldn't reach here\n");
        exit(EXIT_FAILURE);
    } else {
        pid2 = fork();
        if (pid2 < 0) {
            perror("fork second child failed");
            exit(EXIT_FAILURE);
        } else if (pid2 == 0) {
            signal(SIGUSR1, sigusr1_handler);
            printf("Second child is paused\n");
            pause();
            kill(pid, SIGUSR2);
            printf("signal sent to first child\n");
            exit(EXIT_SUCCESS);
        } else {
            printf("this is the parent\n");
            // 短暂休眠,确保第二个子进程完成信号处理函数注册
            sleep(1);
            kill(pid2, SIGUSR1);
            printf("signal sent to second child\n");
            
            // 回收所有子进程
            int status;
            while (wait(&status) > 0);
            
            exit(EXIT_SUCCESS);
        }
    }
}

关键修改说明

  • 所有printf添加\n,强制刷新输出缓冲区,确保内容及时显示
  • 父进程发送信号前添加sleep(1),避免竞争条件(生产环境可使用更可靠的同步机制,如管道、信号量)
  • 父进程用循环wait回收所有子进程,避免僵尸进程
  • 优化错误提示,使用perror打印fork失败的具体原因
  • 重命名信号处理函数为更具语义的sigusr1_handler,提升代码可读性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 14:55:23