理解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.

Edge-triggered: (electronics) Describing a circuit or component which is either positive-edge-triggered or negative-edge-triggered.

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.

参考

comments powered by Disqus