linux select函数例子,select函数用法详解,及实例分析

本文介绍如何利用select函数高效地监控多个输入设备的数据输入情况,通过一个实例展示了如何在一个线程中同时监控2个CAN接口和3个串口,并提供了C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

select函数是系统调用函数,用于多路监控。当没有一个文件满足要求时,select将阻塞调用进程。在有些情况下,采用select函数可以大大简化程序结构。比如一个系统有10个输入设备,如果想实时读取这10个设备的输入数据,就比较困难,采用查询方式,显然达不到实时的目的;或者可以为每一个设备设计一个线程,每个线程实时对设备的输入进行读取,这样会使程序异常复杂,数据的交互也很混乱。有了select函数,这个问题就迎刃而解了。采用一个线程,并用一个selet函数同时对10个设备进行监控,这确实是一个好办法。

1、我们先来看一下select函数的原型,如下所示

int select(int maxfd,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);

在这个函数中,

maxfd:文件描述符的范围,比待监控的最大文件描述符加1。

readfds:它是指向fd_set结构的指针,fd_set是一个描述符集合。这个集合中是要监控的读类型的文件的描述符。

writefds:他也是指向fd_set结构的指针,这个集合是要监控的写类型的文件的描述符。

errorfds:与上面两个参数类似,它是用来监视文件错误异常的文件描述符的集合。

timeout:它是select函数的超时时间,这个参数至关重要,它可以使select处于三种状态。

1,若将NULL以形参传入,即不传入时间结构,则select一直置于阻塞状态,直到监控到文件描述符集合中某个文件描述符发生变化为止;2,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;3,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后返回0。

函数的返回值,在正常情况下返回满足要求的文件描述符个数,时间超时返回0,出错或者select被某个信号中断返回-1。

2、下面我们来看一下几个select函数相关的宏

FD_CLR(inr fd, fd_set *fdset);用来清除描述符集合fdset中的描述符fd

FD_ISSET(int fd,f d_set *fdset);用来检测描述符集合fdset中的描述符fd是否发生了变化

FD_SET(int fd, fd_set *fdset);用来将描述符fd添加到描述符集合fdset中

FD_ZERO(fd_set *fdset);用来清除描述符集合fdset

3、实例分析

下面我们来看一个实例,这个实例是一个程序中的一个线程,它的作用是对2个CAN口和3个串口进行监控,当接口有数据发送来的时候把数据读到数组中,代码用c++编写,所有代码放到一个类中,并且这个类的start函数启动一个线程来执行上面说的监控功能。在类的构造函数中,初始化了接口,如下所示。

ReceiveUartData::ReceiveUartData()

{

//初始化CAN0

can0_fd=can0.CanInit(CAN0);

can1_fd=can1.CanInit(CAN1);

//初始化串口

uart1_fd = uart1.uartOpen(UART1,O_RDWR|O_NONBLOCK,BAUDRATE1);

uart2_fd = uart2.uartOpen(UART2,O_RDWR|O_NONBLOCK,BAUDRATE2);

uart3_fd = uart3.uartOpen(UART3,O_RDWR|O_NONBLOCK,BAUDRATE3);

}

初始化接口时,会返回各接口对应的文件描述符。

在线程的run函数中,通过一个while(1)循环对几个接口进行循环监控。

在执行select函数之前,首先要把相应的描述符填写到集合中,如下所示代码。

//清零描述符集合

FD_ZERO(&read_set);

max_fd=-1;

//装载描述符

for(int i=0;i<5;i++)

{

if(fd_array[i]>max_fd)

max_fd=fd_array[i];

FD_SET(fd_array[i],&read_set);

}

然后是,设置超时时间,并启动select监控,如下所示

//设置超时时间

timeout.tv_sec=3;

timeout.tv_usec=0;

ret = select(max_fd+1, &read_set, 0, 0, &timeout);

这里设置的超时时间是3秒,如果3秒内几个接口都没有数据,则select超时退出,如果某个接口有数据,则向下进行。下面的代码是对几个接口的数据接收。

if (ret == -1)

{

printf("select function failed!\n");

}

else if (ret == 0)

{

printf("select function time out!\n");

}

else

{

//判断是哪个设备来的数据并读取

if(FD_ISSET(can0_fd,&read_set))

{

struct can_frame can0_data = can0.readBus(can0_fd);

}

if(FD_ISSET(can1_fd,&read_set))

{

struct can_frame can1_data = can1.readBus(can1_fd);

}

if(FD_ISSET(uart1_fd,&read_set))

{

num1 = uart1.uartRead(uart1_fd,rbuff1,200);

}

if(FD_ISSET(uart2_fd,&read_set))

{

num2 = uart2.uartRead(uart2_fd,rbuff2,200);

}

if(FD_ISSET(uart3_fd,&read_set))

{

num3 = uart3.uartRead(uart3_fd,rbuff3,200);

}

}

在这段中,如果判断有数据接收,则会对每个接口依次进行判断,并对有数据的接口发生来的数据进行接收。

这个实例的源码可以由本文章的资源中进行下载,由于程序的运行需要依赖相关的环境,所以这个源码不能直接编译运行,只作为编程参考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值