• 【IPC 通信】信号处理接口 Signal API(2)


            收发信号思想是 Linux 程序设计特性之一,一个信号可以认为是一种软中断,通过用来向进程通知异步事件。

            本文讲述的 信号处理内容源自 Linux man。本文主要对各 API 进行详细介绍,从而更好的理解信号编程。


    signal

    遵循 C11,POSIX.1 - 2008

    1.库

    标准 c 库,libc, -lc

    2.头文件

    3.接口定义

    1. #include
    2. typedef void (*sighandler_t)(int);
    3. sighandler_t signal(int signum, sighandler_t handler);

    4.接口描述

           注意:signal() 的行为会根据不同的 UNIX 版本而变化,同样它也会根据 Linux 版本的不同而不同。考虑到程序的可移植性,尽量避免 signal() 的使用,而是使用 sigacton(2) 代替,可以参考下面移植性部分。

            signal() 设置 signum 信号的处理函数为 handler,处理函数可以是SIG_IGN、SIG_DFL,也可以是程序设计人员定义的函数地址。

            如果 signum 被分发给到一个进程,那么会发生以下行为:

    • 如果处理函数设置成了 SIG_IGN,那么信号会被忽略。
    • 如果处理函数设置成了 SIG_DFL,那么信号的默认关联行为会发生(可以参考 signal(7))。
    • 如果处理函数设置成了一个函数,那么首先会将默认处理函数复位为 SIG_DFL,或者信号被阻塞(参考下面可移植性部分),然后会调用 handler 函数并传递 signum 参数。如果处理函数调用导致了该信号处理阻塞,那么在处理函数返回后,该信号会重新被 unblock。

            SIGKILL 和 SIGSTOP 两个信号不能被捕捉或者忽略。

    5.返回值

            signal() 返回信号之前 的处理函数值。发生错误时,signal() 会返回 SIG_ERR,并设置 errno 来提示具体错误。

            错误值定义如下:

    EINVALsignum 参数不合法

    6.版本

           sighandler_t 是一个 GNU 扩展,它会在 _GNU_SOURCE 定义时暴漏出来。如果定义了_BSD_SOURCE(glibc 2.19 或低版本) 或者 _DEFAULT_SOURCE(glibc 2.19 或高版本),glibc 也定义了 sig_t。不使用这些定义的情况下,signal() 的声明就会有些晦涩:

               void ( *signal(int signum, void (*handler)(int)) ) (int);
    

    可移植性 

            signal() 只有在将 handler 设置为 SIG_DFL/SIG_IGN 时,才具有移植性。使用 signal() 建立信号处理函数的语义随着系统的不同而不同,POSIX.1 明确允许这些不同的行为。所以不要使用它于此目的。

            POSIX.1 通过 sigaction(2) 接口解决了这种移植上的混乱,sigaction(2)  提供了信号处理调用的明确语义定义。所以,使用 sigaction(2) 来代替 signal()。

    7.历史

            C89,POSIX.1-2001

            在原来的 UNIX 系统中,当使用 signal() 建立的信号处理函数被调用时,信号的处理会被设置成 SIG_DFT,并且系统不会阻塞该信号通往其他进程的发布。这相当于调用了 sigaction(2),附带以下标记:

               sa.sa_flags = SA_RESETHAND | SA_NODEFER;
    

             System V 已提供了 signal() 的语义,不过这个定义有点差劲,因为在处理函数重新建立连接前,可能会连续收到两个信号。更严重的,同一个信号的频繁分发会导致处理函数的递归调用。

            BSD 对此进行了改善,但不幸的是这种改善却改变了现存 signal() 接口的语义。在 BSD 系统上,当一个处理函数调用时,信号处置并没有被重新设置,后面发生的该信号的实例因该处理函数正在执行无法进行分发。更严重的,一些阻塞系统调用会在信号处理函数打断后自动重启。BSD 语义相当于使用下面标记调用 sigaction(2):

               sa.sa_flags = SA_RESTART;
    

             Linux 上的情景如下:

    • 内核的 signal() 系统调用提供了 System V 语义
    • 默认情况下,glibc 2 及更高版本的 signal() 封装并没有调用内核系统调用,而是调用了 sigaction(2),提供 BSD 语义的标记。只要提供合适的宏定义,就可以提供以上默认行为:glibc 2.19 或低版本的 _BSD_SOURCE 或者 2.19 或更高版本的 _DEFAULT_SOURCE。(默认情况下,这些宏是定义了的,参考 feature_test_macros(7) )。如果这些测试宏没有开启,那么 signal) 提供的是 System V 语义。 

     8.注意

            signal() 在进程的多线程场景下的副作用是未定义的。

            根据 POSIX 定义,进程忽略非 kill(2)/raise(3) 产生的 SIGFPE/SIGILL/SIGSEGV 信号后的行为是未定义的。整数除以 0 是未定义的结果,在一些架构上它会产生 SIGFPE 信号(同样使用 -1 除最大负整数也可能产生 SIGFPE。)忽略这些信号可能会导致无限循环。

            参考 sigaction(2) 获取更多关于将 SIGCHLD 信号的处置设置为 SIGIGN 的信息。

            参考 signal-safety(7) 来查看一些可以在信号处理函数内部调用的异步信号安全的函数。

    9.代码

            下面是一个捕捉 CTRL + C 信号的程序。

    1. #include
    2. #include
    3. #include
    4. #include
    5. void sighandler(int);
    6. int main()
    7. {
    8. signal(SIGINT, sighandler);
    9. while(1)
    10. {
    11. printf("开始休眠一秒钟...\n");
    12. sleep(1);
    13. }
    14. return(0);
    15. }
    16. void sighandler(int signum)
    17. {
    18. printf("捕获信号 %d,跳出...\n", signum);
    19. exit(1);
    20. }

    下一篇 【IPC 通信】信号处理接口 Signal API(3)

  • 相关阅读:
    解决js添加元素后onclick无效问题,解决onclick无效问题
    在Docker环境下部署GeneFace++项目
    PHP 如何创建一个 composer 包 并在 项目中使用自己的 composer sdk 包
    DeepMind 发了篇论文,把我看笑了
    JavaWeb之会话技术&会话固定攻击
    linux之eventfd理解
    redis 支持的数据类型
    并发安全问题之--事物失效问题
    CANoe-什么是Vector Tool Platform(全网唯一讲明白的文章)
    【漏洞复现】maccms苹果cms 命令执行漏洞
  • 原文地址:https://blog.csdn.net/BillyThe/article/details/132979542