这两天在搞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设备驱动中的阻塞与非阻塞(一)相关推荐

  1. linux 设备驱动阻塞,深入浅出:Linux设备驱动中的阻塞和非阻塞I/O

    今天写的是Linux设备驱动中的阻塞和非阻塞I/0,何谓阻塞与非阻塞I/O?简单来说就是对I/O操作的两种不同的方式,驱动程序可以灵活的支持用户空间对设备的这两种访问方式. 一.基本概念: 阻塞操作 ...

  2. linux write引起进程挂起,Linux设备驱动中的阻塞与非阻塞总结

    Linux设备驱动中的阻塞与非阻塞总结 阻塞操作是指,在执行设备操作时,若不能获得资源,则进程挂起直到满足可操作的条件再进行操作. 非阻塞操作的进程在不能进行设备操作时,并不挂起.被挂起的进程进入sl ...

  3. Linux设备驱动中的阻塞和非阻塞IO

    这篇文章我们来了解下Linux设备驱动中阻塞和非阻塞. 阻塞:阻塞是指执行设备操作时,如果不能获得设备资源,则挂起进程,是进程进入休眠模式,直到设备资源可以获取. 非阻塞:非阻塞是在不能获取设备资源时 ...

  4. Linux设备驱动中的并发控制总结

    并发(concurrency)指的是多个执行单元同时.并行被执行.而并发的执行单元对共享资源(硬件资源和软件上的全局.静态变量)的访问则容易导致竞态(race conditions).   SMP是一 ...

  5. linux 两个驱动 竞态,第7章 Linux设备驱动中的并发控制之一(并发与竞态)

    本章导读 Linux设备驱动中必须解决的一个问题是多个进程对共享资源的并发访问,并发的访问会导致竞态(竞争状态). Linux提供了多种解决竞态问题的方式,这些方式适合不同的应用场景. 7.1讲解了并 ...

  6. Linux设备驱动开发详解:第7章 Linux设备驱动中的并发控制

    7.1并发与竞态 (1).竞态的发生场景:CPU0的进程与CPU1的进程之间.CPU0的中断与CPU1的进程之间.CPU0的中断与CPU1的中断之间: (2).解决竞态问题的途径是保证对共享资源的互斥 ...

  7. Linux 设备驱动中的 I/O模型(一)—— 阻塞和非阻塞I/O

    在前面学习网络编程时,曾经学过I/O模型 Linux 系统应用编程--网络编程(I/O模型),下面学习一下I/O模型在设备驱动中的应用. 回顾一下在Unix/Linux下共有五种I/O模型,分别是: ...

  8. Linux设备驱动中的阻塞与非阻塞I/O

    阻塞和非阻塞I/O是设备访问的两种不同模式,驱动程序可以灵活的支持用户空间对设备的这两种访问方式 本例子讲述了这两者的区别 并实现I/O的等待队列机制, 并进行了用户空间的验证 基本概念: 1> ...

  9. Linux 设备驱动中的阻塞与非阻塞 I/O

    阻塞操作是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作.被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足.而非阻塞操作的进程在不能进行设备操作时 ...

最新文章

  1. 男子在大街上捡到一U盘,竟有英国女王在伦敦机场的路线图
  2. python编程视频-【科研资源03】最全Python编程全套系统视频学习教程
  3. 我的Android进阶之旅------gt;Android使用AlarmManager全局定时器实现定时更换壁纸
  4. mongodb 服务器时区设置_关于MongoDB-Balancer设置时间窗口的问题
  5. DDOS学习笔记(《破坏之王-DDOS攻击与防范深度剖析》)
  6. 字节对齐《c和指针》笔记--包含位域结构体的内存对齐(32bit,GCC)
  7. 在诺基亚S60v3上运行.NET程序
  8. c#使用Path.Combine的一个坑
  9. linux ppp漏洞,Linux下ppp拨号的实现(Arm)
  10. Orleans 初接触(一) 入门例子
  11. 多线程笔试题(linux)
  12. python对矩阵对角线进行赋值
  13. paip.模块化与面向对象的关系以及实现
  14. 96Boards MIPI CSI Camera Mezzanine V2.1
  15. php解决缓慢http请求,php CURL 服务器响应慢的问题
  16. 什么是附近推?附近推怎么投放?
  17. zen cart产品分类及产品管理
  18. 00后测试员摸爬滚打近一年,为是否要转行或去学软件测试的学弟们总结出了以下走心建议
  19. Pandas中的pivot操作
  20. LINUX 查看和修改文件系统的block的大小

热门文章

  1. java中boolean转string_Java boolean转String
  2. 如何将CAD文字标注变为ArcGIS属性
  3. Android RecyclerView实现瀑布流,图片自适应高度,不闪烁,解决位置交换
  4. oracle博客北大青鸟,北大青鸟oracle学习笔记25
  5. 如何设置一个计算机用户访问磁盘,怎么把硬盘共享给其他电脑
  6. 大班我和计算机比本领教学反思,大班语言《谁的本领大》教案反思
  7. 基于用户登录测试用例设计产生一点对用例设计的理解
  8. BELLHOP 手册和用户指南(中文版)
  9. 【Golang Leetcode】总目录(Day1~100)
  10. python画五角星代码_Python如何使用27行代码绘制星星图