I/O复用之Select模型
I/O复用使得程序能够同时监听多个文件描述符,但是,他本身也是阻塞的,并且当一个或多个文件描述符准备就绪时,如果不采用其他措施,程序只能按顺序处理其中的每个文件描述符。如果要使程序能够并行运行,只能使用多进程或多线程的方式。
Linux下实现I/O复用的系统调用主要有select、poll和epoll,下面将详细介绍select的系统调用。
Select系统调用的用途:在一段时间内,监听用户感兴趣的文件描述符上面的可读、可写和异常等事件。
相关API的介绍:
#include <sys/select.h>
int select(int nfds, fd_set * readfds, fd_set * writefdds, fd_set * exceptfds, struct timeval * timeout);
①、nfds参数:所有监听的文件描述符的最大值 + 1
②、readfds、writefds和exceptfds参数分别为可读、可写和异常等事件对应的文件描述符集合;程序只需要传入自己感兴趣的文件描述符,内核将修改他们来通知程序那些文件描述符已经准备就绪;fd_set结构体仅包含一个整形数组,该数组的每一个元素的每一位标志一个文件描述符,下面的一组宏用来操作fd_set的每一位:
FD_ZERO(fd_set * fdset);//清除fd_set的所有位
FD_SET(int fd, fd_set * fdset);//设置fdset的位fd
FD_CLR(int fd, fd_set * fdset);//清除fdset的位fd
int FD_ISSET(int fd, fd_set * fdset);//测试fdset的位fd是否被设置
③、ttimeout参数:timeout参数是一个timeval结构体,timeval的结构体的定义如下:
struct timeval
{
long tv_sec;//秒数
long tv_usec;//微秒数
};
如果tv_sec和tv_usec都设置为0,则select立即返回, 如果timeout参数设置为NULL,则select一直阻塞直到某个文件描述符准备就绪。
返回值:成功返回就绪的文件描述符的总数,如果超过时间内没有任何文件描述符准备就绪,select将返回0,失败则返回-1并设置errno;若在select等待事件内程序接收到信号,则select立即返回-1,并设置errno为EINTER。
文件描述符的就绪条件:
可读描述符就绪条件:
①、接收缓冲区中的字节数大于或等于SO_RCVLOWAT标志
‚、有新的连接请求
ƒ、对方关闭连接
④、有未处理的错误
可写描述符的就绪条件:
①、发送缓冲区中的可用字节数大于或等于SO_SENLOWAT标志
‚、写操作被关闭
ƒ、有未处理的错误
异常文件描述符的就绪条件:
①、socket上接收带外数据
代码示例:
http://www.oschina.net/code/snippet_861360_27324
分享的代码主要是使用select系统调用和多线程编程实现一个简单的并行的TCP服务器
select 模型的有缺点:
优点:select模型的优点之一就是良好的跨平台支持
缺点:
1、单个进程能够监测的文件描述符有限,一般为1024,可以通过修改内核修改
2、select维护了存储大量文件描述符的数据结构,随着文件描述符的增加,其复制的开销及遍历造成的开销影响系统效率。