7. I/O复用

一、I/O复用的特点

  • 能同时监听多个文件描述符
  • 自身是阻塞的
  • 多个文件描述符同时就绪时,如果不采取额外的措施,程序就只能按顺序依次处理其中的每一个文件描述符

由于其第三个特点,所以服务器程序看起来仍像是串行工作的,如果要实现并发,只能使用多进程或多线程等编程手段。

二、select系统调用

/* 进程阻塞在select调用上监听多个事件,并在事件就绪或超时后返回 */
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

/* 参数说明 */
// nfds:被监听的文件描述符的总数 
// readfds:指向可读事件对应的文件描述符集合 
// writefds:指向可写事件对应的文件描述符集合 
// exceptfds:指向异常事件对应的文件描述符集合 
// timeout:select函数的超时时间 

1. 功能:在一段指定时间内,监听用户感兴趣的那些文件描述符上的可读、可写和异常事件。

2. nfds参数:select函数监听的文件描述符的总数

假设在我们感兴趣的文件描述符集合中,文件描述符的最大值为maxfd,那么select函数监听的文件描述符总数nfds = maxfd + 1,因为文件描述符是从0开始计数的。这意味着,select函数监听的文件描述符的个数nfds可能超过我们感兴趣的文件描述符的个数。

{我们感兴趣的文件描述符} ⊆ {select函数监听的文件描述符}

3. 中间三个指针参数:分别指向可读、可写和异常事件对应的文件描述符集合

  • 调用select函数时,通过这三个参数传入我们感兴趣的文件描述符
  • select函数返回时,内核将修改它们所指向的文件描述符集合来通知哪些文件描述符已经就绪

当select函数返回时,那些未就绪的文件描述符对应的位均被置零。所以,每次重新调用select函数时,这三个参数指向的文件描述符集均需重新置位。

4. timeout参数:select函数的超时时间

内核将修改它以告诉应用程序select等待了多久。不过一般把它设置为NULL,让select一直阻塞到有事件就绪。

5. fd_set结构体

struct fd_set {
	int fds_bits[FD_SETSIZE / 32];	// 因为int类型占32位,故该数组的总位数等于FD_SETSIZE 
}; 

fd_set结构体仅包含一个整型数组,该数组的每个元素的每一位标记一个文件描述符。

故fd_set能容纳的文件描述符数量由FD_SETSIZE指定,这就限制了select函数能同时处理的文件描述符的总量。  

/* 访问fd_set结构体中的位 */
FD_ZERO(fd_set *fdset);					// 清除fdset的所有位 
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是否被设置 

6. select函数可被信号中断,此时select立即返回-1,并设置errno为EINTR。

7. select函数的缺点

①监听的文件描述符的数目太多。比如我感兴趣的两个文件描述符的值分别为1和711,这种情况下,select函数将监听712个文件描述符。

②一次监听的文件描述符的总量受FD_SETSIZE的限制。

③每次重新调用select函数时,总要重新设置感兴趣的文件描述符。  

三、poll系统调用

猜你喜欢

转载自www.cnblogs.com/xzxl/p/9563348.html