select

 

1.1 select 函数介绍

参考《UNIX网络编程第一卷》P143 6.3select函数

       这个函数允许进程指示内核等待多个事件中的任何一个发生,并且,仅在一个或者多个事件发生,或者经过某个指定的时间之后,才唤醒进程。

       作为一个例子,我们可以调用函数select并通知内核仅仅在下列情况发生时才返回:

1 集合{1, 4, 5} 中的任何描述符准备好读操作。

2 集合{2, 7} 中的任何描述符准备好写操作。

3 集合{1, 4} 中任何描述符有异常条件等待处理。

4 经过了 10.2 秒钟。

       也就是说,通知内核我们对哪些描述符感兴趣(读、写或者异常)以及等待多长时间。我们所关心的描述符不仅仅限制于socket套接口,可以是任何描述符,例如串口等等。

       源自Berkeley的实现已经允许任何描述符的I/O复用。刚开始,SVR3还限制I/O复用只适用于流设备(第33章)的描述符,但SVR4中就没有了这个限制。

函数的格式如下:

int select(int maxfdp1, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

       我们从此函数的最后一个参数开始介绍,它告诉内核等待一组指定的描述符字中的任一个准备好可以花多长时间,结构 timeval 指定了秒数和微秒数成员,定义如下:

struct timeval

{

    long tv_sec;  //秒数

    long tv_usec; //微秒数

}; 

 

有三种可能:

1 永远等待下去,仅在有一个描述符准备好I/O时才返回,此时,我们将参数timeout设置为NULL空指针,就表示永远等待。

2 等待固定的时间,在有一个描述符准备好I/O时返回,但不超过由timeout参数所指定的timeval结构中指定的时间。

3 根本不等待,检查完描述符字之后,马上立即返回,这就是称为轮询(polling)。为了实现这一点,参数timeout必须指向一个timeval结果,但是 timeval 结构中的时间值必须设置为0值。

       在前两种情况的等待中,如果进程捕获了一个信号,并且从信号处理函数返回,那么,等待一般被中断。

       源自Berkeley的内核从不自动重启函数select(TCPV2第527页),但,如果在安装信号处理程序时指明标志SA_RESTART,SVR4就自动重启被中断的select。这意味着,为了实现可移植性,我们在捕获信号的时候,必须准备好函数select返回EINTR错误。

       虽然结构timeval为我们指定了一个微秒级别的分辨率,但内核支持的分辨率却要粗糙得多。例如,很多Unix内核将超时值向上舍入10ms的倍数。另外还有调度延迟现象,即定时器到后内核还需要花一点时间调度相应进程的运行。

       注意:原文P144讲解timeout参数是const格式,那么,该参数在select(); 内是不被修改的。但是,后面的小字部分说到:

       现在的Linux系统修改了结构timeval,因此,为了可移植性,需要假设结构timeval在select(); 返回之后,没有定义,要像再次使用,必须初始化。所以,在每次调用select(); 之前,必须对它进行初始化。Posix. 1g 规定使用限定词const。

       那么,经过自己的测试,timeout 参数是在select(); 函数返回之后是被修改的,在Fedora14 系统中使用 man select 手册查看 select 函数的定义,发现最后一个参数并没有定义为const类型。所以,该参数在select(); 函数中是可以修改的。例如,timeout设置为等待10秒钟超时,而select(); 等待8秒钟之后,就有信号到底,那么,还剩余2秒钟才到超时,那么,timeout的时间被修改为2秒钟。就是,select(); 返回了,还剩下多少时间才到超时。

       中间的三个参数readset, writeset 和 exceptset 指定我们要让内核测试读、写 和 异常条件所需的描述字。现在只支持两个异常条件:

1 套接口带外数据的到达,对此,我们在第21章中再作详细的描述。

2 控制状态信息的存在,可从一个已经置为分组方式的伪终端主端读到。本卷我们不讨论伪终端。 

posted @ 2019-09-20 10:45  lcyw163  阅读(325)  评论(0编辑  收藏  举报
扫码关注 【音视频开发训练营】 音视频开发训练营