Linux signalfd 原理

信号是什么?
 
信号是事件发送时对进程的一种通知机制 。有时也称之为软件中断 。信号与硬件中断的相似之处在于打断了程序执行的正常流程,大多情况下,无法预测信号到达的精确时间 。
一个具有合适权限的进程不仅能够向另一进程发送信号,也可以向自身发送信号 。然而,发往进程的诸多信号,通常都是源于内核 。
linux 信号可由如下条件产生:

  • 对于前台进程,用户可以通过输入特殊的终端字符来给它发送信号 。比如输出 Ctrl+C 通常会给进程发送一个终端信号 。
  • 系统异常 。比如浮点异常和非法访问段内存 。
  • 系统状态变化 。比如 alarm 定时器到期引起的 SIGALRM 信号 。
  • 运行 kill 命令或者调用 kill 函数 。
服务器程序必须处理(或至少忽略)一些常见的信号,以免异常终止 。
 
signalfd 是什么?
 
signalfd 是一个将信号抽象的文件描述符,将信号的异步处理转换为文件的I/O 操作 。通过文件描述符就绪的方法来通知信号的到来,当有信号发生时可以对其 read,这样可以将信号的监听放到 select、poll、epoll 等监听队列中 。
通过文件描述符就绪的方法来通知信号的到来,当有信号发生时可以对其read,这样可以将信号的监听放到 select、poll、epoll 等监听队列中 。
signalfd 的系统调用接口
#include <sys/signalfd.h>
int signalfd(int fd, const sigset_t *mask, int flags);
 
创建并返回一个用于所受信号的文件描述符 。
mask:信号的集合,这里主要是你想监听的信号的集合 。
【Linux signalfd 原理】flags 可以使用以下标志位进行或(or)的结果:
  • SFD_NONBLOCK: 文件会被设置成 O_NONBLOCK,读操作不阻塞 。若不设置,一直阻塞直到计数器中的值大于0 。
  • SFD_CLOEXEC: 在新的文件描述符上设置 close-on-exec ( FD_CLOEXEC ) 标志,简单说就是 fork 子进程时不继承 。
 
获取 signalfd 文件描述符后,我们来查看一下可以对其做哪些操作 。
 
static const struct file_operations signalfd_fops = {
#ifdef CONFIG_PROC_FS
.show_fdinfo = signalfd_show_fdinfo,
#endif
.release = signalfd_release,
.poll = signalfd_poll,
.read = signalfd_read,
.llseek = noop_llseek,
};
 
通过上面 signalfd 实现的调用可知,我们可以对 eventfd 进行 read、poll、close 等操作 。
 
下面通过一个例子来了解下 signalfd 的使用方式,具体完整代码可通过 man signalfd 获取
 
int main(int argc, char *argv[])
{
...
//初始化信号集
sigemptyset(&mask);
//添加信号到信号集中
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGQUIT);
//将mask所指向的信号集中所包含的信号加到当前的信号掩码中,作为新的信号屏蔽字,关闭内核的默认行为 。
sigprocmask(SIG_BLOCK, &mask, NULL)
//创建 signalfd 文件描述符
sfd = signalfd(-1, &mask, 0);
for (;;) {
//阻塞等待信号发生并读取 。根据读取的结果可以知道发生了什么信号
s = read(sfd, &fdsi, sizeof(fdsi));
if (fdsi.ssi_signo == SIGINT) {
printf("Got SIGINTn");
} else if (fdsi.ssi_signo == SIGQUIT) {
printf("Got SIGQUITn");
exit(EXIT_SUCCESS);
} else {
printf("Read unexpected signaln");
}
}
}
 
当没有信号时,进程阻塞在 read 调用上,当有信号发生时,结果如下:
 
$ ./signalfd_demo
^C # Control-C generates SIGINT
Got SIGINT
^C
Got SIGINT
^ # Control- generates SIGQUIT
Got SIGQUIT
$
 
每次 Control + C,进程都会捕获到一次信号,并打印具体信息 。
通过如下查看,得到 signalfd 其实也是一个匿名 fd 类型 。
[root@localhost ~]# ll /proc/48356/fd/
lrwx------ 1 root root 64 5月 23 11:54 3 ->anon_inode:[signalfd]
 
signalfd 源码解析
 
接下来我们通过分析源码的方式来探究 signalfd 的底层实现原理 。
 
signalfd ( signalfd4 )


推荐阅读