需要了解linux设备驱动中的阻塞与非阻塞等问题

 首先说说什么是阻塞和非阻塞的概念:阻塞操作就是指进程在操作设备时,由于不能获取资源或者暂时不能操作设备时,系统就会把进程挂起,被挂起的进程会进入休眠状态并且会从调度器的运行队列移走,放到等待队列中,然后一直休眠,直到该进程满足可操作的条件,再被唤醒,继续执行之前的操作。非阻塞操作的进程在不能进行设备操作时,并不会挂起,要么放弃,要么不停地执行,直到可以进行操作为止。

    需要了解linux设备驱动中的阻塞与非阻塞等问题_设计制作_嵌入式技术  

我们都知道,在应用中,打开一个设备文件时,指定了是以阻塞还是非阻塞打开(缺省是阻塞方式),然后后面的读写一切都是交由驱动来实现,那么驱动是如何实现read()和wri     te   ()的阻塞呢!下面以读写一个内存块为例子,当该内存写满了,不能写的时候,调用write()函数该怎么处理,当该内存已经读取完了,空了的时候,调用read()函数,又改如何处理(该代码简化了,只为说明问题,不能正常编译使用):

w     ai   t_queue_head_t read_queue;      //定义读等待队列头部
wait_queue_head_t write_queue;     //定义写等待队列头部
struct semaphore sem;                     //定义信号量,用于互斥访问公共资源

sta     ti   c ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)

{

if(down_interrup  TI ble(&sem))
return -ERESTARTSYS;      //使用 down_interrup  TI ble,给公共资源上     锁   ,以防出现并发引起的竞态问题

while (!have_data)     //have_data用来判断缓冲区中是否有数据,如果有数据,直接跳过该while语句,执行下面的                                                             // copy_to_user
{
up(&sem);     //由于没有数据,不能进行读取数据操作,要释放锁,解锁,这里的解锁很重要,要是没有解锁,很容                                                //易进入死锁,具体怎样,下面再分析

if(filp->f_flags & O_NONBLOCK)   //判断该文件时以阻塞方式还是非阻塞方式打开

return -EAGAIN;                    //由于是非阻塞打开,直接返回

wait_event_interrup  TI ble(read_queue,have_date);//阻塞方式代开,该语句会让进程进入休眠状态,然后等待其他进程                                                                                                      //的唤醒并且have_data=true时,才会被完全唤醒,执行下面的语句

if(down_interrup  TI ble(&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_f     rom   _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   内核中采取了等待队列的实现方式,就是当一个阻塞进程写入数据时,发现不能写入时,会把这个进程挂起,放到等待队列中休眠,然后一直在休眠,直到有个读进程,把缓冲区的数据读取完毕后,然后读进程会把写进程唤醒,告诉写进程缓冲区可以写入数据了,于是写进程继续写入操作,并且返回。举个例子,小明饿了,要吃饭,于是跑去妈妈那里,说要吃饭,妈妈说放没有做好,你说小明是继续在这里一直等着妈妈把饭做好,还是先去睡一觉好呢,如果我是小明,我就先去睡一觉,然后妈妈把饭做好了,就把小明叫醒,小明,可以吃饭了,于是小明起来,跑去吃饭。当读进程阻塞时,也是这样,就不分析了。

现在说说为什么每次进去阻塞前都要把锁释放掉,然后唤醒时再次上锁,我们试想一下,假如读进程发现缓冲区为空,不能读取时,准备进入休眠了,没有把锁释放,效果会怎样,就相当于读进程带着锁睡着了,一旦读进程带着锁睡着了,写进程来了,可是写进程因为不能获取锁,就不能访问临界区的资源,更不能往缓冲区里面写入数据,所以缓冲区会一直为空,且写进程也会不停地在那里休眠,等到读进程释放锁,可是读进程睡着了,不能释放锁,写进程也休眠了,不能唤醒读进程,于是就发生了死锁了。这就好比小明他爸爸藏了一个还魂丹在保险箱里,有一天,他爸爸晕倒了,可是没有告诉小明锁放在那里,于是小明只能在保险箱外面,看着他爸爸晕过去,却无能为力了.....



23
93
0
65

相关资讯

  1. 1、《无尽大冒险》安卓版本更新新职业圣教军上线2962
  2. 2、加速卡福利来袭!《传世挂机》双倍经验开启4277
  3. 3、《时空炫斗》光之少年阿波罗登场4315
  4. 4、跳跃制作泡面番引关注10月6日东京电视台首发5025
  5. 5、《少年魔兽团》完美版本上线零槽点手游横空出世1321
  6. 6、《星耀少女》“萌战”冠军奖励立绘公开667
  7. 7、双十二抢夺资源《波斯之刃》洗劫黄金宝藏3619
  8. 8、《主宰无双》新版细节调整带给你更多贴心1638
  9. 9、《守护之光》三零一体新概念发布是奇葩还是奇迹?4137
  10. 10、港囧之《江湖霸图》囧途记二测玩家囧事抢鲜报3008
全部评论(0)
我也有话说
0
收藏
点赞
顶部