Linux设备驱动中的阻塞与非阻塞I/O模型

本文深入探讨了Linux系统中的阻塞与非阻塞I/O机制,包括基本概念、等待队列的使用及其实现原理,并通过具体实例介绍了如何在驱动程序和应用程序中实现这两种模式。

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

杂话篇:在学习Linux过程中,遇到很多困难,在大多数情况下,都是借助优快云网站解决的。后来,我也决定加入其中。一方面能够加深自己所学的知识,以后再遇到同样的问题也方便查找。另一方面,还能为更多的同道中人提供自己的微薄之力。
一:阻塞与非阻塞基本概念    
阻塞:在执行设备操作时,若不能获得资源,则挂起(该执行单元处于休眠状态,不占用CPU)直到满足可操作的条件再进行操作。 所以必须确保有唤醒执行单元的操作,否在该执行单元可真到此结束了。
    在驱动程序具有阻塞机制的情况下,我们的应用程序也应该添加O_NONBLOCK,比如open("/dev/xxx",O_RDRW|O_NONBLOC);
非阻塞:与阻赛相反,在对设备执行操作,若不能获得资源,立即返回,并不挂起。    
二     等待队列实现阻塞与非阻塞        
    在linux驱动程序中,可以使用等待队列来实现阻塞进程的唤醒。    
其相关函数如下:
第一个   定义 ”等待队列头部“    
wait_queue_head_t     my_queue   ;
第二个    初始化”定义等待队列头部“        
init_waitqueue_head(& my_queue );    
定义并初始化,也可用宏 DECLARE_WAIT_QUEUE_HEAD(  my_queue )
第三个 定义等待队列元素

DECLARE_WAITQUEUE( name,tsk )    
第四个添加/移除等待队列    
void add_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);        
void remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);      
第五个 等待事件    
wait_event(queue,condition);    
wait_event_interruptible(queue,condition);    
wait_event_timeout(queue,condition,timeout);    
wait_event_interruptible_timeout(queue,condition,timeout);
    等待第一个参数queue作为等待队列头部的队列被唤醒,而且第二个参数condition必须满足,否则继续阻塞。  wait_enent和 wait_enent_interruptible的区别在于后者可以被信号打断,而前者不能。加上_timeout后意味着阻塞等待的超时时间,以jiffy为单位,在第三个参数的timeout到达时,不论condition是否满足,均返回。    
第六个 唤醒队列    
void wake_up(wait_queue_head_t *queue);    
void wake_up_interruptible(wait_queue_head_t *queue);
 wake_up应该与wait_enent或wait_enent_timeout成对使用,wait_enent_interruptible应该与wake_up_interruptible或wait_event_interruptible_timeout成对使用.
三 轮询操作-----非阻塞I/O    
    使用非阻塞I/O的应用程序通常会使用select()和poll()系统调用查询是否可对设备进行无阻塞访问。select()和poll()系统调用最终会使设备中的poll()函数被执行。
    应用程序中的轮询编程    
int select(int numfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,strut timeval *timeout);    
    其中readfds,writefds,exceptfds,exceptfds分别是被select()监视的读写,和异常处理的文件描述集合,numfds的值是需要检查的号码最高的fd加1。readfds文件集合中的任何一个文件变得可读,select()返回,同理,writefds文件集中任何一个文件变得可写,select也返回。    下面我们以一个具体的例子来详细说明。
//以下为poll驱动程序的编写

static unsigned xxx_poll(struct file *file, poll_table *wait)    
{
 unsigned int mask = 0;
 poll_wait(file, &button_waitq, wait); // 加入等待队列
 if (rfds)    //如果文件变为可读状态
  mask |= POLLIN | POLLRDNORM;     //标识数据可以读    
 if (wfds)    //如果文件变为可写状态
   mask |= POLLOUT| POLLRDNORM;     //则标识数据可以写
 return mask;
}
//以下为应用程序的编写
int main(int argc, char **argv)
{
/*xxxxxx其他代码省略,此为模板xxxxxx*/   
fd_set rfds,wfds; //读/写的文件描述集合
 fd = open("/dev/xxxxx", O_RDWR | O_NONBLOCK);   //以非阻塞I/O打开
  while (1)
 {
     FD_ZERO(&rfds);    //清零读
     FD_ZERO(&wfds);  //清零写
     FD_SET(fd,&rfds);   //设置读
     FD_SET(fd,&wfds);//设置写    
     select(fd+1,&rfds,&wfds,NULL,NULL);    //监控    
     if(FD_ISSET(fd,&rfds))   //数据可获得
               printf("POLL monitor :can be read\n");
      if(FD_ISSET(fd,&wfds))    //数据可写    
                printf("poll monitor :can be written\n");
 }
  return 0;
}
四 总结
阻塞与非阻塞访问是I/O操作的两种不同的工作模式,前者在暂时不可进行I/O操作时进程睡眠,后者则不然。
在设备驱动中阻塞I/O一般基于等待队列或者基于等待队列的其他linux内核API来实现,等待队列可用于同步驱动中事件发生的先后顺序。使用非阻塞I/O的应用程序也可借助轮询函数来查询设备是否能立即被访问,用户空间调用select(),poll()或者epoll()相关的系统调用来实现非阻塞I/O。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值