SIGNALFD - Linux手册页
Linux程序员手册 第2部分
更新日期: 2020-06-09
名称
signalfd-创建用于接收信号的文件描述符
语法
#包括
int signalfd(int fd,const sigset_t * mask,int标志);
说明
signalfd()创建一个文件描述符,该文件描述符可用于接受针对调用者的信号。这提供了使用信号处理程序或sigwaitinfo(2)的替代方法,并具有可以通过select(2),poll(2)和epoll(7)监视文件描述符的优点。
mask参数指定调用者希望通过文件描述符接受的信号集。此参数是一个信号集,其内容可以使用sigsetops(3)中描述的宏进行初始化。通常,应使用sigprocmask(2)阻止要通过文件描述符接收的信号集,以防止根据信号的默认设置来处理信号。无法通过signalfd文件描述符接收SIGKILL或SIGSTOP信号。如果在mask中指定,则将忽略这些信号。
如果fd参数为-1,则调用将创建一个新的文件描述符,并将mask中指定的信号集与该文件描述符相关联。如果fd不为-1,则它必须指定有效的现有signalfd文件描述符,并使用mask替换与该文件描述符关联的信号集。
从Linux 2.6.27开始,可以对标志中的以下值进行按位或运算,以更改signalfd()的行为:
- SFD_NONBLOCK
- 在新文件描述符引用的打开文件描述(请参见open(2))上设置O_NONBLOCK文件状态标志。使用此标志可以节省对fcntl(2)的额外调用,以实现相同的结果。
- SFD_CLOEXEC
- 在新文件描述符上设置执行时关闭(FD_CLOEXEC)标志。有关为什么可能有用的原因,请参见open(2)中O_CLOEXEC标志的描述。
在2.6.26版之前的Linux中,flags参数未使用,必须指定为零。
signalfd()返回支持以下操作的文件描述符:
- read(2)
- 如果在mask中指定的一个或多个信号尚待处理,则使用提供给read(2)的缓冲区返回一个或多个描述信号的signalfd_siginfo结构(请参见下文)。 read(2)返回有关未决信号的信息,这些信号将适合所提供的缓冲区。缓冲区必须至少为sizeof(struct signalfd_siginfo)个字节。 read(2)的返回值是读取的字节总数。
- 作为read(2)的结果,信号被消耗,因此它们不再等待处理(即不会被信号处理程序捕获,也无法使用sigwaitinfo(2)接受)。
- 如果掩码中的信号均未等待处理,则read(2)会阻塞,直到为该进程生成掩码中的信号之一为止;否则,如果已将文件描述符设为非阻塞,则失败并显示错误EAGAIN。
- poll(2), select(2) (and similar)
- 如果掩码中的一个或多个信号正在等待处理,则文件描述符是可读的(select(2)readfds参数; poll(2)POLLIN标志)。
- signalfd文件描述符还支持其他文件描述符多路复用API:pselect(2),ppoll(2)和epoll(7)。
- close(2)
- 当不再需要文件描述符时,应将其关闭。关闭与同一signalfd对象关联的所有文件描述符后,内核将释放对象的资源。
The signalfd_siginfo structure
read(2)从signalfd文件描述符返回的signalfd_siginfo结构的格式如下:
struct signalfd_siginfo { uint32_t ssi_signo; /* Signal number */ int32_t ssi_errno; /* Error number (unused) */ int32_t ssi_code; /* Signal code */ uint32_t ssi_pid; /* PID of sender */ uint32_t ssi_uid; /* Real UID of sender */ int32_t ssi_fd; /* File descriptor (SIGIO) */ uint32_t ssi_tid; /* Kernel timer ID (POSIX timers) uint32_t ssi_band; /* Band event (SIGIO) */ uint32_t ssi_overrun; /* POSIX timer overrun count */ uint32_t ssi_trapno; /* Trap number that caused signal */ int32_t ssi_status; /* Exit status or signal (SIGCHLD) */ int32_t ssi_int; /* Integer sent by sigqueue(3) */ uint64_t ssi_ptr; /* Pointer sent by sigqueue(3) */ uint64_t ssi_utime; /* User CPU time consumed (SIGCHLD) */ uint64_t ssi_stime; /* System CPU time consumed (SIGCHLD) */ uint64_t ssi_addr; /* Address that generated signal (for hardware-generated signals) */ uint16_t ssi_addr_lsb; /* Least significant bit of address (SIGBUS; since Linux 2.6.37) uint8_t pad[X]; /* Pad size to 128 bytes (allow for additional fields in the future) */ };
此结构中的每个字段都类似于siginfo_t结构中命名类似的字段。 siginfo_t结构在sigaction(2)中进行了描述。并非返回的signalfd_siginfo结构中的所有字段对于特定信号都有效;可以从ssi_code字段中返回的值确定有效字段的集合。该字段类似于siginfo_t si_code字段;有关详细信息,请参见sigaction(2)。
fork(2) semantics
在fork(2)之后,孩子继承了signalfd文件描述符的副本。子文件描述符中的read(2)将返回有关排队到子文件中的信号的信息。
Semantics of file descriptor passing
与其他文件描述符一样,可以通过UNIX域套接字将signalfd文件描述符传递给另一个进程(请参见unix(7))。在接收过程中,从接收到的文件描述符中读取(2)将返回有关排队到该过程的信号的信息。
execve(2) semantics
就像任何其他文件描述符一样,signalfd文件描述符在execve(2)上保持打开状态,除非已将其标记为close-on-exec(请参见fcntl(2))。 execve(2)之前可以读取的所有信号对于新加载的程序仍然可用。 (这类似于传统的信号语义,在这种情况下,待处理的阻塞信号在execve(2)上仍处于待处理状态。)
Thread semantics
多线程程序中的signalfd文件描述符的语义反映了信号的标准语义。换句话说,当线程从signalfd文件描述符读取时,它将读取定向到该线程本身的信号和定向到该进程的信号(即整个线程组)。 (线程将无法读取该进程中指向其他线程的信号。)
epoll(7) semantics
如果某个进程(通过epoll_ctl(2))将一个signalfd文件描述符添加到epoll(7)实例,则epoll_wait(2)仅返回发送给该进程的信号的事件。特别是,如果该进程随后使用fork(2)创建一个子进程,则该子进程将能够读取(2)使用signalfd文件描述符发送给它的信号,但是epoll_wait(2)不会指示signalfd文件描述符已准备就绪。在这种情况下,可能的解决方法是在fork(2)之后,子进程可以关闭它从父进程继承的signalfd文件描述符,然后创建另一个signalfd文件描述符并将其添加到epoll实例。或者,父级和子级可以延迟创建其(单独的)signalfd文件描述符并将它们添加到epoll实例中,直到调用fork(2)之后。
返回值
成功后,signalfd()返回一个signalfd文件描述符;这是一个新文件描述符(如果fd为-1),或者是fd(如果fd是有效的signalfd文件描述符)。发生错误时,将返回-1并将errno设置为指示错误。
错误说明
- EBADF
- fd文件描述符不是有效的文件描述符。
- EINVAL
- fd不是有效的signalfd文件描述符。
- EINVAL
- 标志无效;或者,在Linux 2.6.26或更早版本中,标志为非零。
- EMFILE
- 已达到打开文件描述符数量的每个进程限制。
- ENFILE
- 已达到系统范围内打开文件总数的限制。
- ENODEV
- 无法安装(内部)匿名inode设备。
- ENOMEM
- 没有足够的内存来创建新的signalfd文件描述符。
版本
从内核2.6.22开始,signalfd()在Linux上可用。从2.8版开始,glibc提供了工作支持。从内核2.6.27开始,可以在Linux上使用signalfd4()系统调用(请参见NOTES)。
遵循规范
signalfd()和signalfd4()特定于Linux。
备注
一个进程可以创建多个signalfd文件描述符。这使得可以在不同的文件描述符上接受不同的信号。 (如果使用select(2),poll(2)或epoll(7)监视文件描述符,这可能很有用:不同信号的到来将使不同的文件描述符准备就绪。)如果信号出现在大于其中一个文件描述符,则可以从任何一个文件描述符中读取(一次)该信号的出现。
尝试在掩码中包含SIGKILL和SIGSTOP会被忽略。
可以通过进程的/ proc / [pid] / fdinfo目录中相应文件描述符的条目来查看signalfd文件描述符所采用的信号掩码。有关更多详细信息,请参见proc(5)。
Limitations
signalfd机制不能用于接收同步生成的信号,例如,由于访问无效的存储器地址而产生的SIGSEGV信号或由于算术错误而导致的SIGFPE信号。只能通过信号处理程序捕获此类信号。
如上所述,在正常使用情况下,一个信号块将通过signalfd()接受。如果生成一个子进程来执行帮助程序(不需要signalfd文件描述符),则在调用fork(2)之后,通常需要在调用execve(2)之前取消阻止这些信号,以便帮助程序可以看到它希望看到的任何信号。但是请注意,在辅助程序被任何程序可能调用的库函数在后台生成的情况下,这是不可能的。在这种情况下,必须回落到使用传统的信号处理程序,该信号处理程序写入由select(2),poll(2)或epoll(7)监视的文件描述符。
C library/kernel differences
底层Linux系统调用需要一个附加参数size_t sizemask,它指定mask参数的大小。 glibc signalfd()包装函数不包含此参数,因为它为基础系统调用提供了所需的值。
有两个底层的Linux系统调用:signalfd()和更新的signalfd4()。前一个系统调用未实现flags参数。后一个系统调用实现上述标记值。从glibc 2.9开始,signalfd()包装函数将在可用的地方使用signalfd4()。
BUGS
在2.6.25之前的内核中,ssi_ptr和ssi_int字段未填充sigqueue(3)发送的信号附带的数据。
示例
下面的程序通过signalfd文件描述符接受信号SIGINT和SIGQUIT。接受SIGQUIT信号后,程序终止。以下shell会话演示了该程序的用法:
$ ./signalfd_demo haC # Control-C generates SIGINT Got SIGINT haC Got SIGINT ha\ # Control-\ generates SIGQUIT Got SIGQUIT $
Program source
#include <sys/signalfd.h> #include <signal.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0) int main(int argc, char *argv[]) { sigset_t mask; int sfd; struct signalfd_siginfo fdsi; ssize_t s; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGQUIT); /* Block signals so that they arenaqt handled according to their default dispositions */ if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) handle_error("sigprocmask"); sfd = signalfd(-1, &mask, 0); if (sfd == -1) handle_error("signalfd"); for (;;) { s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo)); if (s != sizeof(struct signalfd_siginfo)) handle_error("read"); if (fdsi.ssi_signo == SIGINT) { printf("Got SIGINT\n"); } else if (fdsi.ssi_signo == SIGQUIT) { printf("Got SIGQUIT\n"); exit(EXIT_SUCCESS); } else { printf("Read unexpected signal\n"); } } }
另外参见
eventfd(2),poll(2),read(2),select(2),sigaction(2),sigprocmask(2),sigwaitinfo(2),timerfd_create(2),sigsetops(3),sigwait(3), epoll(7),signal(7)
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。