epoll 編程注意事項以及參數查看

每次接受新連接的時候,我監視了這幾個事件。

EPOLLIN | EPOLLET |  EPOLLERR | EPOLLHUP | EPOLLPRI;

每次有一批事件返回,經過統計  
返回的一批fd數量=出錯關閉的fd數量+由EPOLLIN轉爲EPOLLOUT的fd數量+EPOLLOUT正常處理關閉的fd的數量。 也就是說,每批事件都完全處理,沒有遺漏。

觀察發現EPOLLET |  EPOLLERR | EPOLLHUP 這3發事件的發生率爲0。

但fd卻成增大趨勢。以前那寫較小的fd在經歷一段時間後漸漸丟失,不再可用。

請問fd都丟失到哪裏去了?

-----------------------------------------------------------------------

後來經常有人寫信問我這個問題,我在帖子裏回覆過,好象帖子太多了,不好找,還是寫在這裏吧。
單純靠epoll來管理描述符不泄露幾乎是不可能的。
完全解決方案很簡單,就是對每個fd設置超時時間,如果超過timeout的時間,這個fd沒有活躍過,就close掉。

[ 本帖最後由 wyezl 於 2008-12-13 21:42 編輯 ]

 思一克 回覆於:2006-08-18 10:46:50

有程序?

 wyezl 回覆於:2006-08-18 11:10:12




/*-------------------------------------------------------------------------------------------------
gcc -o httpd httpd.c -lpthread 
author: wyezl
2006.4.28
---------------------------------------------------------------------------------------------------*/

#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>

#define PORT 8888
#define MAXFDS 5000
#define EVENTSIZE 100

#define BUFFER "HTTP/1.1 200 OK\r\nContent-Length: 5\r\nConnection: close\r\nContent-Type: text/html\r\n\r\nHello"

int epfd;
void *serv_epoll(void *p);
void setnonblocking(int fd)
{
    int opts;
    opts=fcntl(fd, F_GETFL);
    if (opts < 0)
    {
          fprintf(stderr, "fcntl failed\n");
          return;
    }
    opts = opts | O_NONBLOCK;
    if(fcntl(fd, F_SETFL, opts) < 0)
    {
          fprintf(stderr, "fcntl failed\n");
          return;
    }
    return;
}

int main(int argc, char *argv[])
{
    int fd, cfd,opt=1;
    struct epoll_event ev;
    struct sockaddr_in sin, cin;
    socklen_t sin_len = sizeof(struct sockaddr_in);
    pthread_t tid;
    pthread_attr_t attr;

    epfd = epoll_create(MAXFDS);
    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) <= 0)
    {
          fprintf(stderr, "socket failed\n");
          return -1;
    }
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void*)&opt, sizeof(opt));

    memset(&sin, 0, sizeof(struct sockaddr_in));
    sin.sin_family = AF_INET;
    sin.sin_port = htons((short)(PORT));
    sin.sin_addr.s_addr = INADDR_ANY;
    if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) != 0)
    {
          fprintf(stderr, "bind failed\n");
          return -1;
    }
    if (listen(fd, 32) != 0)
    {
          fprintf(stderr, "listen failed\n");
          return -1;
    }

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
    if (pthread_create(&tid, &attr, serv_epoll, NULL) != 0)
    {
          fprintf(stderr, "pthread_create failed\n");
          return -1;
    }

    while ((cfd = accept(fd, (struct sockaddr *)&cin, &sin_len)) > 0)
    {
          setnonblocking(cfd);
          ev.data.fd = cfd;
          ev.events = EPOLLIN | EPOLLET |  EPOLLERR | EPOLLHUP | EPOLLPRI;
          epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);
          //printf("connect from %s\n",inet_ntoa(cin.sin_addr));
          //printf("cfd=%d\n",cfd);
    }

    if (fd > 0)
          close(fd);
    return 0;
}

void *serv_epoll(void *p)
{
    int i, ret, cfd, nfds;;
    struct epoll_event ev,events[EVENTSIZE];
    char buffer[512];

    while (1)
    {
          nfds = epoll_wait(epfd, events, EVENTSIZE , -1);
          //printf("nfds ........... %d\n",nfds);
          for (i=0; i<nfds; i++)
          {
                if(events.events & EPOLLIN)
                {
                    cfd = events.data.fd;
                    ret = recv(cfd, buffer, sizeof(buffer),0);
                    //printf("read ret..........= %d\n",ret);

                    ev.data.fd = cfd;
                    ev.events = EPOLLOUT | EPOLLET;
                    epoll_ctl(epfd, EPOLL_CTL_MOD, cfd, &ev);
                }
                else if(events.events & EPOLLOUT)
                {
                    cfd = events.data.fd;
                    ret = send(cfd, BUFFER, strlen(BUFFER), 0);
                    //printf("send ret...........= %d\n", ret);

                    ev.data.fd = cfd;
                    epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, &ev);
                  
                    close(cfd);

                }

                   else
      {

cfd = events.data.fd;
ev.data.fd = cfd;
epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, &ev);
close(cfd);
       }


          }
    }
    return NULL;
}










 wyezl 回覆於:2006-08-18 11:23:46

只要能幫我找出描述符從哪耗盡的就行。
:)

 思一克 回覆於:2006-08-18 11:50:07

是不是main()中的cfd沒有關閉

 wyezl 回覆於:2006-08-18 12:25:12

main中只接受連接,加入監視,不做處理。
估計epoll_ctl 沒判斷出錯,我下午再測試一下。

 wyezl 回覆於:2006-08-18 13:27:07

if(epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev)<0)
                        printf("............................................EPOLL_CTL_ADD error!\n");

if(epoll_ctl(epfd, EPOLL_CTL_MOD, cfd, &ev)<0)
                                                printf(".......................................EPOLL_CTL_MOD error!\n");


修改了兩句,但未發現這裏的錯誤輸出,估計也不是這的問題。

不知道到底從哪耗盡的,奇怪。

 wyezl 回覆於:2006-08-18 15:50:15

繼續頂。。。。。。。。。。。。。。。。

 思一克 回覆於:2006-08-18 15:58:14

想幫你實驗,但程序無法編譯

 playmud 回覆於:2006-08-19 20:48:39

挑挑錯吧,說的不對的話,請見諒!
1,    while ((cfd = accept(fd, (struct sockaddr *)&cin, &sin_len)) > 0)
    可夠改成
while(1)
cfd = accept(fd, (struct sockaddr *)&cin, &sin_len;
if(cfd>0)
..
2,你是要讓客戶端發送一次就不再發送了嗎?

                if(events.events & EPOLLIN)
                {
                    cfd = events.data.fd;
                    ret = recv(cfd, buffer, sizeof(buffer),0);
                    //printf("read ret..........= %d\n",ret);

                    ev.data.fd = cfd;
                    ev.events = EPOLLOUT | EPOLLET;
                    epoll_ctl(epfd, EPOLL_CTL_MOD, cfd, &ev);
                }


3,將accept事件列入epoll監控的對象
4,對close最好做一個判斷

 tysn 回覆於:2006-08-20 11:25:26

accept()之後的epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);
要改成:if (epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev)<0) close(cfd);
估計就是這裏錯吧,這機率比較小,
所以出現“fd在經歷一段時間後漸漸丟失”

另外還要考慮playmud挑的4點問題,
比如第一點,lz那樣寫的while好像跟if差不多,
accept也是在很小几率下出錯,出錯之後程序就退出?

說的不對的話,請指正!

 wyezl 回覆於:2006-08-20 12:58:20

引用:原帖由 思一克 於 2006-8-18 15:58 發表
想幫你實驗,但程序無法編譯 


編譯沒問題,需要linux 2。6以上內核的支持。

 wyezl 回覆於:2006-08-20 13:10:17

引用:



挑挑錯吧,說的不對的話,請見諒!
1,    while ((cfd = accept(fd, (struct sockaddr *)&cin, &sin_len)) > 0)
    可夠改成
while(1)
cfd = accept(fd, (struct sockaddr *)&cin, &sin_len;
if(cfd>0)
..
2,你是要讓客戶端發送一次就不再發送了嗎?


CODE:[Copy to clipboard]                if(events.events & EPOLLIN)
                {
                    cfd = events[ i ].data.fd;
                    ret = recv(cfd, buffer, sizeof(buffer),0);
                    //printf("read ret..........= %d\n",ret);

                    ev.data.fd = cfd;
                    ev.events = EPOLLOUT | EPOLLET;
                    epoll_ctl(epfd, EPOLL_CTL_MOD, cfd, &ev);
                }
3,將accept事件列入epoll監控的對象
4,對close最好做一個判斷 





... 


1,如果描述符已經耗盡,這樣判斷cfd永遠不會成立。所以就讓它退出算了。

2,我只取http請求的頭信息的第一行。 get  /xxx  http/1.0 做簡單分析,其他的都不要了。按照tcp協議的特點,本應該多次讀取的,但測試發現,只讀一次就能得到我想要的,基本上沒出過錯。
裏面的數據沒讀完就留那了,會不會對以後的請求造成什麼不好的影響?


3,accept是在住線程裏跑,加入了epoll有什麼好處嗎?

4,對close沒做判斷,這點確實是遺漏了。我得補上。
謝謝你的建議。

[ 本帖最後由 wyezl 於 2006-8-20 13:16 編輯 ]

 wyezl 回覆於:2006-08-20 13:14:43

引用:原帖由 tysn 於 2006-8-20 11:25 發表
accept()之後的epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);
要改成:if (epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev)<0) close(cfd);
估計就是這裏錯吧,這機率比較小,
所以出現“fd在經歷一段時間 ... 


這一條判斷我後來添加了。 但這兒出錯機率幾乎爲0 。


現在仍未發現耗盡的原因。懷疑是不是epoll機制本身有問題呢?

 wyezl 回覆於:2006-08-21 09:41:20

繼續頂。。。。。。。。。

 思一克 回覆於:2006-08-21 09:49:38

EPOLLIN事件後是不是應該關閉CFD?

我看的,不一定準確,因爲無法實驗你的程序

 wyezl 回覆於:2006-08-21 12:14:14

引用:原帖由 思一克 於 2006-8-21 09:49 發表
EPOLLIN事件後是不是應該關閉CFD?

我看的,不一定準確,因爲無法實驗你的程序 


如果關閉了,以後就不會有EPOLLOUT了。 因爲我是接受請求,然後返回所請求的信息。

 思一克 回覆於:2006-08-21 12:18:40

那你把能編譯的貼出,我幫你實驗。如果你願意的話

 billzhou 回覆於:2006-08-21 12:34:25

不懂  什麼是epoll,哪位兄弟給解釋一下

 wyezl 回覆於:2006-08-21 16:47:23

引用:原帖由 思一克 於 2006-8-21 12:18 發表
那你把能編譯的貼出,我幫你實驗。如果你願意的話 


我貼出的代碼就是能編譯的。
請問你用的是什麼操作系統? 版本?
這個程序只能在linux上運行,而且內核版本必須在2.6以上。

[ 本帖最後由 wyezl 於 2006-8-21 16:52 編輯 ]

 wyezl 回覆於:2006-08-21 16:49:00

思一克是個熱心的斑竹,先贊一個。 :)

 精簡指令 回覆於:2006-08-21 20:12:49

“但fd卻成增大趨勢。以前那寫較小的fd在經歷一段時間後漸漸丟失,不再可用。”

是否已經將FD用光了? 如果沒有用光,試着用到最大值,看看是否會重新分配較小的FD

 safedead 回覆於:2006-08-21 21:52:01

我沒有這麼用過epoll

我的程序是僅用epoll彈出發生EPOLIN事件的LISTEN套接字
然後就ACCEPT出客戶端連接就交給線程處理了
沒有發生過fd耗盡的情況

 思一克 回覆於:2006-08-22 08:19:12

to LZ,

你的模型好象不太對,至少不太好。epoll_wait和ACCEPT的次序?

你在網絡上找,有現成的好的。

 wyezl 回覆於:2006-08-22 09:54:56

引用:原帖由 safedead 於 2006-8-21 21:52 發表
我沒有這麼用過epoll

我的程序是僅用epoll彈出發生EPOLIN事件的LISTEN套接字
然後就ACCEPT出客戶端連接就交給線程處理了
沒有發生過fd耗盡的情況 


方便把你的模型大概貼出來看一下嗎?

 wyezl 回覆於:2006-08-22 09:55:54

引用:原帖由 精簡指令 於 2006-8-21 20:12 發表
“但fd卻成增大趨勢。以前那寫較小的fd在經歷一段時間後漸漸丟失,不再可用。”

是否已經將FD用光了? 如果沒有用光,試着用到最大值,看看是否會重新分配較小的FD 


確實用光了。不能分配小的了。

 星之孩子 回覆於:2006-08-22 10:06:27

爲何你用epoll還要用多線程
感覺你這個模型怪怪的

 wyezl 回覆於:2006-08-22 10:32:07

只開了兩個線程。 epoll獨佔一個。

 星之孩子 回覆於:2006-08-22 10:34:49

多個線程這樣用epoll不知道會不會有問題
其實你的應用根本不用多線程

 wyezl 回覆於:2006-08-22 11:05:13

引用:原帖由 思一克 於 2006-8-22 08:19 發表
to LZ,

你的模型好象不太對,至少不太好。epoll_wait和ACCEPT的次序?

你在網絡上找,有現成的好的。 


能把你覺得好的模型共享一下嗎?

 nuclearweapon 回覆於:2006-08-22 11:43:27

剛纔測試了lz的程序。
50個併發,cfd最大達到37然後回落到5。

如上測試多次沒有發現cfd有增大的趨勢。
所以感覺和內核版本有關係。

測試環境:

kernel 2.6.15
gcc 4.0.3

distribution:ubuntu

 wyezl 回覆於:2006-08-22 11:47:58

下面這個模型我也用過。不過效率很底。


#include <iostream>

#include <sys/socket.h>

#include <sys/epoll.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <fcntl.h>

#include <unistd.h>

#include <stdio.h>



#define MAXLINE 10

#define OPEN_MAX 100

#define LISTENQ 20

#define SERV_PORT 5555

#define INFTIM 1000



void setnonblocking(int sock)

{

  int opts;

  opts=fcntl(sock,F_GETFL);

  if(opts<0)

  {

      perror("fcntl(sock,GETFL)");

      exit(1);

  }

  opts = opts|O_NONBLOCK;

  if(fcntl(sock,F_SETFL,opts)<0)

  {

      perror("fcntl(sock,SETFL,opts)");

      exit(1);

  }   

}



int main()

{

  int i, maxi, listenfd, connfd, sockfd,epfd,nfds;

  ssize_t n;

  char line[MAXLINE];

  socklen_t clilen;

  //聲明epoll_event結構體的變量,ev用於註冊事件,數組用於回傳要處理的事件

  struct epoll_event ev,events[20];

  //生成用於處理accept的epoll專用的文件描述符

  epfd=epoll_create(256);



  struct sockaddr_in clientaddr;

  struct sockaddr_in serveraddr;

  listenfd = socket(AF_INET, SOCK_STREAM, 0);

  //把socket設置爲非阻塞方式

  setnonblocking(listenfd);

  //設置與要處理的事件相關的文件描述符

  ev.data.fd=listenfd;

  //設置要處理的事件類型

  ev.events=EPOLLIN|EPOLLET;

  //註冊epoll事件

  epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);



  bzero(&serveraddr, sizeof(serveraddr));

  serveraddr.sin_family = AF_INET;



  char *local_addr="200.200.200.204";

  inet_aton(local_addr,&(serveraddr.sin_addr));//htons(SERV_PORT);

  serveraddr.sin_port=htons(SERV_PORT);

  bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr));

  listen(listenfd, LISTENQ);



  maxi = 0; 

  for ( ; ; ) {

      //等待epoll事件的發生

      nfds=epoll_wait(epfd,events,20,500);

      //處理所發生的所有事件     

      for(i=0;i<nfds;++i)

      {

          if(events.data.fd==listenfd)

          {



            connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen);

            if(connfd<0){

                perror("connfd<0");

                exit(1);

            }

            setnonblocking(connfd);



            char *str = inet_ntoa(clientaddr.sin_addr);

            std::cout<<"connect from "<_u115 ?tr<<std::endl;

            //設置用於讀操作的文件描述符

            ev.data.fd=connfd;

            //設置用於注測的讀操作事件

            ev.events=EPOLLIN|EPOLLET;

            //註冊ev

            epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);

          }

          else if(events.events&EPOLLIN)

          {

            if ( (sockfd = events.data.fd) < 0) continue;

            if ( (n = read(sockfd, line, MAXLINE)) < 0) {

                if (errno == ECONNRESET) {



                    close(sockfd);

                    events.data.fd = -1;

                } else 

                    std::cout<<"readline error"<<std::endl;

            } else if (n == 0) {

                close(sockfd);

                events.data.fd = -1;

            }

            //設置用於寫操作的文件描述符

            ev.data.fd=sockfd;

            //設置用於注測的寫操作事件

            ev.events=EPOLLOUT|EPOLLET;

            //修改sockfd上要處理的事件爲EPOLLOUT

            epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);

          }

          else if(events.events&EPOLLOUT)

          {   

            sockfd = events.data.fd;

            write(sockfd, line, n);

            //設置用於讀操作的文件描述符

            ev.data.fd=sockfd;

            //設置用於注測的讀操作事件

            ev.events=EPOLLIN|EPOLLET;

            //修改sockfd上要處理的事件爲EPOLIN

            epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);

          }



      }



  }

}

 wyezl 回覆於:2006-08-22 11:50:49

引用:原帖由 nuclearweapon 於 2006-8-22 11:43 發表
剛纔測試了lz的程序。
50個併發,cfd最大達到37然後回落到5。

如上測試多次沒有發現cfd有增大的趨勢。
所以感覺和內核版本有關係。

測試環境:

kernel 2.6.15
gcc 4.0.3

distribution:ubuntu 


50個併發,cfd最大達到37然後回落到5。 是有這樣的現象。所以我說是增大趨勢。而沒有說直線增長。

這也不是你幾分鐘就能測試出來的。1024個fd也需要一個多小時才能耗盡(大約處理100萬請求)。而且是量比較大的線上測試。

 nuclearweapon 回覆於:2006-08-22 11:57:36

引用:原帖由 wyezl 於 2006-8-22 11:50 發表


50個併發,cfd最大達到37然後回落到5。 是有這樣的現象。所以我說是增大趨勢。而沒有說直線增長。

這也不是你幾分鐘就能測試出來的。1024個fd也需要一個多小時才能耗盡(大約處理100萬請求)。而且是量比較 ... 


你這裏說的耗盡是什麼意思?

表示Accept不能再接受client了嗎?

 nuclearweapon 回覆於:2006-08-22 12:02:51

就調試來說,當服務程序掛起的時候
你可以看如下目錄:
/proc/your_process_id/fd

看一看有那些fd在被你的進程使用!

 星之孩子 回覆於:2006-08-22 12:38:41

你另外一個模型就是epoll例子的模型吧
憑什麼說人家的就效率低了?

 wyezl 回覆於:2006-08-22 13:10:18

引用:原帖由 星之孩子 於 2006-8-22 12:38 發表
你另外一個模型就是epoll例子的模型吧
憑什麼說人家的就效率低了? 



我測試過。處理能力底了20%。

 思一克 回覆於:2006-08-22 13:11:53

to LZ,

你做什麼服務? 有多少同時連接?

 wyezl 回覆於:2006-08-22 13:18:46

引用:原帖由 nuclearweapon 於 2006-8-22 12:02 發表
就調試來說,當服務程序掛起的時候
你可以看如下目錄:
/proc/your_process_id/fd

看一看有那些fd在被你的進程使用! 


我一共監視了5000個描述符,程序跑了一天,基本上快耗完了。還剩不到1000個了。


ls /proc/24152/fd/
Display all 4051 possibilities? (y or n)

可見這些耗盡的描述符都在使用中。 但不知道什麼地方沒把它們釋放。

 wyezl 回覆於:2006-08-22 13:22:36

引用:原帖由 思一克 於 2006-8-22 13:11 發表
to LZ,

你做什麼服務? 有多少同時連接? 


http服務。簡單的數據。基本上是讀取內存的操作。類似股票行情數據。

當然每秒能處理越多請求越好。

 playmud 回覆於:2006-08-22 13:48:27

5000 個?我怎麼看你的listen才32?
    if (listen(fd, 32) != 0)
    {
          fprintf(stderr, "listen failed\n");
          return -1;
    }

SYNOPSIS
       #include <sys/socket.h>

       int listen(int sockfd, int backlog);

DESCRIPTION
       To  accept connections, a socket is first created with socket(2), a willingness to accept incoming connec-
       tions and a queue limit for incoming connections are specified with listen(), and then the connections are
       accepted with accept(2).  The listen() call applies only to sockets of type SOCK_STREAM or SOCK_SEQPACKET.

       The backlog parameter defines the maximum length the queue of pending connections may grow to.  If a  con-
       nection request arrives with the queue full the client may receive an error with an indication of ECONNRE-
       FUSED or, if the underlying protocol supports retransmission, the request may be ignored so  that  retries
       succeed.

 思一克 回覆於:2006-08-22 14:07:38

你的程序我看了。好象是有漏洞,而且是必須在大量連接,慢速的斷線纔可疑的。

一個fd有事件EPOLLIN後,如果斷線,EPOLLOUT永不再來,你的fd不就永遠不被關閉了嗎?

請討論。

 slay78 回覆於:2006-08-22 14:13:42

引用:原帖由 playmud 於 2006-8-22 13:48 發表
5000 個?我怎麼看你的listen才32?
    if (listen(fd, 32) != 0)
    {
          fprintf(stderr, "listen failed\n");
          return -1;
    }

SYNOPSIS
       #include <s ... 


人家這個32不是表示可以連32個的意思,這個32表示最多有32個同時在連並且都沒連上,第33個進不來而已

 思一克 回覆於:2006-08-22 14:14:15

雖然有HUP等else 控制。但如果EPOLLIN之後由於網絡的不好狀況,其它時間不在來?如何

 wyezl 回覆於:2006-08-22 14:32:13

引用:原帖由 playmud 於 2006-8-22 13:48 發表
5000 個?我怎麼看你的listen才32?
    if (listen(fd, 32) != 0)
    {
          fprintf(stderr, "listen failed\n");
          return -1;
    }

SYNOPSIS
       #include <s ... 



這個32與5000個同時在線並不矛盾。 

又不是說1秒內把這5000個連線全接受進來。
5000是我的epoll所監視的最大描述符個數。

 nuclearweapon 回覆於:2006-08-22 14:33:08

引用:原帖由 思一克 於 2006-8-22 14:14 發表
一個fd有事件EPOLLIN後,如果斷線,EPOLLOUT永不再來,你的fd不就永遠不被關閉了嗎?


由於有SO_KEEPALIVE可以避免這種情況吧(lz的程序也做了處理)。
否則就是client有問題,一直連着不放。

 nuclearweapon 回覆於:2006-08-22 14:34:54

引用:原帖由 wyezl 於 2006-8-22 13:18 發表


我一共監視了5000個描述符,程序跑了一天,基本上快耗完了。還剩不到1000個了。


ls /proc/24152/fd/
Display all 4051 possibilities? (y or n)

可見這些耗盡的描述符都在使用中。 但不知道什麼地方 ... 


用ls -l 看下是那些socket 
再用 netstat -a看下這些socket的狀態

 wyezl 回覆於:2006-08-22 14:38:09

引用:原帖由 思一克 於 2006-8-22 14:07 發表
你的程序我看了。好象是有漏洞,而且是必須在大量連接,慢速的斷線纔可疑的。
 



就是漫漫耗盡的。

第二種模型也一樣。出現這樣耗盡的情況。 以前都是用測試工具測試的,基本上看不出破綻。
在線上測試,就很明顯了。。

 思一克 回覆於:2006-08-22 14:41:07

to wyezl,

FD泄露,原因就是沒有close是無疑問的。
WHY MEIYOU close?
就是事件有的時候(比如網絡壞等原因)沒有到來。

 wyezl 回覆於:2006-08-22 14:51:33

引用:原帖由 nuclearweapon 於 2006-8-22 14:34 發表


用ls -l 看下是那些socket 
再用 netstat -a看下這些socket的狀態 



這是隻監視1024個fds的時候的部分貼圖。

# netstat -a
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State      
tcp        0      0 *:32768                     *:*                         LISTEN      
tcp        0      0 *:sunrpc                    *:*                         LISTEN      
tcp        0      0 *:http                      *:*                         LISTEN      
tcp        0      0 xxx.108.37.77:http          122.48.0.37:56652           SYN_RECV    
tcp        0      0 xxx.108.37.77:http          211.147.253.74:55689        SYN_RECV    
tcp        0      0 xxx.108.37.77:http          219.135.251.110:2669        SYN_RECV    
warning, got duplicate tcp line.
tcp        0      0 xxx.108.37.77:http          122.48.0.89:12881           SYN_RECV    
tcp        0      0 xxx.108.37.77:http          58.246.194.192:4254         SYN_RECV    
tcp        0      0 xxx.108.37.77:http          122.48.0.89:12923           SYN_RECV    
tcp        0      0 xxx.108.37.77:http          218.22.98.170:56851         SYN_RECV    
tcp        0      0 xxx.108.37.77:http          122.48.0.89:12888           SYN_RECV    
tcp        0      0 xxx.108.37.77:http          60.190.192.46:4072          SYN_RECV    
tcp        0      0 xxx.108.37.77:http          61.177.227.182:27180        SYN_RECV    
tcp        0      0 xxx.108.37.77:http          60.0.218.7:41237            SYN_RECV    
tcp        0      0 xxx.108.37.77:http          221.232.42.194:3713         SYN_RECV    
tcp        0      0 xxx.108.37.77:http          218.81.111.25:4495          SYN_RECV    
tcp        0      0 xxx.108.37.77:http          122.48.0.35:59812           SYN_RECV    
tcp        0      0 xxx.108.37.77:http          58.60.5.33:7660             SYN_RECV    
tcp        0      0 xxx.108.37.77:http          222.137.4.7:4296            SYN_RECV 


ls -l /proc/27952/fd/
rwx------  1 root root 64 Aug 22 14:44 837 -> socket:[31398309]
lrwx------  1 root root 64 Aug 22 14:44 838 -> socket:[31397749]
lrwx------  1 root root 64 Aug 22 14:44 839 -> socket:[31394222]
lrwx------  1 root root 64 Aug 22 14:38 84 -> socket:[30926430]
lrwx------  1 root root 64 Aug 22 14:44 840 -> socket:[31398135]
lrwx------  1 root root 64 Aug 22 14:44 841 -> socket:[31398930]
lrwx------  1 root root 64 Aug 22 14:44 842 -> socket:[31397984]
lrwx------  1 root root 64 Aug 22 14:44 843 -> socket:[31390139]
lrwx------  1 root root 64 Aug 22 14:44 844 -> socket:[31398331]
lrwx------  1 root root 64 Aug 22 14:44 845 -> socket:[31397572]
lrwx------  1 root root 64 Aug 22 14:44 846 -> socket:[31397546]
lrwx------  1 root root 64 Aug 22 14:44 847 -> socket:[31396094]
lrwx------  1 root root 64 Aug 22 14:44 848 -> socket:[31393666]
lrwx------  1 root root 64 Aug 22 14:44 849 -> socket:[31398932]
lrwx------  1 root root 64 Aug 22 14:38 85 -> socket:[30884712]
lrwx------  1 root root 64 Aug 22 14:44 852 -> socket:[31397555]
lrwx------  1 root root 64 Aug 22 14:44 856 -> socket:[31390278]
lrwx------  1 root root 64 Aug 22 14:44 858 -> socket:[31392652]
lrwx------  1 root root 64 Aug 22 14:44 859 -> socket:[31392710]
lrwx------  1 root root 64 Aug 22 14:38 86 -> socket:[30883810]
lrwx------  1 root root 64 Aug 22 14:38 87 -> socket:[30913192]
lrwx------  1 root root 64 Aug 22 14:38 88 -> socket:[30943036]
lrwx------  1 root root 64 Aug 22 14:38 89 -> socket:[31133080]
lrwx------  1 root root 64 Aug 22 14:38 9 -> socket:[30951877]
lrwx------  1 root root 64 Aug 22 14:38 90 -> socket:[30999630]
lrwx------  1 root root 64 Aug 22 14:38 91 -> socket:[31134432]
lrwx------  1 root root 64 Aug 22 14:38 92 -> socket:[30928870]
lrwx------  1 root root 64 Aug 22 14:38 93 -> socket:[30975324]
lrwx------  1 root root 64 Aug 22 14:38 94 -> socket:[30936083]

 wyezl 回覆於:2006-08-22 14:52:50

引用:原帖由 思一克 於 2006-8-22 14:41 發表
to wyezl,

FD泄露,原因就是沒有close是無疑問的。
WHY MEIYOU close?
就是事件有的時候(比如網絡壞等原因)沒有到來。 


這種情況怎麼處理纔好呢?

 思一克 回覆於:2006-08-22 14:55:05

你可以設置一個timeout, 超過的cfd(在ACCPTE處)一律關閉

 wyezl 回覆於:2006-08-22 14:56:56

引用:原帖由 nuclearweapon 於 2006-8-22 14:33 發表


由於有SO_KEEPALIVE可以避免這種情況吧(lz的程序也做了處理)。
否則就是client有問題,一直連着不放。 


我沒有支持KEEPALIVE。發送完處理,立刻就關閉描述符了。

 wyezl 回覆於:2006-08-22 15:01:52

引用:原帖由 思一克 於 2006-8-22 14:55 發表
你可以設置一個timeout, 超過的cfd(在ACCPTE處)一律關閉 

怎麼爲每個描述符設置超時?

 nuclearweapon 回覆於:2006-08-22 15:01:58

引用:原帖由 wyezl 於 2006-8-22 14:51 發表



這是隻監視1024個fds的時候的部分貼圖。

# netstat -a
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State ... 



難道有人攻擊你:-(

SYN_RECV有多少個!

 nuclearweapon 回覆於:2006-08-22 15:04:54

你打開tcp_syncookies
試試。
再做測試

 思一克 回覆於:2006-08-22 15:07:31

可能不是好方法:

while( ......... accept()  ....) {


  ....這裏檢查全局的time_t fd_create[5000];

}

關閉時設置fd_create[fd] = 0;

 wyezl 回覆於:2006-08-22 15:11:15

引用:原帖由 nuclearweapon 於 2006-8-22 15:01 發表



難道有人攻擊你:-(

SYN_RECV有多少個! 


幾百個吧。貼出來。省略一部分後面的。
# netstat -a
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State      
tcp        0      0 *:32768                     *:*                         LISTEN      
tcp        0      0 *:sunrpc                    *:*                         LISTEN      
tcp        0      0 *:http                      *:*                         LISTEN      
tcp        0      0 xxx.108.37.77:http          58.217.195.47:4689          SYN_RECV    
tcp        0      0 xxx.108.37.77:http          58.47.42.74:2899            SYN_RECV    
tcp        0      0 xxx.108.37.77:http          218.68.242.127:1238         SYN_RECV    
tcp        0      0 xxx.108.37.77:http          222.67.10.49:30384          SYN_RECV    
tcp        0      0 xxx.108.37.77:http          pc68.broad.dynamic.xm.:2215 SYN_RECV    
tcp        0      0 xxx.108.37.77:http          220.191.231.198:47700       SYN_RECV    
tcp        0      0 xxx.108.37.77:http          218.77.186.194:63425        SYN_RECV    
tcp        0      0 xxx.108.37.77:http          220.207.230.113:3652        SYN_RECV    
tcp        0      0 xxx.108.37.77:http          60.7.59.197:3216            SYN_RECV    
tcp        0      0 xxx.108.37.77:http          123.49.160.226:3127         SYN_RECV    
warning, got duplicate tcp line.
tcp        0      0 xxx.108.37.77:http          122.48.0.89:13698           SYN_RECV    
tcp        0      0 xxx.108.37.77:http          58.67.158.106:krb524        SYN_RECV    
tcp        0      0 xxx.108.37.77:http          218.79.187.16:3129          SYN_RECV    
tcp        0      0 xxx.108.37.77:http          122.48.0.89:13680           SYN_RECV    
tcp        0      0 xxx.108.37.77:http          60.171.192.39:2531          SYN_RECV    
tcp        0      0 xxx.108.37.77:http          60.216.170.48:xxx5          SYN_RECV    
warning, got duplicate tcp line.
tcp        0      0 xxx.108.37.77:http          218.22.68.194:1101          SYN_RECV    
tcp        0      0 xxx.108.37.77:http          219.137.172.101:15053       SYN_RECV    
tcp        0      0 xxx.108.37.77:http          61.141.94.21:4748           SYN_RECV    
tcp        0      0 xxx.108.37.77:http          58.24.101.69:4188           SYN_RECV    
tcp        0      0 xxx.108.37.77:http          222.82.225.134:3118         SYN_RECV    
tcp        0      0 xxx.108.37.77:http          59.52.119.145:55806         SYN_RECV    
tcp        0      0 xxx.108.37.77:http          124.248.1.69:50344          SYN_RECV    
tcp        0      0 xxx.108.37.77:http          220.171.79.196:3014         SYN_RECV    
tcp        0      0 xxx.108.37.77:http          123.49.164.148:55258        SYN_RECV    
tcp        0      0 xxx.108.37.77:http          212.193.163.60.broad.:63429 SYN_RECV    
tcp        0      0 xxx.108.37.77:http          122.48.0.89:13754           SYN_RECV    
tcp        0      0 xxx.108.37.77:http          61.188.210.2:3532           SYN_RECV    
tcp        0      0 xxx.108.37.77:http          122.48.0.89:13752           SYN_RECV    


[root@sina src]# netstat -an | more
warning, got duplicate tcp line.
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State      
tcp        0      0 0.0.0.0:32768               0.0.0.0:*                   LISTEN      
tcp        0      0 0.0.0.0:111                 0.0.0.0:*                   LISTEN      
tcp        0      0 0.0.0.0:80                  0.0.0.0:*                   LISTEN      
tcp        0      0 xxx.108.37.77:80            xxx.103.215.242:62165       SYN_RECV    
tcp        0      0 xxx.108.37.77:80            220.191.231.198:47700       SYN_RECV    
tcp        0      0 xxx.108.37.77:80            218.246.73.21:60164         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            222.64.136.219:2881         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            123.49.160.226:3127         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            60.63.11.185:22834          SYN_RECV    
tcp        0      0 xxx.108.37.77:80            58.51.39.146:1870           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            122.48.0.89:13698           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            211.147.240.74:3076         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            59.54.226.64:1135           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            218.28.166.198:61757        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            222.82.225.134:3118         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            58.35.242.69:23660          SYN_RECV    
tcp        0      0 xxx.108.37.77:80            211.92.156.131:1210         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            221.5.82.81:2100            SYN_RECV    
tcp        0      0 xxx.108.37.77:80            220.171.79.196:3014         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            60.163.193.212:63429        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            122.48.0.89:13754           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            61.188.210.2:3532           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            218.82.23.151:64811         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            122.48.0.89:13752           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            218.18.33.108:4641          SYN_RECV    
tcp        0      0 xxx.108.37.77:80            211.95.165.175:61439        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            218.28.188.86:52324         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            211.92.156.131:1215         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            211.92.156.131:1206         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            218.71.200.192:1503         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            221.200.137.120:1619        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            218.59.106.182:53008        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            211.69.18.125:3158          SYN_RECV    
tcp        0      0 xxx.108.37.77:80            218.81.193.198:1434         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            61.144.254.129:50050        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            211.92.156.131:1216         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            218.28.58.69:9898           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            220.234.241.178:1679        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            219.242.196.109:2261        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            124.21.243.228:1648         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            221.200.137.120:1620        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            222.35.126.211:1336         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            221.234.146.160:2188        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            222.169.187.100:3529        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            122.48.0.89:13748           SYN_RECV    
--More--warning, got duplicate tcp line.
warning, got duplicate tcp line.
warning, got duplicate tcp line.
tcp        0      0 xxx.108.37.77:80            218.246.73.21:60165         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            221.196.131.130:4444        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            220.174.8.13:12043          SYN_RECV    
tcp        0      0 xxx.108.37.77:80            211.95.165.175:61440        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            220.205.30.17:1519          SYN_RECV    
tcp        0      0 xxx.108.37.77:80            61.188.210.2:3531           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            60.163.193.212:63427        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            218.94.127.135:30598        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            219.155.143.97:1120         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            221.7.131.131:4331          SYN_RECV    
tcp        0      0 xxx.108.37.77:80            211.147.240.74:3077         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            210.76.66.35:1524           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            61.138.254.47:29233         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            59.35.87.21:11079           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            219.242.196.109:2260        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            220.205.30.17:1518          SYN_RECV    
tcp        0      0 xxx.108.37.77:80            58.62.84.52:4315            SYN_RECV    
tcp        0      0 xxx.108.37.77:80            221.216.74.228:4444         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            222.169.187.100:3528        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            211.92.156.131:1207         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            222.209.217.21:38807        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            219.238.191.17:17679        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            61.172.137.186:4075         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            211.92.156.131:1212         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            221.214.13.109:17600        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            218.23.149.98:46060         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            221.222.116.178:1658        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            xxx.103.215.242:62164       SYN_RECV    
tcp        0      0 xxx.108.37.77:80            222.173.191.91:1143         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            61.242.112.118:59158        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            222.209.217.21:38551        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            219.129.164.182:62607       SYN_RECV    
tcp        0      0 xxx.108.37.77:80            58.60.67.4:48095            SYN_RECV    
tcp        0      0 xxx.108.37.77:80            211.92.156.131:1213         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            122.48.0.89:13694           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            218.87.71.62:2363           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            61.173.14.106:4444          SYN_RECV    
tcp        0      0 xxx.108.37.77:80            220.234.241.178:1678        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            210.21.209.145:5554         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            122.48.0.89:13750           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            59.35.87.21:11080           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            61.172.137.186:4076         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            222.67.94.46:1434           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            122.48.0.89:13697           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            61.242.112.118:59160        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            58.35.242.69:23659          SYN_RECV    
--More--warning, got duplicate tcp line.
warning, got duplicate tcp line.
tcp        0      0 xxx.108.37.77:80            61.181.71.50:3946           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            211.92.156.131:1209         SYN_RECV    
tcp        0      0 xxx.108.37.77:80            60.63.11.185:22833          SYN_RECV    
tcp        0      0 xxx.108.37.77:80            122.48.0.89:13744           SYN_RECV    
tcp        0      0 xxx.108.37.77:80            222.137.152.197:4755        SYN_RECV    
tcp        0      0 xxx.108.37.77:80            220.169.5.239:3676          SYN_RECV    
tcp        0      0 127.0.0.1:631               0.0.0.0:*                   LISTEN      
tcp        0      0 127.0.0.1:25                0.0.0.0:*                   LISTEN      
tcp        0      0 xxx.108.37.77:80            221.6.163.131:35293         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.198.127.25:1956         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            60.2.106.52:58968           TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.222.144.24:1618         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            211.158.132.14:1808         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            218.89.188.200:60118        TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.0.180.142:58305         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            61.51.223.223:1992          TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            61.170.213.11:14704         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            219.136.26.220:3765         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            211.158.81.195:4087         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            218.71.200.192:1460         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.5.152.50:37803          TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            210.21.232.236:33515        TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.198.127.25:1957         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.15.17.77:1878           TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.1.244.62:1230           TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            61.155.209.139:2875         TIME_WAIT   
tcp        1      1 xxx.108.37.77:80            60.186.14.190:13808         CLOSING     
tcp        0      0 xxx.108.37.77:80            60.208.111.253:57016        TIME_WAIT   
tcp        1      1 xxx.108.37.77:80            219.145.113.15:52970        CLOSING     
tcp        0      0 xxx.108.37.77:80            61.170.213.11:14705         TIME_WAIT   
tcp        1      1 xxx.108.37.77:80            xxx.106.180.254:2266        CLOSING     
tcp        1      1 xxx.108.37.77:80            222.68.248.150:7632         CLOSING     
tcp        0      0 xxx.108.37.77:80            60.17.17.149:62063          TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            60.0.16.66:3159             TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            58.101.33.97:3616           TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            125.32.0.114:1367           TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            125.33.217.83:3502          TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            71.135.63.37:61595          TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            218.26.227.9:31478          TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            220.161.163.113:1141        TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.1.244.62:1229           TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            60.2.106.52:58970           TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.15.17.77:1877           TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            222.64.14.150:64545         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            222.67.166.24:2299          TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            61.51.223.223:1994          TIME_WAIT   
--More--warning, got duplicate tcp line.
warning, got duplicate tcp line.
tcp        0      0 xxx.108.37.77:80            61.155.18.18:56733          TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            60.15.21.5:12313            TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            58.33.225.117:2739          TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            61.167.60.224:58498         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            58.60.5.33:7454             TIME_WAIT   
tcp        0    243 xxx.108.37.77:80            218.71.200.192:1718         FIN_WAIT1   
tcp        0      0 xxx.108.37.77:80            123.49.164.148:55288        TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            125.33.217.83:3501          TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.214.13.109:47024        TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.218.117.87:17919        TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            218.108.44.10:21325         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            124.200.18.190:3484         TIME_WAIT   
tcp        0    300 xxx.108.37.77:80            221.218.117.87:26879        FIN_WAIT1   
tcp        0    567 xxx.108.37.77:80            221.218.117.87:27135        FIN_WAIT1   
tcp        0      0 xxx.108.37.77:80            218.26.227.9:31479          TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            220.161.163.113:1140        TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.222.144.24:1617         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            60.2.106.52:58971           TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.5.181.31:3752           TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            61.49.166.124:62700         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.0.180.142:58306         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            61.51.223.223:1995          TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            60.208.111.253:57018        TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            xxx.111.152.6:56054         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            58.33.225.117:2738          TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            61.181.245.85:25618         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            123.49.164.148:55289        TIME_WAIT   
tcp        1      1 xxx.108.37.77:80            125.92.214.174:6619         CLOSING     
tcp        0      0 xxx.108.37.77:80            124.200.18.190:3483         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            60.166.100.106:48296        TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            222.223.6.176:40086         ESTABLISHED 
tcp        0      0 xxx.108.37.77:80            124.42.126.70:55826         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.226.242.212:64827       TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.198.127.25:1952         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            222.64.14.150:64295         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            xxx.106.113.61:6182         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.229.12.88:4273          TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            61.49.166.124:62699         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            221.8.9.91:5210             TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            210.21.196.86:11655         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            218.87.255.183:3552         TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            61.181.250.190:32782        TIME_WAIT   
tcp        0      0 xxx.108.37.77:80            125.33.217.83:3499          TIME_WAIT

 wyezl 回覆於:2006-08-22 15:12:21

引用:原帖由 nuclearweapon 於 2006-8-22 15:04 發表
你打開tcp_syncookies
試試。
再做測試 

怎麼打開?

 思一克 回覆於:2006-08-22 15:14:30

wyezl,

攻擊不會影響FD。FD是已經建立的連接沒close.

 思一克 回覆於:2006-08-22 15:15:40

這個帖子可以做爲精華了。因爲問題微妙又棘手,有普遍性,尤其對與EPOLL

 nuclearweapon 回覆於:2006-08-22 15:17:25

如果一直有大量的SYN_RECV就有可能是攻擊。

echo 1 > /proc/sys/net/ipv4/tcp_syncookies
可以打開。

下邊還有幾個都是對付 ddos的。太具體要自己查查。

 wyezl 回覆於:2006-08-22 15:23:30

引用:原帖由 思一克 於 2006-8-22 15:14 發表
wyezl,

攻擊不會影響FD。FD是已經建立的連接沒close. 



我暫時的方法是描述符用完了,就退出。
由監視程序定時檢查和重啓。
這不知道是不是epoll自身的缺陷。

 nuclearweapon 回覆於:2006-08-22 15:25:25

引用:原帖由 思一克 於 2006-8-22 15:14 發表
wyezl,

攻擊不會影響FD。FD是已經建立的連接沒close. 


我記得和斑竹有一些出入。
因爲:
只要有socket就會有一個inode,也就會有一個fd。而不是連接以後纔會有fd的。
對於linux2。6來說
每當在內核創建完struct socket後,就會調用sock_map_fd()在調用進程創建一個fd。

如果有SYN_RECV狀態,也就有了struct socket,也就有了inode,進而有了fd。
希望指正!

[ 本帖最後由 nuclearweapon 於 2006-8-22 15:27 編輯 ]

 思一克 回覆於:2006-08-22 15:26:40

應該不是epoll本身的問題。epoll採樣事件,如果事件沒有來它也無能爲力。timeout是必須的

 wyezl 回覆於:2006-08-22 15:27:28

引用:原帖由 思一克 於 2006-8-22 15:15 發表
這個帖子可以做爲精華了。因爲問題微妙又棘手,有普遍性,尤其對與EPOLL 


如果能討論出一個成熟的,通用的epoll模型。 加精華也不虧。呵呵。
我繼續線上測試。

uname -a
Linux xxx.com.cn 2.6.9-34.EL #1 Wed Mar 8 00:07:35 CST 2006 i686 i686 i386 GNU/Linux
[finance@sina src]$ cat /etc/issue
CentOS release 4.1 (Final)
Kernel \r on an \m

 思一克 回覆於:2006-08-22 15:30:35

TO nuclearweapon,

你說的對。但他的問題是accept之後的fd被耗盡了,那就是有連接的socket.

如果那些DDOS攻擊,往往是半連接(部分IP包),accept不了。


引用:原帖由 nuclearweapon 於 2006-8-22 15:25 發表


我記得和斑竹有一些出入。
因爲:
只要有socket就會有一個inode,也就會有一個fd。而不是連接以後纔會有fd的。
對於linux2。6來說
每當在內核創建完struct socket後,就會調用sock_map_fd()在調用進程創建 ... 


 nuclearweapon 回覆於:2006-08-22 15:35:28

引用:原帖由 思一克 於 2006-8-22 15:30 發表
TO nuclearweapon,

你說的對。但他的問題是accept之後的fd被耗盡了,那就是有連接的socket.

如果那些DDOS攻擊,往往是半連接(部分IP包),accept不了。


 



就是這半連接的socket把fd用完了。
因爲只有有了socket纔可能有sycrcv狀態。

 思一克 回覆於:2006-08-22 15:42:47

To nuclearweapon,

也有可能是攻擊引起的。讓他繼續實驗。
半連接accept能成功返回fd嗎?我不是十分肯定。

 nuclearweapon 回覆於:2006-08-22 15:45:33

對於半連接來說accpet是不能返回了,但是在內核中fd已經建立起來了,也就消耗了一個進程的可用fd數量。

 wyezl 回覆於:2006-08-22 15:50:57

引用:原帖由 思一克 於 2006-8-22 15:42 發表
To nuclearweapon,

也有可能是攻擊引起的。讓他繼續實驗。
半連接accept能成功返回fd嗎?我不是十分肯定。 


現在訪問量下降了。
SYN_RECV 也下降到了只有20~30個。

惡意攻擊的可能性比較小。
是不是由於我的次序造成的?

 思一克 回覆於:2006-08-22 15:52:56

TO nuclearweapon,

你肯定沒有建立的連接,沒有accept的也消耗KERNEL中的fd嗎? 我不SURE

 wyezl 回覆於:2006-08-22 15:54:38

引用:原帖由 nuclearweapon 於 2006-8-22 15:45 發表
對於半連接來說accpet是不能返回了,但是在內核中fd已經建立起來了,也就消耗了一個進程的可用fd數量。 



我可以統計一段時間內, accpet的總數,和close的總數。看他們的差是不是等於。
/etc/pid/fd 下面的fd數。

 思一克 回覆於:2006-08-22 16:00:55

TO nuclearweapon,

我剛纔看了KERNEL代碼,看到fd消耗只有3個函數socket() , accept(), socketpair(), 而且都是成功後才消耗fd. 網絡程序其它任何地方沒有看到用fd的?

半連接能消耗fd嗎,我不是很清楚。如果你清楚就寫出來。

謝謝

引用:原帖由 nuclearweapon 於 2006-8-22 15:45 發表
對於半連接來說accpet是不能返回了,但是在內核中fd已經建立起來了,也就消耗了一個進程的可用fd數量。 


 wyezl 回覆於:2006-08-22 16:05:40

現在訪問量比較少的時候。fd隨時間變化情況。當然是重新啓動之後測試的。這樣看起來基本沒問題。
壓力上來後就漫漫變了。

[root@xxx ~]# ls /proc/28305/fd/
0   10  12  14  16  18  2   21  23  25  28  4   6   8   
1   11  13  15  17  19  20  22  24  27  3   5   7   9   
[root@xxx ~]# ls /proc/28305/fd/
0   10  12  14  16  18  2   21  23  25  28  4   6   8   
1   11  13  15  17  19  20  22  24  27  3   5   7   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  12  13  14  15  16  17  18  19  2   20  21  22  24  3   4   5   6   7   8   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   11  12  13  14  15  16  17  18  2   20  21  24  3   4   5   6   8   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  12  13  14  15  16  17  18  2   20  21  24  3   4   5   6   8   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  12  13  14  15  16  17  18  2   20  21  24  3   4   5   6   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  12  13  14  15  16  17  18  2   24  3   4   5   6   7   8   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  12  13  14  15  18  2   3   4   5   6   7   8   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  12  13  14  15  16  18  2   3   4   5   6   7   8   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  13  14  15  16  17  18  2   3   4   5   6   7   8   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  12  13  14  15  16  17  18  19  2   3   4   5   6   7   8   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  12  13  14  15  16  17  2   3   4   5   6   7   8   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   11  12  13  14  15  17  19  2   3   4   5   6   7   8   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  12  13  14  15  17  19  2   3   4   5   6   7   8   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  13  14  15  17  19  2   3   4   5   6   7   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  13  14  15  16  17  19  2   3   4   5   6   7   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   11  12  13  14  15  16  17  19  2   3   4   5   6   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  13  14  15  16  17  18  19  2   20  21  3   4   5   6   7   8   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  13  14  15  16  17  18  19  2   20  21  3   4   5   6   7   8   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  12  13  14  15  16  17  18  19  2   20  21  3   4   5   6   7   8   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  12  13  14  15  17  18  19  2   20  21  3   4   5   6   7   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  12  13  14  15  17  18  19  2   20  21  3   4   5   6   7   8   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  12  13  14  15  18  2   20  21  3   4   5   6   7   8   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  12  13  14  15  2   21  3   4   5   6   7   8   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  12  13  14  15  2   3   4   5   6   7   8   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  12  13  14  15  2   3   4   5   6   7   8   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  12  13  14  15  2   3   4   5   6   7   8   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  13  14  15  2   3   4   5   6   7   8   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  14  15  2   3   4   5   6   7   8   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  13  14  15  2   3   4   5   6   7   8   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  14  15  2   3   4   5   6   8   
[root@xxx ~]# ls /proc/28305/fd/
0   1   10  11  12  14  15  2   3   4   5   6   7   8   9   
[root@xxx ~]# ls /proc/28305/fd/
0   1   11  14  15  2   3   4   5   6   7   8

[ 本帖最後由 wyezl 於 2006-8-22 16:09 編輯 ]

 思一克 回覆於:2006-08-22 16:09:52

TO LZ,

我的服務器也有許多攻擊 SYN_RECV. 但基本不影響,更沒有fd沒有了的情況。

 wyezl 回覆於:2006-08-22 16:22:51

是在線上測試嗎?一小時能達到100萬請求數的時候就能看出來了。

用測試工具好象不行。測試不錯破綻。
我現在訪問量也下降了,不好測了。

 思一克 回覆於:2006-08-22 16:26:09

to LZ,

我試圖在我一個SERVER上跑80(有IP),遺憾的是無法編譯,因爲EPOLL支持問題

 wyezl 回覆於:2006-08-22 16:32:42

引用:原帖由 思一克 於 2006-8-22 16:26 發表
to LZ,

我試圖在我一個SERVER上跑80(有IP),遺憾的是無法編譯,因爲EPOLL支持問題 


謝謝熱心的斑竹,耽誤了你不少時間。
這個沒有環境不好測。 
我再自己想想辦法。
不知道有沒有什麼可以跟蹤一個描述符使用的。

剛纔模擬100萬個請求,只丟了一個描述符。 模擬很難發現問題。
肯定是跟網絡狀態有關。

[ 本帖最後由 wyezl 於 2006-8-22 16:40 編輯 ]

 思一克 回覆於:2006-08-22 16:45:47

不用謝。僅僅是我覺得你這個程序的完善有意義。我也從中學習了。

 nuclearweapon 回覆於:2006-08-22 16:50:09

引用:原帖由 思一克 於 2006-8-22 16:00 發表
TO nuclearweapon,

我剛纔看了KERNEL代碼,看到fd消耗只有3個函數socket() , accept(), socketpair(), 而且都是成功後才消耗fd. 網絡程序其它任何地方沒有看到用fd的?

半連接能消耗fd嗎,我不是很清楚。如 ... 


按照我的理解,netstat都看到了socket的狀態所以就應當有 struct socket了。



可能是我錯了。
我也在看代碼。查找原因

多謝指教!

 tysn 回覆於:2006-08-22 16:55:50

可不可以不用分成
if(events[ i].events & EPOLLIN)

else if(events[ i].events & EPOLLOUT)
兩種情況?

只要有事件來就進行讀寫,
當然要對讀寫cfd的函數進行出錯判斷,一旦出錯就close(cfd)
這樣就能避免EPOLLN之後EPOLLOUT永不再來的情況?
引用:原帖由 思一克 於 2006-8-22 14:07 發表
你的程序我看了。好象是有漏洞,而且是必須在大量連接,慢速的斷線纔可疑的。

一個fd有事件EPOLLIN後,如果斷線,EPOLLOUT永不再來,你的fd不就永遠不被關閉了嗎?

請討論。 

前提是假定產生EPOLLN事件(可讀)的socket一定也可寫

[ 本帖最後由 tysn 於 2006-8-22 16:59 編輯 ]

 思一克 回覆於:2006-08-22 16:56:45

TO nuclearweapon,

我也不是多肯定,所以談不到指教。你太客氣。

fd好象是最後需要(僅僅是一個下標),而且都是本地(本問題中是SERVER)的APP的直接調用SOCK函數纔可以產生。CLIENT要想在本機上產生fd, accept要成功

 wyezl 回覆於:2006-08-22 17:06:47

通過觀察知道
lrwx------  1 root root 64 Aug 22 16:25 480 -> socket:[34370805]
480這個描述符是死掉。不能回收了。我怎麼看它的狀態?



# ls -l  /proc/28477/fd/
total 20
lrwx------  1 root root 64 Aug 22 16:14 0 -> /dev/pts/3
lrwx------  1 root root 64 Aug 22 16:14 1 -> /dev/pts/3
lrwx------  1 root root 64 Aug 22 16:14 10 -> socket:[34584054]
lrwx------  1 root root 64 Aug 22 16:14 11 -> socket:[34584140]
lrwx------  1 root root 64 Aug 22 16:14 12 -> socket:[32509524]
lrwx------  1 root root 64 Aug 22 16:14 13 -> /usr/home/fi/src/home/ww
lrwx------  1 root root 64 Aug 22 16:14 14 -> socket:[34584144]
lrwx------  1 root root 64 Aug 22 16:14 15 -> socket:[34584145]
lrwx------  1 root root 64 Aug 22 16:14 16 -> socket:[34584147]
lrwx------  1 root root 64 Aug 22 16:14 17 -> socket:[34584152]
lrwx------  1 root root 64 Aug 22 16:14 2 -> /dev/pts/3
lrwx------  1 root root 64 Aug 22 16:16 23 -> socket:[34543624]
lr-x------  1 root root 64 Aug 22 16:14 3 -> eventpoll:[32224366]
lrwx------  1 root root 64 Aug 22 16:14 4 -> socket:[32224367]
lrwx------  1 root root 64 Aug 22 16:25 480 -> socket:[34370805]
lr-x------  1 root root 64 Aug 22 16:14 5 -> /usr/home/fi/src/home/ww
lrwx------  1 root root 64 Aug 22 16:36 6 -> socket:[34581817]
lrwx------  1 root root 64 Aug 22 16:14 7 -> socket:[33195785]
lrwx------  1 root root 64 Aug 22 16:36 8 -> socket:[34584130]
lrwx------  1 root root 64 Aug 22 16:14 9 -> socket:[34583662]

 wyezl 回覆於:2006-08-22 17:10:11

引用:原帖由 tysn 於 2006-8-22 16:55 發表
可不可以不用分成
if(events[ i].events & EPOLLIN)

else if(events[ i].events & EPOLLOUT)
兩種情況?

只要有事件來就進行讀寫,
當然要對讀寫cfd的函數進行出錯判斷,一旦出錯就close(cfd) ... 


這個效率有點低。
因爲可能會產生等待可寫。

 playmud 回覆於:2006-08-22 17:35:00

引用:原帖由 wyezl 於 2006-8-22 14:32 發表



這個32與5000個同時在線並不矛盾。 

又不是說1秒內把這5000個連線全接受進來。
5000是我的epoll所監視的最大描述符個數。 

怎麼不矛盾了?
你是if ((fd = socket(AF_INET, SOCK_STREAM, 0)) <= 0)
你把listen設成5000看看。

 wyezl 回覆於:2006-08-22 17:43:33

引用:原帖由 playmud 於 2006-8-22 17:35 發表

怎麼不矛盾了?
你是if ((fd = socket(AF_INET, SOCK_STREAM, 0)) <= 0)
你把listen設成5000看看。 


5000以內的描述符我都能 接受到。

設那個沒什麼影響。 估計listen也不能支持那麼大的。

 playmud 回覆於:2006-08-22 17:49:59

tcp正常的斷開需要3次或者4次握手確認,如果沒有這個確認他就會保持一定的時間。
/proc/sys/net/ipv4/tcp_keepalive_time

 playmud 回覆於:2006-08-22 17:50:54

引用:原帖由 wyezl 於 2006-8-22 17:43 發表


5000以內的描述符我都能 接受到。

設那個沒什麼影響。 估計listen也不能支持那麼大的。 

你能接收到和你能處理掉不是一個概念。

 playmud 回覆於:2006-08-22 17:52:18

實在找不到原因,你可以把那個默認超時時間設成10幾秒或者幾十秒。
系統幫你釋放掉佔用的資源。

 wyezl 回覆於:2006-08-22 17:53:34

引用:原帖由 playmud 於 2006-8-22 17:49 發表
tcp正常的斷開需要3次或者4次握手確認,如果沒有這個確認他就會保持一定的時間。
/proc/sys/net/ipv4/tcp_keepalive_time 


我在服務器端處理完請求就close不行嗎?

有的死掉的描述符再給它一個小時它也不會釋放了,肯定是沒close。

 wyezl 回覆於:2006-08-22 18:02:01

看了lighttpd代碼。有了點思路。明天再繼續修改。




#include <sys/types.h>

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>

#include "fdevent.h"
#include "settings.h"
#include "buffer.h"

#ifdef USE_LINUX_EPOLL
static void fdevent_linux_sysepoll_free(fdevents *ev) {
close(ev->epoll_fd);
free(ev->epoll_events);
}

static int fdevent_linux_sysepoll_event_del(fdevents *ev, int fde_ndx, int fd) {
struct epoll_event ep;

if (fde_ndx < 0) return -1;

memset(&ep, 0, sizeof(ep));

ep.data.fd = fd;
ep.data.ptr = NULL;

if (0 != epoll_ctl(ev->epoll_fd, EPOLL_CTL_DEL, fd, &ep)) {
fprintf(stderr, "%s.%d: epoll_ctl failed: %s, dying\n", __FILE__, __LINE__, strerror(errno));

SEGFAULT();

return 0;
}


return -1;
}

static int fdevent_linux_sysepoll_event_add(fdevents *ev, int fde_ndx, int fd, int events) {
struct epoll_event ep;
int add = 0;

if (fde_ndx == -1) add = 1;

memset(&ep, 0, sizeof(ep));

ep.events = 0;

if (events & FDEVENT_IN)  ep.events |= EPOLLIN;
if (events & FDEVENT_OUT) ep.events |= EPOLLOUT;

/**
 *
 * with EPOLLET we don't get a FDEVENT_HUP
 * if the close is delay after everything has
 * sent.
 *
 */

ep.events |= EPOLLERR | EPOLLHUP /* | EPOLLET */;

ep.data.ptr = NULL;
ep.data.fd = fd;

if (0 != epoll_ctl(ev->epoll_fd, add ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd, &ep)) {
fprintf(stderr, "%s.%d: epoll_ctl failed: %s, dying\n", __FILE__, __LINE__, strerror(errno));

SEGFAULT();

return 0;
}

return fd;
}

static int fdevent_linux_sysepoll_poll(fdevents *ev, int timeout_ms) {
return epoll_wait(ev->epoll_fd, ev->epoll_events, ev->maxfds, timeout_ms);
}

static int fdevent_linux_sysepoll_event_get_revent(fdevents *ev, size_t ndx) {
int events = 0, e;

e = ev->epoll_events[ndx].events;
if (e & EPOLLIN) events |= FDEVENT_IN;
if (e & EPOLLOUT) events |= FDEVENT_OUT;
if (e & EPOLLERR) events |= FDEVENT_ERR;
if (e & EPOLLHUP) events |= FDEVENT_HUP;
if (e & EPOLLPRI) events |= FDEVENT_PRI;

return e;
}

static int fdevent_linux_sysepoll_event_get_fd(fdevents *ev, size_t ndx) {
# if 0
fprintf(stderr, "%s.%d: %d, %d\n", __FILE__, __LINE__, ndx, ev->epoll_events[ndx].data.fd);
# endif

return ev->epoll_events[ndx].data.fd;
}

static int fdevent_linux_sysepoll_event_next_fdndx(fdevents *ev, int ndx) {
size_t i;

UNUSED(ev);

i = (ndx < 0) ? 0 : ndx + 1;

return i;
}

int fdevent_linux_sysepoll_init(fdevents *ev) {
ev->type = FDEVENT_HANDLER_LINUX_SYSEPOLL;
#define SET(x) \
ev->x = fdevent_linux_sysepoll_##x;

SET(free);
SET(poll);

SET(event_del);
SET(event_add);

SET(event_next_fdndx);
SET(event_get_fd);
SET(event_get_revent);

if (-1 == (ev->epoll_fd = epoll_create(ev->maxfds))) {
fprintf(stderr, "%s.%d: epoll_create failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n",
__FILE__, __LINE__, strerror(errno));

return -1;
}

if (-1 == fcntl(ev->epoll_fd, F_SETFD, FD_CLOEXEC)) {
fprintf(stderr, "%s.%d: epoll_create failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n",
__FILE__, __LINE__, strerror(errno));

close(ev->epoll_fd);

return -1;
}

ev->epoll_events = malloc(ev->maxfds * sizeof(*ev->epoll_events));

return 0;
}

#else
int fdevent_linux_sysepoll_init(fdevents *ev) {
UNUSED(ev);

fprintf(stderr, "%s.%d: linux-sysepoll not supported, try to set server.event-handler = \"poll\" or \"select\"\n",
__FILE__, __LINE__);

return -1;
}
#endif



 wyezl 回覆於:2006-08-22 18:25:47

引用:原帖由 playmud 於 2006-8-22 17:52 發表
實在找不到原因,你可以把那個默認超時時間設成10幾秒或者幾十秒。
系統幫你釋放掉佔用的資源。 

哪個默認的超時?

 nuclearweapon 回覆於:2006-08-22 22:36:10

引用:原帖由 wyezl 於 2006-8-22 17:06 發表
通過觀察知道
lrwx------  1 root root 64 Aug 22 16:25 480 -> socket:[34370805]
480這個描述符是死掉。不能回收了。我怎麼看它的狀態?



# ls -l  /proc/28477/fd/
total 20
lrwx------  1 root  ... 


34370805是inode號
cat /proc/net/tcp|grep 34370805   

第2、3項爲socket的adress:port


還可以:
netstat -a | grep _pid_
如果照你說只有一個的話就很好分析出是拿一個是那個死掉的。看看他的狀態是什麼

 safedead 回覆於:2006-08-22 23:15:43

我寫的一個非常簡單的epoll服務器, 
監聽從8000到12999共計5000個端口
預創建4096個線程
主線程用線程鎖調度

具體代碼見: http://www.cublog.cn/u/17999/showart.php?id=159057

 wyezl 回覆於:2006-08-23 09:48:09

引用:原帖由 nuclearweapon 於 2006-8-22 22:36 發表


34370805是inode號
cat /proc/net/tcp|grep 34370805   

第2、3項爲socket的adress:port


還可以:
netstat -a | grep _pid_
如果照你說只有一個的話就很好分析出是拿一個是那個死掉的。看看他的狀 ... 


死掉的fd。
lrwx------  1 root root 64 Aug 23 09:37 7 -> socket:[35074112]

#cat /proc/net/tcp|grep 35074112
17392: 4D256CCA:0050 4ACF18DA:0843 01 00000000:00000000 00:00000000 00000000   527        0 35074112 1 f4a71020 3000 0 0 2 -1 

netstat -a | grep _pid

這個pid是誰的? 
怎麼看狀態?

 nuclearweapon 回覆於:2006-08-23 12:00:27

引用:原帖由 wyezl 於 2006-8-23 09:48 發表


死掉的fd。
lrwx------  1 root root 64 Aug 23 09:37 7 -> socket:[35074112]

#cat /proc/net/tcp|grep 35074112
17392: 4D256CCA:0050 4ACF18DA:0843 01 00000000:00000000 00:00000000 00000000   ... 



實在對不起是我寫錯了
應該是
netstat -a| grep port

 wyezl 回覆於:2006-08-23 14:38:11

time_t cfds[MAXFDS];
time_t  now;
加了一個監視線程。每次accept的時候cfds[cfd]=time(NULL);
close(cfd)的時候 cfds[cfd]=0; 臨時解決一下。




void *loop_check(void *p)
{
        int i;
        struct epoll_event ev;
        while(1)
        {
                time(&now);
                for(i=0;i<MAXFDS;i++)
                {
                        if(cfds!=0)
                                if(now-cfds>TIMEOUT)
                                {
                                        printf("cfd=%d timeout.\n",i);

                                        ev.data.fd = i;
                                        if(epoll_ctl(epfd, EPOLL_CTL_DEL, i, &ev)!=0)
                                                printf("can't del epoll.\n");

                                        if(close(i)==0)
                                                cfds=0;
                                        else
                                                printf("can't close cfds.\n");

                                }
                }
                sleep(SLEEPTIME);
        }
        return NULL;
}




60s檢查一次。發現這樣檢查出來的還真不少。
./httpd
cfd=8 timeout.
cfd=11 timeout.
cfd=12 timeout.
cfd=17 timeout.
cfd=21 timeout.
cfd=30 timeout.
cfd=32 timeout.
cfd=51 timeout.
cfd=52 timeout.
cfd=59 timeout.
cfd=60 timeout.
cfd=71 timeout.
cfd=76 timeout.
cfd=7 timeout.
cfd=10 timeout.
cfd=14 timeout.
cfd=15 timeout.
cfd=16 timeout.
cfd=18 timeout.
cfd=20 timeout.
cfd=25 timeout.
cfd=33 timeout.
cfd=36 timeout.
cfd=53 timeout.
cfd=58 timeout.

 思一克 回覆於:2006-08-23 14:42:07

原因看來就是我原來想的那樣。

 wyezl 回覆於:2006-08-23 15:24:58

這樣做效率低了一些。暫時也沒好的辦法。
如果用線程池效率也是會有影響。

猜測是,一些fd加入了epoll的監視中,但確沒有任何事件觸發。
所以就都死到裏面了。

 思一克 回覆於:2006-08-23 15:27:27

基本不太影響效率。

依賴事件驅動的程序要有TIMEOUT。否則萬一有遺漏,壞情況就出現了

 wyezl 回覆於:2006-08-23 15:28:44

有個問題想問一下,client連接的時候發來的請求,我只讀了一次,有可能沒讀完,因爲我只需要一部分數據,剩餘的數據憋在裏面,我就把描述符關閉了,會不會有問題。

 思一克 回覆於:2006-08-23 15:30:23

應該沒有問題

 wyezl 回覆於:2006-08-23 15:31:32

引用:原帖由 思一克 於 2006-8-23 15:27 發表
基本不太影響效率。

依賴事件驅動的程序要有TIMEOUT。否則萬一有遺漏,壞情況就出現了 

以前一秒能處理1。1萬請求,現在只剩1.0萬了。趕上檢查的時候還不到。

等待以後有人能完善。
不知道是不是我操作系統的原因。
centos

[ 本帖最後由 wyezl 於 2006-8-23 15:37 編輯 ]

 思一克 回覆於:2006-08-23 15:39:58

10%了

 思一克 回覆於:2006-08-23 16:52:33

你的監視縣城有問題,消耗大。

從1找到當前有的最大FD,而你MAXFD。
還有未必用一個縣城呀,在accept循環處每次監視一部分,是不是能節省?

縣城====THREAD

 safedead 回覆於:2006-08-23 19:10:49

你有沒有CLOSE_WAIT狀態的套接字?
默認設置下系統要12小時才釋放CLOSE_WAIT狀態的描述字
需要設置SO_LINGER把CLOSE_WAIT超時改小, 我一般是設在60秒, 
但不要設成0, 那樣對客戶端非常不友好(0表示不用FIN終止連接而是用RST強行釋放)
另外要設置ulimits -n 把進程能夠打開的描述字上限提高, 默認是1024, 我一般設到16384甚至65536
我那個小程序僅僅LISTEN就佔用5000個描述字, 連接到達線程池上限則佔用9000多

epoll我認爲只用於LISTEN套接字肯定沒問題

 playmud 回覆於:2006-08-24 08:42:12

建議摟住還是找出來世紀的原因,這樣對大家避免類似的錯誤都有好處。
短時間如果連接太多的話還需要增加arp表的大小,默認是1024

 wyezl 回覆於:2006-08-24 11:06:48

引用:原帖由 safedead 於 2006-8-23 19:10 發表
你有沒有CLOSE_WAIT狀態的套接字?
默認設置下系統要12小時才釋放CLOSE_WAIT狀態的描述字
需要設置SO_LINGER把CLOSE_WAIT超時改小, 我一般是設在60秒, 
但不要設成0, 那樣對客戶端非常不友好(0表示不用FIN終止連 ... 


很早以前就看過你寫的文章,不過是被轉載到其他地方去的,想必一定有不少編寫高性能server的經驗了。



 netstat -an | grep CLOSE_WAIT 未發現結果。
我是用toot啓動,然後setuid 到別的用戶的。所以可以直接在程序中直接設置MAXFDS,不需要使用ulimit。 我的應用屬於請求比較繁忙,處理時間非常短,單個請求數據量小,的情況。 對我來說開多個線程效率可能會降低不少。

epoll的最大長處在於監視大量在線用戶,也就是長連接,少量活躍用戶的情況。

如果只用於LISTEN套接字,而不去監視其它fd的事件,好象發揮不出它的優勢吧?

 nuclearweapon 回覆於:2006-08-24 20:30:11

to wyezl
請問,你做的測試有超過2個小時的嗎?
或者說你對比過時間t1時候的fd個數和(t1+2小時)後的發的個數嗎?

 wyezl 回覆於:2006-08-25 09:39:29

引用:原帖由 nuclearweapon 於 2006-8-24 20:30 發表
to wyezl
請問,你做的測試有超過2個小時的嗎?
或者說你對比過時間t1時候的fd個數和(t1+2小時)後的發的個數嗎? 


有超過一天的。
訪問量小的時候描述符基本不損耗。
訪問兩大的時候。程增長趨勢。會耗盡。

 思一克 回覆於:2006-08-25 09:56:24

LZ, 可以將標題改一下?

 比如叫
epoll的使用及其程序XXX泄露問題探討

等。這樣便於查找問題

 wyezl 回覆於:2006-08-25 10:19:24

標題已修改。
epoll都出來很久了,難道這個問題以前都沒人遇到過?

 wyezl 回覆於:2006-08-25 14:08:51

man epoll

其中關於EAGAIN 的部分我難以理解,誰能指點一下?



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â?™t.  Receiving  an event from epoll_wait(2) should suggest to you that such
                     file descriptor is ready for the requested I/O operation. You have simply  to  consider
                     it  ready  until  you will receive the next EAGAIN. When and how you will use such file
                     descriptor is entirely up to you. Also, the condition that the read/write I/O space  is
                     exhausted  can be detected by checking the amount of data read/write from/to the target
                     file descriptor. For example, if you call read(2) by asking to read a certain amount of
                     data and read(2) returns a lower number of bytes, you can be sure to have exhausted the
                     read I/O space for such file descriptor. Same is valid when writing using the  write(2)
                     function.

POSSIBLE PITFALLS AND WAYS TO AVOID THEM
              o Starvation ( Edge Triggered )

              If  there  is a large amount of I/O space, it is possible that by trying to drain it the other
              files will not get processed causing starvation. This is not specific to epoll.

              The solution is to maintain a ready list and mark the file descriptor as ready in its  associ-
              ated  data structure, thereby allowing the application to remember which files need to be pro-
              cessed but still round robin amongst all the ready files. This also supports  ignoring  subse-
              quent events you receive for fdâ?™s that are already ready.

              o If using an event cache...

              If you use an event cache or store all the fdâ?™s returned from epoll_wait(2), then make sure to
              provide a way to mark its closure dynamically (ie- caused by a previous  eventâ?™s  processing).
              Suppose  you receive 100 events from epoll_wait(2), and in eventi #47 a condition causes event
              #13 to be closed.  If you remove the structure and close() the fd for  event  #13,  then  your
              event cache might still say there are events waiting for that fd causing confusion.

              One  solution for this is to call, during the processing of event 47, epoll_ctl(EPOLL_CTL_DEL)
              to delete fd 13 and close(), then mark its associated data structure as removed and link it to
              a  cleanup  list.  If you find another event for fd 13 in your batch processing, you will dis-
              cover the fd had been previously removed and there will be no confusion.

CONFORMING TO
       epoll(4) is a new API introduced in Linux kernel 2.5.44.  Its interface should be finalized in  Linux
       kernel 2.5.66.

SEE ALSO
       epoll_ctl(2), epoll_create(2), epoll_wait(2)

Linux                                            2002-10-23                                         EPOLL(4)



 思一克 回覆於:2006-08-25 15:36:49

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.

 wyezl 回覆於:2006-08-25 16:05:37

引用:原帖由 思一克 於 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 。
那我該如何處理?

 思一克 回覆於:2006-08-25 16:12:39

TO LZ,

man page中說的意思是
如果我使用的是EPLLET。
正在讀的時候,返回了EAGAIN 。
那我該如何處理?
那就加入poll_wait(如果原來就在,?)。
如果返回的不是EAGAIN,就不要poll_wait了。

我理解就是如此。說是飢餓症,並非epoll獨有的問題

 wyezl 回覆於:2006-08-25 16:33: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,下面代碼,我只讀一次的情況下。

              




  if(events.events & EPOLLIN)
                        {

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


                                if(ret>0)
                                {
                                         //正常處理
                                }
                                else
                                {
                                        perror("recv:");
                                        printf("recv<=0\n");

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

                                        if(close(cfd)==0)
                                                cfds[cfd]=0;
                                }


打印出了一些這樣的錯誤。爲什麼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

 nuclearweapon 回覆於:2006-08-25 17:34:05

引用:原帖由 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 編輯 ]

 wyezl 回覆於:2006-08-25 18:01:25

引用:原帖由 nuclearweapon 於 2006-8-25 17:34 發表


EAGAIN和EWOULDBLOCK在linux是相同。
socket可讀,是和SO_RCVLOWAT設定的值有關係(但是linux中的select和poll並不遵守這個選項)。

默認這個值默認是1。

現在你希望read讀的字節數爲sizeof(buffer), ... 


這種情況該做什麼處理呢?
順便問一下I, llegal seek 是什麼原因造成的?

 nuclearweapon 回覆於:2006-08-25 22:57:51

EAGAIN
             發生這種erro,大多數應該是你程序邏輯的錯誤。
         這是因爲,當select/poll/epoll返回的時候,這些api只是說可以讀了,但是沒有說可以讀多少。
         val=read(fd,buf,n)。如果val已經小於n了,你還讀此fd,在__大多數__情況下是會EAGAIN。
         不過,也可以不改,直接再把這些fd放到fdset中繼續監測也是可以的。
         
         
     爲什麼讀一次就出錯,我推斷是這樣的。
         就select/poll來說erro也是readable。
         推測epoll也是採取這種策略。所以纔有你這種錯誤。
         這也就說明了,爲什麼訪問量這麼大卻沒有錯誤發生!你把錯誤也作爲了readable了。
     

        至於llegal seek 。
         不太好說,看不到你的程序。
         不過我覺得你肯定沒有seek一個socket。

[ 本帖最後由 nuclearweapon 於 2006-8-27 20:48 編輯 ]

 精簡指令 回覆於:2006-08-27 00:13:44

EPOLLET 是邊沿觸發。如果epoll_wait返回一個可讀的文件描述符,必須要把它緩衝區中的數據都讀出來。如果沒有讀完,就繼續下一次epoll_wait(),epoll就不會在這個描述符上被喚醒。

請參考MAN手冊的建議

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

[ 本帖最後由 精簡指令 於 2006-8-27 00:37 編輯 ]

 精簡指令 回覆於:2006-08-27 00:36:10

突然發現一個問題:

你使用了 EPOLLET(邊沿觸發)  和 非阻塞IO。

但是在接收數據時,你只接收一次,沒有等到recv返回EAGAIN,就設置EPOLLOUT繼續等待了。

假如這個描述符的緩衝區內還有數據沒有讀完,它就可能“死”在epoll裏,不再被返回了。

我看到過一個人非常形象的描述這個問題:
  水平觸發 --> 有事了,你不處理?不斷騷擾你直到你處理。
  邊沿觸發 --> 有事了,告訴你一次,你不處理?拉倒!

[ 本帖最後由 精簡指令 於 2006-8-27 00:37 編輯 ]

 safedead 回覆於:2006-08-27 09:41:48

引用:原帖由 精簡指令 於 2006-8-27 00:36 發表
突然發現一個問題:

你使用了 EPOLLET(邊沿觸發)  和 非阻塞IO。

但是在接收數據時,你只接收一次,沒有等到recv返回EAGAIN,就設置EPOLLOUT繼續等待了。

假如這個描述符的緩衝區內還有數據沒有讀完, ... 


這麼一說我終於理解level和edge的區別了
我只用level和阻塞socket,所以沒碰到類似問題

 精簡指令 回覆於:2006-08-27 16:48:28

我又按照下面步驟測試了一下

第一次:
1、epoll 在一個文件描述符上,等待一個EPOLLIN事件。
2、epoll 在這個文件描述符上被喚醒,然後只接收部分數據。(沒有等到EAGAIN)
3、繼續等待這個EPOLLIN事件。
4、epoll 沒有再被喚醒。

正常現象。

第二次:
1、epoll 在一個文件描述符上,等待一個EPOLLIN事件。
2、epoll 在這個文件描述符上被喚醒,然後只接收部分數據。(沒有等到EAGAIN)
3、繼續等待這個EPOLLIN事件。
4、epoll 沒有再被喚醒。
5、再次收到新數據後,epoll又在這個描述符上被喚醒了。

正常現象。

第三次:
1、epoll 在一個文件描述符上,等待一個EPOLLIN事件。
2、epoll 在這個文件描述符上被喚醒,然後只接收部分數據。(沒有等到EAGAIN)
3、不再等待EPOLLIN,繼續等待另一個EPOLLOUT事件。
4、epoll 在這個文件描述符上被喚醒了。

我懷疑EPOLLIN和EPOLLOUT是分別處理的。但是,也不能保證這種現象是正常行爲。

第四次:
1、epoll 在一個文件描述符上,等待一個EPOLLOUT事件。
2、epoll 在這個文件描述符上被喚醒。
3、繼續等待EPOLLOUT事件。
4、epoll 沒有被喚醒。
5、收到新的數據,epoll_wait 在這個描述符上返回 EPOLLOUT 事件。

是否說明 IN 和 OUT 事件還是會相互影響呢?

[ 本帖最後由 精簡指令 於 2006-8-27 17:01 編輯 ]

 ken1984 回覆於:2006-08-27 19:01:06

頂,關注樓上的結果。

 wyezl 回覆於:2006-08-28 10:44:06

謝謝樓上那幾個兄弟的熱心解答。
我試試把數據都讀出來,直到EAGAIN。

 wyezl 回覆於:2006-08-28 11:19:06

int my_read(int fd,void *buffer,int length)
{
        int bytes_left;
        int bytes_read;
        char *ptr;
        ptr=buffer;
        bytes_left=length;
        while(bytes_left>0)
        {
                bytes_read=read(fd,ptr,bytes_read);

                if(bytes_read<0)
                {
                         if(errno==EINTR)
                                bytes_read=0;
                        else if(errno==EAGAIN)
                                break;
                        else
                        {
                                perror("read");
                                return(-1);
                        }
                }
                else if(bytes_read==0)
                break;

                bytes_left-=bytes_read;
                ptr+=bytes_read;

        }

        return(length-bytes_left);
}

把recv改成這個了,還是不行。

 GodArmy 回覆於:2006-08-30 15:54:34

借光,問一個問題,這裏對於同一個fd,只能是發送或者接收是嗎?不能用同一個fd進行發送和接收嗎?

 wyezl 回覆於:2006-08-30 16:26:24

本來就是用同一個fd,先接受,再發送的。

 GodArmy 回覆於:2006-08-31 09:34:24

引用:原帖由 wyezl 於 2006-8-30 16:26 發表
本來就是用同一個fd,先接受,再發送的。 



你的意思是整個過程就只有一個fd?

 GodArmy 回覆於:2006-08-31 10:53:42

http://www.xmailserver.org/linux-patches/dphttpd_last.tar.gz 

 給樓主推薦一個,在網上查到了,你看你能用嗎?

 精簡指令 回覆於:2006-08-31 22:16:07

我看到樓主前面記錄了一些日誌,並且有一些連接確實通過超時檢測出是死在epoll裏了。

能否統計一下,在這些FD上是 EPOLLIN 事件沒有發生,還是後面的 EPOLLOUT 事件沒有發生?

 wyezl 回覆於:2006-09-01 09:40:02

引用:原帖由 GodArmy 於 2006-8-31 10:53 發表
http://www.xmailserver.org/linux-patches/dphttpd_last.tar.gz 

 給樓主推薦一個,在網上查到了,你看你能用嗎? 


這個可能是2.6以前的epoll吧。
反正跟現在的不一樣。

 wyezl 回覆於:2006-09-01 09:42:27

引用:原帖由 精簡指令 於 2006-8-31 22:16 發表
我看到樓主前面記錄了一些日誌,並且有一些連接確實通過超時檢測出是死在epoll裏了。

能否統計一下,在這些FD上是 EPOLLIN 事件沒有發生,還是後面的 EPOLLOUT 事件沒有發生? 



這個不太好統計。

 solegoose 回覆於:2006-09-01 10:13:57

我認爲原因是沒有對每個FD進行超時的管理.
假設下列情況,一用戶發起連接,服務器accept成功,返回了FD,但是在用戶發起請求前,如果網絡有問題,此FD當然無法返回POLLIN,當然就不會有POLLOUT等等了,這樣FD就無法關閉.
KEEPALIVE可以解決此問題,這個不是指HTTP中的KEEPALIVE,是指socket選項.
個人觀點,歡迎指教.

另,我個人認爲,半連接,是不會佔用FD的.如果只有半連接,我相信內核中的sock->ops->accept()函數是無法正確返回的.只有在這個函數正確返回的情況下,纔會調用sock_map_fd(),把socket和fd關聯起來.

 思一克 回覆於:2006-09-01 10:18:01

LZ, 樓上說的對,和我原來和你說的差不多。你採取關閉那些長時間FD不行嗎

 cds 回覆於:2006-09-01 15:27:29

用了EPOLLET的緣故,所以會產生hang的句柄。

 精簡指令 回覆於:2006-09-02 01:40:55

我同意 solegoose 和 思一克 的說法。

其實,把這個問題的焦點集中在epoll上是不對的,我們有一點糊塗了。

當客戶端的機器在發送“請求”前,就崩潰了(或者網絡斷掉了),則服務器一端是無從知曉的。

按照你現在的這個“請求響應方式”,無論是否使用epoll,都必須要做超時檢查。

因此,這個問題與epoll無關。

[ 本帖最後由 精簡指令 於 2006-9-2 13:26 編輯 ]

 wyezl 回覆於:2006-09-03 00:13:57

引用:原帖由 思一克 於 2006-9-1 10:18 發表
LZ, 樓上說的對,和我原來和你說的差不多。你採取關閉那些長時間FD不行嗎 


暫時用的是這種方法。

 wyezl 回覆於:2006-09-03 00:16:09

引用:原帖由 solegoose 於 2006-9-1 10:13 發表
我認爲原因是沒有對每個FD進行超時的管理.
假設下列情況,一用戶發起連接,服務器accept成功,返回了FD,但是在用戶發起請求前,如果網絡有問題,此FD當然無法返回POLLIN,當然就不會有POLLOUT等等了,這樣FD就無法關閉. ... 



KEEPALIVE,socket選項.怎麼用?
能直接設置超時時間和自動回收嗎?

 wyezl 回覆於:2006-09-04 09:35:50

引用:原帖由 solegoose 於 2006-9-1 10:13 發表
我認爲原因是沒有對每個FD進行超時的管理.
假設下列情況,一用戶發起連接,服務器accept成功,返回了FD,但是在用戶發起請求前,如果網絡有問題,此FD當然無法返回POLLIN,當然就不會有POLLOUT等等了,這樣FD就無法關閉. ... 


無法返回POLLIN,那爲什麼也 不返回 EPOLLERR | EPOLLHUP呢?

 solegoose 回覆於:2006-09-04 09:46:23

怎麼設置KEEPALIVE,請參考《UNIX網絡編程卷1》第七章。
能否返回POLLERR這個我不是很確定,我對epoll不是很熟悉。但是在對方主機突然崩潰或者網絡突然斷開的時候,因爲來不及有數據包的交換,估計epoll不容易探測到此連接有問題,在這種情況下,就是調用write,都能馬上成功返回。

 wyezl 回覆於:2006-09-04 18:33:23

引用:原帖由 solegoose 於 2006-9-4 09:46 發表
怎麼設置KEEPALIVE,請參考《UNIX網絡編程卷1》第七章。
能否返回POLLERR這個我不是很確定,我對epoll不是很熟悉。但是在對方主機突然崩潰或者網絡突然斷開的時候,因爲來不及有數據包的交換,估計epoll不容易探 ... 


關於KEEPALIVE我瞭解了一下,那個需要兩個小時,還要發送9個分節。
我覺得這用於web server不太合適吧。

 wyezl 回覆於:2006-09-06 14:07:27

在computer_xu的BLOG  http://blog.sina.com.cn/u/544465b0010000bp
中看到了如下翻譯。也貼在這。


在man epoll中的Notes說到:
 
EPOLL事件分發系統可以運轉在兩種模式下:
   Edge Triggered (ET)
   Level Triggered (LT)
接下來說明ET, LT這兩種事件分發機制的不同。我們假定一個環境:
1. 我們已經把一個用來從管道中讀取數據的文件句柄(RFD)添加到epoll描述符
2. 這個時候從管道的另一端被寫入了2KB的數據
3. 調用epoll_wait(2),並且它會返回RFD,說明它已經準備好讀取操作
4. 然後我們讀取了1KB的數據
5. 調用epoll_wait(2)......
 
Edge Triggered 工作模式:
如果我們在第1步將RFD添加到epoll描述符的時候使用了EPOLLET標誌,那麼在第5步調用epoll_wait(2)之後將有可能會掛起,因爲剩餘的數據還存在於文件的輸入緩衝區內,而且數據發出端還在等待一個針對已經發出數據的反饋信息。只有在監視的文件句柄上發生了某個事件的時候 ET 工作模式纔會彙報事件。因此在第5步的時候,調用者可能會放棄等待仍在存在於文件輸入緩衝區內的剩餘數據。在上面的例子中,會有一個事件產生在RFD句柄上,因爲在第2步執行了一個寫操作,然後,事件將會在第3步被銷燬。因爲第4步的讀取操作沒有讀空文件輸入緩衝區內的數據,因此我們在第5步調用epoll_wait(2)完成後,是否掛起是不確定的。epoll工作在ET模式的時候,必須使用非阻塞套接口,以避免由於一個文件句柄的阻塞讀/阻塞寫操作把處理多個文件描述符的任務餓死。最好以下面的方式調用ET模式的epoll接口,在後面會介紹避免可能的缺陷。
   i    基於非阻塞文件句柄
   ii   只有當read(2)或者write(2)返回EAGAIN時才需要掛起,等待
 
Level Triggered 工作模式
相反的,以LT方式調用epoll接口的時候,它就相當於一個速度比較快的poll(2),並且無論後面的數據是否被使用,因此他們具有同樣的職能。因爲即使使用ET模式的epoll,在收到多個chunk的數據的時候仍然會產生多個事件。調用者可以設定EPOLLONESHOT標誌,在epoll_wait(2)收到事件後epoll會與事件關聯的文件句柄從epoll描述符中禁止掉。因此當EPOLLONESHOT設定後,使用帶有EPOLL_CTL_MOD標誌的epoll_ctl(2)處理文件句柄就成爲調用者必須作的事情。
 
以上翻譯自man epoll.
 
然後詳細解釋ET, LT:
 
LT(level triggered)是缺省的工作方式,並且同時支持block和no-block socket.在這種做法中,內核告訴你一個文件描述符是否就緒了,然後你可以對這個就緒的fd進行IO操作。如果你不作任何操作,內核還是會繼續通知你的,所以,這種模式編程出錯誤可能性要小一點。傳統的select/poll都是這種模型的代表.
 
ET(edge-triggered)是高速工作方式,只支持no-block socket。在這種模式下,當描述符從未就緒變爲就緒時,內核通過epoll告訴你。然後它會假設你知道文件描述符已經就緒,並且不會再爲那個文件描述符發送更多的就緒通知,直到你做了某些操作導致那個文件描述符不再爲就緒狀態了(比如,你在發送,接收或者接收請求,或者發送接收的數據少於一定量時導致了一個EWOULDBLOCK 錯誤)。但是請注意,如果一直不對這個fd作IO操作(從而導致它再次變成未就緒),內核不會發送更多的通知(only once),不過在TCP協議中,ET模式的加速效用仍需要更多的benchmark確認。
 
在許多測試中我們會看到如果沒有大量的idle-connection或者dead-connection,epoll的效率並不會比select/poll高很多,但是當我們遇到大量的idle-connection(例如WAN環境中存在大量的慢速連接),就會發現epoll的效率大大高於select/poll。

 realclimber 回覆於:2006-09-10 19:37:30

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

這裏應該判斷一下狀態吧,如果對方網絡這個時候斷開,你這邊就不會關閉連接.

 wyezl 回覆於:2006-09-11 18:18:01

引用:原帖由 realclimber 於 2006-9-10 19:37 發表
ret = recv(cfd, buffer, sizeof(buffer),0);

這裏應該判斷一下狀態吧,如果對方網絡這個時候斷開,你這邊就不會關閉連接. 



我在實際的程序中做了些簡單的判斷。
這是個最初的演示版本,所以有些沒寫上。

 ken1984 回覆於:2006-09-15 22:34:43

現在到底是什麼問題?

 seeLnd 回覆於:2006-09-19 10:59:18

問題還是沒有解決嗎?
樓主有沒有采用其他的變通做法?

 wyezl 回覆於:2006-09-19 11:11:30

沒想到更好的辦法,只有一個監視線程。定期關閉一些超時的fd。
估計這是不可避免的了。

 wyezl 回覆於:2006-09-25 10:24:13

繼續這個問題。 有過實際網絡編程經驗的兄弟看一下。
這個server在訪問量不是非常大的情況下,有時候cpu利用率能達到99%,但過幾分鐘又自己下去了。
而且99%的時候服務不受任何影響,速度還是很快。
load average: 0.20    最高能達到1。

在99%的時候,重新啓動http server,立刻能降下去。 但一會還有可能上去。很奇怪。


請幫忙分析下是什麼原因。

 safedead 回覆於:2006-09-25 22:13:18

這陣子一直在編寫代理服務器
發現了一個有趣的現象:
當服務器很快,客戶端很慢的時候
大量數據積壓在代理服務器的tcp緩衝區裏
代理服務器內存佔用急劇上升,內存快用完的時候,發生SWAP然後CPU佔用也上去了
最糟糕的時候是內存佔用增長過快,代理服務程序被內核直接殺掉
在默認TCP內核參數下,代理1000個併發連接(2000個套接字)最高消耗掉800M內存
在top界面看不到服務程序內存內存消耗增加,但系統可用內存幾分鐘內就沒有了
斷開連接內存就恢復了

若是客戶端或服務器拔網線或是拔電,就只好等KEEPALIVE起作用了,
此期間內存佔用一直居高不下

 wyezl 回覆於:2006-09-26 10:02:20

引用:原帖由 safedead 於 2006-9-25 22:13 發表
這陣子一直在編寫代理服務器
發現了一個有趣的現象:
當服務器很快,客戶端很慢的時候
大量數據積壓在代理服務器的tcp緩衝區裏
代理服務器內存佔用急劇上升,內存快用完的時候,發生SWAP然後CPU佔用也上去了
 ... 


網絡編程在實際應用中經常出現一些奇怪的現象。
遠遠沒有書上講的幾個調用那麼幹淨簡單。
我那個程序沒有動態分配內存。 所以內存基本上空閒。
就是cpu用了n多。

正常情況輸出。
strace  -c   -p16940
Process 16940 attached - interrupt to quit

Process 16940 detached
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 81.33    4.041201         313     12929           accept
 13.48    0.669718          52     12929           epoll_ctl
  3.39    0.168595           7     25858           fcntl64
  1.79    0.089180           7     12929           time
------ ----------- ----------- --------- --------- ----------------
100.00    4.968694                 64645           total



 vmstat
procs -----------memory---------- ---swap-- -----io---- --system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in    cs us sy id wa
 0  0   4988 873736  48832  53464    0    0     1     5    0     9  2  4 94  0



accept在嚴重的時候佔用99。95%。 估計是因爲沒有使用非阻塞導致的吧。

[ 本帖最後由 wyezl 於 2006-9-26 10:03 編輯 ]

 hightman 回覆於:2006-09-26 10:38:11

暈,頂這麼多貼?? 小的描述符關閉後可以複用的, 只需要在程序設計時自己注意一下.  一般設計時會設定一個最大上限比如允許 1024 個併發連接. 那麼你可以將套接字描述符控制在 1100 以下.(假設主程序還有若干文件描述符打開)

比如在 accept 之後調用系統調用 dup(), 它會複製一個描述符出來, 如果你確定有較小的可以嘗試5次調用dup()直到返回較小的爲止.

fd2 = dup(fd);
close(fd);

..................

[ 本帖最後由 hightman 於 2006-9-26 10:39 編輯 ]

 wyezl 回覆於:2006-09-26 10:46:31

引用:原帖由 hightman 於 2006-9-26 10:38 發表
暈,頂這麼多貼?? 小的描述符關閉後可以複用的, 只需要在程序設計時自己注意一下.  一般設計時會設定一個最大上限比如允許 1024 個併發連接. 那麼你可以將套接字描述符控制在 1100 以下.(假設主程序還有若干文件描 ... 


我更暈。你那治標不治本。
現在問題已經轉移到cpu利用率上來了。跟fd無關了。
偶爾cpu利用率過高的問題。

 safedead 回覆於:2006-09-26 20:20:27

accept佔用高我還從沒遇到過
我用的是阻塞socket
每秒新建連接數不超過2000
併發控制在8000之內

你的accept速度是不是太快了, 我記得你的每秒新建連接超過1萬了
我發生CPU高漲全是軟中斷和TCP緩存不夠引起的
跟網卡有關(PCI-X 64bit 66MHz效率太低了)

2.6內核的異步IO機制還有內核級線程使得高併發容易實現多了,
但是按下葫蘆又起瓢, 其它以前看不到的問題現在都出現了

 wyezl 回覆於:2006-09-28 10:46:48

引用:原帖由 safedead 於 2006-9-26 20:20 發表
accept佔用高我還從沒遇到過
我用的是阻塞socket
每秒新建連接數不超過2000
併發控制在8000之內

你的accept速度是不是太快了, 我記得你的每秒新建連接超過1萬了
我發生CPU高漲全是軟中斷和TCP緩存不夠引起 ... 


問題是,不上線測試問題就是出不來。
用工具測試情況都很理想。

估計跟client的網絡狀態有關。


站內短信給我留個msn吧,有空跟你請教網絡編程。

[ 本帖最後由 wyezl 於 2006-9-28 10:49 編輯 ]

 GodArmy 回覆於:2006-09-29 17:39:10

引用:原帖由 solegoose 於 2006-9-1 10:13 發表
我認爲原因是沒有對每個FD進行超時的管理.
假設下列情況,一用戶發起連接,服務器accept成功,返回了FD,但是在用戶發起請求前,如果網絡有問題,此FD當然無法返回POLLIN,當然就不會有POLLOUT等等了,這樣FD就無法關閉. ... 


爲什麼你們總是想着關閉FD呢?
我是這樣做的

void *thread(void *data)
{
struct epoll_event events[20];


for ( ; ; ) {
printf("begin epoll wait, threadid = %d\n", pthread_self());
int nfds=epoll_wait(epfd,events,20, -1);

for(int i=0;i<nfds;++i)
{
//printf("begin epoll wait, fd  = %d, events = %lu\n", events.data.fd, events.events);
if(events.data.fd == g_listenfd )
{
num ++;
struct sockaddr_in clientaddr;
socklen_t clilen;

int connfd = accept(g_listenfd,(struct sockaddr *)&clientaddr, &clilen);
if(connfd<0){
printf("error in g_listenfd\n");
break;
}
//printf("ip = %s\n", inet_ntoa(clientaddr.sin_addr));
int intMsg;
intMsg = allowip.select_ip(clientaddr.sin_addr.s_addr);
//printf("intMsg = %d\n", intMsg);
if(intMsg == 1 || num == 1)
{
setnonblocking(connfd);

//printf("new connection fd = %d\n", connfd);

struct epoll_event ev;
ev.data.fd=connfd;
ev.events=EPOLLHUP|EPOLLERR|EPOLLIN|EPOLLET|EPOLLPRI;
epoll_ctl(epfd, EPOLL_CTL_ADD, connfd ,&ev);
}
}

else if(events.events&EPOLLIN)
{

char buffer[102400] = {0};
if ( (events.data.fd) < 0) continue;
int connfd = events.data.fd;

int a = recv(connfd, buffer, sizeof(buffer), 0);

if (a > 0)
{
               //業務處理
}
else
{
if (a == 0 || errno == ECONNRESET)
{

struct epoll_event ev;
ev.data.fd=connfd;
if (epoll_ctl(epfd, EPOLL_CTL_DEL, connfd ,&ev) == 0)
{
//ev.events=EPOLLHUP|EPOLLERR|EPOLLOUT|EPOLLET|EPOLLPRI;
//printf("epoll_ctl after del return = %d\n", epoll_ctl(epfd, EPOLL_CTL_MOD, connfd ,&ev));
}

if (myclose(connfd) == 0)
printf("close success\n");
else
printf("close error\n");
}

}
}

else if(events.events&EPOLLOUT)
{    
if ( (events.data.fd) < 0) continue;
int connfd = events.data.fd;

//send(connfd, buffer, strlen(buffer), 0);

struct epoll_event ev;
ev.data.fd=connfd;
ev.events=EPOLLHUP|EPOLLERR|EPOLLIN|EPOLLET|EPOLLPRI;
epoll_ctl(epfd, EPOLL_CTL_MOD, connfd ,&ev);

}
else if(events.events&EPOLLERR)
{
printf("error on fd = %d\n", events.data.fd);
}
else if(events.events&EPOLLHUP)
{
printf("hup on fd = %d\n", events.data.fd);
}
else if (events.events & EPOLLPRI)
{
printf(" epollpri %d\n", events.data.fd);
}
else if (events.events & EPOLLET)
printf("epollet %d\n", events.data.fd);
}
}
}

[ 本帖最後由 GodArmy 於 2006-9-29 17:40 編輯 ]

 wyezl 回覆於:2006-09-29 18:41:21

引用:原帖由 GodArmy 於 2006-9-29 17:39 發表

爲什麼你們總是想着關閉FD呢?
我是這樣做的

void *thread(void *data)
{
struct epoll_event events[20];


for ( ; ; ) {
printf("begin epoll wait, threadid = %d\n", pthread_sel ... 


我覺得這個程序能用但效率比較低。
能把程序中的高明之處指出來嗎?


現在描述符到是小事了。
accept成了大事,有時候莫名其妙把cpu吃到99%。
比較奇怪,我還單獨一個線程接受的呢。

 cwinl 回覆於:2006-11-02 19:25:45

libevent封裝了epoll的操作
默認是用的EPOLLLT
雖然沒有用EPOLLET,但滿足千個左右的client應該是沒有問題的吧

我也在用libevent封裝自己的程序
使用過程中發現了一些問題
一起探討吧

 oknet 回覆於:2007-02-26 16:35:22

對於非阻塞socket,recv如果讀取到0字節,那麼就意味着remote side主動關閉了連接,如果這個時候在server side執行close(fd),那麼以後永遠都不會觸發epoll events,因此這個文件描述字將永遠不會關閉了

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

因此在這句之後要判斷一次,是否讀到了0長度的內容

 wyezl 回覆於:2007-02-26 16:42:01

設定個超時,定期檢查不活動的連接,close掉,就ok了。

 oknet 回覆於:2007-02-26 16:59:33

在樓主給出的第二種架構裏面就有對 recv 返回 0 長度的判斷
            } else if (n == 0) { 

                close(sockfd); 

                events.data.fd = -1; 

            } 

所以第二種架構不會出問題

 wyezl 回覆於:2007-02-27 09:45:22

我把返回值不大於0 的情況,都關閉了。
而且顯式執行了 epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, &ev);

儘管如此,問題還是有的。

所以必須設置一個超時監控線程來解決。

 空靈靜世 回覆於:2007-04-19 16:07:20

你的文件描述符號是怎麼耗盡的,有什麼結論嗎?

 空靈靜世 回覆於:2007-04-19 16:21:42

引用:原帖由 oknet 於 2007-2-26 16:35 發表
對於非阻塞socket,recv如果讀取到0字節,那麼就意味着remote side主動關閉了連接,如果這個時候在server side執行close(fd),那麼以後永遠都不會觸發epoll events,因此這個文件描述字將永遠不會關閉了

ret = ... 

你的說法我怎麼看不懂阿,server side 都已經關閉了,怎麼又說這個文件描述自將永遠不會關閉了,都已經關了還要關什麼啊?

 xhl 回覆於:2007-04-19 16:33:44

我覺得LZ既然是做TCP的SERVER,而且有這樣大的訪問量,不應該不做TCPKEEPALIVE的操作,而且要把BACKLOG增加到500或者更大。。

感覺LZ的問題,就是因爲很多用戶在保持與LZ的SERVER連接的時候,直接關機器或者拔網絡線,都會操作你這邊出現一個死連接。
默認的情況下, LINUX或經過大概7200秒纔會回收這個FD,這樣肯定會影響你的服務的。

我貼段KEEPALIVE的代碼, 希望對LZ有幫助:

# include <netinet/tcp.h>

int keepalive; // 打開TCP KEEPALIVE開關
int keepidle; // 系統等待多長時間開始檢測
int keepintvl; // 系統每次探測時間
int keepcnt; // 系統探測幾次後執行關閉操作。

setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&keepalive, sizeof(keepalive));
isetsockopt(fd, SOL_TCP, TCP_KEEPIDLE, (char *)&keepidle, sizeof(keepidle));
setsockopt(fd, SOL_TCP, TCP_KEEPINTVL,(char *)&keepintvl, sizeof(keepintvl));
setsockopt(fd, SOL_TCP, TCP_KEEPCNT,(char *)&keepcnt, sizeof(keepcnt));

這個功能是協議棧實現的,應用層不用關心,但當發生檢測失敗後,他會叫你的epoll函數。

 wyezl 回覆於:2007-04-19 18:56:07

謝謝你的建議。BACKLOG我一般都用8192。
最大fd一般用20w。
現在服務器很穩定,幾個月都不會down掉,已經用做商業應用有半了。

 yangsf5 回覆於:2008-04-14 18:35:32

引用:原帖由 wyezl 於 2006-8-25 16:33 發表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=5684249&ptid=813588]


事情是這樣的:
以前accept的時候把一個cfd加入了epoll。
後來wait返回。說這個cfd可讀,我就去讀,讀了一次或兩次,就返回了EAGAIN。
我難道新把以前的剔除要重 ADD一下?  
那已經讀取的一部分數據怎 ... 


我難道新把以前的剔除要重 ADD一下?  
那已經讀取的一部分數據怎麼處理呢?
找個buffer存着? 等待下一次wait返回再接着讀?


請問這個怎麼處理的?

 bical 回覆於:2008-10-06 11:28:36

在linsten之後把listen的fd加到epoll監控對象中epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev)

 wwwsq 回覆於:2008-10-06 13:06:11

引用:原帖由 wyezl 於 2007-2-27 09:45 發表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=6478257&ptid=813588]
我把返回值不大於0 的情況,都關閉了。
而且顯式執行了 epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, &ev);

儘管如此,問題還是有的。

所以必須設置一個超時監控線程來解決。 



tcp connection是可能斷開而不觸發任何事件的,這種情況在internet上要比在局域網裏面常見得多。這可能也是爲什麼你用測試工具測試都ok,而上線就會有問題。

tcp connection你可以看作是兩個村子之間有條山路,這條山路到底通還是不通,你必須派信使實際走一趟才知道。如果沒有信使到來,你就不知道這條路通不通,也許發生山崩了呢。(信使就相當於網線上的高低電平信號)

 chenzhanyiczy 回覆於:2008-10-06 14:53:20

引用:原帖由 精簡指令 於 2006-8-27 16:48 發表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=5689775&ptid=813588]
我又按照下面步驟測試了一下

第一次:
1、epoll 在一個文件描述符上,等待一個EPOLLIN事件。
2、epoll 在這個文件描述符上被喚醒,然後只接收部分數據。(沒有等到EAGAIN)
3、繼續等待這個EPOLLIN事件。 ... 


“第二次:
1、epoll 在一個文件描述符上,等待一個EPOLLIN事件。
2、epoll 在這個文件描述符上被喚醒,然後只接收部分數據。(沒有等到EAGAIN)
3、繼續等待這個EPOLLIN事件。
4、epoll 沒有再被喚醒。
5、再次收到新數據後,epoll又在這個描述符上被喚醒了。

正常現象。”

這個有問題吧。再次收到新數據,epoll 應該沒有再被喚醒纔對。因爲文件描述符狀態並沒有改變,仍然是就緒狀態

 redor 回覆於:2008-10-06 15:39:30

引用:原帖由 wyezl 於 2006-8-18 11:23 發表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=5647820&ptid=813588]
只要能幫我找出描述符從哪耗盡的就行。
:) 



您老的程序有很多TIMEWAIT吧? shutdown(fd, SHUT_RDWR); 了麼?

 bobozhang 回覆於:2008-10-06 19:52:00

這個帖子有些年代了,不知道你們怎麼翻出來了
對於樓主這個問題,我覺得會不會可能是accept太快,而且每次epoll_wait滿足io的fd要大於EVENTSIZE這樣慢慢積累到最後就滿足Io條件但又得不到處理的fd越來越多,最終導致fd用完。

 yuanyuan025 回覆於:2008-10-07 03:10:49

好 啊幫丁了

 alexhappy 回覆於:2008-10-08 15:05:54

我曾碰到跟樓主類似的問題,正好學習一下。。。。

 lauxp 回覆於:2009-02-26 23:17:38

引用:原帖由 wyezl 於 2006-8-28 11:19 發表 [url=http://bbs2.chinaunix.net/redirect.php?goto=findpost&pid=5692376&ptid=813588]
int my_read(int fd,void *buffer,int length)
{
        int bytes_left;
        int bytes_read;
        char *ptr;
        ptr=buffer;
        bytes_left=length;
        while(bytes_left>0)
 ... 


在這個函數裏面讀到0的地方,表示對方已經主動關閉

挖墳..



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章