免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: wyezl
打印 上一主题 下一主题

epoll模型的使用及其描述符耗尽问题的探讨 [复制链接]

论坛徽章:
0
111 [报告]
发表于 2006-08-25 09:39 |只看该作者
原帖由 nuclearweapon 于 2006-8-24 20:30 发表
to wyezl
请问,你做的测试有超过2个小时的吗?
或者说你对比过时间t1时候的fd个数和(t1+2小时)后的发的个数吗?


有超过一天的。
访问量小的时候描述符基本不损耗。
访问两大的时候。程增长趋势。会耗尽。

论坛徽章:
0
112 [报告]
发表于 2006-08-25 09:56 |只看该作者
LZ, 可以将标题改一下?

比如叫
epoll的使用及其程序XXX泄露问题探讨

等。这样便于查找问题

论坛徽章:
0
113 [报告]
发表于 2006-08-25 10:19 |只看该作者
标题已修改。
epoll都出来很久了,难道这个问题以前都没人遇到过?

论坛徽章:
0
114 [报告]
发表于 2006-08-25 14:08 |只看该作者
man epoll

其中关于EAGAIN 的部分我难以理解,谁能指点一下?

[code]

NAME
       epoll - I/O event notification facility

SYNOPSIS
       #include <sys/epoll.h>

DESCRIPTION
       epoll is a variant of poll(2) that can be used either as Edge or Level Triggered interface and scales
       well to large numbers of watched fds. Three system calls are provided to set up and control an  epoll
       set: epoll_create(2), epoll_ctl(2), epoll_wait(2).

       An epoll set is connected to a file descriptor created by epoll_create(2).  Interest for certain file
       descriptors  is  then  registered  via  epoll_ctl(2).   Finally,  the  actual  wait  is  started   by
       epoll_wait(2).

NOTES
       The  epoll  event  distribution  interface  is able to behave both as Edge Triggered ( ET ) and Level
       Triggered ( LT ). The difference between ET and LT event distribution mechanism can be  described  as
       follows. Suppose that this scenario happens :

       1      The  file descriptor that represents the read side of a pipe ( RFD ) is added inside the epoll
              device.

       2      Pipe writer writes 2Kb of data on the write side of the pipe.

       3      A call to epoll_wait(2) is done that will return RFD as ready file descriptor.

       4      The pipe reader reads 1Kb of data from RFD.

       5      A call to epoll_wait(2) is done.

       If the RFD file descriptor has been added to the epoll interface using the EPOLLET flag, the call  to
       epoll_wait(2)  done  in  step 5 will probably hang because of the available data still present in the
       file input buffers and the remote peer might be expecting a response based on  the  data  it  already
       sent.  The reason for this is that Edge Triggered event distribution delivers events only when events
       happens on the monitored file.  So, in step 5 the caller might end up waiting for some data  that  is
       already  present  inside  the  input  buffer. In the above example, an event on RFD will be generated
       because of the write done in 2 , and the event is consumed in 3.  Since the read operation done in  4
       does  not  consume the whole buffer data, the call to epoll_wait(2) done in step 5 might lock indefi-
       nitely. The epoll interface, when used with the EPOLLET flag ( Edge Triggered ) should use non-block-
       ing file descriptors to avoid having a blocking read or write starve the task that is handling multi-
       ple file descriptors.  The suggested way to use epoll as an Edge Triggered ( EPOLLET )  interface  is
       below, and possible pitfalls to avoid follow.

              i      with non-blocking file descriptors

              ii     by going to wait for an event only after read(2) or write(2) return EAGAIN

       On  the  contrary,  when used as a Level Triggered interface, epoll is by all means a faster poll(2),
       and can be used wherever the latter is used since it shares the same semantics. Since even  with  the
       Edge  Triggered epoll multiple events can be generated up on receival of multiple chunks of data, the
       caller has the option to specify the EPOLLONESHOT flag, to tell epoll to disable the associated  file
       descriptor  after  the receival of an event with epoll_wait(2).  When the EPOLLONESHOT flag is speci-
       fied, it is caller responsibility to rearm the file descriptor using epoll_ctl(2) with EPOLL_CTL_MOD.

EXAMPLE FOR SUGGESTED USAGE
       While  the usage of epoll when employed like a Level Triggered interface does have the same semantics
       of poll(2), an Edge Triggered usage requires more clarifiction to avoid  stalls  in  the  application
       event  loop.  In  this example, listener is a non-blocking socket on which listen(2) has been called.
       The function do_use_fd() uses the new ready file  descriptor  until  EAGAIN  is  returned  by  either
       read(2) or write(2).  An event driven state machine application should, after having received EAGAIN,
       record its current state so that at the next call to do_use_fd()  it  will  continue  to  read(2)  or
       write(2) from where it stopped before.

       struct epoll_event ev, *events;

       for(; {
           nfds = epoll_wait(kdpfd, events, maxevents, -1);

           for(n = 0; n < nfds; ++n) {
               if(events[n].data.fd == listener) {
                   client = accept(listener, (struct sockaddr *) &local,
                                   &addrlen);
                   if(client < 0){
                       perror("accept";
                       continue;
                   }
                   setnonblocking(client);
                   ev.events = EPOLLIN | EPOLLET;
                   ev.data.fd = client;
                   if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {
                       fprintf(stderr, "epoll set insertion error: fd=%d0,
                               client);
                       return -1;
                   }
               }
               else
                   do_use_fd(events[n].data.fd);
           }
       }

       When  used  as  an  Edge triggered interface, for performance reasons, it is possible to add the file
       descriptor inside the epoll interface ( EPOLL_CTL_ADD ) once by specifying ( EPOLLIN|EPOLLOUT ). This
       allows  you  to  avoid  continuously switching between EPOLLIN and EPOLLOUT calling epoll_ctl(2) with
       EPOLL_CTL_MOD.

QUESTIONS AND ANSWERS (from linux-kernel)
              Q1     What happens if you add the same fd to an epoll_set twice?

              A1     You will probably get EEXIST. However, it is possible that two threads may add the same
                     fd twice. This is a harmless condition.

              Q2     Can  two epoll sets wait for the same fd? If so, are events reported to both epoll sets
                     fds?

              A2     Yes. However, it is not recommended. Yes it would be reported to both.

              Q3     Is the epoll fd itself poll/epoll/selectable?

              A3     Yes.

              Q4     What happens if the epoll fd is put into its own fd set?

              A4     It will fail. However, you can add an epoll fd inside another epoll fd set.

              Q5     Can I send the epoll fd over a unix-socket to another process?

              A5     No.

              Q6     Will the close of an fd cause it to be removed from all epoll sets automatically?

              A6     Yes.

              Q7     If more than one event comes in between  epoll_wait(2)  calls,  are  they  combined  or
                     reported separately?

              A7     They will be combined.

              Q8     Does an operation on an fd affect the already collected but not yet reported events?

              A8     You can do two operations on an existing fd. Remove would be meaningless for this case.
                     Modify will re-read available I/O.

              Q9     Do I need to continuously read/write an fd until EAGAIN when using the EPOLLET  flag  (
                     Edge Triggered behaviour ) ?

              A9     No  you  don&acirc;

论坛徽章:
0
115 [报告]
发表于 2006-08-25 15:36 |只看该作者
LZ,

你注意看man epoll的EPLLET部分了吗? 如果不用ET方式可能不会有你的问题。

看PITFALLS AND SOLUTIONS部分。

你的问题:
by going to wait for an event only after read(2) or write(2) return EAGAIN
意思是仅仅将这样的fd放入epll_wait, 如果它上次读写的返回错误是EAGAIN.

论坛徽章:
0
116 [报告]
发表于 2006-08-25 16:05 |只看该作者
原帖由 思一克 于 2006-8-25 15:36 发表
LZ,

你注意看man epoll的EPLLET部分了吗? 如果不用ET方式可能不会有你的问题。

看PITFALLS AND SOLUTIONS部分。

你的问题:
by going to wait for an event only after read(2) or write(2) return EA ...


如果我使用的是EPLLET。
正在读的时候,返回了EAGAIN 。
那我该如何处理?

论坛徽章:
0
117 [报告]
发表于 2006-08-25 16:12 |只看该作者
TO LZ,

man page中说的意思是
如果我使用的是EPLLET。
正在读的时候,返回了EAGAIN 。
那我该如何处理?
那就加入poll_wait(如果原来就在,?)。
如果返回的不是EAGAIN,就不要poll_wait了。

我理解就是如此。说是饥饿症,并非epoll独有的问题

论坛徽章:
0
118 [报告]
发表于 2006-08-25 16:33 |只看该作者
原帖由 思一克 于 2006-8-25 16:12 发表
TO LZ,

man page中说的意思是
如果我使用的是EPLLET。
正在读的时候,返回了EAGAIN 。
那我该如何处理?
那就加入poll_wait(如果原来就在,?)。
如果返回的不是EAGAIN,就不要poll_wait了。

我理解就 ...


事情是这样的:
以前accept的时候把一个cfd加入了epoll。
后来wait返回。说这个cfd可读,我就去读,读了一次或两次,就返回了EAGAIN。
我难道新把以前的剔除要重 ADD一下?  
那已经读取的一部分数据怎么处理呢?
找个buffer存着? 等待下一次wait返回再接着读?



另外,不管有没使用EPLLET,下面代码,我只读一次的情况下。

              



  1.   if(events[i].events & EPOLLIN)
  2.                         {

  3.                                 ret = recv(cfd, buffer, sizeof(buffer),0);


  4.                                 if(ret>0)
  5.                                 {
  6.                                          //正常处理
  7.                                 }
  8.                                 else
  9.                                 {
  10.                                         perror("recv:");
  11.                                         printf("recv<=0\n");

  12.                                         epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, &ev);

  13.                                         if(close(cfd)==0)
  14.                                                 cfds[cfd]=0;
  15.                                 }



复制代码

打印出了一些这样的错误。为什么wait返回可读,我去读,还能出错呢?
ecv<=0
recv:: Illegal seek
recv<=0
recv:: Illegal seek
recv<=0
recv:: Connection reset by peer
recv<=0
recv:: Illegal seek
recv<=0
recv:: Illegal seek
recv<=0
recv:: Illegal seek
recv<=0
recv:: Illegal seek
recv<=0
recv:: Illegal seek
recv<=0
recv:: Illegal seek
recv<=0
recv:: Illegal seek
recv<=0
recv:: Illegal seek
recv<=0
recv:: Illegal seek
recv<=0
recv:: Illegal seek
recv<=0
recv:: Connection reset by peer
recv<=0
recv:: Connection reset by peer

论坛徽章:
0
119 [报告]
发表于 2006-08-25 17:34 |只看该作者
原帖由 wyezl 于 2006-8-25 16:33 发表


事情是这样的:
以前accept的时候把一个cfd加入了epoll。
后来wait返回。说这个cfd可读,我就去读,读了一次或两次,就返回了EAGAIN。
我难道新把以前的剔除要重 ADD一下?  
那已经读取的一部分数据怎么 ...


EAGAIN和EWOULDBLOCK在linux是相同。
socket可读,是和SO_RCVLOWAT设定的值有关系(但是linux中的select和poll并不遵守这个选项)。

默认这个值默认是1。

现在你希望read读的字节数为sizeof(buffer),现在有可能没有数据,所以回返回EWOULDBLOCK,在linux中其实就是EAGAIN

[ 本帖最后由 nuclearweapon 于 2006-8-25 17:39 编辑 ]

论坛徽章:
0
120 [报告]
发表于 2006-08-25 18:01 |只看该作者
原帖由 nuclearweapon 于 2006-8-25 17:34 发表


EAGAIN和EWOULDBLOCK在linux是相同。
socket可读,是和SO_RCVLOWAT设定的值有关系(但是linux中的select和poll并不遵守这个选项)。

默认这个值默认是1。

现在你希望read读的字节数为sizeof(buffer), ...


这种情况该做什么处理呢?
顺便问一下I, llegal seek 是什么原因造成的?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP