为进一步提升服务器效率,人们提出了一种被称为I/O多路转接的模型。其中“多路”指代连接到服务器的多个客户端程序,而“转接”则是指在服务器主线与各分支之间设置一个“岗位”,由该岗位实现监控多路连接中数据状态的功能,若某路连接中数据就绪,就通知服务器,使主程序对该路请求作出处理。
与多进程和多线程并发服务器相比,I/O多路转接服务器实现了I/O多路复用,系统不必创建多进程或多线程,也不必维护多个进程或线程,因此大大降低了系统开销。
Linux系统实现I/O多路转接函数有:
- select()
- poll()
- epoll()
图 select通信模型示意图
- 使用select搭建的I/O多路转接服务器是一种基于非阻塞的服务器;
- 当有客户端连接请求到达时,accept会返回一个文件描述符,该文件描述符会被存储到由select监控的文件描述符表中,每个文件描述符对应的文件都可进行I/O操作,因此select可通过监控表中各个文件描述符,来获取对应的客户端I/O状态。
- 若每路程序中都没有数据到达,线程将阻塞在select上;否则select将已就绪客户端程序的数量返回到服务器。
#includeint select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); struct timeval{ long tv_sec; long tv_user; };
功能: 监视客户端I/O接口的状态
参数说明:
- nfds:设置select监控的文件描述符的范围,需设置为文件描述符最大值加1;
- readfds:可读取数据的文件描述符集,fd_set实质为长整型,这些集合中的每一位都对应一个文件描述符的状态;若集合参数被设置为NULL,表示不关心文件的对应状态;
- writefds:可写入数据的文件描述符集;
- exceptfds:发生异常的文件描述符集;
- timeout:设置select的阻塞时长,其取值有如下几种情况:
– 若timeval=NULL,表示永远等待;
– 若timeval>0,表示等待固定时长;
– 若timeval=0,select将在检查过指定文件描述符后立即返回(轮询)。
Linux系统提供了一系列操作文件描述符集的函数:
| 函数声明 | 函数功能 |
|---|---|
| void FD_CLR(int fd,fd_set *set); | 将集合中的文件描述符fd清除(将fd位置为0) |
| int FD_ISSET(int fd,fd_set *set); | 测试集合中文件描述符fd是否存在于集合中,若存在则返回非0 |
| void FD_SET(int fd,fd_set *set); | 将文件描述符fd添加到集合中(将fd位置为1) |
| void FD_ZERO(fd_set *set); | 清除集合中所有的文件描述符(所有位置0) |
返回值说明:
- 大于0:表示已就绪文件描述符的数量,此种情况下某些文件可读写或有错误信息;
- 等于0:表示等待超时,没有可读写或错误的文件;
- -1:表示出错返回,同时errno将被设置。
特别说明:
- select可监控的进程数量是有限的,该数量受到两个因素的限制。
–第一个因素是进程可打开的文件数量;
–第二个因素是select中的集合fd_set的容量。 - 进程可打开文件的上限可通过ulimit –n命令或setrlimit函数设置,但系统所能打开的最大文件数也是有限的;
- select中集合fd_set的容量由宏FD_SETSIZE(定义在linux/posix_types.h中)指定;
- 即便通过重新编译内核的方式修改FD_SETSIZE,也不一定能提升select服务器的性能,因为若select一次监测的进程过多,单轮询便要耗费大量的时间。



