Skip to content

信号机制

约 438 个字 45 行代码 2 张图片 预计阅读时间 2 分钟

信号是事件发生时对进程的通知机制,也被成为软中断,与硬中断的相似之处在于打断了程序执行的正常流程。

进程可以向另一进程发送信号,也可以向自身发送信号。信号可以由进程自行产生,也可以是发生异常后由内核产生。

针对每个信号,都定义了一个唯一的整数,从1开始,定义在<signal.h>中。

kill命令这是管理进程的核心工具,本质是向指定进程 / 进程组发送信号(其他进程、当前进程、进程组),而非直接 “杀死” 进程(仅当发送终止类信号时才会终止进程)。

linux也提供相应的kill系统调用。raise系统调用仅针对当前进程自己,底层原理是封装了kill系统调用,即raise(sig) 等价于 kill(getpid(), sig)。

每个进程都具有一个信号掩码,代表阻塞一组信号。可以通过sigpending系统调用查看当前处于阻塞的信号集,由于该信号集其实也是一个掩码只能表征哪些信号阻塞了并不能表征这些信号期间阻塞的次数,即同一个阻塞的信号后续只会被处理一次;

image.png

sigsuspend() 系统调用 —— 这是 Linux 中安全的信号等待机制,核心作用是:临时替换进程的信号阻塞集,然后阻塞进程直到收到一个未被阻塞的信号(并处理该信号),处理完成后恢复原阻塞集。它解决了 pause() + sigprocmask() 组合的 “竞态条件” 问题,是信号处理中更可靠的阻塞等待方式。

image.png

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

static void sig_handler(int sig)
{
    printf("pid is %d, receive signal %s\n", getpid(), strsignal(sig));
}


void child_start(pid_t pid)
{
    printf("this is child pid: %d, parent is %d\n", getpid(), getppid());

    // 子进程向自己发送信号
    kill(getpid(), SIGINT);
    // 子进程向其他进程发送信号
    kill(getppid(), SIGINT);
}

void parent_start(pid_t pid)
{
    printf("this is parent pid: %d, child is %d\n", getpid(), pid);
}


int main(int argc, char** args) {

    pid_t pid = fork();

    // 父子进程都注册信号处理函数
    if (signal(SIGINT, sig_handler) == SIG_ERR) {
        printf("register signal failed\n");
    }

    if (pid == 0) {
        child_start(pid);
    } else {
        parent_start(pid);
        wait(NULL);
        printf("child is exit\n");
    }
}