⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 5.txt

📁 linux环境下的TCP网络服务器端实现原代码
💻 TXT
字号:
(2)epoll的优点 



<1>支持一个进程打开大数目的socket描述符(FD) 



select 最不能忍受的是一个进程所打开的FD是有一定限制的,由FD_SETSIZE设置,默认值是2048。对于那些需要支持的上万连接数目的IM服务器来说显然太少了。这时候你一是可以选择修改这个宏然后重新编译内核,不过资料也同时指出这样会带来网络效率的下降,二是可以选择多进程的解决方案(传统的Apache方案),不过虽然linux上面创建进程的代价比较小,但仍旧是不可忽视的,加上进程间数据同步远比不上线程间同步的高效,所以也不是一种完美的方案。不过 epoll则没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。 



<2>IO效率不随FD数目增加而线性下降 



传统的select/poll另一个致命弱点就是当你拥有一个很大的socket集合,不过由于网络延时,任一时间只有部分的socket是"活跃"的,但是select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降。但是epoll不存在这个问题,它只会对"活跃"的socket进行操作---这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。那么,只有"活跃"的socket才会主动的去调用 callback函数,其他idle状态socket则不会,在这点上,epoll实现了一个"伪"AIO,因为这时候推动力在os内核。在一些 benchmark中,如果所有的socket基本上都是活跃的---比如一个高速LAN环境,epoll并不比select/poll有什么效率,相反,如果过多使用epoll_ctl,效率相比还有稍微的下降。但是一旦使用idle connections模拟WAN环境,epoll的效率就远在select/poll之上了。 



<3>使用mmap加速内核与用户空间的消息传递。 



这点实际上涉及到epoll的具体实现了。无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的。而如果你想我一样从2.5内核就关注epoll的话,一定不会忘记手工 mmap这一步的。 



<4>内核微调 



这一点其实不算epoll的优点了,而是整个linux平台的优点。也许你可以怀疑linux平台,但是你无法回避linux平台赋予你微调内核的能力。比如,内核TCP/IP协议栈使用内存池管理sk_buff结构,那么可以在运行时期动态调整这个内存pool(skb_head_pool)的大小--- 通过echo XXXX>/proc/sys/net/core/hot_list_length完成。再比如listen函数的第2个参数(TCP完成3次握手的数据包队列长度),也可以根据你平台内存大小动态调整。更甚至在一个数据包面数目巨大但同时每个数据包本身大小却很小的特殊系统上尝试最新的NAPI网卡驱动架构。 



(3)epoll的使用 



令人高兴的是,2.6内核的epoll比其2.5开发版本的/dev/epoll简洁了许多,所以,大部分情况下,强大的东西往往是简单的。唯一有点麻烦是epoll有2种工作方式:LT和ET。 



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确认。 



epoll只有epoll_create,epoll_ctl,epoll_wait 3个系统调用,具体用法请参考http://www.xmailserver.org/linux-patches/nio-improve.html , 

在http://www.kegel.com/rn/也有一个完整的例子,大家一看就知道如何使用了 



(4)Leader/follower模式线程pool实现,以及和epoll的配合 



.....未完成,主要是要避免过多的epoll_ctl调用,以及尝试使用EPOLLONESHOT加速...... 



(5)benchmark 



.......未完成 

/*********************************引用结束******************************/ 

[/QUOTE] 

3、epoll的使用方法 

这是epoll的man手册提供的一个例子,这段代码假设一个非阻塞的socket监听listener被建立并且一个epoll句柄kdpfd已经提前用epoll_create建立了: 

[CODE] 
       struct epoll_event ev, *events; 



       for(;;) { 

           nfds = epoll_wait(kdpfd, events, maxevents, -1);/*wait for an I/O event. All notes here added by zhoulifa(http://zhoulifa.bokee.com) on 2006-7-6 22:10:00*/ 



           for(n = 0; n < nfds; ++n) { 

               if(events[n].data.fd == listener) {/*if listen socket has an I/O, accept the new connect*/ 

                   client = accept(listener, (struct sockaddr *) &local, 

                                   &addrlen); 

                   if(client < 0){ 

                       perror("accept"); 

                       continue; 

                   } 

                   setnonblocking(client); 

                   ev.events = EPOLLIN | EPOLLET;/*EPOLLIN-available for read*/ 

                   ev.data.fd = client; 

                   if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {/*add the new socket into the epoll file descriptors*/ 

                       fprintf(stderr, "epoll set insertion error: fd=%d\n", 

                               client); 

                       return -1; 

                   } 

               } 

               else 

                   do_use_fd(events[n].data.fd);/*read from a socket which has data come*/ 

           } 

       } 

[/CODE] 
4、epoll使用方法示意代码 

以下代码由chinaunix.net上BBS用户safedead(http://bbs.chinaunix.net/viewpro.php?uid=407631)提供: 



[CODE] 
static int        s_epfd;//epoll描述字 



{//初始化epoll 

        struct epoll_event        ev; 



        //设置epoll 

        s_epfd = epoll_create(65535); 



        {//这个过程可以循环以便加入多个LISTEN套接字进入epoll事件集合 

                //服务器监听创建 

                rc = listen();//listen参数这里省略 



                //加入epoll事件集合 

                ev.events = EPOLLIN; 

                ev.data.fd = rc; 

                if (epoll_ctl(s_epfd, EPOLL_CTL_ADD, rc, &ev) < 0) { 

                        fprintf(stderr, "epoll set insertion error: fd=%d", rc); 

                        return(-1); 

                } 

        } 

} 



{//epoll事件处理 

        int        i, nfds, sock_new; 

        struct epoll_event        events[16384]; 

        for( ; ; ) { 

                //等待epoll事件 

                nfds = epoll_wait(s_epfd, events, 16384, -1); 

                //处理epoll事件 

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

                        //events.data.fd是epoll事件中弹出的套接字 

                        //接收连接 

                        sock_new = accept(events.data.fd);//accept其它参数这里省略了 

                        if(0 > sock_new) { 

                                fprintf(stderr, "接收客户端连接失败\n"); 

                                continue; 

                        } 

                } 

        } 

} 


对照safedead和前面的一份代码,我想大家一定是明白了的。 



5、参考文档 

Improving (network) I/O performance ... 

http://www.xmailserver.org/linux-patches/nio-improve.html

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -