惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI
MongoDB | Blog
MongoDB | Blog
Blog — PlanetScale
Blog — PlanetScale
腾讯CDC
U
Unit 42
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Apple Machine Learning Research
Apple Machine Learning Research
M
MIT News - Artificial intelligence
人人都是产品经理
人人都是产品经理
Hugging Face - Blog
Hugging Face - Blog
MyScale Blog
MyScale Blog
小众软件
小众软件
博客园 - 三生石上(FineUI控件)
N
Netflix TechBlog - Medium
阮一峰的网络日志
阮一峰的网络日志
博客园 - Franky
Recent Announcements
Recent Announcements
A
About on SuperTechFans
Stack Overflow Blog
Stack Overflow Blog
The GitHub Blog
The GitHub Blog
D
Docker
H
Hackread – Cybersecurity News, Data Breaches, AI and More

博客园 - yiyide266

[Express.js]next()函数的作用 [css]浏览器默认样式 have的实质 This comes after Chinese authorities announced an investigation against Mayday, in response to a viral video. [Linux]终端ssh远程连接如何保持 [PHP]PDO的dsn对mysql的连接影响 [PHP]回调函数参数(callable类型)的一些细节 [javascript]端序(endian)和Buffer对象的read|write系列函数 vscode处理HTML标签两个光标的问题 intend,intent和indent [css]一个块格式化上下文(BFC)阻止外边距重叠的示例 [PHP_yaf]内部注册到spl_autoload的方法 [PHP_yaf]Yaf_Application [PHP_yaf]写在用户代码的配置项 [PHP_yaf]保存在php.ini中的配置项 [PHP]spl_autoload函数栈的向下遍历机制 [英语单词]关于战争 [MySql]1820错误码重置密码方法 [Linux]信号捕捉函数总是第一时间执行
[Linux]信号捕捉函数与exec
yiyide266 · 2020-09-22 · via 博客园 - yiyide266

一、概述

       fork之后,子进程虽然会继承父进程的“信号屏蔽和安排”,但是exec后,只有“被忽略的信号”和“设置为默认”处理行为是被保留的,被用户修改自定义函数动作的信号安排会被内核设置为系统默认动作,原因是exec后进程的上下文被替换成了被执行程序的上下文,而被替换的上下文恰恰就包括exec之前定义过的函数,此时传到信号处理函数的函数指针可能已经指向了一个无效的空间。所以在执行一个程序时,系统会把被修改为自定义函数动作的信号改为默认动作或忽略,除非这个信号在exec之前被用户设置为忽略。来看看《APUE》第10.3节是怎么说的:

当执行一个程序时,所有信号的状态都是系统默认或忽略。通常所有信号都被设置为它们的默认动作,除非调用exec的进程忽略该信号。确切地讲,exec函数将原先设置为要捕捉的信号都更改为默认动作,其他信号的状态则不变(一个进程原先要捕捉的信号,当其执行一个新程序后,就不能再捕捉了,因为信号捕捉函数的地址很可能在所执行的新程序文件中已无意义)。

二、示例

1.自定义信号函数被内核修改至默认值的例子:

在这个例子里,父进程A在启动子进程B之前捕获了信号SIGINT并自定义了处理函数:

/*Program A*/
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

static void sig_usr(int signo)
{
    if(signo == SIGINT){
        printf("received SIGINT.\n");
    }else{
        printf("received signal %d.\n", signo);
    }
}

int main(void){
    if(signal(SIGINT, sig_usr) == SIG_ERR){
        printf("cat't catch SIGINT.\n");
        return 0;
    }

    pid_t pid = fork();
    if(pid < 0){
        printf("Error occurred when fork.\n");
    }
    if(pid == 0){
        if(execl("/lroot/c/temp/B", NULL) < 0){
            printf("Error occurred when execle.\n");
        }
    }

    int   status;
    pid_t pid_done = wait(&status);
    printf("pid: %d\n", pid_done);

    return 0;
}
/*Program B*/
#include <unistd.h>

int main(void){
    for(;;){
        pause();
    }
    return 0;
}

父进程在fork子进程后调用wait等待子进程返回,如果内核没有改写子进程的信号捕获安排,那么按照常理来说对B发出一个SIGINT信号,B会向终端打印“received SIGINT.\n”。

但是现在它并没有,因为内核在exec之后把进程对SIGINT的安排设置为了系统默认处理方式,所以这里B进程终止。

2.被用户设置为忽略的信号exec后依然保留忽略的例子:

说明这种情况只需要把程序A中的捕捉函数修改为SIG_IGN就可以了。

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


int main(void){
    if(signal(SIGINT, SIG_IGN) == SIG_ERR){
        printf("cat't catch SIGINT.\n");
        return 0;
    }

    pid_t pid = fork();
    if(pid < 0){
        printf("Error occurred when fork.\n");
    }
    if(pid == 0){
        if(execl("/lroot/c/temp/B", NULL) < 0){
            printf("Error occurred when execle.\n");
        }
    }

    int   status;
    pid_t pid_done = wait(&status);
    printf("pid: %d\n", pid_done);

    return 0;
}

自此向进程发出SIGINT信号会被进程B忽略,不做任何处理。