linux select 动态添加,嵌入式Linux编程之select使用总结

select 的作用是为了解决阻塞I/O的问题,这样说可能 有些抽象,简单的讲,在linux下,很多的操作都是基于文件操作方式,不管操作的对象是普通文件,还是各种设备(串口等实际设备),操作的函数为write和read,这两个 函数都是可能会出现“阻塞”的,比如说串口吧,write的功能,其实 是将数据先写到发送缓冲,而read函数则是 读取缓冲,write的阻塞发生在,当前发送缓冲里有数据,而read的阻塞则发生在当前 读缓冲中没有数据,我们在实际使用的过程中,往往read阻塞居多,毕竟串口不是一直都有数据接收的。那么在这种情况下,read是会阻塞的,阻塞的意思就是CPU会干等在那,很显然,我们肯定是不会允许cpu干等的,那么这个时候,我们可能会有一下两种方式:

(1)将1个进程编程2个进程(fork),主进程控制流程,子进程则作为串口接收,这时是允许阻塞的,反正CPU会调度,不过我们要考虑下,这么点功能,就使用多进程,是否合适,毕竟进程的开销蛮大的,而且接收进程必然要与主进行要进行通信,在Linux下进程的通信还是不太容易的。

(2)创建新的线程,线程中可以阻塞,不过这也要分情况,有些线程也是不允许阻塞的,而且线程之间也必然需要进行同步通信,所以也无疑会增加复杂性。

基于上述的缺点和需求,有了一种解决技术,官方的名称是I/O多路转接,其实就是select机制,select的功能概括起来就是:

select允许设定固定时间的监听文件描述符状态等待,一旦文件描述符有状态,立即返回,如果超过等待时间,也返回。

而返回后,我们 就可以使用read和write对 相应的文件进行操作了。linux下select的函数原型如下:

#include int select( int maxfdp1,

fd_set *restrict readfds,

fd_set *restrict writefds,

fd_set *restrict exceptfds,

struct timeval *restrict tvptr);

* 参数 maxfdp1 :最大文件描述符加1,也就是被监听的描述符中的最大值+1,之所以加1,这是因为文件描

* 述符是从0开始的。

* ex: 我们要监听的 文件描述符为 iFd1,iFd2.., 则 maxfdp1的值是这些文件描述符

* 最大值+1,也就是select会同时监听所有的这些文件描述,哪一个有变化,则返回

*

* 参数 readfds :“读集”监听,如果对其中一个描述符进行read操作不阻塞(可以被read),则认为此描

* 述符准备就绪,select会返回。

*

* 参数 writefds :“写集”监听,如果对其中一个描述符进行write操作不阻塞(可以被write),则认为此

* 描述符准备就绪,select会返回。

*

* 参数 execptfds :“异常集”监听,如果对其中一个描述符有异常,则select会返回

*

* 参数 tvptr :这是一个结构体,可以实现“秒 + 微秒”组合延时。

*

* 返回: 准备就绪的描述符数目,若超时,返回0,若出错,返回-1

*

关于上述参数的 监听集类型 fd_set,可以参考另一篇文章Linux下 fd_set 结构小结,fd_set定义后,必须要进行初始化, 然后使用FD_SET进行设置,从select返回后,可以 使用FD_ISSET来进一步确认给定位是否仍处于打开状态。

使用select进行编程的框架如下:

{

fd_set rd;

struct timeval tv;

int iFd,err;

iFd = open(xxxxxx);

FD_ZERO( &rd );

FD_SET(iFd, &rd);

err = select(iFd+1, &rd, NULL, NULL, &tv);

if( err ){

if(FD_ISSET(fd, &rd)){

/应用功能

}

}

}

我们在实际使用的项目案例中,常用select功能的有串口编程、socket等,可能有些人会觉得 socket编程,用fork居多,其实也是可以通过select 实现的,因为本质上都是要涉及到非阻塞接收机制的。

我们拿串口来举例说明,代码如下:

int usart_recv(int iFd, char *rcv_buf, int len)

{

int err;

int res;

fd_set fs_read;

struct timeval tv_timeout;

FD_ZERO(&fs_read); //清空集合

FD_SET(iFd,&fs_read); // 将一个给定的文件描述符加入集合之中

tv_timeout.tv_sec = 5;

tv_timeout.tv_usec = 0;

err = select(iFd + 1, &fs_read, NULL, NULL, &tv_timeout);

//如果select返回值大于0,说明文件描述符发生了变化,串口可以进行read操作

if( err ){

if( FD_ISSET(iFd, &fs_read) ){

res = read(iFd, rcv_buf, len);

return res;

}

}

else return 0;

}

上述的串口接收函数,就可以实现阻塞5秒接收串口数据,我们可以在我们的程序中直接调用该函数。不会发生阻塞。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值