提问人:Akese315 提问时间:10/1/2023 更新时间:10/1/2023 访问量:47
Epoll wait 修改文件描述符集
Epoll wait modifies filedescriptor set
问:
我目前正在用 c++ 构建一个服务器,所以当我没有任何请求时,我用epoll_wait来阻止。它运行良好,直到我决定在epollfd_set上添加一根管道。我不知道epoll_wait是否可以管理管道,但由于它是一个文件描述符,我认为它可以。于是我照做了,一件奇怪的事情发生了。当 epoll 停止阻塞时,我检查了epollfd_set,而不是像那样使用 fd 数组: [] = {1,2}, 我得到 [] = {2,2},第一个套接字消失了,已被 2 取代。我知道 epoll 应该只更改事件而不是 fd。你能告诉我发生了什么吗?
我在一个简单的主文件中重现了这个错误。
#include <iostream>
#include <sys/epoll.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
bool loop;
struct epoll_event fds[255];
int main()
{
int pipefd[2];
//create a pipe, pipefd[0] is the read fd and pipefd[1] is the write one.
if(pipe(pipefd) == -1)
{
std::cout<<"erreur"<<std::endl;
}
int epollfd = epoll_create1(0);
if (epollfd == -1) {
std::cout<<"erreur"<<std::endl;
}
if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) == -1) {
std::cout<<"erreur"<<std::endl;
}
//adding it to the fds set
fds[0].events = EPOLLIN| EPOLLET;
fds[0].data.fd = pipefd[0];
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, pipefd[0], &fds[0]) == -1) {
std::cout <<"erreur"<<std::endl;
}
int on = 1;
//Bind socket to adress and add some parameters
sockaddr_in bindParams;
bindParams.sin_family =AF_INET;
bindParams.sin_port = htons(2000);
int sock = socket(AF_INET,SOCK_STREAM,0);
inet_aton("127.0.0.1",&bindParams.sin_addr);
bind(sock,(struct sockaddr *)&bindParams,(socklen_t)sizeof(bindParams));
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,(char *)&on,(socklen_t)sizeof(on));
fcntl(sock, F_SETFL, O_NONBLOCK);
//add it to the fds set
fds[1].events = EPOLLIN | EPOLLET;
fds[1].data.fd = sock;
if(epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &fds[1]) == -1)
{
std::cout<<"erreur"<<std::endl;
}
listen(fds[1].data.fd, 5);
std::cout << "this is the first fd : " <<fds[0].data.fd<<std::endl;
std::cout << "this is the second fd : " <<fds[1].data.fd<<std::endl;
std::cout <<"before epollwait"<<std::endl;
do {
int fdr = epoll_wait(epollfd, fds, 255, -1);
std::cout <<"after epollwait"<<std::endl;
std::cout << "this is the first fd : " <<fds[0].data.fd<<std::endl;
std::cout << "this is the second fd : " <<fds[1].data.fd<<std::endl;
if(fds[0].events == EPOLLIN)
{
std::cout << "pipe called\n";
}
if(fds[1].events == EPOLLIN)
{
std::cout << "listener called\n";
}
}while(true);
return 0;
};
此外,要退出epoll_wait您必须使用像 netcat 这样的客户端在端口 2000 上发送某些内容。我希望这只是我的理解,这很糟糕,而不是真正的epoll_wait错误。
我希望你能够解决这个烦人的问题。提前致谢。
答:
显示的代码对事件的用法似乎是基于一种误解,即事件的功能类似于管理其文件描述符数组的方式。这是不正确的。epoll
epoll
poll()
所示代码仔细地初始化 ,并将其馈送到 、 初始化 ,然后馈送到 。之后,显示的代码调用 ,并假定将具有第一个文件描述符的事件,并且将具有任何其他第二个文件描述符的事件。fds[0]
EPOLL_CTL_ADD
fds[1]
EPOLL_CTL_ADD
epoll_wait
fds[0]
fds[1]
这不是工作方式。对于初学者来说,可用于设置被 使用的两个文件描述符。 狼吞虎咽地吃掉你喂它的每一口,然后它被储存起来,以便安全地保存在内核中。epoll
fds[0]
EPOLL_CTL_ADD
EPOLL_CTL_ADD
然后,从 epoll 集中的所有文件描述符中抛出事件,一次一个事件。epoll_wait
返回值 from 告诉您传递给 的 epoll 数组参数中有多少个事件。显示的代码期望始终有两个事件,将是 的事件,将是 的事件,将是 的事件:epoll_wait
epoll_wait
fds[0]
pipefd[0]
fds[1]
sock
if(fds[0].events == EPOLLIN)
{
std::cout << "pipe called\n";
}
if(fds[1].events == EPOLLIN)
{
std::cout << "listener called\n";
}
这是错误的。 可能会返回 1。这意味着它包含一个事件。就是这样。 告诉您哪个文件描述符生成了事件,它可以是 EITHER 或 。epoll_wait
fds[0]
fds[0].data.fd
pipefd[0]
sock
如果返回 2 表示 和 都有事件,并且任何文件描述符都可以出现在 和 中。epoll_wait
fds[0]
fds[1]
fds[0].data.fd
fds[1].data.fd
此时, 你被引导去检查 epoll(7) 手册页中的 “建议用法示例”。这个例子让你大饱眼福,它用代码向你解释了如何正确地做到这一点:
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
您必须从 中捕获返回值。不要忽视它。把它放在你面前,怀着崇敬和尊重的心情握住它。因为您需要使用它:epoll_wait
for (n = 0; n < nfds; ++n) {
返回值 from 告诉您有多少个事件。epoll_wait
if (events[n].data.fd == listen_sock) {
然后检查每个事件的 ,它告诉您该事件是针对哪个文件描述符的,然后相应地继续。data.fd
评论
:)
评论
epoll_wait