select函数中的坑(C语言)
最近写了一个测试驱动的poll函数的应用程序,在应用层中调用select进行操作,设置好timeout之后,如果只对select()调用一次,就没有问题。但一旦多次调用后,就变成只有第一次timeout有效,后面的都没有效果了。
#include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> #include <fcntl.h> #include <stdio.h> #include <poll.h> #include <unistd.h> int main(int argc , char ** argv) { int i; int fd; int ret; int press_cnt[4]; fd_set rfds; struct timeval time; time.tv_sec = 5; //5s fd = open("/dev/buttons", O_RDWR); //打开设备 if(fd<0) { printf("Can't open"); return -1; } while(1) { FD_ZERO(&rfds); FD_SET(fd, &rfds); ret = select(fd + 1, &rfds, NULL, NULL, &time); if (!ret) { printf("time out\n"); } else { read(fd, press_cnt, sizeof(press_cnt)); //读取值到press_cnt缓存区,读取长度sizeof(press_cnt) for(i = 0 ; i<sizeof(press_cnt)/sizeof(press_cnt[0]);i++) { if(press_cnt[i]) //如果按下次数不为0,打印出来 printf("K%d has been pressed %d times \n", i+1, press_cnt[i]); } } } }
后来查看了select()源码,发现它会把timeout更新为0,所以每次调用select(), 都要重新对timeout进行赋值。
#include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> #include <fcntl.h> #include <stdio.h> #include <poll.h> #include <unistd.h> int main(int argc , char ** argv) { int i; int fd; int ret; int press_cnt[4]; fd_set rfds; struct timeval time; //time.tv_sec = 5; //5s fd = open("/dev/buttons", O_RDWR); //打开设备 if(fd<0) { printf("Can't open"); return -1; } while(1) { time.tv_sec = 5; //5s time.tv_usec = 0;//select函数会不断修改timeout的值,所以每次循环都应该重新赋值[windows不受此影响] FD_ZERO(&rfds); FD_SET(fd, &rfds); ret = select(fd + 1, &rfds, NULL, NULL, &time); if (!ret) { printf("time out\n"); } else { read(fd, press_cnt, sizeof(press_cnt)); //读取值到press_cnt缓存区,读取长度sizeof(press_cnt) for(i = 0 ; i<sizeof(press_cnt)/sizeof(press_cnt[0]);i++) { if(press_cnt[i]) //如果按下次数不为0,打印出来 printf("K%d has been pressed %d times \n", i+1, press_cnt[i]); } } } }
原文链接:https://blog.csdn.net/u012041204/article/details/93972963
select系统调用是用来让我们的程序监视多个文件句柄(file descriptor)的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有某一个或多个发生了状态改变。
select() 函数原型:
/* According to POSIX.1-2001 */ #include <sys/select.h> /* According to earlier standards */ #include <sys/time.h> #include <sys/types.h> #include <unistd.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); void FD_CLR(int fd, fd_set *set); int FD_ISSET(int fd, fd_set *set); void FD_SET(int fd, fd_set *set); void FD_ZERO(fd_set *set);
nfds:表示需要监听的文件描述符的最大值 + 1;(即待测试的描述集的总个数)
那为什么需要 + 1呢?
因为待测试的描述集总是从0, 1, 2, ...开始的;所以,如果要检测的描述符为9, 10, 那么系统实际也要监测0, 1, 2, 3, 4, 5, 6, 7,8; 此时真正待测试的描述符的个数为11个, 也就是 MAX(9, 10) + 1;
有两点要注意:
1. 如果你要检测的文件描述符是9,10,但是你把select的第一个参数定为9, 实际上只检测0到8, 所以select不会感知到 9,10文件描述符的变化。
2. 如果你要检测的文件描述符是9,10,且你把select的第一个参数定为11, 实际上会检测0-10, 但是如果你不把描述如0 set到描述符中, 那么select也不会感知到0描述符的变化。
所以, select感知到描述符变化的必要条件是, 第一个参数要合理, 比如定义为fdMax+1, 且把需要检测的文件描述符set到描述集中。
可使用以下代码来测试下:
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<sys/time.h> #include<sys/types.h> int main(void) { struct timeval tv; tv.tv_sec = 10; tv.tv_usec = 500; // us fd_set rdfds; FD_ZERO(&rdfds); FD_SET(STDIN_FILENO, &rdfds); // STDIN_FILENO是标准输入, 加入描述集 int ret= select(STDIN_FILENO + 1, &rdfds, NULL, NULL, &tv); if (ret < 0) { printf("selcet error, ret = %d\n", ret); return -1; } else if (ret == 0) { printf("timeout \n"); } printf("ret = %d \n", ret); return 0; }
原文链接:https://blog.csdn.net/llzhang_fly/article/details/120116942