select,epool,pool解释
内容主要来自搜狗实验室技术交流文档,
编写链接数巨大的高负载服务器程序时,经典的多线程模式和select模式都不再适合了.应该采用epool/kqueue/dev_pool来捕获IO事件.
------
问题的由来:
C10K问题的最大特点就是:设计不够良好的程序,其性能和链接数以及机器性能的关系是非线性的.
例子:没有考虑过C10k问题,一个经典的基于select的程序能在就服务器上很耗处理1000并发的吞吐量,但是在2倍性能新服务器上往往处理不了并发2000的吞吐量.
因为:大量操作的消耗和当前链接数n成线性相关.
=-==================
基本策略:
主要有两个方面的策略:
1,应用软件以何种方式和操作系统合作,获取IO事件并调度多个socket上的IO操作;
2,应用软件以何种方式处理任务和线程/进程的关系.
前者主要有阻塞IO,费阻塞IO,异步IO三种方式
后者主要有每任务1进程,每任务1线程,单线程,多任务共享线程池以及一些更复杂的变种方案.
常用的经典策略如下:
1,serve one client with each thread/process, and use blocking IO.,
2,serve many clients with single thread, and use nonblocking IO and readiness notification.
3,serve many clients with each thread, and use nonblocking IO and readliness notification
4,serve many clienets witch each thread, and use asynchronous IO.
接下俩主要介绍策略2.
=======================
经典的单线程服务器程序结构往往如下:
do{
get readiness notification of all sockets
dispatch ready handles to corresponding handlers
if(readable){
read the socketsif
if(read done){
handler process the request }
}
if(writable){
write response
}
if(nothing to do){
close socket
}
}while(True)
其中关键的部分就是readiness notification,找出哪一个socket上面发生了IO事件.
一般从教科书和例子程序中会学到select来实现,
select函数的定义:
int select(int n,fd_set *rd_fds,fd_set *wr_fds, fd_set *ex_fds,struct timeval *timeout);
select用到了fd_set结构,从man page里可以看到fd_set能容纳的句柄和FD_SETSIZE相关.实际上fd_set在*nix下是一个bit标志数组,每个bit表示对应下标的fd是不是在fd_set中. fd_set只能容纳编号小于FD_SETSIZE的那些句柄.
----
FD_SETSIZE默认是1024,如果向fd_set中放入过大的句柄,数组越界以后程序就会垮掉.系统默认限制了一个进程最大的句柄号小于 1024,但是可以通过ulimit -n命令或者setrlimit函数来扩大这一限制.如果不幸一个程序在FD_SETSIZE=1024的环境下编译,运行时又遇到ulimit --n>1014的,会出现未定义错误.
-----
针对fd_set的问题,*nix提供了poll函数作为select的一个替代品,
int poll(struct poollfd *ufds, unsigned int nfds ,int timeout);
第一个参数ufds是用户提供的一个pollfd数组,数组大小由用户自行决定.因此避免了FD_SETSIZE带来的麻烦.
ufds是fd_set的一个完全替代品,从select到poll的一直很方便,到此我们面对C10k,可以写出一个能work的程序了.
------
但是select/poll在链接数增加时,性能急剧下降.
因为:
1,os面对每次的select/poll操作,都需要重新建立一个当前线程的关心事件列表,并把线程挂到这个复杂的等待队列上,耗时.
2,app在select/poll返回后,也需要堆传入的句柄列表做一次扫描来dispatch,耗时.
这两件事,都是和并发数相关,而事件IO的密度也和并发数相关,导致cpu占用率和并发数近似成O(n2)的关系.
-----------epoll出厂了.
因为以上原因,*nix的开发者开发了epoll,kqueue,/dev/poll这3套利器来帮助大家,
epoll是linux的方案,kqueue是freebsd方案,/dev/poll是最古老的solaris方案,使用难度一次递增.
为什么这些api是优化方案:
1,避免了每次调用select/poll时kernel分析参数建立事件等待结构的开销,kernel维护一个长期的事件关注列表,
应用程序通过句柄修改这个列表和捕获IO事件
2,避免了select/poll返回后,app扫描整个句柄表的开销,kernel直接返回具体的事件列表为app.
---先了解
边缘触发(edge trigger):指每当状态变化时发生一个IO事件
和条件触发(level trigger):只要满足条件就发生一个IO事件
举个例子:读socket,假设进过长时间沉默后,来了100个字节,这是无论边缘触发/条件触发都会产生一个read ready notification通知应用程序可读. app先读了50bytes,重新调用api等待io,这时条件触发的api因为还有50bytes刻度可立即返回用户一个read ready notification. 而边缘触发的api因为这个可读状态没变陷入长期等待.
使用边缘触发的api时,注意每次要读到socket返回EWOULDBLOCK为止,否则这个socket就废了.
而条件触发的api,如果app不需要写就不要关注socket可写的事件,否则会无限次的立即返回一个write ready notification.
条件触发比较常用.
int epoll_create(int size);
int epool_ctl(int epfd,int op,int fd, struct epoll_event *event);
int epool_wait(int epfd,struct epoll_event *events, int maxevents,int timeout);
epoll_create 创建kernel中的关注事件表,相当于创建fd_set
epoll_ctl 修改这个表,相当于FD_SET等操作
epoll_wait 等待IO事件发生,相当于select/poll函数
epoll完全是select/poll的升级版,支持的事件一致.并且epoll同时支持条件/边缘触发(后者较好).
struct epoll_event ev,*events;
int kdpdf = epoll_create();
ev.events = EPOOL|EPOLLET;//edge trigger
ev.data.fd = listener;
epoll_ctl(kdpfd,EPOLL_CTL_ADD,listener,&ev);
for(;;){
nfds = epoll_wait(kdpfd,events,maxevents,-);
for(n = ;n<nfds;n++){
if(events[n].data.fd == listener){
client = accept(listener,(struct sockaddr *)&local,&addrlen);
if(client <){
perror("accept");
continue;
}
setnonblocking(client);
ev.events = EPOOLIN|EPOOLET;
ev.data.fd = client;
if(epoll_ctl(kdpfd,EPOLL_CTL_ADD,client,&ev)<){
fprintf(stderr,"epoll set insertion error: fd=%d0",client)
return -;
}
}else{
do_use_fd(events[n].data.fd);
}
}
}
select,epool,pool解释的更多相关文章
- socket阻塞与非阻塞,同步与异步,select,pool,epool
概念理解 一.与I/O相关的五个重要概念 1. 第一个概念:用户空间与内核空间 1. 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方) 2. ...
- 事件驱动模型 IO多路复用 阻塞IO与非阻塞IO select epool
一.事件驱动 1.要理解事件驱动和程序,就需要与非事件驱动的程序进行比较.实际上,现代的程序大多是事件驱动的,比如多线程的程序,肯定是事件驱动的.早期则存在许多非事件驱动的程序,这样的程序,在需要等待 ...
- selectDOM操作详解-select option详细解释
首先从一个面试题来讲,我最近在做前端面试题,题目如下: 完成函数showlmg(),要求能够动态根据下拉列表的选项变化,更新图片的显示<body><script type=" ...
- 网络编程socket 结合IO多路复用select; epool机制分别实现单线程并发TCP服务器
select版-TCP服务器 1. select 原理 在多路复用的模型中,比较常用的有select模型和epoll模型.这两个都是系统接口,由操作系统提供.当然,Python的select模块进行了 ...
- 关于select那点事
select: 通过监视多个文件描述符的数组.当select()返回后 文件描述符便会被内核修改标志位,使进程 能进行后续操作 ------------------------------------ ...
- MySQL中EXPLAIN的解释
EXPLAIN是查看MySQL优化器如何决定执行查询的主要方法,这个功能具有局限性,以为它并总是会说出真相,但是却可以获得最好信息. 学会解释EXPLAIN,你就会了解MySQL优化器是如何工作,你才 ...
- python select模块详解
要理解select.select模块其实主要就是要理解它的参数, 以及其三个返回值.select()方法接收并监控3个通信列表, 第一个是所有的输入的data,就是指外部发过来的数据,第2个是监控和接 ...
- UNIX网络编程——select函数的并发限制和 poll 函数应用举例
一.用select实现的并发服务器,能达到的并发数,受两方面限制 1.一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n来调整或者使用setrlimit函数设置, ...
- python select.select模块通信全过程详解
要理解select.select模块其实主要就是要理解它的参数, 以及其三个返回值.select()方法接收并监控3个通信列表, 第一个是所有的输入的data,就是指外部发过来的数据,第2个是监控和接 ...
随机推荐
- 解决Volley请求网络数据返回的数据乱码
本人可参考http://tieba.baidu.com/p/4039693566 以往一般我们如下写就可以了 StringRequest request=new StringRequest(url, ...
- Android 中类似ModelWindow的一个实现
Android里一般的画面(Activity)都是尽量占满整个屏幕,这样符合单线程的设计, 而有些类似popup之类的子画面,却希望他弹出来的时候表现的如同web的模态窗口 (ModelWindow, ...
- 输入DStream和Receiver详解
输入DStream代表了来自数据源的输入数据流.在之前的wordcount例子中,lines就是一个输入DStream(JavaReceiverInputDStream),代表了从netcat(nc) ...
- jQuery使用伪递归重复执行动画
使用setInterval()来重复执行动画,会因为动画执行过程的时候,setInterval()的时间依然是在走的,所以会导致动画的调用时间不理想,因此只能使用递归来重复执行动画. // 首页LOG ...
- QueryRunner使用
在相继学习了JDBC和数据库操作之后,我们明显感到编写JDBC代码并非一件轻松的事儿.为了帮助我们更高效的学习工作,从JDBC的繁重代码中解脱出来,老佟给我们详尽介绍了一个简化JDBC操作的组件——D ...
- EDIUS工程里面的素材保存方法
在实际使用EDIUS剪辑一段视频时,会有好多人问这样一个问题:EDIUS工程里面使用的素材不能自动复制到工程文件夹里面吗,难道要自己一个一个复制进去?面对这样的疑问,来看看小编是如何做的吧. 为了用户 ...
- SQL Server中数据库文件的存放方式,文件和文件组
原文地址:http://www.cnblogs.com/CareySon/archive/2011/12/26/2301597.html SQL Server中数据库文件的存放方式,文件和文件组 ...
- Linq的Distinct方法的扩展
原文地址:如何很好的使用Linq的Distinct方法 Person1: Id=1, Name="Test1" Person2: Id=1, Name="Test1&qu ...
- linux服务之drbd
http://www.drbd.org/docs/about/http://oss.linbit.com/drbd/ 一般我们会在生产环境的MYSQL中用drbd +ha做master 备份,当然这是 ...
- linux 下查看机器是cpu是几核的
几个cpu more /proc/cpuinfo |grep "physical id"|uniq|wc -l 每个cpu是几核(假设cpu配置相同) more /proc/cpu ...