SIGNALFD - Linux手册页

时间:2019-08-20 17:59:26  来源:igfitidea点击:

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/