epoll等待修改文件描述符集

问题描述 投票:0回答:1

我目前正在用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 的真正错误。

我希望您能够解决这个恼人的问题。预先感谢。

c++ server epoll
1个回答
0
投票

所示代码对

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_wait
向您抛出事件来自 epoll 集中所有文件描述符一次一个事件

epoll_wait
的返回值告诉您传递给epoll_wait的epoll数组参数中有多少个事件
。显示的代码预计总是有两个事件,
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";
        }

这是错误的。

epoll_wait
可能会返回 1。这意味着
fds[0]
包含一个事件。就是这样。
fds[0].data.fd
告诉您哪个文件描述符生成了事件,它可以是
pipefd[0]
sock

如果

epoll_wait
返回2,则表示
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) {

epoll_wait

的返回值告诉您有多少个事件。

if (events[n].data.fd == listen_sock) {
然后检查每个事件的 

data.fd

,它会告诉您 
该事件针对哪个 文件描述符,然后进行相应操作。

© www.soinside.com 2019 - 2024. All rights reserved.