select与阻塞和非阻塞

一、设备的阻塞与非阻塞

  阻塞操作是指,在执行设备操作时,若不能获得资源,则进程挂起直到满足可操作的条件再进行操作。非阻塞操作的进程在不能进行设备操作时,并不挂起。被挂起的进程进入sleep状态,被调度器的运行队列移走,直到等待的条件被满足。在Linux驱动程序中,我们可以使用等待队列(wait queue)来实现阻塞操作。

注:当使用socket()函数和WSASocket()函数创建套接字时,默认都是阻塞的。在创建套接字之后,通过调用ioctlsocket()函数,将该套接字设置为非阻塞模式。Linux下的函数是:fcntl()。

二、select函数

1、使用

select函数本身是阻塞的(与socket是否阻塞没有关系),直到:

1)有监测事件发生,返回>0
2)超时,返回0
3)select函数错误,返回-1

2、场景

对任何一种套接字的轮询检测,超时时间都是有效的,区别就在于:当select完毕,认为该套接字可读时:
1)阻塞的套接字,会让read阻塞,直到读到所需要的所有字节;
2)非阻塞的套接字,会让read读完fd中的数据后就返回,但如果原本你要求读10个数据,这时只读了8个数据,如果你不再次使用select来判断它是否可读,而是直接read,很可能返回EAGAIN或=EWOULDBLOCK(BSD风格) ,此错误由在非阻塞套接字上不能立即完成的操作返回,例如,当套接字上没有排队数据可读时调用了recv()函数。此错误不是严重错误,相应操作应该稍后重试。对于在非阻塞 SOCK_STREAM套接字上调用connect()函数来说,报告EWOULDBLOCK是正常的,因为建立一个连接必须花费一些时间。

3、意义

  当进程调用一个阻塞的系统函数时,该进程被置于睡眠(Sleep)状态,这时内核调度其它进程运行,直到该进程等待的事件发生了(比如网络上接收到数据包,或者调用sleep 指定的睡眠时间到了)它才有可能继续运行。与睡眠状态相对的是运行(Running)状态,在Linux内核中处于运行状态的进程分为两种情况: 正在被调度执行和就绪状态。假设一个进程同时监视多个设备,如果read(设备1)是阻塞的,那么只要设备1没有数据到达就会一直阻塞在设备1的read 调用上,即使设备2有数据到达也不能处理,使用非阻塞I/O就可以避免设备2得不到及时处理。
  在open 一个设备时指定了O_NONBLOCK 标志,read / write 就不会阻塞。以read 为例,如果设备暂时没有数据可读就返回-1,同时置errno 为EWOULDBLOCK(或者EAGAIN,这两个宏定义的值相同),表示本来应该阻塞在这里,那么调用者不是阻塞在这里死等,这样可以同时监视多个设备。
非阻塞I/O有一个缺点,如果所有设备都一直没有数据到达,调用者需要反复查询做无用功,如果阻塞在那里操作系统可以调度别的进程执行,就不会做无用功了。select 函数可以阻塞地同时监视多个设备,还可以设定阻塞等待的超时时间,从而圆满地解决了这个问题。

三、select函数的使用

  select系统调用是用来让我们的程序监视多个文件句柄的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变。关于文件句柄,其实就是一个整数,我们最熟悉的句柄是0、1、2三个,0是标准输入,1是标准输出,2是标准错误输出。0、1、2是整数表示的,对应的FILE *结构的表示就是stdin、stdout、stderr。

int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, truct timeval *timeout);
fd_set set;    //一组文件描述符(fd)的集合
FD_ZERO(fd_set *set); //将指定的文件描述符集清空 FD_SET(int fd, fd_set *set); //用于在文件描述符集合中增加一个新的文件描述符 FD_ISSET(inf fd, fd_set *set); //用于测试指定的文件描述符是否在该集合中 FD_CLR(int fd, fd_set *set); //用于在文件描述符集合中删除一个文件描述符

timeout有三种可能:

1)timeout=NULL(阻塞:直到有一个fd位被置为1函数才返回)
2)timeout所指向的结构设为非零时间(等待固定时间:有一个fd位被置为1或者时间耗尽,函数均返回)
3)timeout所指向的结构,时间设为0(非阻塞:函数检查完每个fd后立即返回)

使用select函数的过程一般是:先调用宏FD_ZERO将指定的fd_set清零,然后调用宏FD_SET将需要测试的fd加入fd_set,接着调用函数select测试fd_set中的所有fd,最后用宏FD_ISSET检查某个fd在函数select调用后,相应位是否仍然为1。

 

扩展:Linux下5种IO模型以及阻塞/非阻塞/同步/异步区别_yxtxiaotian的博客-CSDN博客_同步异步阻塞非阻塞及五种io

posted @ 2022-06-29 16:17  Lin_泠沐  阅读(1285)  评论(0编辑  收藏  举报