使用libevent的一次strace结果分析
直接看strace 结果
11:07:04.369821 accept4(6, {sa_family=AF_INET, sin_port=htons(20321), sin_addr=inet_addr("170.1.7.223")}, [16], SOCK_NONBLOCK) = 2843 <0.000013> 11:07:04.369919 getsockname(2843, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("170.1.2.1")}, [16]) = 0 <0.000011> 11:07:04.370003 getpeername(2843, {sa_family=AF_INET, sin_port=htons(20321), sin_addr=inet_addr("170.1.7.223")}, [16]) = 0 <0.000011> 11:07:04.370092 socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 2844 <0.000014> 11:07:04.370170 fcntl(2844, F_SETFL, O_RDONLY|O_NONBLOCK) = 0 <0.000011> 11:07:04.370254 fcntl(2843, F_SETFL, O_RDONLY|O_NONBLOCK) = 0 <0.000010> 11:07:04.370339 setsockopt(2844, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 <0.000010> 11:07:04.370423 setsockopt(2844, SOL_IP, IP_TRANSPARENT, [1], 4) = 0 <0.000011> 11:07:04.370516 setsockopt(2843, SOL_IP, IP_TRANSPARENT, [1], 4) = 0 <0.000010> 11:07:04.370596 setsockopt(2844, SOL_SOCKET, SO_BINDTODEVICE, "eth2\0", 5) = 0 <0.000011> 11:07:04.370682 setsockopt(2844, SOL_SOCKET, SO_LINGER, {onoff=1, linger=0}, 8) = 0 <0.000010> 11:07:04.370772 bind(2844, {sa_family=AF_INET, sin_port=htons(20321), sin_addr=inet_addr("170.1.7.223")}, 16) = 0 <0.000023> 11:07:04.370924 connect(2844, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("170.1.2.1")}, 16) = -1 EINPROGRESS (Operation now in progress) <0.000019> 11:07:04.371026 epoll_ctl(3, EPOLL_CTL_ADD, 2844, {EPOLLOUT, {u32=2844, u64=2844}}) = 0 <0.000014> 11:07:04.371113 epoll_ctl(3, EPOLL_CTL_ADD, 2843, {EPOLLIN|EPOLLRDHUP, {u32=2843, u64=2843}}) = 0 <0.000012> 11:07:04.371189 epoll_ctl(3, EPOLL_CTL_MOD, 2844, {EPOLLIN|EPOLLOUT, {u32=2844, u64=2844}}) = 0 <0.000011> 11:07:04.371256 accept4(6, 0x7fff23b228c0, 0x7fff23b228bc, SOCK_NONBLOCK) = -1 EAGAIN (Resource temporarily unavailable) <0.000014> 11:07:04.371332 epoll_wait(3, [{EPOLLOUT, {u32=2844, u64=2844}}, {EPOLLIN, {u32=2843, u64=2843}}], 32, -1) = 2 <0.000011> 11:07:04.371413 getsockopt(2844, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 <0.000011> 11:07:04.371491 epoll_ctl(3, EPOLL_CTL_MOD, 2844, {EPOLLIN, {u32=2844, u64=2844}}) = 0 <0.000010> 11:07:04.371571 ioctl(2843, FIONREAD, [358]) = 0 <0.000011> 11:07:04.371638 readv(2843, [{"GET /bsp.index HTTP/1.1\r\nHost: www.server-aa010201.cn\r\nConnection: Keep-Alive\r\nIf-None-Match: 1e5297c5234134780983853e410eff18\r\nUser-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; XBLWP7; ZuneWP7)\r\nAccept: */*\r\nAccept-Language: zh, en-gb;q=0.8, en;q=0.7\r\nAccept-Encoding: gzip,deflate\r\nUA-CPU: x86\r\nCookie: SFDsM=B-p-ProxY23a8d00e\r\n\r\n", 358}], 1) = 358 <0.000013> 11:07:04.371715 writev(2844, [{"GET /bsp.index HTTP/1.1\r\nHost: www.server-aa010201.cn\r\nConnection: Keep-Alive\r\nIf-None-Match: 1e5297c5234134780983853e410eff18\r\nUser-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; XBLWP7; ZuneWP7)\r\nAccept: */*\r\nAccept-Language: zh, en-gb;q=0.8, en;q=0.7\r\nAccept-Encoding: gzip,deflate\r\nUA-CPU: x86\r\nCookie: SFDsM=B-p-ProxY23a8d00e\r\n\r\n", 358}], 1) = 358 <0.000016> 11:07:04.371802 epoll_wait(3, [{EPOLLIN, {u32=2844, u64=2844}}], 32, -1) = 1 <0.000037> 11:07:04.371911 ioctl(2844, FIONREAD, [434]) = 0 <0.000011> 11:07:04.371997 readv(2844, [{"HTTP/1.1 200 OK\r\nDate: Fri, 23 Sep 22 11:18:03 GMT\r\nLast-Modified: Thu, 02 Jun 22 00:54:08 GMT\r\nETag: 68e190732409e9fdde136d9b4e14a761\r\nConnection: Keep-Alive\r\nServer: Apache/1.3.37 (Unix) mod_perl/1.29\r\nAccept-Ranges: bytes\r\nSet-Cookie: vNZHB=B-p-ProxY23a8d052; path=/\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: 100\r\n\r\n\275\370~x+\351{\267\3\243\\K\330l\306\315\210\214\261s\332<i\31W+\325\335\235\214h\344I\3\373\27\316\216b\301\273k\363\243\2B\356F#\300\235\247\252T\27@9W\252\203\262-\242#\257\f#\254:\26+\vS:3eN\271\251\f\345\371\255\337\212\21\262\213o\246\331\245sUwW:*i\371", 434}], 1) = 434 <0.000011> 11:07:04.372070 writev(2843, [{"HTTP/1.1 200 OK\r\nDate: Fri, 23 Sep 22 11:18:03 GMT\r\nLast-Modified: Thu, 02 Jun 22 00:54:08 GMT\r\nETag: 68e190732409e9fdde136d9b4e14a761\r\nConnection: Keep-Alive\r\nServer: Apache/1.3.37 (Unix) mod_perl/1.29\r\nAccept-Ranges: bytes\r\nSet-Cookie: vNZHB=B-p-ProxY23a8d052; path=/\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: 100\r\n\r\n\275\370~x+\351{\267\3\243\\K\330l\306\315\210\214\261s\332<i\31W+\325\335\235\214h\344I\3\373\27\316\216b\301\273k\363\243\2B\356F#\300\235\247\252T\27@9W\252\203\262-\242#\257\f#\254:\26+\vS:3eN\271\251\f\345\371\255\337\212\21\262\213o\246\331\245sUwW:*i\371", 434}], 1) = 434 <0.000018> 11:07:04.372159 epoll_wait(3, [{EPOLLIN, {u32=6, u64=6}}], 32, -1) = 1 <0.030592> 11:07:04.402824 accept4(6, {sa_family=AF_INET, sin_port=htons(57467), sin_addr=inet_addr("170.1.4.223")}, [16], SOCK_NONBLOCK) = 2845 <0.000013> 11:07:04.402894 getsockname(2845, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("170.1.2.1")}, [16]) = 0 <0.000010> 11:07:04.402954 getpeername(2845, {sa_family=AF_INET, sin_port=htons(57467), sin_addr=inet_addr("170.1.4.223")}, [16]) = 0 <0.000009> 11:07:04.403013 socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 2846 <0.000012> 11:07:04.403071 fcntl(2846, F_SETFL, O_RDONLY|O_NONBLOCK) = 0 <0.000009> 11:07:04.403124 fcntl(2845, F_SETFL, O_RDONLY|O_NONBLOCK) = 0 <0.000009> 11:07:04.403178 setsockopt(2846, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 <0.000009> 11:07:04.403235 setsockopt(2846, SOL_IP, IP_TRANSPARENT, [1], 4) = 0 <0.000010> 11:07:04.403291 setsockopt(2845, SOL_IP, IP_TRANSPARENT, [1], 4) = 0 <0.000009> 11:07:04.403346 setsockopt(2846, SOL_SOCKET, SO_BINDTODEVICE, "eth2\0", 5) = 0 <0.000010> 11:07:04.403400 setsockopt(2846, SOL_SOCKET, SO_LINGER, {onoff=1, linger=0}, 8) = 0 <0.000009> 11:07:04.403453 bind(2846, {sa_family=AF_INET, sin_port=htons(57467), sin_addr=inet_addr("170.1.4.223")}, 16) = 0 <0.000010> 11:07:04.403512 connect(2846, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("170.1.2.1")}, 16) = -1 EINPROGRESS (Operation now in progress) <0.000018> 11:07:04.403579 epoll_ctl(3, EPOLL_CTL_ADD, 2846, {EPOLLOUT, {u32=2846, u64=2846}}) = 0 <0.000012> 11:07:04.403641 epoll_ctl(3, EPOLL_CTL_ADD, 2845, {EPOLLIN|EPOLLRDHUP, {u32=2845, u64=2845}}) = 0 <0.000010> 11:07:04.403698 epoll_ctl(3, EPOLL_CTL_MOD, 2846, {EPOLLIN|EPOLLOUT, {u32=2846, u64=2846}}) = 0 <0.000010> 11:07:04.403757 accept4(6, 0x7fff23b228c0, 0x7fff23b228bc, SOCK_NONBLOCK) = -1 EAGAIN (Resource temporarily unavailable) <0.000012> 11:07:04.403817 epoll_wait(3, [{EPOLLOUT, {u32=2846, u64=2846}}, {EPOLLIN, {u32=2845, u64=2845}}], 32, -1) = 2 <0.000010> 11:07:04.403877 getsockopt(2846, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 <0.000009> 11:07:04.403935 epoll_ctl(3, EPOLL_CTL_MOD, 2846, {EPOLLIN, {u32=2846, u64=2846}}) = 0 <0.000009> 11:07:04.403991 ioctl(2845, FIONREAD, [358]) = 0 <0.000009> 11:07:04.404047 readv(2845, [{"GET /bsp.index HTTP/1.1\r\nHost: www.server-aa010201.cn\r\nConnection: Keep-Alive\r\nIf-None-Match: 1e5297c5234134780983853e410eff18\r\nUser-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; XBLWP7; ZuneWP7)\r\nAccept: */*\r\nAccept-Language: zh, en-gb;q=0.8, en;q=0.7\r\nAccept-Encoding: gzip,deflate\r\nUA-CPU: x86\r\nCookie: SFDsM=B-p-ProxY23a8d5c8\r\n\r\n", 358}], 1) = 358 <0.000011> 11:07:04.404108 writev(2846, [{"GET /bsp.index HTTP/1.1\r\nHost: www.server-aa010201.cn\r\nConnection: Keep-Alive\r\nIf-None-Match: 1e5297c5234134780983853e410eff18\r\nUser-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; XBLWP7; ZuneWP7)\r\nAccept: */*\r\nAccept-Language: zh, en-gb;q=0.8, en;q=0.7\r\nAccept-Encoding: gzip,deflate\r\nUA-CPU: x86\r\nCookie: SFDsM=B-p-ProxY23a8d5c8\r\n\r\n", 358}], 1) = 358 <0.000016> 11:07:04.404174 epoll_wait(3, [{EPOLLIN, {u32=2846, u64=2846}}], 32, -1) = 1 <0.000061> 11:07:04.404300 ioctl(2846, FIONREAD, [434]) = 0 <0.000012> 11:07:04.404386 readv(2846, [{"HTTP/1.1 200 OK\r\nDate: Fri, 23 Sep 22 11:18:03 GMT\r\nLast-Modified: Thu, 02 Jun 22 00:54:08 GMT\r\nETag: 68e190732409e9fdde136d9b4e14a761\r\nConnection: Keep-Alive\r\nServer: Apache/1.3.37 (Unix) mod_perl/1.29\r\nAccept-Ranges: bytes\r\nSet-Cookie: vNZHB=B-p-ProxY23a8d5e0; path=/\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: 100\r\n\r\n\275\370~x+\351{\267\3\243\\K\330l\306\315\210\214\261s\332<i\31W+\325\335\235\214h\344I\3\373\27\316\216b\301\273k\363\243\2B\356F#\300\235\247\252T\27@9W\252\203\262-\242#\257\f#\254:\26+\vS:3eN\271\251\f\345\371\255\337\212\21\262\213o\246\331\245sUwW:*i\371", 434}], 1) = 434 <0.000012> 11:07:04.404462 writev(2845, [{"HTTP/1.1 200 OK\r\nDate: Fri, 23 Sep 22 11:18:03 GMT\r\nLast-Modified: Thu, 02 Jun 22 00:54:08 GMT\r\nETag: 68e190732409e9fdde136d9b4e14a761\r\nConnection: Keep-Alive\r\nServer: Apache/1.3.37 (Unix) mod_perl/1.29\r\nAccept-Ranges: bytes\r\nSet-Cookie: vNZHB=B-p-ProxY23a8d5e0; path=/\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: 100\r\n\r\n\275\370~x+\351{\267\3\243\\K\330l\306\315\210\214\261s\332<i\31W+\325\335\235\214h\344I\3\373\27\316\216b\301\273k\363\243\2B\356F#\300\235\247\252T\27@9W\252\203\262-\242#\257\f#\254:\26+\vS:3eN\271\251\f\345\371\255\337\212\21\262\213o\246\331\245sUwW:*i\371", 434}], 1) = 434 <0.000017> 11:07:04.404533 epoll_wait(3, [{EPOLLIN, {u32=2844, u64=2844}}], 32, -1) = 1 <0.014573> 11:07:04.419160 ioctl(2844, FIONREAD, [0]) = 0 <0.000011> 11:07:04.419230 readv(2844, [{"", 4096}], 1) = 0 <0.000012> 11:07:04.419297 epoll_ctl(3, EPOLL_CTL_DEL, 2844, 0x7fff23b22810) = 0 <0.000013> 11:07:04.419364 epoll_ctl(3, EPOLL_CTL_DEL, 2843, 0x7fff23b22740) = 0 <0.000011> 11:07:04.419421 close(2843) = 0 <0.000020> 11:07:04.419488 close(2844) = 0 <0.000020> 11:07:04.419560 epoll_wait(3, ^Cstrace: Process 215395 detached
跟踪一下 fd 2844的结果如下:

流量模型如下: client----proxy--proxy-----serever
使用fd2844 connect server 后, 将fd2843 httpget请求转发到2844, 2844收到server 的响应后转发到2843 client;但是 最后关闭的时候close 和 最后一次收包相隔50ms;
也就是 recv2844 的时候,如果fin和data一起发过来,那么 epoll 应该时能感知到的,所以此时应该监听EPOLLRDUP事件掩码,
问题:如果data 和fin一起过来, epoll 没有对EPOLLRDUP处理而是当做EPOLLIN处理,如果内核已经执行sock_read_wake 和 sock_state_chage, 此时epoll_wait还在返回途中,根据epoll源码分析
可知只会将此event 加入到rdlist一次,那为什么第二次还会唤醒呢?
原因:使用epoll LT模式 会重复将fd 添加到rdlist里面,下次epollwait时,会继续检测event—-->event_mask
如果此时使用的是epoll ET模式 那会怎样呢???
记住使用
ioctl(2846, FIONREAD, [434]) = 0
可以检测当前未读取的缓存多少
样例使用情况:
#include <unistd.h> #include <iostream> #include <sys/epoll.h> using namespace std; int main(void) { int epfd,nfds; struct epoll_event ev,events[5];//ev用于注册事件,数组用于返回要处理的事件 epfd=epoll_create(1);//只需要监听一个描述符——标准输入 ev.data.fd=STDIN_FILENO; ev.events=EPOLLIN|EPOLLET;//监听读状态同时设置ET模式 epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&ev);//注册epoll事件 for(;;) { nfds=epoll_wait(epfd,events,5,-1); for(int i=0;i<nfds;i++) { if(events[i].data.fd==STDIN_FILENO) cout<<"hello world!"<<endl; } } }
结果是:

(1)当用户输入一组字符,这组字符被送入buffer,字符停留在buffer中,又因为buffer由空变为不空,所以ET返回读就绪,输出”hello world!”。
(2) 之后程序再次执行epoll_wait,此时虽然buffer中有内容可读,但是根据我们上节的分析,ET并不返回就绪,导致epoll_wait阻塞。(底层原因是ET下就绪fd的epitem只被放入rdlist一次)。
(3) 用户再次输入一组字符,导致buffer中的内容增多,根据我们上节的分析这将导致fd状态的改变,是对应的epitem再次加入rdlist,从而使epoll_wait返回读就绪,再次输出“hello world!”。
如果代码中添加:epoll mod呢
{ nfds=epoll_wait(epfd,events,5,-1); for(int i=0;i<nfds;i++) { if(events[i].data.fd==STDIN_FILENO) { cout<<"hello world!"<<endl; ev.data.fd=STDIN_FILENO; ev.events=EPOLLIN|EPOLLET;//使用默认LT模式 epoll_ctl(epfd,EPOLL_CTL_MOD,STDIN_FILENO,&ev);//重新MOD事件(ADD无效) } }
依然使用ET,但是每次读就绪后都主动的再次MOD IN事件,我们发现程序再次出现死循环,也就是每次返回读就绪

ET模型下可读操作:
(1) 当buffer由不可读状态变为可读的时候,即由空变为不空的时候。
(2) 当有新数据到达时,即buffer中的待读内容变多的时候。
(3) 当buffer中有数据可读(即buffer不空)且用户对相应fd进行epoll_mod IN事件时
ET模型 对于写操作:
(1) 当buffer由不可写变为可写的时候,即由满状态变为不满状态的时候。
(2) 当有旧数据被发送走时,即buffer中待写的内容变少得时候。
(3) 当buffer中有可写空间(即buffer不满)且用户对相应fd进行epoll_mod OUT事件时
???为什么存在第三种情况:可以见代码分析epoll文章:
switch (op) { case EPOLL_CTL_ADD: if (!epi) { epds.events |= POLLERR | POLLHUP; error = ep_insert(ep, &epds, tf.file, fd, full_check); } else error = -EEXIST; if (full_check) clear_tfile_check_list(); break; case EPOLL_CTL_DEL: if (epi) error = ep_remove(ep, epi); else error = -ENOENT; break; case EPOLL_CTL_MOD: if (epi) { epds.events |= POLLERR | POLLHUP; error = ep_modify(ep, epi, &epds); } else error = -ENOENT; break; }
/* * Modify the interest event mask by dropping an event if the new mask * has a match in the current file status. Must be called with "mtx" held. */ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_event *event) { int pwake = 0; unsigned int revents; poll_table pt; init_poll_funcptr(&pt, NULL); /* * Set the new event interest mask before calling f_op->poll(); * otherwise we might miss an event that happens between the * f_op->poll() call and the new event set registering. */ epi->event.events = event->events; /* need barrier below */ epi->event.data = event->data; /* protected by mtx */ /* * The following barrier has two effects: * * 1) Flush epi changes above to other CPUs. This ensures * we do not miss events from ep_poll_callback if an * event occurs immediately after we call f_op->poll(). * We need this because we did not take ep->lock while * changing epi above (but ep_poll_callback does take * ep->lock). * * 2) We also need to ensure we do not miss _past_ events * when calling f_op->poll(). This barrier also * pairs with the barrier in wq_has_sleeper (see * comments for wq_has_sleeper). * * This barrier will now guarantee ep_poll_callback or f_op->poll * (or both) will notice the readiness of an item. */ smp_mb(); /* * Get current event bits. We can safely use the file* here because * its usage count has been increased by the caller of this function. */ revents = ep_item_poll(epi, &pt); //revents = epi->ffd.file->f_op->poll(epi->ffd.file, &pt); ------------------------ return 0; }
1:可见重复epoll add 会返回EEXIST 错误掩码;
2、使用MOD会直接调用ep_modify函数: 会去获取ready 事件掩码 revents = epi->ffd.file->f_op->poll(epi->ffd.file, NULL) & epi->event.events; 翻入就绪链表


浙公网安备 33010602011771号