epoll:指定EPOLLONESHOT时电平触发和边沿触发的区别
epoll: difference between level triggered and edge triggered when EPOLLONESHOT specified
当指定 EPOLLONESHOT 时,电平触发和边沿触发模式有什么区别?
已经有一个类似的问题 here。 "Crouching Kitten" 的答案似乎不正确(据我了解,另一个答案没有回答我的问题)。
我试过以下方法:
- 服务器向客户端发送 2 个字节,而客户端在
epoll_wait
中等待
- 来自
epoll_wait
的客户端 returns,然后读取 1 个字节。
- 客户端重新准备事件(因为 EPOLLONESHOT)
- 客户再次调用
epoll_wait
。在这里,对于这两种情况(LT 和 ET),epoll_wait
不等待,而是立即 returns(与 "Crouching Kitten" 的答案相反)
- 客户端可以读取第二个字节
当指定 EPOLLONESHOT 时,LT 和 ET 之间有什么区别吗?
我认为底线答案是 "there is not difference"。
查看the code,fd 似乎记得在被一次性禁用之前最后设置的位。它记得是一枪,它记得是不是外星人。
这是徒劳的,因为 fd 被禁用直到被修改,下一次调用 EPOLL_CTL_MOD
将清除所有这些,并替换为新的 MOD 所说的内容。
话虽如此,我不明白为什么有人会同时想要 EPOLLET
和 EPOLLONESHOT
。对我来说,EPOLLET
的全部意义在于,在某些编程模型(即微线程)下,它完美地遵循语义。这意味着我可以在一开始就将 fd 添加到 epoll,然后再也不必执行另一个 epoll 相关的系统调用。
另一方面,EPOLLONESHOT
被那些想要严格控制何时观看 fd 和何时不观看的人使用。根据定义,这与 EPOLLET
的用途相反。我只是认为这两者在概念上不兼容。
另一位发帖人说“我不明白为什么有人会同时想要 EPOLLET
和 EPOLLONESHOT
。”实际上,根据 epoll(7),有一个用例:
Since even with edge-triggered epoll
, multiple events can be generated upon receipt of multiple chunks of data, the caller has the option to specify the EPOLLONESHOT
flag, to tell epoll
to disable the associated file descriptor after the receipt of an event with epoll_wait(2)
.
关键是EPOLL会不会对待EPOLLET的组合| EPOLLONESHOT 和 EPOLLLT | EPOLLONESHOT 作为特例。据我所知,事实并非如此。 EPOLL 只是分别关心它们。对于EPOLLET和EPOLLLT,不同之处仅在于函数ep_send_events
,如果设置了EPOLLET,则函数会调用list_add_tail
将epitem
添加到[=15]的就绪列表中=] 对象。
给EPOLLONESHOT
,作用是禁用fd
。所以我认为他们之间的区别就是ET
和LT
之间的区别。你可以使用下面的代码检查结果我认为
// 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, '[=10=]', 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)
当指定 EPOLLONESHOT 时,电平触发和边沿触发模式有什么区别?
已经有一个类似的问题 here。 "Crouching Kitten" 的答案似乎不正确(据我了解,另一个答案没有回答我的问题)。
我试过以下方法:
- 服务器向客户端发送 2 个字节,而客户端在
epoll_wait
中等待
- 来自
epoll_wait
的客户端 returns,然后读取 1 个字节。 - 客户端重新准备事件(因为 EPOLLONESHOT)
- 客户再次调用
epoll_wait
。在这里,对于这两种情况(LT 和 ET),epoll_wait
不等待,而是立即 returns(与 "Crouching Kitten" 的答案相反) - 客户端可以读取第二个字节
当指定 EPOLLONESHOT 时,LT 和 ET 之间有什么区别吗?
我认为底线答案是 "there is not difference"。
查看the code,fd 似乎记得在被一次性禁用之前最后设置的位。它记得是一枪,它记得是不是外星人。
这是徒劳的,因为 fd 被禁用直到被修改,下一次调用 EPOLL_CTL_MOD
将清除所有这些,并替换为新的 MOD 所说的内容。
话虽如此,我不明白为什么有人会同时想要 EPOLLET
和 EPOLLONESHOT
。对我来说,EPOLLET
的全部意义在于,在某些编程模型(即微线程)下,它完美地遵循语义。这意味着我可以在一开始就将 fd 添加到 epoll,然后再也不必执行另一个 epoll 相关的系统调用。
EPOLLONESHOT
被那些想要严格控制何时观看 fd 和何时不观看的人使用。根据定义,这与 EPOLLET
的用途相反。我只是认为这两者在概念上不兼容。
另一位发帖人说“我不明白为什么有人会同时想要 EPOLLET
和 EPOLLONESHOT
。”实际上,根据 epoll(7),有一个用例:
Since even with edge-triggered
epoll
, multiple events can be generated upon receipt of multiple chunks of data, the caller has the option to specify theEPOLLONESHOT
flag, to tellepoll
to disable the associated file descriptor after the receipt of an event withepoll_wait(2)
.
关键是EPOLL会不会对待EPOLLET的组合| EPOLLONESHOT 和 EPOLLLT | EPOLLONESHOT 作为特例。据我所知,事实并非如此。 EPOLL 只是分别关心它们。对于EPOLLET和EPOLLLT,不同之处仅在于函数ep_send_events
,如果设置了EPOLLET,则函数会调用list_add_tail
将epitem
添加到[=15]的就绪列表中=] 对象。
给EPOLLONESHOT
,作用是禁用fd
。所以我认为他们之间的区别就是ET
和LT
之间的区别。你可以使用下面的代码检查结果我认为
// 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, '[=10=]', 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)