前言:笔者前几天参加面试,被问到select函数的限制,然后感觉一头雾水,回来之后赶紧查找了资料弥补知识短板,也意识到以后使用一些系统调用应该多想些为什么,结合现实去思考实际中会遇到的问题。
下面是对selecth函数的一些总结:
select是网络IO模型中的IO复用(在之前笔者还以为select属于异步io),了解select之前大家可以先了解下IO模型,下面两篇文章讲的很不错:
网络IO模型:
http://blog.csdn.net/historyasamirror/article/details/5778378
IO - 同步,异步,阻塞,非阻塞 (亡羊补牢篇) - 智障大师 的专栏 - 博客频道 - CSDN.NET
http://www.cnblogs.com/Anker/p/3254269.html
网络IO之阻塞、非阻塞、同步、异步总结 - Anker's Blog - 博客园
I/O复用之select:
int select( int nfds,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
const struct timeval *timeout
);
一、参数:
1、int nfds:select监视的文件句柄数,视进程中打开的文件数而定,一般设为你要监视各文件中的最大文件号加一。
2、readfds:select监视的可读文件句柄集合。
当rdfds映象的文件句柄状态变成可读时系统告诉select函数返回。
这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值,可以传入NULL值,表示不关心任何文件的读变化
3、writefds: select监视的可写文件句柄集合。
当wtfds映象的文件句柄状态变成可写时系统告诉select函数返回。
如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值,可以传入NULL值,表示不关心任何文件的写变化。
4、exceptfds:select监视的异常文件句柄集合。
当exfds映象的文件句柄上有特殊情况发生时系统会告诉select函数返回。
5、timeout:本次select()的超时结束时间。(见/usr/sys/select.h,可精确至百万分之一秒!)
这个参数有以下三种可能:
1、参数设为空指针:永远等待下去:仅在有一个文件描述符准备好I/O时才返回。
2、等待一段固定时间:在有一个描述符准备好I/O时才返回,但是不超过该参数所指向timeout结构中指定的秒数和微秒数
3、根本不等待;检查描述符后立即返回,这称为轮询(polling)。为此,该参数必须指向timeout结构,而且其中的定时器值必须是0.
这里涉及到两个结构体:
1、struct fd_set:存放文件描述符(flie descriptor)的集合,即文件句柄的集合,实际上是long类型的数组,每一个数组元素都能与一打开的文件句柄(不管是socket句柄,还是其他文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成。如下:
FD_ZERO(fd_set *fdset):清空fdset与所有文件句柄的联系。
FD_SET(int fd, fd_set *fdset):建立文件句柄fd与fdset的联系。
FD_CLR(int fd, fd_set *fdset):清除文件句柄fd与fdset的联系。
FD_ISSET(int fd, fdset *fdset):检查fdset联系的文件句柄fd是否可读写,>0表示可读写。
2、struct timeval用来表示时间值,有两个成员,一个是秒数tv_sec,另一个是毫秒tv_usec.
二、返回值:
负值:select错误
0:等待超时,没有可读写或错误的文件
正值:某些文件可读可写或出错
三、句柄数限制
select 最不能忍受的是一个进程所打开的FD是有一定限制的,由FD_SETSIZE设置,默认值是1024。对于那些需要支持的上万连接数目的IM服务器来说显然太少了。这时候你一是可以选择修改这个宏然后重新编译内核,不过资料也同时指出这样会带来网络效率下降。(不过 epoll则没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max查看,一般来说这个数目和系统内存关系很大。)