通过CTRL-C接收SIGINT时发送者pid为零

3
在我的信号处理程序中,通过使用带有SA_SIGINFO标志的sigaction()设置的方式,当通过CTRL-C触发SIGINT时,siginfo_t结构体的si_pid成员(存储发送进程的ID)为零。以下是使用signalfd()演示此“问题”的简单示例:
#include <sys/signalfd.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main(void) {
        sigset_t mask;
        int sfd;
        struct signalfd_siginfo fdsi;
        ssize_t s;

        printf("my process id: %d\n", getpid());

        sigemptyset(&mask);
        sigaddset(&mask, SIGINT);

        /* block signals to avoid default handling */
        if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
                perror("sigprocmask");
                return 1;
        }

        sfd = signalfd(-1, &mask, 0);
        if (sfd == -1) {
                perror("signalfd");
                return 1;
        }

        printf("waiting for sigint ...\n");

        /* raise(SIGINT); */

        s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));
        if (s != sizeof(struct signalfd_siginfo)) {
                perror("reading from signal fd");
                return 1;
        }

        if (fdsi.ssi_signo != SIGINT) {
                fprintf(stderr, "received unexpected signal %d\n", fdsi.ssi_signo);
                return 1;
        }

        printf("received SIGINT from process %d\n", fdsi.ssi_pid);

        return 0;
}

如果您运行此程序并从另一个tty触发kill -INT pid,则程序的输出为:

my process id: 23540
waiting for sigint ...
received SIGINT from process 23186

现在,如果您在启动程序后按CTRL-C,输出结果是:
my process id: 23551
waiting for sigint ...
^Creceived SIGINT from process 0

如果我在程序中引发SIGINT(通过取消代码示例中的/* raise(SIGINT); */行的注释),则输出结果为:
my process id: 23577
waiting for sigint ...
received SIGINT from process 23577

现在,如果我启用了Linux的ftrace系统调用跟踪器,对于killtkilltgkill,我可以验证信号是由第一个示例中的sys_kill处理的,而由最后一个示例中的sys_tgkill处理的。

然而,在按下CTRL-C时,没有任何这些系统调用被调用。

问题: 在按下CTRL-C时,哪个(如果有)系统调用被调用? 谁将指令指针传递给CTRL-C上的系统调用处理程序? 为什么siginfo_t结构中的发送方pid在CTRL-C上为零(bug或文档化的特性)?

1个回答

2
当按下CTRL-C时,没有任何进程调用系统调用。这就是为什么发送者的PID为零。
信号来自内核,具体来说是TTY模块。每个进程(可选)绑定到一个终端,即“控制终端”。每个终端都有一个定义好的按键序列,该序列会向绑定到该终端的所有进程发送SIGINT。默认的按键序列当然是CTRL-C
在这种情况下,您可以将PID 0视为“内核进程”。
参考资料: 请看看stty的输出中是否能找到CTRL-C
$ stty -a
speed 38400 baud; rows 24; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?;
swtch = M-^?; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W;
lnext = ^V; discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 hupcl -cstopb cread -clocal -crtscts
-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff
-iuclc ixany imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke -extproc
  • 更多关于控制终端的信息,请参见setpgid(2)手册页中的“NOTES”部分。

太棒了!这解释了一切。负责发送信号的内核部分在这里:http://lxr.free-electrons.com/source/drivers/tty/n_tty.c?v=3.19#L1124 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接