博客首页 注册 建议与交流 排行榜 加入友情链接
推荐 投诉 搜索: 帮助

cugb_cat

咱只谈技术
   rainlx.cublog.cn
关于作者  
姓名:cugb_cat
职业:IT
年龄:
位置:
个性介绍:

我的分类  




经验实在太重要了:epoll的一些补充
man中给出了epoll的用法,example程序如下:
       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=%d\n",
                               client);
                       return -1;
                   }
               }
               else
                   do_use_fd(events[n].data.fd);
           }
       }
此时使用的是ET模式,即,边沿触发,类似于电平触发,epoll中的边沿触发的意思是只对新到的数据进行通知,而内核缓冲区中如果是旧数据则不进行通知,所以在do_use_fd函数中应该使用如下循环,才能将内核缓冲区中的数据读完。
        while (1) {
           len = recv(*******);
           if (len == -1) {
             if(errno == EAGAIN)
                break;
             perror("recv");
             break;
           }
           do something with the recved data........
        }

在上面例子中没有说明对于listen socket fd该如何处理,有的时候会使用两个线程,一个用来监听accept另一个用来监听epoll_wait,如果是这样使用的话,则listen socket fd使用默认的阻塞方式就行了,而如果epoll_wait和accept处于一个线程中,即,全部由epoll_wait进行监听,则,需将listen socket fd也设置成非阻塞的,这样,对accept也应该使用while包起来(类似于上面的recv),因为,epoll_wait返回时只是说有连接到来了,并没有说有几个连接,而且在ET模式下epoll_wait不会再因为上一次的连接还没读完而返回,这种情况确实存在,我因为这个问题而耗费了一天多的时间,这里需要说明的是,每调用一次accept将从内核中的已连接队列中的队头读取一个连接,因为在并发访问的环境下,有可能有多个连接“同时”到达,而epoll_wait只返回了一次。
其实是很简单的问题,对于有经验的人来说根本不会犯这样的错误,而我却耗费了一天多的时间,经验确实太重要了。

 发表于: 2007-07-06,修改于: 2007-07-06 12:56 已浏览1513次,有评论15条 推荐 投诉

  网友评论
  本站网友 时间:2007-08-04 00:04:24 IP地址:60.211.39.★
非常好,
还想问一下epoll_create的那个初始化值,和epoll_wait的那个maxevents有什么关系么? 谢谢

  cugb_cat 时间:2007-08-11 00:40:27 IP地址:123.103.46.★
epoll_create的参数是该epoll_create描述符所监听的最大描述符数,epoll_wait的maxevents是epoll_wait一次返回所带回的最大的事件数。

  本站网友  时间:2007-09-06 13:44:56 IP地址:60.211.38.★
"epoll_create的参数是该epoll_create描述符所监听的最大描述符数"
这个是个硬性参数么?
不能超过么? 超过会怎样?

  cugb_cat 时间:2007-09-08 14:31:14 IP地址:123.103.46.★
在我看到的资料中,好像都没有特别强调这个参数的值,我想,应该把这个值设置为至少要大于实际要监听的描述符。

  网友 时间:2007-09-20 15:51:14 IP地址:211.144.13.★
您号是不是可以把你的while(1){
accept}这段代码发布一下,我现在碰到的问题是当服务器总是延迟1次响应并处理请求,我想看看你怎么写的

  cugb_cat 时间:2007-09-25 14:11:39 IP地址:218.249.89.★
我的代码量比较大,而且是公司内部的代码,我没有权限发布(即使是我一个人写的代码),我的QQ:271682939,gtalk:xielichao1984@gmail.com,可以交流一下,最好加gtalk,我平时都在Linux下,QQ有时候不好使,谢谢。

  本站网友 时间:2008-01-15 15:45:33 IP地址:202.106.179.★
哥们,有个问题想问下,
如果用另外一个线程进行accept,当有新连接到来建立新句柄handle,如果主线程正在epoll_wait之中,如何将这个handle加入到epoll_wait等待集合中?
直接使用epoll_ctl就可以了么,还是说需要等epoll_wait之后加入集合才能生效?

Blog作者的回复:
直接epoll_ctl把描述符加和事件加进去就行了.

  本站网友 时间:2008-01-16 14:06:03 IP地址:202.106.179.★
就是说,在epoll_wait返回之前进行epoll_ctl,会不会有问题?

比如说,其他epoll_wait正在监测的有100个handle,timeout是10000S。
如果epoll_ctl新建的handle之后,epoll_wait就会立刻监测这个handle么?

Blog作者的回复:
我写程序测试过了,没有问题,epoll_wait不会把事件漏掉的.

  本站网友 时间:2008-01-16 14:08:58 IP地址:202.106.179.★
    

就是说,在epoll_wait返回之前进行epoll_ctl,会不会有问题?



比如说,其他epoll_wait正在监测的有100个handle,timeout是10000S。如果这100个handle都没有事件,那么epoll_wait就处于等待状态。而这时,另外一个线程对这个epoll句柄进行了epoll_ctl操作,添加了一个新的handle。
这个新的handle上的事件,是要等到epoll_wait返回后才生效?

Blog作者的回复:
我觉得应该是即时生效的,我写程序得到的测试结果也是这样.

  本站网友 时间:2008-01-17 13:38:55 IP地址:202.106.179.★
我对这个有点怀疑,因为我发现在ACE中,epoll_ctl之前,如果epoll_wait在阻塞,是会先唤醒epoll_wait的。
我接触EPOLL时间不长,所以,对这个拿不定把握,不知道ACE中这么做是否有别的考量。

Blog作者的回复:
应该是有别的考虑,我试验过,不用重启epoll_wait,还是可以检测到事件的。

  本站网友 时间:2008-01-18 15:12:53 IP地址:202.106.179.★
恩,对了,你试验是在单CPU还是多CPU下进行的。

Blog作者的回复:
下面是我的测试程序,测试机为单CPU,但有超线程.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <strings.h>

int         epoll_handle;    
struct epoll_event    events[50];
pthread_t accept_tid;
static void *accept_thread(void *arg);

int main(int argc, char *argv[])
{
    int perr, nfds, i;

    epoll_handle = epoll_create(50);
    if (epoll_handle == -1) {
        perror("process exit\n");
        exit(EXIT_FAILURE);
    }
    perr = pthread_create(&accept_tid, NULL, accept_thread, NULL);
    if (perr != 0) {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }
    while (1) {
        nfds = epoll_wait(epoll_handle, events, 50, -1);
        if (nfds == -1) {
            perror("epoll_wait");
            exit(EXIT_FAILURE);
        }
        for (i = 0; i < nfds; i++) {
            printf("%d recv socket fd %d is done\n", i, events[i].data.fd);
        } //for (i = 0; i < nfds; i++)
    } //while(1)
    return 0;
}
static void *accept_thread(void *arg)
{
    int                 sockfd;
    int                    connfd;
    int                    t;
    socklen_t            clilen = 0;
    struct sockaddr     servaddr, cliaddr;
    struct sockaddr_in    *st_in = (struct sockaddr_in *)&servaddr;
    struct epoll_event    ev;

    t = pthread_detach(pthread_self());        
    if (t != 0) {
        perror("pthread_detach");
        return NULL;
    }

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("process exit\n");
        exit(EXIT_FAILURE);
    }
    bzero(&servaddr, sizeof(struct sockaddr));
    st_in->sin_family = AF_INET;
    st_in->sin_port = htons(5000);
    st_in->sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(sockfd, &servaddr, sizeof(servaddr)) == -1) {
        perror("bind");
        exit(EXIT_FAILURE);
    }
    if (listen(sockfd, 10) == -1) {
        perror("process exit\n");
        exit(EXIT_FAILURE);
    }

    clilen = sizeof(cliaddr);
    ev.events = EPOLLIN | EPOLLET;

    while (1) {
        if ( (connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen)) < 0) {
            perror("accept");
            break;
        }
        printf("accept return %d\n", connfd);
        ev.data.fd = connfd;
        if (epoll_ctl(epoll_handle, EPOLL_CTL_ADD, connfd, &ev) < 0) {
            perror("epoll_ctl");
        }
    }

    return NULL;
}

  本站网友 时间:2008-01-18 15:13:47 IP地址:202.106.179.★
我是楼上
我QQ是:45654868,有机会多多交流。
加的时候说明下。

Blog作者的回复:
QQ Linux下不怎么好使,还是MSN或GTALK吧xielichao1984@126.com

  bjnw 时间:2008-01-28 12:23:19 IP地址:221.220.24.★
DING

  bjnw 时间:2008-01-28 12:30:28 IP地址:221.220.24.★
int lnsize = sizeof(T_FILE_DATA);
char ltempbuff[sizeof(T_FILE_DATA)] = {0};
int lnRecvBytes = 0;
while(lnRecvBytes != lnsize)
        {
            lnRecvBytes += recv(connectfd,ltempbuff + lnRecvBytes,lnsize - lnRecvBytes,0);
            if(lnRecvBytes < 0)
            {
                if(errno == EAGAIN)
                    break;
            }
            else if(lnRecvBytes == 0)
            {//对端SOCKET已经关闭
                break;
            }

        }
这是我在接受线程里做的事。想接受一个结构大小的数据包。但发现总不对

Blog作者的回复:
接收recv返回值的问题。

  prolj 时间:2008-01-29 11:36:58 IP地址:60.1.52.★
单CPU和多CPU很大区别吗?博主用的肯定是(超)标量CPU。

Blog作者的回复:
单纯对于epoll来说,多CPU好像也没什么性能提升,但是对于多线程程序来说,性能提高还是比较大的。


  发表评论



Copyright © 2001-2006 ChinaUnix.net All Rights Reserved

感谢所有关心和支持过ChinaUnix的朋友们
页面生成时间:0.02334

京ICP证041476号