在《linux设备驱动--非阻塞IO与select,poll调用》中给出了驱动中poll函数的一般写法,很简单明了。但是为什么这么写,还是要稍微追究一下的。
首先,用manselect看下select的用法:
NAME
select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchronous I/O
multiplexing
SYNOPSIS
/*According to POSIX.1-2001 */
#include<sys/select.h>
/*According to earlier standards */
#include<sys/time.h>
#include<sys/types.h>
#include<unistd.h>
intselect(int nfds, fd_set *readfds, fd_set *writefds,
fd_set*exceptfds, struct timeval *timeout);
voidFD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
voidFD_SET(int fd, fd_set *set);
voidFD_ZERO(fd_set *set);
select的简短描述如下:
select()and pselect() allow a program to monitor multiple file
descriptors, waiting until one or more of the file descriptors become
"ready"for some class of I/O operation (e.g., input possible). A file
descriptor is considered ready if it is possible to perform the corre‐
spondingI/O operation (e.g., read(2)) without blocking.
大意是:select和pselect允许程序监控多个文件描述符,一直到一个或多个文件描述“准备”可以被某类IO操作。如果相关的非阻塞的IO操作可以执行,我们就认为文件描述符“准备”好了。
在man手册最后给出了select函数的一个简单例子:
#include<stdio.h> #include<stdlib.h> #include<sys/time.h> #include<sys/types.h> #include<unistd.h> int main(void) { fd_setrfds; structtimeval tv; intretval; /*Watch stdin (fd 0) to see when it has input. */ FD_ZERO(&rfds); FD_SET(0,&rfds); /*Wait up to five seconds. */ tv.tv_sec= 5; tv.tv_usec= 0; retval= select(1, &rfds, NULL, NULL, &tv); /*Don't rely on the value of tv now! */ if(retval == -1) perror("select()"); elseif (retval) printf("Datais available now.\n"); /*FD_ISSET(0, &rfds) will be true. */ else printf("Nodata within five seconds.\n"); exit(EXIT_SUCCESS); }
这个例子是监听文件描述符0的,文件描述符0,1,2分别定位stdin,stdout和stderr。
这个看起来简单,其实还是有点不简单的。
Fd_set这个定义是啥?从字面上看,就是fd的set,代表一簇文件描述符。
typedef__kernel_fd_set fd_set; #undef__NFDBITS #define__NFDBITS (8 * sizeof(unsigned long)) //8×4 = 32 #undef__FD_SETSIZE #define__FD_SETSIZE 1024 #undef__FDSET_LONGS #define__FDSET_LONGS (__FD_SETSIZE/__NFDBITS) //1024/32 = 32 #undef__FDELT #define __FDELT(d) ((d)/ __NFDBITS) #undef__FDMASK #define __FDMASK(d) (1UL<< ((d) % __NFDBITS)) typedefstruct { unsignedlong fds_bits [__FDSET_LONGS]; }__kernel_fd_set;
这样一看,fd_set就是一个包含着数组的的结构体。
Fd_set实际上是用到了bitmap,bitmap的一些常见应用可以看《》;没想到的是bitmap除了linux中文件系统的omfs(较简单的fs)中有应用,在select中也有应用。
这里还看不出为何是bitmap,看了
voidFD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
voidFD_SET(int fd, fd_set *set);
voidFD_ZERO(fd_set *set);
这四个函数的实现后,就会清楚。
这四个函数的具体实现与cpu的架构相关,以arch\mips\include\asm\posix_types.h为例:
#ifdefined(__KERNEL__) #undef__FD_SET static__inline__ void __FD_SET(unsigned long __fd, __kernel_fd_set*__fdsetp) { unsignedlong __tmp = __fd / __NFDBITS; unsignedlong __rem = __fd % __NFDBITS; __fdsetp->fds_bits[__tmp]|= (1UL<<__rem); } #undef__FD_CLR static__inline__ void __FD_CLR(unsigned long __fd, __kernel_fd_set*__fdsetp) { unsignedlong __tmp = __fd / __NFDBITS; unsignedlong __rem = __fd % __NFDBITS; __fdsetp->fds_bits[__tmp]&= ~(1UL<<__rem); } #undef__FD_ISSET static__inline__ int __FD_ISSET(unsigned long __fd, const __kernel_fd_set*__p) { unsignedlong __tmp = __fd / __NFDBITS; unsignedlong __rem = __fd % __NFDBITS; return(__p->fds_bits[__tmp] & (1UL<<__rem)) != 0; } /* *This will unroll the loop for the normal constant case (8 ints, *for a 256-bit fd_set) */ #undef__FD_ZERO static__inline__ void __FD_ZERO(__kernel_fd_set *__p) { unsignedlong *__tmp = __p->fds_bits; int__i; if(__builtin_constant_p(__FDSET_LONGS)) { switch(__FDSET_LONGS) { case16: __tmp[0] = 0; __tmp[ 1] = 0; __tmp[2] = 0; __tmp[ 3] = 0; __tmp[4] = 0; __tmp[ 5] = 0; __tmp[6] = 0; __tmp[ 7] = 0; __tmp[8] = 0; __tmp[ 9] = 0; __tmp[10]= 0; __tmp[11] = 0; __tmp[12]= 0; __tmp[13] = 0; __tmp[14]= 0; __tmp[15] = 0; return; case8: __tmp[0] = 0; __tmp[ 1] = 0; __tmp[2] = 0; __tmp[ 3] = 0; __tmp[4] = 0; __tmp[ 5] = 0; __tmp[6] = 0; __tmp[ 7] = 0; return; case4: __tmp[0] = 0; __tmp[ 1] = 0; __tmp[2] = 0; __tmp[ 3] = 0; return; } } __i= __FDSET_LONGS; while(__i) { __i--; *__tmp= 0; __tmp++; } } #endif/* defined(__KERNEL__) */
以FD_SET和FD_CLR来看,很明显看出是bitmap的操作。
了解这里的bitmap对后面select系统调用的分析很有帮助,先到这里.