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

ArchLinux上Kill系统调用异常问题求助

问题原因与代码错误分析

核心问题点

  1. signal函数的行为不确定性
    不同Linux发行版对signal的实现遵循不同标准:BSD风格会保留信号处理函数,而System V风格在信号触发后会自动恢复为默认处理。新版本内核/发行版可能默认采用System V行为,导致你的代码中handler里重新注册信号的操作赶不上后续信号,子进程对SIGUSR1的处理变回默认(忽略),因此没有输出。

  2. 子进程信号注册的竞争条件
    子进程在while循环里每次pause前都重新注册信号,存在时间窗口:父进程发送的SIGUSR1可能在子进程调用signal前到达,此时信号被默认处理(忽略),后续pause一直阻塞,直到下一个信号,但此时处理函数可能已被恢复默认,依旧无输出。

  3. 非异步信号安全函数的调用
    printf不属于异步信号安全函数,在信号处理函数中调用会触发未定义行为。不同内核/发行版的标准库对这种违规操作的容错性不同,这也是输出差异的原因之一。

  4. 退出信号不匹配
    父进程最后发送的是SIGTERM,但子进程只注册了SIGUSR1和SIGUSR2的处理函数,SIGTERM的默认行为是直接终止进程,子进程没有机会执行输出逻辑就退出了。


修正后的代码示例

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

void handler(int sig)
{
        char msg[64];
        int len = snprintf(msg, sizeof(msg), "Received %d signal in %d!\n", sig, getpid());
        // 使用异步信号安全的write函数输出
        write(STDOUT_FILENO, msg, len);
        if(sig == SIGUSR2)
                _exit(0); // 信号处理函数中用_exit而非exit,避免缓冲区刷新问题
}

int main()
{
        pid_t child = fork();
        if(child == 0)
        {
                struct sigaction sa;
                sa.sa_handler = handler;
                sigemptyset(&sa.sa_mask);
                sa.sa_flags = SA_RESTART; // 让被信号打断的系统调用自动重启
                // 用sigaction注册信号,行为稳定可预测
                sigaction(SIGUSR1, &sa, NULL);
                sigaction(SIGUSR2, &sa, NULL);

                while(1)
                {
                        pause();
                }
        }
        else
        {
                for(int i = 0; i < 10; i++)
                {
                        printf("Sending SIGUSR1 signal to %d\n", child);
                        kill(child, SIGUSR1);
                        sleep(1);
                }
                printf("Sending SIGUSR2 signal\n");
                kill(child, SIGUSR2); // 发送SIGUSR2匹配子进程退出逻辑
                wait(NULL);
        }
}

关键修正说明

  • 用sigaction替代signal:sigaction是POSIX标准接口,行为一致,不会因发行版/内核版本产生差异,同时SA_RESTART可以让pause在被信号打断后自动重启,避免阻塞。
  • 替换printf为write:write是异步信号安全函数,消除未定义行为。
  • 子进程仅注册一次信号处理函数:彻底解决循环注册导致的竞争问题。
  • 父进程发送SIGUSR2而非SIGTERM:匹配子进程的退出逻辑,确保子进程正常执行退出前的输出。

内容的提问来源于Stack Exchange,提问作者Setu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 03:47:08