用到select,就难免不了要接触一下几个宏
fd_set set;
FD_ZERO(&set);
FD_SET(read_fd,&set);
select(MAX_NUM+1,&set,NULL,NULL,NULL);
if(FD_ISSET(read_fd,set){……}
这几个经常是按这种次序组合出现的,之前虽然也用过,但说实话,也就是用,根本不知道怎么回事,现在有了初步的了解。
首先是 fd_set,这是什么?其实这是一个数组的宏定义,数组的类型是(unsigned) long型的(个人推测无符号),占有128位,无论是32位的linux下,还是64位下,他都是128个字节(128*8位=1024位,也就是可以检测1024个任务,这也是select默认的个数,可以通过编译内核修改),但是由于不同位数的系统下long型的长度不同,所以导致数组的元素个数也是不同的。理解fd_set可以根据位图的方式来理解。
代码:
#include <stdio.h> #include <sys/select.h> #include <unistd.h> int main(void) { fd_set fdset; FD_ZERO (&fdset); FD_SET(STDIN_FILENO,&fdset); FD_SET(STDOUT_FILENO,&fdset); FD_SET(STDERR_FILENO,&fdset); FD_SET(7,&fdset); if(FD_ISSET(STDOUT_FILENO,&fdset)!=0) printf("stdout has been set\n"); else printf("stdout has not been set\n"); FD_CLR(STDOUT_FILENO,&fdset); if(FD_ISSET(STDOUT_FILENO,&fdset)!=0) printf("stdout has been set\n"); else printf("stdout has not been set\n"); return 0; }
下面是我的简单调试
GNU gdb (GDB) Red Hat Enterprise Linux (7.0.1-42.el5) Copyright (C) 2009 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-redhat-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /home/fy/SystemV_ipc/select/run...done. (gdb) l 1 #include <stdio.h> 2 #include <sys/select.h> 3 #include <unistd.h> 4 int main(void) 5 { 6 fd_set fdset; 7 FD_ZERO (&fdset); 8 FD_SET(STDIN_FILENO,&fdset); 9 FD_SET(STDOUT_FILENO,&fdset); 10 FD_SET(STDERR_FILENO,&fdset); (gdb) b 7 Breakpoint 1 at 0x4004a3: file select.c, line 7. (gdb) b 8 Breakpoint 2 at 0x4004d2: file select.c, line 8. (gdb) b 9 Breakpoint 3 at 0x4004e4: file select.c, line 9. (gdb) b 10 Breakpoint 4 at 0x4004f6: file select.c, line 10. (gdb) l 11 FD_SET(7,&fdset); 12 if(FD_ISSET(STDOUT_FILENO,&fdset)!=0) 13 printf("stdout has been set\n"); 14 else 15 printf("stdout has not been set\n"); 16 FD_CLR(STDOUT_FILENO,&fdset); 17 if(FD_ISSET(STDOUT_FILENO,&fdset)!=0) 18 printf("stdout has been set\n"); 19 else 20 printf("stdout has not been set\n"); (gdb) b 11 Breakpoint 5 at 0x400508: file select.c, line 11. (gdb) b 12 Breakpoint 6 at 0x400518: file select.c, line 12. (gdb) r Starting program: /home/fy/SystemV_ipc/select/run warning: no loadable sections found in added symbol-file system-supplied DSO at 0x2aaaaaaab000 Breakpoint 1, main () at select.c:7 7 FD_ZERO (&fdset); (gdb) p fdset 注释:在执行FD_ZERO (&fdset);可以看下fdset里面到底放着什么 $1 = {__fds_bits = {0, 15774429, 194, 15774429, 140737488348710, 140737488348711, 0, 267984686016, 4195728, 4195203, 705412764499181569, 4195783, 0, 267984686016, 4195728, 0}} (gdb) p sizeof(fdset)注释:看看fdset有多少位 $2 = 128 (gdb) c Continuing. Breakpoint 2, main () at select.c:8 8 FD_SET(STDIN_FILENO,&fdset); (gdb) p fdset 注释:在执行FD_ZERO (&fdset);后可以看下fdset里面到底放着什么,这也告诉了我们FD_ZERO的作用 $3 = {__fds_bits = {0 <repeats 16 times>}}注释:我的系统是64位,long占有8个字节,128个字节空间,所以是<repeats 16 times>(gdb) c Continuing.也就是一个有16个元素的数组 Breakpoint 3, main () at select.c:9 9 FD_SET(STDOUT_FILENO,&fdset); (gdb) p fdset 注释:在执行FD_SET(STDIN_FILENO,&fdset);STDIN_FILENO为标准输入为0 最低八位00000001,0占了第0位 $4 = {__fds_bits = {1, 0 <repeats 15 times>}} (gdb) c Continuing. Breakpoint 4, main () at select.c:10 10 FD_SET(STDERR_FILENO,&fdset); (gdb) p fdset $5 = {__fds_bits = {3, 0 <repeats 15 times>}}注释:加入了STDOUT(1),最低八位为00000011,所以FD_SET应该是设置位的 (gdb) c Continuing. Breakpoint 5, main () at select.c:11 11 FD_SET(7,&fdset); (gdb) p fdset $6 = {__fds_bits = {7, 0 <repeats 15 times>}}注释:加入了标准出粗输出(2),最低八位为00000111==7 (gdb) c Continuing. Breakpoint 6, main () at select.c:12 12 if(FD_ISSET(STDOUT_FILENO,&fdset)!=0)注释:j加入了自定义7后,最低八位10000111==135 (gdb) p fdset $7 = {__fds_bits = {135, 0 <repeats 15 times>}} (gdb) c Continuing. stdout has been set stdout has not been set Program exited normally. (gdb)OK。这就是fd_set。
FD_ZERO(&set); /*将set清零使集合中不含任何fd*/
FD_SET(fd, &set); /*将fd加入set集合*/
FD_CLR(fd, &set); /*将fd从set集合中清除*/
FD_ISSET(fd, &set); /*测试fd是否在set集合中*/
对于这几个宏的实现,自然越是难事,只要你位操作熟练掌握。
PS:另外说一下,STDIN_FILENO==0,STDOUT_FILENO=1,STDERR_FILENO==2,这也说明了为什么我们建立某些文件描述符从3开始。