理解epoll中Level-triggered 和 edge-triggered
June 4, 2019
0x00 名词解释
Level-triggered: (electronics) Describing a circuit or component whose output is sensitive to changes of the inputs only so long as the clock input’s signal is high.
- 当处于高电平状态时持续触发,在epoll中表示
- 读取时:如果缓存中存在未读数据则 持续 触发
- 写入时:fd处于可写状态则持续触发
Edge-triggered: (electronics) Describing a circuit or component which is either positive-edge-triggered or negative-edge-triggered.
- 当信号有变化时触发,在epoll中表示
- 读取时:有新数据到来时触发,(如果缓存中存在上次没读完的数据, 但是也没有新数据到来也不会触发)
- 写入时:状态从不可写入变为可写时触发
0x01 举个栗子
Edge-triggered:就像是一把98k(非自动步枪),扣下扳机后只会发射一发子弹, 即使你一直扣着扳机也没用 ,只会发射一发子弹
Level-triggered:就像是一把M416(自动步枪),只要弹匣里面有足够的子弹, 扣下后就会一直发射
0x02 如何申明Edge-triggered
默认epoll增加和修改socket的是处于
level-triggered模式
,如果要设置为edge-triggered
模式则需要显式
设置需要注意:在首次增加和每次修改套接字的events时,都需要指明EPOLLET,并不是ADD一次后就会永久是EPOLLET模式
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLET; /* ET模式 */
ev.data.fd = sockfd;
if (-1 == epoll_ctl(efd, EPOLL_CTL_ADD, sockfd, &ev)) {
perror("epoll_ctl EPOLL_CTL_ADD fail");
0x03 skynet使用的是什么方式
摘自skynet-src/socket_epoll.h
static int
sp_add(int efd, int sock, void *ud) {
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.ptr = ud;
if (epoll_ctl(efd, EPOLL_CTL_ADD, sock, &ev) == -1) {
return 1;
}
return 0;
}
ev.events = EPOLLIN;
可见skynet使用的是level-triggered方式
0x04 redis使用的是什么方式
摘自src/ae_epoll.c
static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
aeApiState *state = eventLoop->apidata;
struct epoll_event ee = {0}; /* avoid valgrind warning */
/* If the fd was already monitored for some event, we need a MOD
* operation. Otherwise we need an ADD operation. */
int op = eventLoop->events[fd].mask == AE_NONE ?
EPOLL_CTL_ADD : EPOLL_CTL_MOD;
ee.events = 0;
mask |= eventLoop->events[fd].mask; /* Merge old events */
if (mask & AE_READABLE) ee.events |= EPOLLIN;
if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
ee.data.fd = fd;
if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;
return 0;
}
redis使用的也是level-trigger方式。关于为什么不用ET模式,开发者在github的issue_615中给出了回复:
mnunberg commented on Oct 2, 2018
Thank you, let me see if my connfix branch addresses this.. I recall seeing something like this before.
However, we would need to be aware of whether we are in ET mode or not. Otherwise we risk starving other I/O (and CPU time) if there is a lot of data on the socket.