Linux device drives 5(并发与竟态)

原创 2012年03月24日 00:03:15

竟态会导致对共享数据的非控制访问。发生这种错误模式的访问,会产生非预期结果。

内核提供了许多可延迟代码执行的机制,比如workqueue(工作队列)、tasklet(小任务)、以及timer(定时器),而且内核代码可以是抢占式的。对于竟态的发生时很有可能的,虽然竟态一般都是小概率时间,但是带来的危害却很大。

对于这种竟态问题,只要有可能就应该避免资源的共享。

处理并发和竟态的办法就是把这个共享的数据变成原子操作,仅有一个进程来执行。信号量就出来了。一个信号量就是一个整数值,它和一对函数联合使用。这一对函数通常称为P和V操作,希望进入临界区的进程竟在相关的信号量上调用P操作,如果信号量大于0,则该值会减小1,而进程可以继续,相反如果信号量值为0(或更小),进程必须等待直到其他进程来释放信号量。对信号量进行解锁通过调用V操作,该函数增加信号量的值,并在必要时唤醒等待的进程。

信号量用于互斥时候,它的值应该初始化为1,表明仅仅有一个进程能够访问,共享单一访问,就成了互斥,一定要区别 互斥和信号量的概念。

 

信号量和互斥体

Linux内核支持信号量的实现。内核代码必须包含<asm/semaphore.h>相关的类型是struct semaphore

/*
*      初始化信号量函数
*      @param sem 需要初始化的信号量指针
*      @param val 把信号量初始化的初始化值
*      返回值是void
*/
voidsemaphore_init(struct semaphore *sem, int val)
初始化互斥体
DECLARE_MUTEX(name);//初始化一个互斥体
DECLARE_MUTEX_LOCKED(name);//该互斥体处于锁定状态
P操作函数
void down(strcutsemaphore *sema);       //    不可中断
intdown_interruptible(struct semaphore *sema);//可被中断
intdown_trylock(struct semaphore *sema); //如果不能获得就会立即返回一个非0值
Linux 的V操作(释放信号量)
void up(structsemaphore *sema);

正确使用锁的机制是①明确要保护的资源②确保每一个对这些资源的访问使用正确的锁定

读写信号量

一些任务只需要读取受保护的数据结构,而其他的的必须要做出修改。Linux内核提供了一种特殊的信号量,称为rwsem(读写信号量),使用rwsem的代码必须包含 <linux/rwsem.h>

void init_rwsem(structrw_semaphore *sema);//初始化信号量
读取借口
voiddown_read(struct rw_semaphore *sema);
intdown_read_trylock(struct rw_semaphore *sema);
voidup_read(struct rw_semaphore *sema);
读写信号量对于读操作可以共享
voiddown_write(struct rw_semaphore *sema);
intdown_write_trylock(struct rw_semaphore *sema);
void up_write(structrw_semaphore *sema);

一个rw_semaphore可允许一个写入或者无限多个读取者拥有该信号量。写入者的优先级会高些,在写入者完成之前,是不允许读取操作的,这样如果一直有写入者,就会导致读取者一直等待,就是所说的饿死。

completion(完成)接口,completion是一种轻量级的机制,它允许一个线程告诉另外一个线程某个工作已经完成,为了使用completion,代码必须包含<asm/completion.h>

init_completion(strcutcompletion *sema)//初始化一个信号量
voidwait_for_completion(struct completion *c);//等待前一个完成
wait_for_completion是一个非中断的等待。
voidcomplete(struct completion *sema)//完成后通知另外的等待的(阻塞在wait_for_completion)
void complete_all(structcompletion *sema)//通知所有等待该信号量的线程
complete仅仅会唤醒一个线程。而complete_all会唤醒所有等待该信号量的线程

自旋锁

自旋锁可以在不能休眠的代码中执行

完整定义:一个自旋锁是一个互斥设备,他只能有两个值,锁定或者解锁。它通常的实现位某个整数值宏的单个位。希望获得某特定锁的代码测试相关的位。如果锁可以使用,则锁定位被设置,而代码继续进入临界区;相反,如果锁被其他人获得,则代码进入忙循环并重复检查这个锁,知道该锁可用为止,这个循环就是自旋锁的自旋部分。

自旋锁所要包含的文件是<linux/spinlock.h>

spinlock_t mylock= SPIN_LOCK_UNLOCKED;//初始化
voidspin_lock_init(spinlock_t *lock);//动态初始化
voidspin_lock_(spinlock_t *lock);//加锁
voidspin_unlock(spinlock_t *lock);//解锁

适用于自旋锁的核心规则是:任何拥有自旋锁的代码必须都是原子的,它不能睡眠,实际上,它不能因为任何原因放弃处理器

自旋锁和信号量区别

  自旋锁和信号量是解决互斥问题的基本手段,无论是单处理系统还是多处理系统,它们可以不需修改代码地进行移植。那么,这两个手段应该如何选择呢?这就要考虑临界区的性质和系统处理的要求。
    从严格意义上说,信号量和自旋锁属于不同层次的互斥手段,前者的实现有赖于后者。
信 号量是进程级的,用于多个进程之间对资源的互斥,虽然也是在内核中,但是该内核执行路径是以进程的身份,代表进程来争夺资源的。如果竞争不上,会有上下文切换,进程可以去睡眠,但CPU不会停,会接着运行其他的执行路径。从概念上说,这与单CPU或多CPU没有直接的关系,只是在信号量本身的实现上,为了 保证信号量结构存取的原子性,在多CPU中需要自旋锁来互斥。但是值得注意的是上下文切换需要一定时间,并且会使高速缓冲失效,对系统性能影响是很大的。 因此,只有当进程占用资源很长时间时,用信号量才是不错的选择。
   当所要保护的临界区比较短时,用自旋锁是非常方便的,因为它节省上下文切换的时间。但是CPU得不到自旋锁会在那里空转直到锁成功为止,所以要求锁不能在临界区里停留很长时间,否则会降低系统的效率。

   综上,自旋锁是一种保护数据结构或代码片段的原始方式,主要用于SMP中,用于CPU同步,在某个时刻只允许一个进程访问临界区内的代码。它的实现是基于 CPU锁定数据总线的指令。为保证系统效率,自旋锁锁定的临界区一般比较短。在单CPU系统中,使用自旋锁的意义不大,还容易因为递归调用自旋锁造成死锁。


linux设备驱动第五篇:驱动中的并发与竟态

在上一篇介绍了linux驱动的调试方法,这一篇介绍一下在驱动编程中会遇到的并发和竟态以及如何处理并发和竞争。 首先什么是并发与竟态呢?并发(concurrency)指的是多个执行单元同时、并行被执行。...
  • HAOMCU
  • HAOMCU
  • 2015年04月11日 13:23
  • 2398

Qt:文件系统浏览实例(QDir、QFileInfoList、QListWidgetItem)

文件系统的浏览是目录操作的一个常用功能,下面通过程序,实现一个能够显示所有文件的浏览功能。 讲一讲如何使用QDir类以及各种过滤方式显示文件列表。...
  • rl529014
  • rl529014
  • 2016年06月23日 14:10
  • 3189

《Linux总线、设备与驱动》USBHID设备驱动

一、HID虚拟总线驱动加载 drivers/hid/hid-core.c module_init(hid_init); static int __init hid_init(void){   ret ...
  • tankai19880619
  • tankai19880619
  • 2013年12月20日 15:30
  • 7345

Linux device drives chapter 4(调试技术)

第四章 内核调试技术 内核调试技术支持 make menuconfig对内核进行配置 在kernel hacking这一项中包含了一些内核的调试支持 make menuconfig之后就会看到...
  • youkuxiaobin
  • youkuxiaobin
  • 2012年03月17日 23:37
  • 997

Linux device drives chapter 2(构造和运行模块)

Linux device drives chapter 2 1.        如果你想编译驱动程序,那么你的系统必须存在构造好的内核源码树,否则驱动编译的时候头文件和链接文件找不到。 2. ...
  • youkuxiaobin
  • youkuxiaobin
  • 2012年03月04日 01:11
  • 860

《Linux Device Drivers》第五章 并发和竞态——note

并发及其管理 竞态通常作为对资源的共享访问结果而产生当两个执行线程需要访问相同的数据结构(或硬件资源)时,混合的可能性就永远存在只要可能就应该避免资源的共享共享通常是必需的,硬件资源本质上就是共享的...
  • luopingfeng
  • luopingfeng
  • 2014年09月27日 21:55
  • 390011

Linux Device Drivers学习笔记-Chapter.5

本章主要涉及并发管理,我觉得这部分的理解需要建立在大量实践的基础之上,光是像本科操作系统课上那样讲道理是很难体会的。我记录了一些关键的函数和数据结构。 1.Race Conditions(R...
  • kyokowl
  • kyokowl
  • 2011年04月01日 11:11
  • 558

Linux device drivers学习笔记(5)——uart primary driver code

#include #include #include #include #include #include #include #include #include #...
  • u012914709
  • u012914709
  • 2014年05月14日 21:28
  • 637

Linux设备驱动程序第三版学习(4)- 并发和竟态 .

第五章学习:并发和竟态 其实可以拿上“公共厕所”来做一个比喻,就是那种在大街上只有一个隔间的小型公厕。更容易理解并发、竟态和信号量。 把“临界区”理解为“公共厕所”(临界区:在任意给定的时刻,代码...
  • zuijinzhao
  • zuijinzhao
  • 2012年09月13日 20:49
  • 267

linux设备驱动第五篇:驱动中的并发与竟态

综述 在上一篇介绍了linux驱动的调试方法,这一篇介绍一下在驱动编程中会遇到的并发和竟态以及如何处理并发和竞争。 首先什么是并发与竟态呢?并发(concurrency)指的是多个执行单元同时、并行被...
  • lihui130135
  • lihui130135
  • 2015年04月11日 22:36
  • 467
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Linux device drives 5(并发与竟态)
举报原因:
原因补充:

(最多只允许输入30个字)