select()机制中提供一种fd_set的数据结构,它实际上是long类型的数组,每一个数组元素都能与一打开的文件句柄(不管是socket句柄,还是其他文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一socket或文件发生了可读或可写事件。
fd_set结构体原型:
typedef struct
{
/*XPG4.2requiresthismembername.Otherwiseavoidthename
fromtheglobalnamespace.*/
#ifdef__USE_XOPEN
__fd_maskfds_bits[__FD_SETSIZE/__NFDBITS];
#define__FDS_BITS(set)((set)->fds_bits)
#else
__fd_mask__fds_bits[__FD_SETSIZE/__NFDBITS];
#define__FDS_BITS(set)((set)->__fds_bits)
#endif
}fd_set;
系统提供了4个宏对描述符集进行操作:
#include
#include
void FD_SET(int fd, fd_set *fdset);
void FD_CLR(int fd, fd_set *fdset);
void FD_ISSET(int fd, fd_set *fdset);
void FD_ZERO(fd_set *fdset);
宏FD_SET设置文件描述符集fdset中对应于文件描述符fd的位(设置为1),宏FD_CLR清除文件描述符集fdset中对应于文件描述符fd的位(设置为0),宏FD_ZERO清除文件描述符集fdset中的所有位(既把所有位都设置为0)。使用这3个宏在调用select前设置描述符屏蔽位,在调用select后使用FD_ISSET来检测文件描述符集fdset中对应于文件描述符fd的位是否被设置。
常见用法:
fd_set fdset;
FD_ZERO(&fdset); /*将set清零使集合中不含任何fd,清空fdset与所有文件句柄的联系*/
FD_SET(fd, &fdset); /*将fd加入set集合,建立文件句柄fd与fdset的联系*/
FD_CLR(fd, &fdset); /*将fd从set集合中清除,清除文件句柄fd与fdset的联系*/
FD_ISSET(fd, &fdset); /*在调用select()函数后,用FD_ISSET来检测fd是否在set集合中,当检测到fd在set中则返回真,否则,返回假(0)*/
以上式子中的fd为socket句柄。
示例:
#include
#include
#include
int main(int argc, char **argv){
fd_set fdset;
FD_ZERO (&fdset); /*清空集合中所有的元素*/
FD_SET(STDOUT_FILENO,&fdset); /*设置stdout,使集合中包含stdout*/
if(FD_ISSET(STDOUT_FILENO,&fdset)!=0) /*测试stdout是否包含在集合中*/
printf("stdout has been set\n");
else
printf("stdout has not been set\n");
FD_CLR(STDOUT_FILENO,&fdset); /*从位向量中清除stdout*/
if(FD_ISSET(STDOUT_FILENO,&fdset)!=0) /*再次测试*/
printf("stdout has been set\n");
else
printf("stdout has not been set\n");
FD_ZERO(&fdset);
FD_SET(1, &fdset);
FD_SET(2, &fdset);
FD_SET(3, &fdset);
FD_SET(7, &fdset);
printf("fdset.__fds_bits[0] = %d\n", fdset.__fds_bits[0]);
return 0;
}
运行结果:
stdout has been set
stdout has not been set
fdset.__fds_bits[0] = 142
即:将fdset所有元素的二进制连接为一个整体,FD_SET(1, &fdset)、FD_SET(2, &fdset)、FD_SET(3, &fdset)、FD_SET(7, &fdset)相当于将二进制中的bit1、bit2、bit3、bit7设置为1,即1000 1110,所以fdset.__fds_bits[0]值为142。
可以查看fdset的定义,是一个long类型数组,数组大小为__FD_SETSIZE/__NFDBITS,前者为1024,后者与系统有关,这种定义保证了fdset数组最大可以存储__FD_SETSIZE个bit位。
一句话:fd_set就是一个long类型数组,数组中所有元素按照二进制位排列,每一位都对应一个文件描述符(通过索引关联)。
通常,操作系统通过宏FD_SETSIZE来声明在一个进程中select所能操作的文件描述符的最大数目。例如:
在4.4BSD的头文件中我们可以看到:
#ifndef FD_SETSIZE
#define FD_SETSIZE 1024
#endif
在红帽Linux的头文件
#define __FD_SETSIZE 1024
以及在头文件
#include
#define FD_SETSIZE __FD_SETSIZE
既定义FD_SETSIZE为1024,一个整数占4个字节,既32位,那么就是用包含32个元素的整数数组来表示文件描述符集。我们可以在头文件中修改这个值来改变select使用的文件描述符集的大小,但是必须重新编译内核才能使修改后的值有效。当前版本的unix操作系统没有限制FD_SETSIZE的最大值,通常只受内存以及系统管理上的限制。