C epoll事件不会引发SIGIO

C epoll事件不会引发SIGIO,c,linux,signals,epoll,epollet,C,Linux,Signals,Epoll,Epollet,根据我的研究,您可以向poll、select或其他epoll添加一个epoll文件描述符,如果事件可用,它将返回POLLIN。根据epoll(7): 这在我的测试中起作用。但是,我现在正在尝试将O_ASYNC应用于我的epoll fd,以便在事件准备就绪时它将引发SIGIO: //Epoll setup code - trySc runs perror on the string and exits if the function returns -1 int epollFd = epoll_c

根据我的研究,您可以向poll、select或其他epoll添加一个epoll文件描述符,如果事件可用,它将返回POLLIN。根据epoll(7):

这在我的测试中起作用。但是,我现在正在尝试将O_ASYNC应用于我的epoll fd,以便在事件准备就绪时它将引发SIGIO:

//Epoll setup code - trySc runs perror on the string and exits if the function returns -1
int epollFd = epoll_create1(EPOLL_CLOEXEC);
trySc(epollFd, "epoll_create1");
trySc(fcntl(epollFd, F_SETOWN, getpid()), "fcntl F_SETOWN");
trySc(fcntl(epollFd, F_SETSIG, SIGIO), "fcntl F_SETSIG");
int oldFlags = fcntl(epollFd, F_GETFL, 0);
trySc(oldFlags, "fcntl F_GETFL");
trySc(fcntl(epollFd, F_SETFL, oldFlags | O_ASYNC), "fcntl F_SETFL");

//Set up SIGIO and get a socket fd, you don't need to see this
//All my handler does is exit - it's a definite method of knowing SIGIO was raised

struct epoll_event event = {
        .events = EPOLLIN | EPOLLET,
        .data.fd = socketFd
};
trySc(epoll_ctl(epollFd, EPOLL_CTL_ADD, socketFd, &event), "epoll_ctl");
//Then connect
while(1){
        struct pollfd pfd = {
                .fd = epollFd,
                .events = POLLIN
        };
        //This blocks until I write something to the socket. Then it enters an infinite loop.
        printf("Returning to main(), poll = %d\n", poll(&pfd, 1, -1));
}
当我这样做时,它不会为epoll中的新事件引发SIGIO。Poll表明epollFd中有准备就绪的事件,但它应该首先引发SIGIO(它只是检查事件是否在epollFd中并退出)。我知道我可以将O_ASYNC应用于套接字(我也尝试过),但我希望在事件中包含数据。埃波尔让我这么做

这是我的全部代码:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <netdb.h>
#include <poll.h>
#include <signal.h>
#include <errno.h>

void handleSIGIO(int, siginfo_t *, void *);

const struct sigaction saSIGIO = {
    .sa_sigaction = handleSIGIO,
    .sa_flags = SA_SIGINFO
};

const struct addrinfo hints = {
    .ai_flags = AI_PASSIVE,
    .ai_family = AF_INET,
    .ai_socktype = SOCK_STREAM
};

void trySc(int err, const char *msg){
    if(err != -1) return;
    perror(msg);
    exit(errno);
}

int main(){
    signal(SIGPIPE, SIG_IGN);
    trySc(sigaction(SIGIO, &saSIGIO, NULL), "sigaction");
    int epollFd = epoll_create1(EPOLL_CLOEXEC);
    trySc(epollFd, "epoll_create1");
    trySc(fcntl(epollFd, F_SETOWN, getpid()), "fcntl F_SETOWN");
    trySc(fcntl(epollFd, F_SETSIG, SIGIO), "fcntl F_SETSIG");
    int oldFlags = fcntl(epollFd, F_GETFL, 0);
    trySc(oldFlags, "fcntl F_GETFL");
    trySc(fcntl(epollFd, F_SETFL, oldFlags | O_ASYNC), "fcntl F_SETFL");
    int socketFd = socket(AF_INET, SOCK_STREAM, 0);
    trySc(socketFd, "socket");
    struct addrinfo *servinfo;
    trySc(getaddrinfo("127.0.0.1", "5000", &hints, &servinfo),
        "getaddrinfo");
    struct epoll_event event = {
        .events = EPOLLIN | EPOLLET,
        .data.fd = socketFd
    };
    trySc(epoll_ctl(epollFd, EPOLL_CTL_ADD, socketFd, &event), "epoll_ctl");
    trySc(connect(socketFd, servinfo->ai_addr, servinfo->ai_addrlen),
        "connect");
    printf("Connected\n");
    while(1){
        struct pollfd pfd = {
            .fd = epollFd,
            .events = POLLIN
        };
        printf("Returning to main(), poll = %d\n", poll(&pfd, 1, -1));
    }
}

void handleSIGIO(int sn, siginfo_t *info, void *ctx){
    printf("SIGIO called\n");
    struct epoll_event event;
    if(epoll_wait(info->si_fd, &event, 1, 0) != 1){
        printf("Warning: no event available\n");
        return;
    }
    printf("Event raised for fd %d\n", event.data.fd);
    exit(0);
}

在做了更多的研究之后,我发现这是不可能的。根据
open(2)

异步的

启用信号驱动I/O:在输入或输出时生成信号(默认情况下为SIGIO,但可通过fcntl(2)进行更改) 在该文件描述符上成为可能此功能不可用 仅适用于端子、假端子、插座和 (自Linux 2.6版起)管道和FIFO。有关详细信息,请参阅fcntl(2) 细节。另请参见下面的bug

我真希望我能用epoll和SIGIO。我会找到其他我能做的事

我找到了这个问题的解决方案:对每个套接字应用
O_ASYNC
,并在您的SIGIO处理程序中,在您的epoll fd上调用
epoll_wait
。然后像处理普通事件一样处理来自epoll的事件。它不能与不支持SIGIO的FD一起工作,即使它们支持epoll,但它可以与套接字和管道一起工作,这可能是异步IO最常见的用法

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <netdb.h>
#include <poll.h>
#include <signal.h>
#include <errno.h>

void handleSIGIO(int, siginfo_t *, void *);

const struct sigaction saSIGIO = {
    .sa_sigaction = handleSIGIO,
    .sa_flags = SA_SIGINFO
};

const struct addrinfo hints = {
    .ai_flags = AI_PASSIVE,
    .ai_family = AF_INET,
    .ai_socktype = SOCK_STREAM
};

void trySc(int err, const char *msg){
    if(err != -1) return;
    perror(msg);
    exit(errno);
}

int main(){
    signal(SIGPIPE, SIG_IGN);
    trySc(sigaction(SIGIO, &saSIGIO, NULL), "sigaction");
    int epollFd = epoll_create1(EPOLL_CLOEXEC);
    trySc(epollFd, "epoll_create1");
    trySc(fcntl(epollFd, F_SETOWN, getpid()), "fcntl F_SETOWN");
    trySc(fcntl(epollFd, F_SETSIG, SIGIO), "fcntl F_SETSIG");
    int oldFlags = fcntl(epollFd, F_GETFL, 0);
    trySc(oldFlags, "fcntl F_GETFL");
    trySc(fcntl(epollFd, F_SETFL, oldFlags | O_ASYNC), "fcntl F_SETFL");
    int socketFd = socket(AF_INET, SOCK_STREAM, 0);
    trySc(socketFd, "socket");
    struct addrinfo *servinfo;
    trySc(getaddrinfo("127.0.0.1", "5000", &hints, &servinfo),
        "getaddrinfo");
    struct epoll_event event = {
        .events = EPOLLIN | EPOLLET,
        .data.fd = socketFd
    };
    trySc(epoll_ctl(epollFd, EPOLL_CTL_ADD, socketFd, &event), "epoll_ctl");
    trySc(connect(socketFd, servinfo->ai_addr, servinfo->ai_addrlen),
        "connect");
    printf("Connected\n");
    while(1){
        struct pollfd pfd = {
            .fd = epollFd,
            .events = POLLIN
        };
        printf("Returning to main(), poll = %d\n", poll(&pfd, 1, -1));
    }
}

void handleSIGIO(int sn, siginfo_t *info, void *ctx){
    printf("SIGIO called\n");
    struct epoll_event event;
    if(epoll_wait(info->si_fd, &event, 1, 0) != 1){
        printf("Warning: no event available\n");
        return;
    }
    printf("Event raised for fd %d\n", event.data.fd);
    exit(0);
}
Note that an epoll set descriptor can be used much like a regular file 
descriptor. That is, it can be made to generate SIGIO (or another signal)
when input (i.e. events) is available on it; likewise it can be used with 
poll() and can even be stored inside another epoll set.