select函数(一)

select函数使我们在SVR4和4.3+BSD之下可以执行I/O多路转接,传向select的参数告诉内核:
(1) 我们所关心的描述符。
(2) 对于每个描述符我们所关心的条件(是否读一个给定的描述符?是否想写一个给定的
描述符?是否关心一个描述符的异常条件?)。
(3) 希望等待多长时间(可以永远等待,等待一个固定量时间,或完全不等待)。

    对select指定读、写和异常条件描述符 对 fg_set 数据类型可以进行的处理是: ( a )分配一个这种类型的变量, ( b )将这种类型的一个变量赋与同类型的另一个变量, ( c )对于这种类型的变量使用下列四个宏: 以下列方式说明了一个描述符集后: fd_set rset; int fd; 必须用FDZERO清除其所有位: FD_ZERO (&rset); 然后在其中设置我们关心的各位: FD_SET (fd,&rset); FD_SET (STDIN_FILENO,&rset); 从select返回时,用FDISSET测试该集中的一个给定位是否仍旧设置: if (FD_ISSET(fd, &rset)){ . . . } select 中间三个参数中的任意一个(或全部)可以是空指针,这表示对相应条件并不关心。如 果所有三个指针都是空指针,则 select 提供了较 sleep 更精确的计时器(s l e e p等待整数秒,而对于s e l e c t,其等待的时间可以小于1秒;其实际分辨率取决于系统时钟。) selcet 第一个参数 maxfdp1 的意思是“最大f d加1(max fd plus 1)”。在三个描述符集中找出 最高描述符编号值,然后加1,这就是第一个参数值。也可将第一个参数设置为FDSETSIZE, 这是一个< s y s / t y p e s . h >中的常数,它说明了最大的描述符数(经常是2 5 6或1 0 2 4)。但是对大多 数应用程序而言,此值太大了。确实,大多数应用程序只应用3 ~ 1 0个描述符。如果将第三个参 数设置为最高描述符编号值加1,内核就只需在此范围内寻找打开的位,而不必在数百位的大 范围内搜索。 例如,若编写下列代码: fd_set readset, writeset; FDZERO ( &readset ) ; FDZERO ( &writeset ) ; FD_SET(0, &readset); FD_SET(3, &readset); FD_SET(1, &writeset); FD_SET(2, &writeset); select (4, &readset, &writeset, NULL, NULL); 然后,图1 2 - 1 0显示了这两个描述符集的情况。   因为描述符编号从0开始,所以要在最大描述符编号值上加1。第一个参数实际上是要检查的描述符数(从描述符0开始)。 select有三个可能的返回值。 (1) 返回值-1表示出错。这是可能发生的,例如在所指定的描述符都没有准备好时捕捉到 一个信号。 (2) 返回值0表示没有描述符准备好。若指定的描述符都没有准备好,而且指定的时间已经 超过,则发生这种情况。 (3) 返回一个正值说明了已经准备好的描述符数,在这种情况下,三个描述符集中仍旧打 开的位是对应于已准备好的描述符位。 注意,除非返回正值,否则在返回后检查描述符集是没有意义的。若捕捉到信号或计时器超时,那么描述符集的值取决于实现。确实,若计时器超时,4 . 3 + B S D并不改变描述符集,而S V R 4则清除描述符集。在S V R 4和B S D的s e l e c t实现之间,有另一些差异。B S D系统总是返回每一个集中准备就绪的描述符数之和。若两个集中的同一描述符准备就绪(例如,读集和写集),则该描述符计两次。不幸, S V R 4更改了这一点,若同一描述符在多个集中准备就绪,该描述符只计一次。这再一次显示了我们将会碰到的问题,直至P O S I X标准化了s e l e c t这样的函数才能解决此问题。 对于“准备好”的意思要作一些更具体的说明: (1) 若对读集(re a d f d s)中的一个描述符的read不会阻塞,则此描述符是准备好的。 (2) 若对写集(w r i t e f d s)中的一个描述符的write不会阻塞,则此描述符是准备好的。 (3) 若对异常条件集( e x c e p t f d s)中的一个描述符有一个未决异常条件,则此描述符是准 备好的。现在,异常条件包括: ( a )在网络连接上到达指定波特率外的数据,或者( b )在处于数 据包方式的伪终端上发生了某些条件。(S t e v e n s〔1 9 9 0〕的1 5 . 1 0节中说明了这种条件。) 应当理解一个描述符阻塞与否并不影响s e l e c t是否阻塞。也就是说,如果希望读一个非阻 塞描述符,并且以超时值为5秒调用s e l e c t,则s e l e c t最多阻塞5秒。相类似,如果指定一个无限 的超时值,则s e l e c t阻塞到对该描述符数据准备好,或捕捉到一个信号。 如果在一个描述符上碰到了文件结束,则s e l e c t认为该描述符是可读的。然后调用r e a d,它 返回0,这是U N I X指示到达文件结尾处的方法。(很多人错误地认为,当到达文件结尾处时,s e l e c t会指示一个异常条件。)  

从select返回时,内核告诉我们: (1) 已准备好的描述符的数量。 (2) 哪一个描述符已准备好读、写或异常条件。 使用这种返回值,就可调用相应的I / O函数(一般是r e a d或w r i t e),并且确知该函数不会阻塞。 #include <sys/types.h>/* fd_set data type */ #include <sys/time.h> /* struct timeval */ #include <unistd.h> /* function prototype might be here */ int select (int maxfd1, fd_set  *readfd, fd_set  *writefds, fd_set  *exceptfds, struct timeval  *tvptr) ; 返回:准备就绪的描述符数,若超时则为0,若出错则为- 1 先说明最后一个参数,它指定愿意等待的时间。 struct timeval{ long tv_sec; /* seconds */ long tv_usec; /* and microseconds */ }; 有三种情况: *tvptr= =NULL 永远等待。如果捕捉到一个信号则中断此无限期等待。当所指定的描述符中的一个已准备 好或捕捉到一个信号则返回。如果捕捉到一个信号,则s e l e c t返回-1, e r r n o设置为E I N T R。 *tvptr->tvsec==0 && tvptr->tvusec==0 完全不等待。测试所有指定的描述符并立即返回。这是得到多个描述符的状态而不阻塞select函数的轮询方法。 *tvptr->tvsec!=0 || tvptr->tvusec!=0 等待指定的秒数和微秒数。当指定的描述符之一已准备好,或当指定的时间值已经超过时 立即返回。如果在超时时还没有一个描述符准备好,则返回值是0,(如果系统不提供微秒分辨 率,则tvptr->tvusec值取整到最近的支持值。)与第一种情况一样,这种等待可被捕捉到的信 号中断。 中间三个参数 readfds、writefds 和 exceptfds 是指向描述符集的指针。这三个描述符集说明了 我们关心的可读、可写或处于异常条件的各个描述符。每个描述符集存放在一个f d s e t数据类 型中。这种数据类型的实现可见图12-9,它为每一可能的描述符保持了一位。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值