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

父进程SIGCONT信号异常:唤醒后无法读取输入及执行后续代码

父进程被SIGCONT唤醒后无法读取输入的问题排查与解决

问题背景

需求:让父进程暂停,直到子进程发送SIGCONT唤醒信号后,再继续执行剩余代码。
问题:编写的C语言代码中,父进程被唤醒后仅输出提示语句,无法读取字符串输入,也不会执行最后的printf语句。

问题代码

#include <stdio.h>
#include <stdlib.h>     // EXIT_FAILURE
#include <unistd.h>     // for fork
#include<sys/wait.h>    // for wait
#include <signal.h>     // for sa_handler

#define SIZE 100    // maximum characters allowed in the chat

int main(){

    char ptr[100] = "\0";
    
    pid_t pid = fork(); // fork a processes

    if(pid < 0){
        perror("Fork #1 failed..\n");
        exit(EXIT_FAILURE);
    }
    else if (pid > 0){  // Parent Process
        raise(SIGSTOP);
        printf("Please enter message...\n");
        
        fgets(ptr, SIZE, stdin);
        
        printf("Parent Message: \033[0;31m%s\n", ptr);      // RED color font
    }
    else {          // Child A
        sleep(2);           
        kill(getppid(), SIGCONT);   
            
        exit(0);
    }

    wait(NULL);
    
    return 0;
}

原因分析

  • SIGSTOP导致父进程脱离终端前台进程组:程序启动时父进程处于终端前台进程组,调用raise(SIGSTOP)后父进程被暂停,此时shell自动接管前台进程组。子进程发送SIGCONT唤醒父进程后,父进程在后台运行,而终端输入仅会发送给前台进程组的进程,因此fgets会一直阻塞,无法读取用户输入,后续的printf语句自然也无法执行。
  • wait(NULL)位于main函数末尾,父进程需要执行完分支内的代码才能到达这一步,但由于fgets阻塞,父进程无法走到该逻辑。

解决建议

方案1:使用自定义信号替代SIGSTOP/SIGCONT

父进程通过pause()阻塞等待子进程发送自定义信号,这种方式不会让父进程脱离前台进程组,避免终端输入问题:

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

#define SIZE 100

// 自定义信号处理函数,仅用于唤醒pause
void sig_usr_handler(int sig) {}

int main(){
    char ptr[SIZE] = "\0";
    struct sigaction sa;

    // 注册SIGUSR1信号的处理函数
    sa.sa_handler = sig_usr_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGUSR1, &sa, NULL);

    pid_t pid = fork();
    if(pid < 0){
        perror("Fork failed");
        exit(EXIT_FAILURE);
    } else if (pid > 0){  // 父进程
        pause(); // 阻塞等待子进程的信号
        printf("Please enter message...\n");
        fgets(ptr, SIZE, stdin);
        // 输出后恢复终端默认颜色
        printf("Parent Message: \033[0;31m%s\033[0m\n", ptr);
        wait(NULL);
    } else { // 子进程
        sleep(2);
        kill(getppid(), SIGUSR1); // 发送自定义信号唤醒父进程
        exit(0);
    }

    return 0;
}

方案2:唤醒后重新获取前台进程组控制权

如果坚持使用SIGSTOP/SIGCONT,可以在父进程被唤醒后,调用tcsetpgrp()将自己重新设置为终端前台进程组:

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

#define SIZE 100

int main(){
    char ptr[SIZE] = "\0";
    int terminal_fd = STDIN_FILENO;
    pid_t parent_pgid = getpgrp(); // 获取父进程的进程组ID

    pid_t pid = fork();
    if(pid < 0){
        perror("Fork failed");
        exit(EXIT_FAILURE);
    } else if (pid > 0){  // 父进程
        raise(SIGSTOP);
        // 重新获取终端前台进程组控制权
        if(tcsetpgrp(terminal_fd, parent_pgid) == -1){
            perror("Failed to regain terminal foreground control");
            exit(EXIT_FAILURE);
        }
        printf("Please enter message...\n");
        fgets(ptr, SIZE, stdin);
        printf("Parent Message: \033[0;31m%s\033[0m\n", ptr);
        wait(NULL);
    } else { // 子进程
        sleep(2);
        kill(getppid(), SIGCONT);
        exit(0);
    }

    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 05:42:56