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


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

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


    kill(2)

    遵循 POSIX.1 - 2008

    1.库

    标准 c 库,libc, -lc

    2.接口定义

            这个接口依 _POSIX_C_SOURCE 特性测试宏。

    1. #include
    2. int kill(pid_t pid, int sig);

    3.接口描述

            kill() 系统调用可以用来向任何进程组或者进程发送任何信号。

            如果 pid 是正值,那么信号 sig 是发送给 pid 指定的进程。

            如果 pid 是 0,信号 sig 是发送给调用进程所在进程组的所有进程。

            如果 pid 是 -1,那么信号 sig 会发送给所有调用进程具有发送权限的进程,除了 1 号进程(init),具体下面会讨论。

            如果 pid 小于 -1,那么信号 sig 会发送给 -pid 指定进程的进程组里所有的进程。

            如果 sig 是 0,那么不会发送任何信号,但是仍然会进行进程是否存在和是否有权限等相关检查,这个就可以用来检查是否存在允许调用者发送信号的进程 ID 或者进程组 ID 存在。

            对于一个具有发送信号权限的进程来说,它要么是特权的(Linux 下需要具有目标进程用户名字空间的 CAP_KILL 能力),要么发送进程的真实或者有效的用户 ID 和目标进程的真实或者保存的 set-user-ID 相同。对于 SIGCONT 信号,发送和接收进程属于同一会话即可。(一些历史版本的规则可能不太一样,可以参考注意章节。)

    4.返回值

            一旦成功(至少有一个信号发送出去了),就会返回 0。失败时,会返回 -1,具体错误信息通过 errno 来指示。

            错误代码如下:

    EINVAL指定的信号不可用
    EPERM调用进程没有向目标进程组任何进程发送信号的权限
    ESRCH目标进程或者进程组不存在。注意:一个存在的进程可能是一个僵尸,即已经被终止执行(terminated)但是还没有 wait(2) 等待

    5.历史

            在不同的 Linux 内核版本上,Linux 对非特权进程向其他进程发送信号实施了不同的策略。在 Linux 1.0 到 1.2.2 中,如果发送进程的有效用户 ID 和目标的有效用户 ID 匹配或者它们的真实 ID 匹配,那么就可以发送信号 。在 Linux 1.2.3 到 1.3.77,只要发送进程的有效用户 ID 和目标进程的有效或者真实 ID 匹配就可以发送信号。目前的策略是遵循 POSIX.1,并且在 Linux 1.3.78 中引入。

     6.注意

            对于 init 进程,也就是 1 号进程,只能向它发送它明确安装了信号处理函数的信号。这样做是为了保证系统不被偶然情况宕机。

            POSIX.1 要求 kill(-1,sig) 向调用者所有能发送的进程发送信号,除了一些实现定义的系统进程。Linux 允许进程向自己发送信号,但是 kill(-1,sig) 不会向调用进程发送。

            POSIX.1 要求如果进程向自己发送信号,如果发送线程没有阻塞信号,并且其他线程阻塞了该信号或者没有通过 sigwait(3) 等待该信号,那么在 kill() 返回前至少要向发送线程发生一个非阻塞信号。

    7.BUGS

            Linux 2.6 后一直到 2.6.7,该接口一直存在一个 bug:在向进程组进程发送信号时,只要进程里有哪个进程没有权限发送信号,接口就会返回 EPERM。即使返回这个错误,信号还是发送给了有权限发送的进程了。

    8.代码

            下面是一个父进程通过 kill() 杀死子进程的例子。

    1. #include
    2. #include
    3. #include
    4. #include
    5. int main(void){
    6. pid_t retVal;
    7. retVal = fork();
    8. if(retVal > 0){
    9. int i = 0;
    10. while(i++ < 5){
    11. printf("in the parent process.\n");
    12. sleep(1);
    13. }
    14. //kill the child process
    15. kill(retVal, SIGKILL);
    16. } else if (retVal == 0){
    17. int i = 0;
    18. //will not ever get to 15, because
    19. //the parent process will kill it
    20. while(i++ < 15){
    21. printf("In the child process.\n");
    22. sleep(1);
    23. }
    24. } else {
    25. //something bad happened.
    26. printf("Something bad happened.");
    27. exit(EXIT_FAILURE);
    28. }
    29. return 0;
    30. }

    tkill(2)

    遵循 Linux

    1.库

    标准 c 库,libc, -lc

    2.接口定义

    1. #include /* Definition of SIG* constants */
    2. #include /* Definition of SYS_* constants */
    3. #include
    4. [[deprecated]] int syscall(SYS_tkill, pid_t tid, int sig);
    5. #include
    6. int tgkill(pid_t tgid, pid_t tid, int sig);
    7. Note: glibc provides no wrapper for tkill(), necessitating the
    8. use of syscall(2).

    3.接口描述

            tgkill() 向线程组 tgid 中的线程 tid 发送信号 sig。(相反,kill(2) 只能用来向进程(线程组)发送信号,信号会被发送给进程中的任意线程。)

            tkill() 是 tgkill() 的过时版本。它只允许指定目标线程 ID,这会导致线程 ID 回收重新分配时信号发送到错误的线程。尽量避免使用这个系统调用。

            这些都是原始系统调用接口,只能被线程库内部使用。

    4.返回值

            一旦成功,就会返回 0。失败时,会返回 -1,具体错误信息通过 errno 来指示。

            错误代码如下:

    EAGAINsig 是实时信号并且达到了 RLIMIT_SIGPENDING 资源限制
    EAGAINsig 是一个实时信号,并且内核内存不足
    EINVAL线程 ID、线程组 ID或者信号 不合法
    EPERM权限拒绝。对于需要的权限,参考 kill(2)
    ESRCH指定的进程不存在

    5.历史

            tkill()         Linux 2.4.19/2.5.4

            tgkill()        Linux 2.5.75,glibc 2.30

     6.注意

           对于进程组的解释,可以参考 clone(2) 的CLONE_THREAD 描述。

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

  • 相关阅读:
    【ICCV2023】DECO:野外环境场景下的 3D 人体-场景的接触估计
    Go | 浅谈包管理模式
    【C++基础】9. 数组
    PMP_模考三 180题(附答案及解析)
    Kotlin学习笔记之泛型的高级特性
    Java学习笔记(十九)
    SpringMVC 05: SpringMVC中携带数据的页面跳转
    7-FreeRTOS软件定时器
    LRU、LFU 内存淘汰算法的设计与实现
    机器语言编写helloworld
  • 原文地址:https://blog.csdn.net/BillyThe/article/details/133383796