IO多路复用select函数详解

11 篇文章 1 订阅
7 篇文章 4 订阅

Linux select函数详解

一 五种I/O操作模式

​ linux下的I/O操作:I/O是指数据流的操作,比如说网络编程的I/O操作,串口的读写等等可以称为I/O操作。在linux系统中一共有下面五种I/O操作模式。

  • 阻塞I/O(blocking I/O)
  • 非阻塞I/O (nonblocking I/O)
  • I/O复用 (I/O multiplexing)
  • 信号驱动I/O (signal driven I/O (SIGIO))
  • 异步I/O (asynchronous I/O (the POSIX aio_functions))

前四种都是同步I/O,只有最后一种是异步I/O

二 阻塞型I/O

​阻塞型I/O:应用程序调用一个I/O函数,导致应用程序阻塞,直到数据准备好。数据准备好了,从内核拷贝到用户空间(或者反过来),I/O函数返回成功

linux系统默认的I/O下操作模式为阻塞模式,也是最常见的I/O操作模式。例如创建了一个套接字,想要使用非阻塞模式,那么你需要进行设置,因为默认的是阻塞模式。

以TCP通信中 recv() 函数为例:

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

创建套接字,bind和listen后,以及使用accept连接上后就会使用这个recv函数去等待数据到来,等对方通过socket将数据发送到内核,内核就会通知进程有数据到来,此时这个函数就会返回,返回接收数据的字节数,否则一直会在阻塞状态等待数据的到来。

三 非阻塞型I/O

非阻塞型I/O:就是告诉内核,当我们调用IO函数时,不管数据有没有准备好,都立即返回(即使出错)

//把套接字设置成非阻塞模式
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); 

非阻塞模式的使用并不普遍,因为非阻塞模式会浪费大量的CPU资源

四 I/O多路复用(重点)

背景:前面讲过,阻塞模式是最常用的一种I/O模式,从socket的角度出发,一个client去连接连接一个server端,那么如果此时我们使用阻塞模式的话,如果只使用一个线程的话,此时第一个client连接过来了,调用了recv()函数,阻塞在等待消息这里,此时如果第二个客户端连接的话,因为程序一直在recv()这里等着,无法处理这个连接请求。那么如果我们使用多线程机制,对每个客户端都使用一个线程,那么如果有大量的客户连接的话,服务端就要创建大量的线程,大量的线程创建会消耗linux的资源。

在Linux中,我们可以使用select函数实现I/O端口的复用,传递给select函数的参数会告诉内核

  1. 我们所关心的文件描述符
  2. 对每个描述符,我们所关心的状态。(我们是要想从一个文件描述符中读或者写,还是关注一个描述符中是否出现异常)
  3. 我们要等待多长时间。(我们可以等待无限长的时间,等待固定的一段时间,或者根本就不等待)

从 select函数返回后,内核告诉我们一下信息:

  1. 对我们的要求已经做好准备的描述符的个数
  2. 对于三种条件哪些描述符已经做好准备.(读,写,异常)

有了这些返回信息,我们再调用合适的I/O函数(通常是 read 或 write),并且这些函数不会再阻塞.

1.select函数
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select(int maxfd, fd_set *readfds, fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);
/*
功能:实现IO的多路复用
参数:
			  int maxfd:指集合中所有文件描述符的范围,即文件描述符的最大值加1(默认最大值为1024)
			  			 内核只需要在我们打开的最大值的描述符以内进行轮询,已减少轮询时间和系统开销
		fd_set *readfds:需要监视的可读的描述符的集合,在参数1的范围内,把需要监视的描述需要提前置位(下文详解)
	   fd_set *writefds:需要监视的可写的描述符的集合,要求同上
  	  fd_set *exceptfds:需要监视的可能异常的描述符的集合,要求同上
struct timeval *timeout:下面是这个参数的结构体类型
					struct timeval{      
        				long tv_sec;  //秒 
        				long tv_usec; //微秒 
        				}
        				1.timeout == NULL 
        						等待无限长的时间
        						等待可以被一个信号中断,如果捕获到一个信号, select函数将返回 -1,并将变量 erro设为 EINTR。
        						当有一个描述符做好准备或者是捕获到一个信号时函数会返回。
        				2.timeout->tv_sec == 0 && timeout->tv_usec == 0 
        						不等待,直接返回
        						加入描述符集的描述符都会被测试,并且立即返回
        						这种方法通过轮询,无阻塞地获得了多个文件描述符状态
        				3.timeout->tv_sec !=0 ||timeout->tv_usec!= 0
        						等待指定的时间(期间也会被信号中断)
        						当有描述符符合条件或者超过设定的时间,函数返回
        									
返回值:	
		retval > 0 :当监视的相应的文件描述符集中满足条件时,比如说读文件描述符集中有数据到来时,
					内核(I/O)根据状态修改文件描述符集,并返回一个大于0的数
		retval = 0 :当没有满足条件的文件描述符,且设置的timeval监控时间超时,select函数会返回 0
		retval < 0 :出错返回-1(信号中断等)
*/
2.描述符的集合

对于 fd_set类型的变量我们所能做的就是声明一个变量,为变量赋一个同种类型的值,或者使用以下几个宏来控制它:

#include <sys/select.h>   

int FD_ZERO(int fd, fd_set *fdset);  //将一个 fd_set类型变量的所有位都设为 0
int FD_SET(int fd, fd_set *fd_set);  //将变量的某个位置位(置1)
int FD_ISSET(int fd, fd_set *fdset); //测试某个位是否被置位
int FD_CLR(int fd, fd_set *fdset);   //清除变量某个位

当声明了一个文件描述符集后,必须用FD_ZERO将所有位置零。之后将我们所感兴趣的描述符所对应的位置位,操作如下:

fd_set rset; 
int fd;   
FD_ZERO(&rset);   
FD_SET(fd, &rset);   
FD_SET(stdin, &rset);

select返回后,用FD_ISSET测试给定位是否置位

if(FD_ISSET(fd, &rset)
{
	;//此时调用对应的IO函数时不会阻塞
}
2.理解select模型

理解select模型的关键在于理解fd_set,实际上用sizeof(fd_set)可以获取描述符集合的大小,那么我们可以监控的描述符的数量为:8*sizeof(fd_set)

为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。

  1. 执行fd_set set;FD_ZERO(&set);则set用位表示是0000,0000。
  2. 若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)
  3. 若再加入fd=2,fd=1,则set变为0001,0011
  4. 执行select(6,&set,0,0,0)阻塞等待
  5. 若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。
    在这里插入图片描述
    参考资料:
    https://blog.csdn.net/lingfengtengfei/article/details/12392449
    https://blog.csdn.net/qq_18059143/article/details/120374229
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值