根据我的研究,您可以添加一个epoll文件描述符来轮询,选择或另一个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。轮询表明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.
答案 0 :(得分:1)
经过更多的研究,我发现它是不可能的。根据{{1}}
O_ASYNC
启用信号驱动I / O:输入或输出时生成一个信号(默认为SIGIO,但可以通过fcntl(2)更改) 在此文件描述符上成为可能。 此功能是 仅适用于终端,伪终端,插座和 (自Linux 2.6起)管道和FIFO。 有关详细信息,请参阅fcntl(2) 细节。另见下面的BUGS。
我真的希望我可以将epoll用于SIGIO。我会找到我能做的其他事情。
我找到了解决此问题的方法:将open(2)
应用于每个套接字,并在SIGIO处理程序中,在epoll fd上调用O_ASYNC
。然后像普通一样处理来自epoll的事件。它不适用于不支持SIGIO的FD,即使它们支持epoll,但它适用于套接字和管道,这可能是最常见的异步IO使用方式。