Unix/Linux编程:通过文件描述符来获取信号------signalfd()

从内核2.6.22开始,Linux提供了(非标准的)signalfd()系统调用:利用该调用可以创建一个特殊文件描述符,发往调用者的信号都可从该描述符中读取。signalfd机制为同步接受信号提供了sigwaitinfo()之外的另一种选择。

NAME
       signalfd - create a file descriptor for accepting signals

SYNOPSIS
       #include <sys/signalfd.h>

       int signalfd(int fd, const sigset_t *mask, int flags);

DESCRIPTION
       signalfd() 创建一个文件描述符,可用于接受以调用者为目标的信号。这提供了使用信号处理程序
       或sigwaitinfo(2)的替代方法,并且具有文件描述符可由select(2)poll(2)epoll(7)监视
       的优点。

       mask 指定了有意通过 signalfd 文件描述符来读取的信号
       		此参数是一个信号集,其内容可以使用sigsetops(3)中描述的宏进行初始化。
       		如同sigwaitinfo()一样,通常也应该使用 sigprocmask()阻塞 mask 中的所有信号,以确
       		保在有机会读取这些信号之前,不会按照默认处置对它们进行处理
       		不可能通过signalfd文件描述符接收SIGKILL或SIGSTOP信号;如果在mask中指定,这些信号将被忽略。

       如果指定 fd 为−1,那么 signalfd()会创建一个新的文件描述符,用于读取 mask 中的信号;
       如果fd不是-1,那么将修改与 fd 相关的 mask 值,且该 fd 一定是由之前对 signalfd()的一次调用创建
而成

	   在版本2.6.26之前的Linux中,flags参数未使用,必须指定为零。
       
       从Linux 2.6.27开始,以下值可以在标志中按位“或”来更改signalfd()的行为:

		  SFD_NONBLOCK  为底层的打开文件描述设置 O_NONBLOCK 标志,以确保不会阻塞未来的读操作。
		  				 既省去了一个额外的 fcntl()调用,又获得了相同的结果。

          SFD_CLOEXEC  请参阅open(2)中的O_CLOEXEC 标志的描述,以了解这可能有用的原因。

    

创建文件描述符之后,可以使用 read()调用从中读取信号。提供给 read()的缓冲区必须足够大,至少应能够容纳一个 signalfd_siginfo 结构。<sys/signalfd.h>文件定义了该结构,如下所示:

		 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) */
               uint8_t  pad[X];      /* Pad size to 128 bytes (allow for
                                         additional fields in the future) */
           };

该结构中字段所返回的信息与传统 siginfo_t 结构中类似命名的字段信息相同

read()每次调用都将返回与等待信号数目相等的signalfd_siginfo 结构,并填充到已提供的缓冲区中。如果调用时并无信号正在等待,那么read()将会阻塞,直到有信号到达。也可以使用fcntl()的F_SETFL 操作来为文件描述符设置 O_NONBLOCK 标志,使得读操作不再阻塞,且若无信号等待,则调用失败,errno 为 EAGAIN。

当从 signalfd 文件描述符中读取到一信号时,该信号获得接纳,且不再为该进程而等待。

select()、poll()和 epoll可以将 signalfd 描述符和其他描述符混合起来进行监控。

当不再需要 signalfd 文件描述符时,应当关闭 signalfd 以释放相关内核资源

看个例子:下面是接收函数

// signalfd_sigbval
#include <cstring>
#include <signal.h>
#include <stdio.h>
#include <cstdlib>
#include <zconf.h>
#include <sys/signalfd.h>
#include <signal.h>
int main(int argc, char *argv[])
{
    if (argc < 2 || strcmp(argv[1], "--help") == 0){
        printf("%s sig-num...\n", argv[0]);
        exit(1);
    }


    printf("%s: PID = %ld\n", argv[0], (long) getpid());

	// 为在命令行参数中指定的信号创建掩码
	sigset_t mask;
    sigemptyset(&mask);
    for (int j = 1; j < argc; j++)
        sigaddset(&mask, atoi(argv[j]));
      
    // 阻塞这些信号  
    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1){
        perror("sigprocmask");
        exit(1);
    }


	// 创建用来读取这些信号的 signalfd 文件描述符
 	int sfd;
    sfd = signalfd(-1, &mask, 0);
    if (sfd == -1){
        perror("signalfd");
        exit(1);
    }

	ssize_t s;
	struct signalfd_siginfo fdsi;
    for (;;) {
       // 循环从文件描述符sfd中读取信号,并显示返回的 signalfd_siginfo 结构中的部分信息
        s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));  //read()将会阻塞,直到有信号到达
        if (s != sizeof(struct signalfd_siginfo)){
            perror("read");
            exit(1);
        }

        printf("%s: got signal %d", argv[0], fdsi.ssi_signo);
        if (fdsi.ssi_code == SI_QUEUE) {
            printf("; ssi_pid = %d; ", fdsi.ssi_pid);
            printf("ssi_int = %d", fdsi.ssi_int);
        }
        printf("\n");
    }
}
//(t_sigqueue.c)
#include <memory.h>
#include <stdlib.h>
#include <stdio.h>
#include <zconf.h>
#define _POSIX_C_SOURCE 199309
#include <signal.h>
#include <string.h>
#include <signal.h>
#include <climits>

int main(int argc, char *argv[])
{
    int sig, numSigs, j, sigData;
    union sigval sv;

    if (argc < 4 || strcmp(argv[1], "--help") == 0){
        printf("%s pid sig-num data [num-sigs]\n", argv[0]);
        // 该程序最多接受 4 个参数,
        //     其中前 3 项为必填项:目标进程 ID、信号编号以及伴随实时信号的整型值
        //     如果需要为指定信号发送多个实例,那么可以用可选的第 4 个参数来指定实例数量。在这种情况下,会为每个信号的伴随整
        //     型值依次加 1
        exit(EXIT_FAILURE);
    }


    printf("%s: PID is %ld, UID is %ld\n", argv[0],
           (long) getpid(), (long) getuid());

    sig = atoi(argv[2]);
    sigData = atoi(argv[3]);
    numSigs = (argc > 4) ? atoi(argv[4]) : 1;

    for(j = 0; j < numSigs; j++){
        sv.sival_int = sigData + j;
        if (sigqueue(atoi(argv[1]), sig, sv) == -1){
            printf("sigqueue %d", j);
            exit(EXIT_FAILURE);
        }
    }

    exit(EXIT_SUCCESS);

}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值