【问题标题】:epoll: difference between level triggered and edge triggered when EPOLLONESHOT specifiedepoll:指定 EPOLLONESHOT 时电平触发和边缘触发之间的差异
【发布时间】:2017-10-07 14:18:48
【问题描述】:

指定EPOLLONESHOT时,电平触发和边沿触发模式有什么区别?

here 已经有一个类似的问题。 “蹲伏小猫”的答案似乎不正确(据我了解,另一个答案没有回答我的问题)。

我尝试了以下方法:

  • 服务器向客户端发送2个字节,而客户端在epoll_wait中等待
  • 客户端从epoll_wait返回,然后读取1个字节。
  • 客户端重新武装事件(因为 EPOLLONESHOT)
  • 客户端再次调用epoll_wait。在这里,对于这两种情况(LT 和 ET),epoll_wait 不会等待,而是立即返回(与“蹲伏小猫”的答案相反)
  • 客户端可以读取第二个字节

当指定 EPOLLONESHOT 时,LT 和 ET 之间有什么区别吗?

【问题讨论】:

  • 如果你能把你的代码贴在上面就好了。我相信你做了你声称你做过的事,但这会让下一个出现的人的问题变得更好。
  • @ShacharShemesh:不幸的是,这段代码是更大框架的一部分。但是,我刚刚检查了内核源代码,据我了解,如果指定了 EPOLLONESHOT,LT 和 ET 之间确实没有区别。我会等一段时间,如果没有答案,我会回答我自己的问题。
  • 当你在吃饭的时候打断写答案时会发生这种情况。在你不在的时候人们会找到自己的答案:-)

标签: linux network-programming epoll


【解决方案1】:

我认为最重要的答案是“没有区别”。

查看the code,似乎 fd 在被一次性禁用之前记住了最后设置的位。它记得这是一枪,它记得它是否是外星人。

这是徒劳的,因为 fd 在修改之前被禁用,并且下次调用 EPOLL_CTL_MOD 将删除所有这些,并替换为新 MOD 所说的任何内容。

话虽如此,我不明白为什么有人会同时想要EPOLLETEPOLLONESHOT。对我来说,EPOLLET 的全部意义在于,在某些编程模型(即微线程)下,它完全遵循语义。这意味着我可以一开始就将 fd 添加到 epoll 中,然后再也不必执行另一个与 epoll 相关的系统调用。

另一方面,EPOLLONESHOT 被那些想要严格控制何时观看 fd 以及何时不观看的人使用。根据定义,这与 EPOLLET 的用途相反。我只是不认为这两者在概念上是兼容的。

【讨论】:

  • 这是什么意思? “这意味着我可以在一开始就将 fd 添加到 epoll 中,然后永远不必执行另一个与 epoll 相关的系统调用”我认为 epoll_wait 仍然会被调用,不是吗?
  • 另外,还有另一种组合,似乎没有意义:ET+EXCLUSIVE。您对此有何看法?
  • 没有太多经验。按照我的阅读方式,EPOLLET 对微线程很有用,而独占和 oneshot 对多线程很有用。因此,我认为在您想要使用它们时不会存在任何固有的摩擦。
  • 至于你的第一个问题,我觉得评论太长了。我知道我是(一个以 W 开头的词,所以不会让我写,因为它认为我在针对你)这里的业力点,但如果你问“什么时候使用 EPOLLET 有意义” ,我保证会尽力而为:-)(提示)
  • @geza 请参阅stackoverflow.com/questions/46633433/… 以回答您的问题。
【解决方案2】:

另一位发帖人说:“我不明白为什么有人会同时想要EPOLLETEPOLLONESHOT。”实际上,根据epoll(7),有一个用例:

因为即使是边缘触发的epoll,在接收到多个数据块时也可以生成多个事件,调用者可以选择指定EPOLLONESHOT 标志,告诉epoll 禁用关联的文件描述符在收到epoll_wait(2) 的事件后。

【讨论】:

    【解决方案3】:

    关键是EPOLL是否会对待EPOLLET | 的组合EPOLLONESHOT 和 EPOLLLT | EPOLLONESHOT 作为特例。据我所知,事实并非如此。 EPOLL 只是单独关心它们。对于 EPOLLET 和 EPOLLLT,不同之处仅在于函数ep_send_events,如果设置了 EPOLLET,则该函数将调用list_add_tailepitem 添加到epoll_fd/eventepoll 对象的就绪列表中。 对于EPOLLONESHOT,作用是禁用fd。所以我认为他们之间的不同是ETLT之间的不同。您可以使用我认为的以下代码检查结果

    // server.cc
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <assert.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <sys/epoll.h>
    #include <pthread.h>
    
    #define MAX_EVENT_NUMBER 1024
    
    int setnonblocking(int fd)
    {
        int old_option = fcntl(fd, F_GETFL);
        int new_option = old_option | O_NONBLOCK;
        fcntl(fd, F_SETFL, new_option);
        return old_option;
    }
    
    void addfd(int epollfd, int fd, bool oneshot)
    {
        epoll_event event;
        event.data.fd = fd;
        event.events = EPOLLIN | EPOLLET;
        if(oneshot)
            event.events |= EPOLLONESHOT;
        epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
        setnonblocking(fd);
    }
    // reset the fd with EPOLLONESHOT
    void reset_oneshot(int epollfd, int fd)
    {
        epoll_event event;
        event.data.fd = fd;
        event.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
        epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
    }
    
    int main(int argc, char** argv)
    {
        if(argc <= 2)
        {
            printf("usage: %s ip_address port_number\n", basename(argv[0]));
            return 1;
        }
        const char* ip = argv[1];
        int port = atoi(argv[2]);
    
        int ret = 0;
        struct sockaddr_in address;
        bzero(&address, sizeof(address));
        address.sin_family = AF_INET;
        inet_pton(AF_INET, ip, &address.sin_addr);
        address.sin_port = htons(port);
    
        int listenfd = socket(PF_INET, SOCK_STREAM, 0);
        assert(listenfd >= 0);
        ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
        assert(ret != -1);
    
        ret = listen(listenfd, 5);
        assert(ret != -1);
    
        epoll_event events[MAX_EVENT_NUMBER];
        int epollfd = epoll_create(5);
        addfd(epollfd, listenfd, false);
        while(1)
        {
            printf("next loop: -----------------------------");
            int ret = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
            if(ret < 0)
            {
                printf("epoll failure\n");
                break;
            }
            for(int i = 0; i < ret; i++)
            {
                int sockfd = events[i].data.fd;
                if(sockfd == listenfd)
                {
                    printf("into listenfd part\n");
                    struct sockaddr_in client_address;
                    socklen_t client_addrlength = sizeof(client_address);
                    int connfd = accept(listenfd, (struct sockaddr*)&client_address,
                         &client_addrlength);
                    printf("receive connfd: %d\n", connfd);
                    addfd(epollfd, connfd, true);
                    // reset_oneshot(epollfd, listenfd);
                }
                else if(events[i].events & EPOLLIN)
                {
                    printf("into linkedfd part\n");
                    printf("start new thread to receive data on fd: %d\n", sockfd);
                    char buf[2];
                    memset(buf, '\0', 2);
                    // just read one byte, and reset the fd with EPOLLONESHOT, check whether still EPOLLIN event
                    int ret = recv(sockfd, buf, 2 - 1, 0);
    
                    if(ret == 0)
                    {
                        close(sockfd);
                        printf("foreigner closed the connection\n");
                        break;
                    }
                    else if(ret < 0)
                    {
                        if(errno == EAGAIN)
                        {
                            printf("wait to the client send the new data, check the oneshot memchnism\n");
                            sleep(10);
                            reset_oneshot(epollfd, sockfd);
                            printf("read later\n");
                            break;
                        }
                    }
                    else {
                        printf("receive the content: %s\n", buf);
                        reset_oneshot(epollfd, sockfd);
                        printf("reset the oneshot successfully\n");
                    }
                }
                else 
                    printf("something unknown happend\n");
            }
            sleep(1);
        }
        close(listenfd);
        return 0;
    }
    

    客户是

    from socket import *
    import sys
    import time
    
    long_string = b"this is a long content which need two time to fetch"
    
    def sendOneTimeThenSleepAndClose(ip, port):
        s = socket(AF_INET, SOCK_STREAM);
        a = s.connect((ip, int(port)));
        print("connect success: {}".format(a));
        data = s.send(b"this is test");
        print("send successfuly");
        time.sleep(50);
        s.close();
    sendOneTimeThenSleepAndClose('127.0.0.1', 9999)
    

    【讨论】:

      猜你喜欢
      • 2016-07-08
      • 2014-10-14
      • 2013-01-25
      • 1970-01-01
      • 1970-01-01
      • 2016-10-03
      • 2012-02-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多