[kernel] epoll在ET模式下“带出EPOLLOUT”的疑问

几个月前朱照远同学向我提过一个epoll的疑似bug:

1. 创建一个socket(sfd)并connect到某个server
2. 创建 epoll_create(efd)
3. 将socket的描述符sfd加入efd,  采用ET模式
4. 调用epoll_wait将返回一个EPOLLOUT事件(因为连接成功了)

以上是正常的,但是,此时如果从server来了一个消息, epoll_wait将会返回一个event,这个event包含了EPOLLIN和EPOLLOUT
“照理说”,既然采用了ET模式,EPOLLOUT上次已经出现了,不应该再出现,但是它被EPOLLIN事件给“带出来”了

这看上去似乎像个问题,但似乎也可以理解为epoll的一个特性,所以,保险起见,我发了个邮件给 linux-api 和 linux-netdev 说明了一下这个事儿,希望能得到权威回答。
不久就有两位回邮件了,其中一位是 Eric Dumazet(从git log里看,他提交了很多kernel network方面的patch,应该是很有发言权的),说得很明白:

"Its not true. Same "status" can be delivered several time.

Think about Edge and Level trigger. An event (change of status) is the
trigger.

As soon as on trigger is done, epoll delivers a status.

And your file status is indeed EPOLLOUT | EPOLLIN, since you can read or
write on it.
....
Not a bug, but a misinterpretation of what is an event and what is a
status.​"


看来正确的理解是:ET模式只保证在边缘条件出现时(上面的例子是从不可读变为可读)从epoll_wait里返回,但不保证fd的event里只有“从不可读变为可读”带来的EPOLLIN。毕竟epoll_wait返回的event是指fd的“状态”,既然这个fd可写可读,那么包含EPOLLIN和EPOLLOUT就不能被认为是有错。


相关文章

分类

7 Comments

hero said:

牛啊,我都看不懂了。什么时候给我上上课,让我面试的时候多个题目。

DongHao Author Profile Page said:

不是牛,是我没写清楚写绕了,所以你没看懂......面试问基础的就好了,这个有点坑人了 :)

汉字林 said:

写的还可以了,懂得人能看懂。嘻嘻

飞得更高 said:

ev.events = EPOLLIN | EPOLLET | EPOLLOUT;
ev.data.fd = connfd;
if (-1 == epoll_ctl(kpfd, EPOLL_CTL_ADD, connfd, &ev)){
fprintf(stderr, "epoll_ctl error:%s\n", strerror(errno));
return -1;
}

我是这样把connfd(connfd1,connfd2)加入epoll监听事件集合的。

1)server端建立了两个连接(connfd1, connfd2);

2)epoll_wait后,返回:二者可读的事件;

3)client1(对应connfd1)发送消息,epoll_wait返回connfd1可读,可写事件,(这在您的博客中提到了,我也看明白了,谢谢!);

4)在读connfd1的时候额外做了下面的工作:

ev.events = EPOLLIN | EPOLLET | EPOLLOUT;
ev.data.fd = connfd2;
if (-1 == epoll_ctl(kpfd, EPOLL_CTL_MOD, connfd, &ev)){
fprintf(stderr, "epoll_ctl error:%s\n", strerror(errno));
return -1;
}

5)再次epoll_wait,返回connfd2可写。

这是怎么回事儿呢?对于connfd2,status没有发生变化,event也没有发生变化啊(没有可读事件到来,可写已经返回过了)。

我自己想的答案是:EPOLL_CTL_MOD动作也是改变了connfd2的event,因为:man手册里讲到:

EPOLL_CTL_MOD
Change the event event associated with the target file descriptor fd.

我对自己的答案不是很有信心,

第一:EPOLL_CTL_MOD没有改变connfd2的event,只是做了一下重新设置监听事件而已。

第二:重新设置的监听事件跟首次设置的一样,没有任何不同。

还有一个问题不明白:epoll_ctl到底做了什么工作?是不是仅仅更改了监听事件集合,而没有其他任何影响?对于“更改监听事件集合”这个说法,是我自己想出来的,不知道说的准确不,也不知道正确的说法是什么?如果,您知道的话,麻烦恳请您告诉我。

好了,就这些。O(∩_∩)O谢谢

DongHao Author Profile Page said:

epoll_ctl的MOD不仅仅是“更改了监听时间集合”,还“热心的”重新检测了connfd2(如果按您的场景),如果connfd2的status(这个是sock的status)包含了MOD后的事件,就把connfd2放入rdllist(这个rdllist我以前的文章写过)。
代码在 fs/eventpoll.c 的 ep_modify 函数里:

revents = epi->ffd.file->f_op->poll(epi->ffd.file, NULL);

/*
* If the item is "hot" and it is not registered inside the ready
* list, push it inside.
*/
if (revents & event->events) {
spin_lock_irq(&ep->lock);
if (!ep_is_linked(&epi->rdllink)) {
list_add_tail(&epi->rdllink, &ep->rdllist);

/* Notify waiting tasks that events are available */
if (waitqueue_active(&ep->wq))
wake_up_locked(&ep->wq);
if (waitqueue_active(&ep->poll_wait))
pwake++;
}
spin_unlock_irq(&ep->lock);
}

飞得更高 said:

非常感谢您的回复。
我明白了。O(∩_∩)O谢谢

您的博客写的很好,有个师兄向我推荐的。他也总是看您的博客,已经向很多人推荐了。呵呵

DongHao Author Profile Page said:

多谢您的鼓励 :)

留言:

关于文章

This page contains a single entry by DongHao published on 12 3, 2011 11:41 AM.

[招聘转发] 淘宝内核组招聘实习生 was the previous entry in this blog.

2011年读书 is the next entry in this blog.

Find recent content on the main index or look in the 存档 to find all content.