zoukankan      html  css  js  c++  java
  • 发送信号

    【摘自《Linux/Unix系统编程手册》】

    kill()

    一个进程能够使用 kill() 系统调用向另一个进程发送信号。(之所以选择 kill 作为术语,是因为早期UNIX实现中大多数信号的默认行为是终止进程)

    #include <signal.h>
    int kill(pid_t pid, int sig);
            Returns 0 on success, or -1 on error

     pid 参数标识一个或多个目标进程,而 sig 则指定了要发送的信号。如何解释 pid,要视以下4种情况而定:

    • 如果 pid 大于 0,那么会发送信号给 pid 指定的进程
    • 如果 pid 等于 0,那么会发送信号给与调用进程同组的每个进程,包括调用进程自身
    • 如果 pid 小于 -1,那么会向组 ID 等于该 pid 绝对值的进程组内所有下属进程发送信号。向一个进程组的所有进程发送信号在 shell 作业控制中有特殊有途
    • 如果 pid 等于 -1,那么信号的发送范围是:调用进程有权将信号发往的每个目标进程,除去 init(进程 ID 为 1)和调用进程自身。如果特权级进程发起这一调用,那么会发送信号给系统中的所有进程,上述两个进程除外。显而易见,有时也将这种信号发送方式称之为广播信号

    如果并无进程与指定的 pid 相匹配,那么 kill() 调用失败,同时将 errno 置为 ESRCH(“查无此进程”)

    进程要发送信号给另一进程,需要适当的权限,权限规则如下:

    • 特权级(CAP_KILL)进程可以向任何进程发送信号
    • 以 root 用户和组运行的 init 进程(进程号为 1),是一种特例,仅能接受已安装了处理器函数的信号。这可以防止系统管理员意外杀死 init 进程——这一系统运作的基石
    • 如图,如果发送者的实际或有效用户 ID 匹配于接受者的实际用户 ID 或者保存设置用户 ID (saved set-user-id),那么非特权进程也可以向另一个进程发送信号。利用这一规则,用户可以向由他们启动的 set-user-ID 程序发送信号,而无需考虑目标进程有效用户 ID 的当前设置。将目标进程有效用户 ID 排除在检查范围之外,这一举措的辅助作用在于防止用户某甲向用户某乙的进程发送信号,而该进程正在执行的 set-user-ID 程序又属于用户某甲。
    • SIGCONT 信号需要特殊处理。无论对用户 ID 的检查结果如何,非特权进程可以向同一会话中的任何其他进程发送这一信号。利用这一规则,运作作业控制的 shell 可以重启已停止的作业(进程组),及时作业进程已经修改了它们的用户 ID。

    如果进程无权发送信号给所请求的 pid, 那么 kill() 调用将失败,且将 errno 置为 EPERM。若 pid 所指为一系列进程(即 pid 为负值)时,只要可以向其中之一发送信号,则 kill() 调用成功。

    检查进程的存在 

    kill() 系统调用还有另一重功用。若将参数 sig 指定为 0(即所谓空信号),则无信号发送。相反,kill() 仅会去执行错误检查,查看是否可以向目标进程发送信号。从另一个角度来看,这意味着,可以使用空信号来检测具有特定进程 ID 的进程是否存在。若发送空信号失败,且 errno 为 ESRCH,则表明目标进程不存在。如果调用失败,且 errno 为 EPERM(表示进程存在,但无权向目标进程发送信号)或者调用成功(有权向进程发送信号),那么就表示进程存在。

    验证一个特定进程 ID 的存在并不能保证特定程序仍在运行。因为内核会随着进程的生灭而循环使用进程 ID。而一段时间之后,同一进程 ID 所指恐怕是另一进程了。此外,特定进程 ID 可能存在,但是一个僵尸(进程已死,但其父进程尚未执行 wait() 来获取其终止状态)。

    还可使用各种其他技术来检查某一特定进程是否正在运行,其中包括如下技术:

    • wait() 系统调用:仅用于监控调用者的子进程
    • 信号量和排他文件锁:如果进程持续持有某一信号量或文件锁,并且一直处于被监控状态,那么如能获取到信号量或锁时,即表明该进程已经终止。
    • 诸如管道和 FIFO 之类的 IPC 通道:可对监控目标进程进行设置,令其在自身生命周期内持有对通道进行写操作的打开文件描述符。同时,令监控进程持有针对通道进行读操作的打开文件描述符,且当通道写入端关闭时(遭遇文件结束符),即可获知监控目标进程已经终止。监控进程对此情况的判定,既可借助于自身文件描述符的读取,可也采用第63章所述的描述符监控技术之一
    • /proc/PID 接口:例如,如果进程 ID 为12345 的进程存在,那么目录 /proc/12345 将存在,可以发起诸如 stat() 之类的调用来进行检查

    出去最后一项之外,循环使用进程 ID 不会影响上诉所有技术。

     1 #include <signal.h>
     2 #include "tlpi_hdr.h"
     3 
     4 int main(int argc, char* argv[])
     5 {
     6     int s, sig;
     7     if (argc != 3 || strcmp(argv[1], "--help") == 0)
     8         usageErr("%s sig-num pid
    ", argv[0]);
     9 
    10     sig = getInt(argv[2], 0, "sig-num");
    11     s = kill(getLong(argv[1], 0, "pid"), sig);
    12 
    13     if (sig != 0) {
    14         if (s == -1)
    15             errExit("kill");
    16     } else { /* Null signal: process existence check*/
    17         if (s == 0) {
    18             printf("Process exists and we can send it a signal
    ");
    19         } else {
    20             if (errno == EPERM)
    21                 printf("Process exists, but we don't have permission to send it a signal
    ");
    22             else if (errno == ESRCH)
    23                 printf("Process does not exist
    ");
    24             else
    25                 errExit("kill");
    26         }
    27     }
    28     exit(EXIT_SUCCESS);
    29 }


    发送信号的其它方式:raise() 和 killpg()

    有时,进程需要向自身发送信号,raise() 函数就执行了这一任务

    #include <signal.h>
    int raise(int sig);
            Returns 0 on success, or nonzero on error

    在单线程程序中,调用 raise() 相当于对 kill() 的如下调用:

    kill(getpid(), sig);

    支持线程的系统会将 raise() 实现为:

    pthread_kill(pthread_self(), sig);

    该实现意味着将信号传递给调用 raise() 的特定线程。相比之下,kill(getpid(), sig) 调用会发送一个信号给调用进程,并可将该信号传递给该进程的任一线程。

    当进程使用 raise() (或者kill())向自身发送信号时,信号将立即传递(即在 raise() 返回调用者之前)

    注意,raise() 出错将返回非 0 值(不一定为 -1)。调用 raise() 唯一可能发生的错误为 EINVAL,即 sig 无效。

    killpg() 函数向某一进程组的所有成员发送一个信号

    #include <signal.h>
    int killpg(pid_t pgrp, int sig);
            Returns 0 on success, or -1 on error

    killpg() 的调用相当于对 kill() 的如下调用:

    kill(-pgrp, sig);

    如果指定 pgrp 的值为 0,那么会向调用者所属进程组的所有进程发送此信号。

  • 相关阅读:
    sort uniq 命令 企业应用场景实战排序
    网络管理相关命令常用必回基础实战
    Zabbix 3.0入门到企业实战(自带模板介绍)
    jsp页面指令
    jsp九大内置对象
    如何将静态页面转化为动态页面
    转发与重定向区别
    cookie的保存时间
    登陆界面 实现思路
    卸载了mysql之后,mysql服务仍在,显示读取描述失败,错误代码2
  • 原文地址:https://www.cnblogs.com/jingyg/p/5180078.html
Copyright © 2011-2022 走看看