c - epoll 事件不会引发 SIGIO

标签 c linux signals epoll epollet

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

   Q3  Is the epoll file descriptor itself poll/epoll/selectable?

   A3  Yes.  If an epoll file descriptor has events waiting, then it  will
       indicate as being readable.

这在我的测试中有效。但是,我现在尝试将 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 应用于套接字(我也尝试过这样做),但我希望在我的事件中包含数据。 Epoll 让我可以做到这一点。

这是我的完整代码:

#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);
}

编辑:根据this website ,我想做的应该有效:

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.

最佳答案

经过更多研究后,我发现这是不可能的。根据open(2)

O_ASYNC

Enable signal-driven I/O: generate a signal (SIGIO by default, but this can be changed via fcntl(2)) when input or output becomes possible on this file descriptor. This feature is available only for terminals, pseudoterminals, sockets, and (since Linux 2.6) pipes and FIFOs. See fcntl(2) for further details. See also BUGS, below.

我真的希望能够将 epoll 与 SIGIO 一起使用。我会找到一些我能做的事情。

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

关于c - epoll 事件不会引发 SIGIO,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47764803/

相关文章:

c - 为什么我的 <signal.h> 将 __sig_atomic_t 定义为 int 而不是 volatile int

c - 删除二维数组中的重复项

c - 使用 GCC 编译简单的 C 程序时出现 clang 错误

c - 如何使用作为结构成员的函数指针

linux - 如何使用 SVN 重置存储库

linux - 更改ssh端口

c - 如何使用 CreateThread() 创建多个线程,每个线程具有不同的 ThreadProc() 函数

linux - 内核中的malloc

c - GCC - 使用 POSIX 信号时优化无效

c - 如何从 C 中的信号处理程序获取返回值?