select 函数使用注意事项 时间重置和检测描述符范围

发布时间 2023-12-29 09:34:13作者: bailinjun

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