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

📄 4.txt

📁 linux环境下的TCP网络服务器端实现原代码
💻 TXT
字号:
进入2.6内核时代, select应该进垃圾堆了 
高并发服务器用select效率极低, 特别是使用非阻塞IO时更是慢得一蹋糊涂 
改用epoll会大大改善 
我一个程序监听从8000到18000共计1万个端口, 启动1万个LISTEN 
用epoll来阻塞, 系统非常轻松, 完全没有惊群现象 

epoll用法比select简单 

初始化:创建epoll描述字; 向epoll描述字添加需要响应的套接字, 初始化过程只要一次即可 

使用: 等待epoll事件发生, 提取事件的套接字进行相应的读写操作 


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; 
} 
} 
} 
}

1、为什么select是落后的? 

首先,在Linux内核中,select所用到的FD_SET是有限的,即内核中有个参数__FD_SETSIZE定义了每个FD_SET的句柄个数,在我用的2.6.15-25-386内核中,该值是1024,搜索内核源代码得到: 

include/linux/posix_types.h:#define __FD_SETSIZE        1024 

也就是说,如果想要同时检测1025个句柄的可读状态是不可能用select实现的。或者同时检测1025个句柄的可写状态也是不可能的。 

其次,内核中实现select是用轮询方法,即每次检测都会遍历所有FD_SET中的句柄,显然,select函数执行时间与FD_SET中的句柄个数有一个比例关系,即select要检测的句柄数越多就会越费时。 

当然,在前文中我并没有提及poll方法,事实上用select的朋友一定也试过poll,我个人觉得select和poll大同小异,个人偏好于用select而已。 



/************关于本文档******************************************** 

*filename: Linux 2.6内核中提高网络I/O性能的新方法epoll 

*purpose: 补充“Linux下各类TCP网络服务器的实现源代码”一文的不足之处 

*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com) 

Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言 

*date time:2006-07-06 22:30:00 

*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途 

* 但请遵循GPL 

*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力 

*********************************************************************/ 



2、2.6内核中提高I/O性能的新方法epoll 



epoll是什么?按照man手册的说法:是为处理大批量句柄而作了改进的poll。要使用epoll只需要这三个系统调用:epoll_create(2), epoll_ctl(2), epoll_wait(2)。 

当然,这不是2.6内核才有的,它是在2.5.44内核中被引进的(epoll(4) is a new API introduced in Linux kernel 2.5.44) 



以下文章转自滕昱的Web Log http://mechgouki.spaces.msn.com/blog/PersonalSpace.aspx 
[QUOTE] 

/*********************************引用开始******************************/ 


[版权声明]:此文档遵循GNU自由文档许可证(GNU Free Documentation License).任何人可以自由复制,分发,修改,不过如果方便,请注明出处和作者:) 



(1)导言: 



首先,我强烈建议大家阅读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的字节. 



其实对于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有个线程池,你应用也需要一个线程池...... 很多文档其实已经指出了这带来的线程context-switch带来的代价。 



在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直接写硬盘,对于流服务器在内存平稳性上有相当帮助)。 



所以,剩下的select模型基本上就是我们在linux上面的唯一选择,其实,如果加上no-block socket的配置,可以完成一个"伪"AIO的实现,只不过推动力在于你而不是os而已。不过传统的select/poll函数有着一些无法忍受的缺点,所以改进一直是2.4-2.5开发版本内核的任务,包括/dev/poll,realtime signal等等。最终,Davide Libenzi开发的epoll进入2.6内核成为正式的解决方案 

⌨️ 快捷键说明

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