linux设备驱动中的阻塞与非阻塞(一)
这两天在搞linux驱动的阻塞和非阻塞,困扰了两天,看了不少博客,有了点自己的想法,也不知是否对错,但还是写写吧,让各位大神给我指点指点。
首先说说什么是阻塞和非阻塞的概念:阻塞操作就是指进程在操作设备时,由于不能获取资源或者暂时不能操作设备时,系统就会把进程挂起,被挂起的进程会进入休眠状态并且会从调度器的运行队列移走,放到等待队列中,然后一直休眠,直到该进程满足可操作的条件,再被唤醒,继续执行之前的操作。非阻塞操作的进程在不能进行设备操作时,并不会挂起,要么放弃,要么不停地执行,直到可以进行操作为止。
我们都知道,在应用中,打开一个设备文件时,指定了是以阻塞还是非阻塞打开(缺省是阻塞方式),然后后面的读写一切都是交由驱动来实现,那么驱动是如何实现read()和write()的阻塞呢!下面以读写一个内存块为例子,当该内存写满了,不能写的时候,调用write()函数该怎么处理,当该内存已经读取完了,空了的时候,调用read()函数,又改如何处理(该代码简化了,只为说明问题,不能正常编译使用):
wait_queue_head_t read_queue; //定义读等待队列头部
wait_queue_head_t write_queue; //定义写等待队列头部
struct semaphore sem; //定义信号量,用于互斥访问公共资源
static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
if(down_interruptible(&sem))
return -ERESTARTSYS; //使用 down_interruptible,给公共资源上锁,以防出现并发引起的竞态问题
while (!have_data) //have_data用来判断缓冲区中是否有数据,如果有数据,直接跳过该while语句,执行下面的 // copy_to_user
{
up(&sem); //由于没有数据,不能进行读取数据操作,要释放锁,解锁,这里的解锁很重要,要是没有解锁,很容 //易进入死锁,具体怎样,下面再分析
if(filp->f_flags & O_NONBLOCK) //判断该文件时以阻塞方式还是非阻塞方式打开
return -EAGAIN; //由于是非阻塞打开,直接返回
wait_event_interruptible(read_queue,have_date);//阻塞方式代开,该语句会让进程进入休眠状态,然后等待其他进程 //的唤醒并且have_data=true时,才会被完全唤醒,执行下面的语句
if(down_interruptible(&sem)) //由于可以进行读取了,所以在此给公共资源上锁
return -ERESTARTSYS;
if (copy_to_user(buf, (void*)(dev->data + p), count)) { //实现数据从内核空间读取到用户空间,完成读取操作
..................
}
have_data = false; //标记该数据已经读取完毕
up(&sem); //释放锁
wake_up(&write_queue); //读取完毕,缓冲区有空间可以写入了,就唤醒写进程,让写进程把数据写入
return ;
}
下面分析write函数,其原理和实现也是和read函数一样,都是先给公共资源上锁,再判断是阻塞访问还是非阻塞访问,如果是非阻塞访问,且资源不能获取时,直接返回,若果时阻塞且不能获取资源时,就进入休眠,等待其他进程的唤醒。
static ssize_t mem_write(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
if(down_interruptible(&sem))
return -ERESTARTSYS; //使用 down_interruptible,给公共资源上锁,以防出现并发引起的竞态问题
while (have_data) //have_data用来判断缓冲区中是否有数据,如果有数据,表示缓冲区已经满了,不能写入,
//如果have_data是false,即没有数据,缓冲区是空的,可以写入数据,就执行下面的copy_from_user
{
up(&sem); //由于有数据,不能进行写入数据操作,要释放锁,解锁 if(filp->f_flags & O_NONBLOCK) //判断该文件时以阻塞方式还是非阻塞方式打开
return -EAGAIN; //由于是非阻塞打开,直接返回
wait_event_interruptible(write_queue,!have_date);//阻塞方式代开,该语句会让进程进入休眠状态,然后等待其他进程 //的唤醒并且have_data=false时,才会被完全唤醒,执行下面的语句
if(down_interruptible(&sem)) //由于可以进行写入操作了,所以在此给公共资源上锁
return -ERESTARTSYS;
if (copy_from_user((dev->data + p), buf,count)) { //实现数据从内核空间读取到用户空间,完成读取操作
..................
}
have_data = true; //标记该数据已经读取完毕
up(&sem); //释放锁
wake_up(&read_queue); //写入数据完毕,缓冲区有数据可以读取了,就唤醒读进程,让读进程开始读取数据
return ;
}
以上是驱动中的读取和写入操作,当写进程发现数据已满,不能写入时,且上层应用是以阻塞的方式打开设备文件时,所以必须要写入数据才能返回,否则不能返回,那么就有两种实现机制,要不就是不停地忙等待,等待设备可以写入时,便写入,然后返回,可是这样做的话,非常影响CPU的执行效率,大大降低了CPU的性能,所以linux内核中采取了等待队列的实现方式,就是当一个阻塞进程写入数据时,发现不能写入时,会把这个进程挂起,放到等待队列中休眠,然后一直在休眠,直到有个读进程,把缓冲区的数据读取完毕后,然后读进程会把写进程唤醒,告诉写进程缓冲区可以写入数据了,于是写进程继续写入操作,并且返回。举个例子,小明饿了,要吃饭,于是跑去妈妈那里,说要吃饭,妈妈说放没有做好,你说小明是继续在这里一直等着妈妈把饭做好,还是先去睡一觉好呢,如果我是小明,我就先去睡一觉,然后妈妈把饭做好了,就把小明叫醒,小明,可以吃饭了,于是小明起来,跑去吃饭。当读进程阻塞时,也是这样,就不分析了。
现在说说为什么每次进去阻塞前都要把锁释放掉,然后唤醒时再次上锁,我们试想一下,假如读进程发现缓冲区为空,不能读取时,准备进入休眠了,没有把锁释放,效果会怎样,就相当于读进程带着锁睡着了,一旦读进程带着锁睡着了,写进程来了,可是写进程因为不能获取锁,就不能访问临界区的资源,更不能往缓冲区里面写入数据,所以缓冲区会一直为空,且写进程也会不停地在那里休眠,等到读进程释放锁,可是读进程睡着了,不能释放锁,写进程也休眠了,不能唤醒读进程,于是就发生了死锁了。这就好比小明他爸爸藏了一个还魂丹在保险箱里,有一天,他爸爸晕倒了,可是没有告诉小明锁放在那里,于是小明只能在保险箱外面,看着他爸爸晕过去,却无能为力了.....
下一节再分析linux驱动中的非阻塞!
linux设备驱动中的阻塞与非阻塞(一)相关推荐
- linux 设备驱动阻塞,深入浅出:Linux设备驱动中的阻塞和非阻塞I/O
今天写的是Linux设备驱动中的阻塞和非阻塞I/0,何谓阻塞与非阻塞I/O?简单来说就是对I/O操作的两种不同的方式,驱动程序可以灵活的支持用户空间对设备的这两种访问方式. 一.基本概念: 阻塞操作 ...
- linux write引起进程挂起,Linux设备驱动中的阻塞与非阻塞总结
Linux设备驱动中的阻塞与非阻塞总结 阻塞操作是指,在执行设备操作时,若不能获得资源,则进程挂起直到满足可操作的条件再进行操作. 非阻塞操作的进程在不能进行设备操作时,并不挂起.被挂起的进程进入sl ...
- Linux设备驱动中的阻塞和非阻塞IO
这篇文章我们来了解下Linux设备驱动中阻塞和非阻塞. 阻塞:阻塞是指执行设备操作时,如果不能获得设备资源,则挂起进程,是进程进入休眠模式,直到设备资源可以获取. 非阻塞:非阻塞是在不能获取设备资源时 ...
- Linux设备驱动中的并发控制总结
并发(concurrency)指的是多个执行单元同时.并行被执行.而并发的执行单元对共享资源(硬件资源和软件上的全局.静态变量)的访问则容易导致竞态(race conditions). SMP是一 ...
- linux 两个驱动 竞态,第7章 Linux设备驱动中的并发控制之一(并发与竞态)
本章导读 Linux设备驱动中必须解决的一个问题是多个进程对共享资源的并发访问,并发的访问会导致竞态(竞争状态). Linux提供了多种解决竞态问题的方式,这些方式适合不同的应用场景. 7.1讲解了并 ...
- Linux设备驱动开发详解:第7章 Linux设备驱动中的并发控制
7.1并发与竞态 (1).竞态的发生场景:CPU0的进程与CPU1的进程之间.CPU0的中断与CPU1的进程之间.CPU0的中断与CPU1的中断之间: (2).解决竞态问题的途径是保证对共享资源的互斥 ...
- Linux 设备驱动中的 I/O模型(一)—— 阻塞和非阻塞I/O
在前面学习网络编程时,曾经学过I/O模型 Linux 系统应用编程--网络编程(I/O模型),下面学习一下I/O模型在设备驱动中的应用. 回顾一下在Unix/Linux下共有五种I/O模型,分别是: ...
- Linux设备驱动中的阻塞与非阻塞I/O
阻塞和非阻塞I/O是设备访问的两种不同模式,驱动程序可以灵活的支持用户空间对设备的这两种访问方式 本例子讲述了这两者的区别 并实现I/O的等待队列机制, 并进行了用户空间的验证 基本概念: 1> ...
- Linux 设备驱动中的阻塞与非阻塞 I/O
阻塞操作是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作.被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足.而非阻塞操作的进程在不能进行设备操作时 ...
最新文章
- 男子在大街上捡到一U盘,竟有英国女王在伦敦机场的路线图
- python编程视频-【科研资源03】最全Python编程全套系统视频学习教程
- 我的Android进阶之旅------gt;Android使用AlarmManager全局定时器实现定时更换壁纸
- mongodb 服务器时区设置_关于MongoDB-Balancer设置时间窗口的问题
- DDOS学习笔记(《破坏之王-DDOS攻击与防范深度剖析》)
- 字节对齐《c和指针》笔记--包含位域结构体的内存对齐(32bit,GCC)
- 在诺基亚S60v3上运行.NET程序
- c#使用Path.Combine的一个坑
- linux ppp漏洞,Linux下ppp拨号的实现(Arm)
- Orleans 初接触(一) 入门例子
- 多线程笔试题(linux)
- python对矩阵对角线进行赋值
- paip.模块化与面向对象的关系以及实现
- 96Boards MIPI CSI Camera Mezzanine V2.1
- php解决缓慢http请求,php CURL 服务器响应慢的问题
- 什么是附近推?附近推怎么投放?
- zen cart产品分类及产品管理
- 00后测试员摸爬滚打近一年,为是否要转行或去学软件测试的学弟们总结出了以下走心建议
- Pandas中的pivot操作
- LINUX 查看和修改文件系统的block的大小
热门文章
- java中boolean转string_Java boolean转String
- 如何将CAD文字标注变为ArcGIS属性
- Android RecyclerView实现瀑布流,图片自适应高度,不闪烁,解决位置交换
- oracle博客北大青鸟,北大青鸟oracle学习笔记25
- 如何设置一个计算机用户访问磁盘,怎么把硬盘共享给其他电脑
- 大班我和计算机比本领教学反思,大班语言《谁的本领大》教案反思
- 基于用户登录测试用例设计产生一点对用例设计的理解
- BELLHOP 手册和用户指南(中文版)
- 【Golang Leetcode】总目录(Day1~100)
- python画五角星代码_Python如何使用27行代码绘制星星图