使用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; 翻入就绪链表

 

 

 

 

 

 

 

 

 

posted @ 2022-09-28 11:21  codestacklinuxer  阅读(32)  评论(0)    收藏  举报