
设备驱动之阻塞
发布日期:2021-05-07 13:26:42
浏览次数:12
分类:原创文章
本文共 3492 字,大约阅读时间需要 11 分钟。
一:阻塞与非阻塞概念:
1.1
阻塞操作是指执行设备操作时,如果不能获取资源,则挂起进程直到满足可操作的条件后再进行操作 被挂起的进程进入睡眠状态,被从调度器的运行队列移走,直到等待的条件被满足,而非阻塞操作的 进程在不能进设备操作的时候,并不挂起,它要么放弃,要么不停的查询,直到可以进行操作为止,
1.2
在阻塞访问时候,不能获取资源的进程将会进入休眠,将CPU资源让给其他进程。因为阻塞的进程会进入 休眠状态,所以必须保证有一个地方可以唤醒休眠的进程,否则,进程就真的寿正终寝了,唤醒进程的地 方最大可能发生在中断里面,因为在硬件资源获得的同时往往伴随这一个中断。非阻塞的进程不断的尝试 知道了可以进行I/O
1.3
应用程序可以选择是以 阻塞 或者 非阻塞的方式访问: 阻塞的访问方式: fd = open("/dev/xxx", O_RDWD); 非阻塞的当时访问 : fd = open("/dev/xxx", O_RDWD | O_NONBLOCK);
1.4
问题:为什么要引入阻塞与非阻塞操作 答案:驱动程序通常需要提供这样的能力:当应用程序进行 read() write() 等系统调用的时候,若 设备资源不能获取,正常情况下read() write()等操作会立即返回,需要重新访问,但是用户 要求上层只进行一次设备的读写操作,驱动内部的xxx_read xxx_write 等待资源可获取,完成 上层read() write();此时便有了阻塞访问,以及对应的非阻塞访问。
二:关于 阻塞-等待队列 的操作
Linux设备驱动中,可以使用的等待队列(Qait Queue)来实现阻塞进程的唤醒,阻塞的进程可以使用等待队列(Wait_Queue)来实现唤醒,等待队列很早就作为一个基本功能出现在Linux内核里面了,等待队列以 队列 为基础数据结构,与进程调度机制紧密结合,可以用来同步对系统资源的访问。
2.1 等待队列操作:
1 wait_queue_head_t my_head //定义等待队列头 2 init_waitqueue_head(&my_head) //初始化等待队列头 DECLARE_WAIT_QUEUE_HEAD(name) //初始化等待队列头 宏 3 DECLARE_WAITQUEUE(name,tsk) //定义等待队列元素 4 add_wait_queue(wait_queue_head_t *q,wait_queue_t *wait) //添加等待队列元素 remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait)//移除等待队列元素 5 wait_event(queue,condition)//等待事件 wait_event_interruptible(queue,condition) // 等待第一个参数作为等待队列头部的队列被唤醒,第二个参数condition必须被满足,他们的区别 是 wait_event_interruptible 可以被信号打断,前者不能; 6 wake_up(wait_queue_head_t *queue)//唤醒队列 wake_up_interruptible(wait_queue_head_t *queue)//唤醒队列 唤醒以 queue作为等待队列头部的队列中的所有进程,wake_up可有唤醒处于 TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE 的进程,而 wake_up_interruptible 只能唤醒处于TASK_INTERRUPTIBLE状态的 进程
2.2:例程:
在设备驱动中使用等待队列模板: static ssize_t mac_read(struct file *filp, char *buffer, size_t count, loff_t *ppos) { 0 //定义等待队列头部 1 //定义等待队列元素 DECLARE_WAITQUEUE(wait,current); mutex_lock(&dev->mutex); 2 //添加到等待队列 add_wait_queue(&dev->r_wait, &wait); //等待 缓冲区可写 while(dev->current_len == 0){ //如果是非阻塞访问 if(filp->f_flags & O_NONBLOCK){ return -EAGAIN; } //阻塞访问 3 //进行进程状态切换,设置当前进程状态为 浅睡眠,并没有真正睡眠 __set_current_state(TASK_INTERRUPTIBLE); mutex_unlock(&dev->mutex); 4 //调度当前读进程出去 其他进程执行 此时读进程进入睡眠状态 schedule(); //由于当前读进程切换出去的时候是 TASK_INTERRUPTIBLE 状态,可以被信号唤醒 //判断是否是信号唤醒进程,如果是 将读进程移除等待队列,并设置进程状态已运行 5 if(signal_pending(current)) { remove_wait_queue(&dev->r_wait, &wait);//从附属的等待队列移除 set_current_state(TASK_RUNNING); return - ERESTARTSYS; } mutex_lock(&dev->mutex); } wake_up_interruptible(&dev->w_wait);//唤醒可能阻塞的写进程 return 0; } 注意 :因为进程调度出去的时候 的进程状态是 TASK_INTERRUPTIBLE ,即浅度睡眠,所以 唤醒他的可能是信号,因此我们首先通过 signal_pending(current)了解是不是信号 唤醒 无论是 读函数 还是写函数,在执行 schedule() 把自己切换出去之前,都要主动释放互斥体 因为如果读进程阻塞,说明是 fifo 空,必须依赖写进程往 FIFO 里面写东西来唤醒读进程,但是 写进程为了写 FIFO 也必须要拿到这个互斥锁来访问这个FIFO临界资源,如果读进程在把自己调度 出去之前没有释放这个互斥体,那么写进程就无法获得互斥体,也就是无法访问FIFO临界资源,导致 读写进程的锁死。
发表评论
最新留言
能坚持,总会有不一样的收获!
[***.219.124.196]2025年03月28日 04时22分02秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
linux 下安装kolla报错 提示Cannot uninstall requests
2019-03-04
Linux MySQL的socket文件存在位置更改
2019-03-04
Linux RPM和yum命令的使用技巧
2019-03-04
写博客常用的字体颜色(待续)
2019-03-04
C++ throw、try、catch、noexcept
2019-03-04
vim之vim滚屏与跳转
2019-03-04
C指针之函数指针与typedef
2019-03-04
CentOS8 字体大小调整
2019-03-04
设计模式之组合模式
2019-03-04
设计模式之外观模式
2019-03-04
Linux 验证、数字证书、RPM包中文件的提取
2019-03-04
《Redis开发与运维》阅读笔记:键管理之单个键管理
2019-03-04
(恋上数据结构笔记):优先级队列(Priority Queue)
2019-03-04
(Python学习笔记):条件语句
2019-03-04
(Python学习笔记):字典
2019-03-04
(C++11/14/17学习笔记):并发基本概念及实现,进程、线程基本概念
2019-03-04
(C++11/14/17学习笔记):线程启动、结束,创建线程多法、join,detach
2019-03-04
(C++11/14/17学习笔记):创建多个线程、数据共享问题分析及案例
2019-03-04
(QT学习笔记):按钮组中的常用控件
2019-03-04