Linux内核深度解析
上QQ阅读APP看书,第一时间看更新

2.6.2 终止进程

系统调用kill(源文件“kernel/signal.c”)负责向线程组或者进程组发送信号,执行流程如图2.19所示。

图2.19 系统调用kill的执行流程

(1)如果参数pid大于0,那么调用函数kill_pid_info来向线程pid所属的线程组发送信号。

(2)如果参数pid等于0,那么向当前进程组发送信号。

(3)如果参数pid小于−1,那么向组长标识符为-pid的进程组发送信号。

(4)如果参数pid等于−1,那么向除了1号进程和当前线程组以外的所有线程组发送信号。

函数kill_pid_info负责向线程组发送信号,执行流程如图2.20所示,函数check_kill_permission检查当前进程是否有权限发送信号,函数__send_signal负责发送信号。

图2.20 向线程组发送信号的执行流程

函数__send_signal的主要代码如下:

    kernel/signal.c
    1   static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,
    2               int group, int from_ancestor_ns)
    3   {
    4    struct sigpending *pending;
    5    struct sigqueue *q;
    6    int override_rlimit;
    7    int ret = 0, result;
    8
    9    …
    10   result = TRACE_SIGNAL_IGNORED;
    11   if (! prepare_signal(sig, t,
    12              from_ancestor_ns || (info == SEND_SIG_FORCED)))
    13        goto ret;
    14
    15   pending = group ? &t->signal->shared_pending : &t->pending;
    16
    17   result = TRACE_SIGNAL_ALREADY_PENDING;
    18   if (legacy_queue(pending, sig))
    19        goto ret;
    20
    21   …
    22   if (sig < SIGRTMIN)
    23        override_rlimit = (is_si_special(info) || info->si_code >= 0);
    24   else
    25        override_rlimit = 0;
    26
    27   q = __sigqueue_alloc(sig, t, GFP_ATOMIC | __GFP_NOTRACK_FALSE_POSITIVE,
    28        override_rlimit);
    29   if (q) {
    30        list_add_tail(&q->list, &pending->list);
    31        …
    32   } else if (! is_si_special(info)) {
    33        …
    34   }
    35
    36  out_set:
    37   signalfd_notify(t, sig);
    38   sigaddset(&pending->signal, sig);
    39   complete_signal(sig, t, group);
    40  ret:
    41   …
    42   return ret;
    43  }

第11~13行代码,如果目标线程忽略信号,那么没必要发送信号。

第15行代码,确定把信号添加到哪个信号队列和集合。线程组有一个共享的信号队列和集合,每个线程有一个私有的信号队列和集合。如果向线程组发送信号,那么应该把信号添加到线程组共享的信号队列和集合中;如果向线程发送信号,那么应该把信号添加到线程私有的信号队列和集合中。

第18行代码,如果是传统信号,并且信号集合已经包含同一个信号,那么没必要重复发送信号。

第22~25行代码,判断分配信号队列节点时是否可以忽略信号队列长度的限制:对于传统信号,如果是特殊的信号信息,或者信号的编码大于0,那么允许忽略;如果是实时信号,那么不允许忽略。

第27行和第28行代码,分配一个信号队列节点。

第29行和第30行代码,如果分配信号队列节点成功,那么把它添加到信号队列中。

第37行代码,如果某个进程正在通过信号文件描述符(signalfd)监听信号,那么通知进程。signalfd是进程创建用来接收信号的文件描述符,进程可以使用select或poll监听信号文件描述符。

第38行代码,把信号添加到信号集合中。

第39行代码,调用函数complete_signal:如果向线程组发送信号,那么需要在线程组中查找一个没有屏蔽信号的线程,唤醒它,让它处理信号。

上一节已经介绍过,当线程准备从内核模式返回用户模式时,检查是否收到信号,如果收到信号,那么处理信号。