在《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系统调用的分析很有帮助,先到这里.