I/O复用的作用
I/O复用使得程序能同时监听多个文件描述符,一旦用户所关心的文件描述符上有事件发生,则内核就会通知用户去处理,如果不使用I/O复用,则进程需要依次对用户添加的文件描述符进行处理,但可能有些文件描述符并没有事件发生,则会造成阻塞,使得程序性能的降低。
select系统调用
select系统调用的用途是:在一段制定时间内,监听用户感兴趣的文件描述上的可读、可写和异常等事件。
select函数
int selec(int nfds, fd_set *readfds, fd_set *wrritefds, fd_set *exceptfds, struct timeval *timeout);
nfds:指定被监听的文件描述符的总数,通常为select监听的所有文件描述符中最大的值加一。
timeout:设置超时时间,采用一个struct timeval结构体指针参数,但调用失败时timeout值是不确定的。
中间三个参数:分别指可读、可写和异常等事件对应的文件描述符集合,它们都是fd_set结构指针。当select调用返回时,内核将修改它们来通知应用程序哪些文件描述符已经就绪。
typedef __kernel_fd_set fd_set;//定义
//维护一个数组
typedef struct
{
unsigned long fds_bits [__FDSET_LONGS];
} __kernel_fd_set;
//数组大小 是计算得来的
#undef __FDSET_LONGS
#define __FDSET_LONGS (__FD_SETSIZE/__NFDBITS)
//下面可以看出数组大小为 1024 / 8 个unsigned long 类型的值
#undef __FD_SETSIZE
#define __FD_SETSIZE 1024
#undef __NFDBITS
#define __NFDBITS (8 * sizeof(unsigned long))
#include <sys/select.h>
int FD_ZERO(int fd, fd_set *fdset);
int FD_CLR(int fd, fd_set *fdset);
int FD_SET(int fd, fd_set *fd_set);
int FD_ISSET(int fd, fd_set *fdset);
FD_CLR(inr fd,fd_set* set);//用来清除描述词组set中相关fd 的位 FD_ISSET(int fd,fd_set set);用来测试描述词组set中相关fd 的位是否为真 FD_SET(int fd,fd_setset);用来设置描述词组set中相关fd的位 FD_ZERO(fd_set *set);用来清除描述词组set的全部位
timeval结构体
struct timeval
{
time_t tv_sec; /* 秒 */
suseconds_t tv_usec; /* 微秒 */
};
select成功时返回就绪文件描述符的总数。如果超过超时时间则返回0,失败时返回-1并设置errno。如果在select等待期间,程序接收到信号,则select立即返回-1,并设置errno为EINTR。
select总结
select工作在LT模式,用户通过3个参数分别传入感兴趣的可读、可写和异常事件,内核通过对这些参数的在线修改来通知用户处理就绪事件。这使得用户每次调用select都要重置这三个参数。而且select采用的是轮询的方式,每次调用都要扫描整个注册文件描述符集合,它的检测就绪事件的算法时间复杂度是O(n)。