linux社区爱心援助Linux认证系列教程业界动态站务新闻公司招聘建议留言网址大全LPI专题CISCO专题
设为首页
加入收藏
管理团队
JSP  
JAVA  
PERL  
 您的位置:首页 > article > Linux开发区 > 内核研究 >
栏目导栏
资料搜索
热门文章
·linux-2.6内核升级文档
·Linux源代码的注释
·linux2.6内核编译方法详述
·Linux操作系统内核编译详解
·Linux内核结构详解
·Linux配置与编译内核
·定时器
·Linux内核升级全攻略
·FC5(Fedora Core5)下编译内核总
·Ubuntu 6.06 dapper 内核编译初
·Linux 内核编译详解
·Linux内核管理基础知识概述
·Linux2.4升级到2.6内核升级指南
·第八章 设备驱动
·如何编译linux内核
最新文章
·Linux内核bootsplash功能的实现
·Linux内核2.6.25全新发布加入众
·Debian Linux系统编译内核标准
·Linux2.4内核和2.6内核对Initr
·2.6.24内核编译 initrd-2.6.24
·Qtopia应用程序与Linux内核数据
·Linux 2.6内核中sysfs文件系统
·Linux2.6内核驱动移植参考
·Andrew Morton:Linux内核的执法
·Fedora 8 Linux系统的内核配置
·Kernel中的irq.c函数
·Linux核心出现权限扩张及记忆体
·Linux 2.6本地权限提升漏洞
·结合Linux系统内核源码理解SYN
·关于Linux 内核中五个主要子系
Google
 
Linux2.6内核epoll 网络编程
[ 作者:  加入时间:2007-06-01 10:18:27  来自:Linux联盟收集整理 ]

(1)导言:NN5Linux联盟
NN5Linux联盟
首先,我强烈建议大家阅读Richard Stevens著作《TCP/IP Illustracted Volume 1,2,3》和《UNIX Network Programming Volume 1,2》。虽然他离开我们大家已经5年多了,但是他的书依然是进入网络编程的最直接的道路。其中的3卷的《TCP/IP Illustracted》卷1是必读-如果你不了解tcp协议各个选项的详细定义,你就失去了优化程序重要的一个手段。卷2,3可以选读一下。比如卷2 讲解的是4.4BSD内核TCP/IP协议栈实现----这个版本的协议栈几乎影响了现在所有的主流os,但是因为年代久远,内容不一定那么vogue. 在这里我多推荐一本《The Linux Networking Architecture--Design and Implementation of Network Protocols in the Linux Kernel》,以2.4内核讲解Linux TCP/IP实现,相当不错.作为一个现实世界中的实现,很多时候你必须作很多权衡,这时候参考一个久经考验的系统更有实际意义。举个例子,linux内核中sk_buff结构为了追求速度和安全,牺牲了部分内存,所以在发送TCP包的时候,无论应用层数据多大,sk_buff最小也有272的字节.NN5Linux联盟
NN5Linux联盟
其实对于socket应用层程序来说,《UNIX Network Programming Volume 1》意义更大一点.2003年的时候,这本书出了最新的第3版本,不过主要还是修订第2版本。其中第6章《I/O Multiplexing》是最重要的。Stevens给出了网络IO的基本模型。在这里最重要的莫过于select模型和Asynchronous I/O模型.从理论上说,AIO似乎是最高效的,你的IO操作可以立即返回,然后等待os告诉你IO操作完成。但是一直以来,如何实现就没有一个完美的方案。最著名的windows完成端口实现的AIO,实际上也是内部用线程池实现的罢了,最后的结果是IO有个线程池,你应用也需要一个线程池...... 很多文档其实已经指出了这带来的线程contexttch带来的代价。NN5Linux联盟
NN5Linux联盟
在linux 平台上,关于网络AIO一直是改动最多的地方,2.4的年代就有很多AIO内核patch,最著名的应该算是SGI那个。但是一直到2.6内核发布,网络模块的AIO一直没有进入稳定内核版本(大部分都是使用用户线程模拟方法,在使用了NPTL的linux上面其实和windows的完成端口基本上差不多了)。2.6内核所支持的AIO特指磁盘的AIO---支持io_submit(),io_getevents()以及对Direct IO的支持(就是绕过VFS系统buffer直接写硬盘,对于流服务器在内存平稳性上有相当帮助)NN5Linux联盟
NN5Linux联盟
所以,剩下的select模型基本上就是我们在linux上面的唯一选择,其实,如果加上no-block socket的配置,可以完成一个"伪"AIO的实现,只不过推动力在于你而不是os而已。不过传统的select/poll函数有着一些无法忍受的缺点,所以改进一直是2.4-2.5开发版本内核的任务,包括/dev/poll,realtime signal等等。最终,Davide Libenzi开发的epoll进入2.6内核成为正式的解决方案NN5Linux联盟
NN5Linux联盟
(2)epoll的优点NN5Linux联盟
NN5Linux联盟
<1>支持一个进程打开大数目的socket描述符(FD)NN5Linux联盟
NN5Linux联盟
select 最不能忍受的是一个进程所打开的FD是有一定限制的,由FD_SETSIZE设置,默认值是2048。对于那些需要支持的上万连接数目的IM服务器来说显然太少了。这时候你一是可以选择修改这个宏然后重新编译内核,不过资料也同时指出这样会带来网络效率的下降,二是可以选择多进程的解决方案(传统的 Apache方案),不过虽然linux上面创建进程的代价比较小,但仍旧是不可忽视的,加上进程间数据同步远比不上线程间同步的高效,所以也不是一种完美的方案。不过 epoll则没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。NN5Linux联盟
NN5Linux联盟
<2>IO效率不随FD数目增加而线性下降NN5Linux联盟
NN5Linux联盟
传统的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之上了。NN5Linux联盟
NN5Linux联盟
<3>使用mmap加速内核与用户空间的消息传递。NN5Linux联盟
NN5Linux联盟
这点实际上涉及到epoll的具体实现了。无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的。而如果你想我一样从2.5内核就关注epoll的话,一定不会忘记手工 mmap这一步的。NN5Linux联盟
NN5Linux联盟
<4>内核微调NN5Linux联盟
NN5Linux联盟
这一点其实不算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网卡驱动架构。NN5Linux联盟
NN5Linux联盟
(3)epoll的使用NN5Linux联盟
NN5Linux联盟
令人高兴的是,2.6内核的epoll比其2.5开发版本的/dev/epoll简洁了许多,所以,大部分情况下,强大的东西往往是简单的。唯一有点麻烦是epoll有2种工作方式:LT和ET。NN5Linux联盟
NN5Linux联盟
LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.NN5Linux联盟
NN5Linux联盟
ET (edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认。NN5Linux联盟
NN5Linux联盟
epoll只有epoll_create,epoll_ctl,epoll_wait 3个系统调用,具体用法请参考http:
//www.xmailserver.org/linux-patches/nio-improve.html ,NN5Linux联盟
NN5Linux联盟
在http:
//www.kegel.com/rn/也有一个完整的例子,大家一看就知道如何使用了NN5Linux联盟
NN5Linux联盟
NN5Linux联盟
(4)Leader/follower模式线程pool实现,以及和epoll的配合NN5Linux联盟
NN5Linux联盟
.....未完成,主要是要避免过多的epoll_ctl调用,以及尝试使用EPOLLONESHOT加速......NN5Linux联盟
NN5Linux联盟
(5)benchmarkNN5Linux联盟
NN5Linux联盟
.......未完成NN5Linux联盟
NN5Linux联盟
NN5Linux联盟
一个简单的基于epoll的web server,性能还不错NN5Linux联盟
NN5Linux联盟
NN5Linux联盟
我根据一个epoll的模型改了一个http server出来。只有129行,还可以精简不少,呵呵。NN5Linux联盟
小测了一下,一秒钟处理了一万了请求。当然这里只是把现成的东西输出。没考虑到发送数据处理。和请求的解析。NN5Linux联盟
注意了,epoll只基于linux 2.6内核的。其他平台不能用。NN5Linux联盟
NN5Linux联盟
NN5Linux联盟
NN5Linux联盟
NN5Linux联盟
NN5Linux联盟
/*-------------------------------------------------------------------------------------------------NN5Linux联盟
gcc -o httpd httpd.c -lpthreadNN5Linux联盟
author: wyezlNN5Linux联盟
2006.4.28NN5Linux联盟
---------------------------------------------------------------------------------------------------*/
NN5Linux联盟
NN5Linux联盟
#include <sys/socket.h>NN5Linux联盟
#include <sys/epoll.h>NN5Linux联盟
#include <netinet/in.h>NN5Linux联盟
#include <arpa/inet.h>NN5Linux联盟
#include <fcntl.h>NN5Linux联盟
#include <unistd.h>NN5Linux联盟
#include <stdio.h>NN5Linux联盟
#include <pthread.h>NN5Linux联盟
#include <errno.h>NN5Linux联盟
NN5Linux联盟
#define PORT 8888NN5Linux联盟
#define MAXFDS 5000NN5Linux联盟
#define EVENTSIZE 100NN5Linux联盟
NN5Linux联盟
#define BUFFER "HTTP/1.1 200 OK\r\nContent-Length: 5\r\nConnection: close\r\nContent-Type: text/html\r\n\r\nHello"NN5Linux联盟
NN5Linux联盟
int epfd;NN5Linux联盟
void *serv_epoll(void *p);NN5Linux联盟
void setnonblocking(int fd)NN5Linux联盟
{NN5Linux联盟
    int opts;NN5Linux联盟
    opts=fcntl(fd, F_GETFL);NN5Linux联盟
    if (opts < 0)NN5Linux联盟
    {NN5Linux联盟
          fprintf(stderr, "fcntl failed\n");NN5Linux联盟
          return;NN5Linux联盟
    }NN5Linux联盟
    opts = opts | O_NONBLOCK;NN5Linux联盟
    if(fcntl(fd, F_SETFL, opts) < 0)NN5Linux联盟
    {NN5Linux联盟
          fprintf(stderr, "fcntl failed\n");NN5Linux联盟
          return;NN5Linux联盟
    }NN5Linux联盟
    return;NN5Linux联盟
}NN5Linux联盟
NN5Linux联盟
int main(int argc, char *argv[])NN5Linux联盟
{NN5Linux联盟
    int fd, cfd,opt=1;NN5Linux联盟
    struct epoll_event ev;NN5Linux联盟
    struct sockaddr_in sin, cin;NN5Linux联盟
    socklen_t sin_len = sizeof(struct sockaddr_in);NN5Linux联盟
    pthread_t tid;NN5Linux联盟
    pthread_attr_t attr;NN5Linux联盟
NN5Linux联盟
    epfd = epoll_create(MAXFDS);NN5Linux联盟
    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) <= 0)NN5Linux联盟
    {NN5Linux联盟
          fprintf(stderr, "socket failed\n");NN5Linux联盟
          return -1;NN5Linux联盟
    }NN5Linux联盟
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void*)&opt, sizeof(opt));NN5Linux联盟
NN5Linux联盟
    memset(&sin, 0, sizeof(struct sockaddr_in));NN5Linux联盟
    sin.sin_family = AF_INET;NN5Linux联盟
    sin.sin_port = htons((short)(PORT));NN5Linux联盟
    sin.sin_addr.s_addr = INADDR_ANY;NN5Linux联盟
    if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) != 0)NN5Linux联盟
    {NN5Linux联盟
          fprintf(stderr, "bind failed\n");NN5Linux联盟
          return -1;NN5Linux联盟
    }NN5Linux联盟
    if (listen(fd, 32) != 0)NN5Linux联盟
    {NN5Linux联盟
          fprintf(stderr, "listen failed\n");NN5Linux联盟
          return -1;NN5Linux联盟
    }NN5Linux联盟
NN5Linux联盟
    pthread_attr_init(&attr);NN5Linux联盟
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);NN5Linux联盟
    if (pthread_create(&tid, &attr, serv_epoll, NULL) != 0)NN5Linux联盟
    {NN5Linux联盟
          fprintf(stderr, "pthread_create failed\n");NN5Linux联盟
          return -1;NN5Linux联盟
    }NN5Linux联盟
NN5Linux联盟
    while ((cfd = accept(fd, (struct sockaddr *)&cin, &sin_len)) > 0)NN5Linux联盟
    {NN5Linux联盟
          setnonblocking(cfd);NN5Linux联盟
          ev.data.fd = cfd;NN5Linux联盟
          ev.events = EPOLLIN | EPOLLET;NN5Linux联盟
          epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);NN5Linux联盟
          
//printf("connect from %s\n",inet_ntoa(cin.sin_addr));NN5Linux联盟
NN5Linux联盟
          
//printf("cfd=%d\n",cfd);NN5Linux联盟
NN5Linux联盟
    }NN5Linux联盟
NN5Linux联盟
    if (fd > 0)NN5Linux联盟
          close(fd);NN5Linux联盟
    return 0;NN5Linux联盟
}NN5Linux联盟
NN5Linux联盟
void *serv_epoll(void *p)NN5Linux联盟
{NN5Linux联盟
    int i, ret, cfd, nfds;;NN5Linux联盟
    struct epoll_event ev,events[EVENTSIZE];NN5Linux联盟
    char buffer[512];NN5Linux联盟
NN5Linux联盟
    while (1)NN5Linux联盟
    {NN5Linux联盟
          nfds = epoll_wait(epfd, events, EVENTSIZE , -1);NN5Linux联盟
          
//printf("nfds ........... %d\n",nfds);NN5Linux联盟
NN5Linux联盟
          for (i=0; i<nfds; i++)NN5Linux联盟
          {NN5Linux联盟
                if(events[i].events & EPOLLIN)NN5Linux联盟
                {NN5Linux联盟
                    cfd = events[i].data.fd;NN5Linux联盟
                    ret = recv(cfd, buffer, sizeof(buffer),0);NN5Linux联盟
                    
//printf("read ret..........= %d\n",ret);NN5Linux联盟
NN5Linux联盟
NN5Linux联盟
                    ev.data.fd = cfd;NN5Linux联盟
                    ev.events = EPOLLOUT | EPOLLET;NN5Linux联盟
                    epoll_ctl(epfd, EPOLL_CTL_MOD, cfd, &ev);NN5Linux联盟
                }NN5Linux联盟
                else if(events[i].events & EPOLLOUT)NN5Linux联盟
                {NN5Linux联盟
                    cfd = events[i].data.fd;NN5Linux联盟
                    ret = send(cfd, BUFFER, strlen(BUFFER), 0);NN5Linux联盟
                    
//printf("send ret...........= %d\n", ret);NN5Linux联盟
NN5Linux联盟
NN5Linux联盟
                    ev.data.fd = cfd;NN5Linux联盟
                    epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, &ev);NN5Linux联盟
                    
//shutdown(cfd, 1);NN5Linux联盟
NN5Linux联盟
                    close(cfd);NN5Linux联盟
NN5Linux联盟
                }NN5Linux联盟
          }NN5Linux联盟
    }NN5Linux联盟
    return NULL;NN5Linux联盟
}NN5Linux联盟
NN5Linux联盟
NN5Linux联盟
NN5Linux联盟
NN5Linux联盟
下面是测试结果:NN5Linux联盟
NN5Linux联盟
[yangjian2@localhost bin]$ ./ab -c 50 -n 10000 [url]http:
//202.108.xxx.xxx:8888/[/url]NN5Linux联盟
NN5Linux联盟
This is ApacheBench, Version 2.0.41-dev <$Revision: 1.121.2.12 $> apache-2.0NN5Linux联盟
Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, [url]http:
//www.zeustech.net/[/url]NN5Linux联盟
NN5Linux联盟
Copyright (c) 1998-2002 The Apache Software Foundation, [url]http:
//www.apache.org/[/url]NN5Linux联盟
NN5Linux联盟
NN5Linux联盟
Benchmarking 202.108.xxx.xxx (be patient)NN5Linux联盟
Completed 1000 requestsNN5Linux联盟
Completed 2000 requestsNN5Linux联盟
Completed 3000 requestsNN5Linux联盟
Completed 4000 requestsNN5Linux联盟
Completed 5000 requestsNN5Linux联盟
Completed 6000 requestsNN5Linux联盟
Completed 7000 requestsNN5Linux联盟
Completed 8000 requestsNN5Linux联盟
Completed 9000 requestsNN5Linux联盟
Finished 10000 requestsNN5Linux联盟
NN5Linux联盟
NN5Linux联盟
Server Software: NN5Linux联盟
Server Hostname: 202.108.xxx.xxxNN5Linux联盟
Server Port: 8888NN5Linux联盟
NN5Linux联盟
Document Path: /NN5Linux联盟
Document Length: 5 bytesNN5Linux联盟
NN5Linux联盟
Concurrency Level: 50NN5Linux联盟
Time taken for tests: 0.921732 secondsNN5Linux联盟
Complete requests: 10000NN5Linux联盟
Failed requests: 0NN5Linux联盟
Write errors: 0NN5Linux联盟
Total transferred: 872088 bytesNN5Linux联盟
HTML transferred: 50120 bytesNN5Linux联盟
Requests per second: 10849.14 [#/sec] (mean)NN5Linux联盟
Time per request: 4.609 [ms] (mean)NN5Linux联盟
Time per request: 0.092 [ms] (mean, across all concurrent requests)NN5Linux联盟
Transfer rate: 923.26 [Kbytes/sec] receivedNN5Linux联盟
NN5Linux联盟
Connection Times (ms)NN5Linux联盟
        min mean[+/-sd] median maxNN5Linux联盟
Connect: 1 1 0.9 2 3NN5Linux联盟
Processing: 1 2 0.5 2 4NN5Linux联盟
Waiting: 0 1 0.3 1 3NN5Linux联盟
Total: 4 4 0.2 4 5NN5Linux联盟
WARNING: The median and mean for the initial connection time are not within a normal deviationNN5Linux联盟
    These results are probably not that reliable.NN5Linux联盟
NN5Linux联盟
Percentage of the requests served within a certain time (ms)NN5Linux联盟
50% 4NN5Linux联盟
66% 4NN5Linux联盟
75% 4NN5Linux联盟
80% 4NN5Linux联盟
90% 4NN5Linux联盟
95% 4NN5Linux联盟
98% 5NN5Linux联盟
99% 5NN5Linux联盟
100% 5 (longest request)

Linux联盟收集整理 ,转贴请标明原始链接,如有任何疑问欢迎来本站Linux论坛讨论
评论】【加入收藏夹】【 】【打印】【关闭
※ 相关链接
无相关信息