竞态与并发

自旋锁

若一个进程要访问临界资源,测试锁空闲,则进程获得这个锁并继续执行;若测试结果表明锁扔被 占用,进程将在一个小的循环内重复“测试并设置”操作,进行所谓的“自旋”,等待自旋锁持有者释 放这个锁。自旋锁与互斥锁类似,但是互斥锁不能用在可能睡眠的代码中,而自旋锁可以用在可睡 眠的代码中,典型的应用是可以用在中断处理函数中。自旋锁的相关操作: 自旋锁

01.// 定义自旋锁

02.spinlock_t spin;

03.

04.// 初始化自旋锁

05.spin_lock_init(lock);

06.

07.// 获得自旋锁:若能立即获得锁,它获得锁并返回,否则,自旋,直到该锁持有者释放

08.spin_lock(lock);

09.

10.// 尝试获得自旋锁:若能立即获得锁,它获得并返回真,否则立即返回假,不再自旋

11.spin_trylock(lock);

12.

13.// 释放自旋锁: 与spin_lock(lock)和spin_trylock(lock)配对使用

14.spin_unlock(lock);

15.

16.  自旋锁的使用:

17.// 定义一个自旋锁

18.spinlock_t lock;

19.spin_lock_init(&lock);

20.

21.spin_lock(&lock);  // 获取自旋锁,保护临界区

22....  // 临界区

23.spin_unlock();  // 解锁

自旋锁持有期间内核的抢占将被禁止。自旋锁可以保证临界区不受别的CPU和本CPU内的抢占进程打扰, 但是得到锁的代码路径在执行临界区的时候还可能受到中断和底半部(BH)的影响。为防止这种影响,

需要用到自旋锁的衍生:

01.spin_lock_irq() = spin_lock() + local_irq_disable()

02.spin_unlock_irq() = spin_unlock() + local_irq_enable()

03.spin_lock_irqsave() = spin_lock() + local_irq_save()

04.spin_unlock_irqrestore() = spin_unlock() + local_irq_restore()

05.spin_lock_bh() = spin_lock() + local_bh_disable()  06.spin_unlock_bh() = spin_unlock() + local_bh_enable()

信号量:   信号量(semaphore)是用于保护临界区的一种常用方法,它的使用方式和自旋锁类似。与自 旋锁相同,只有得到信号量的进程才能执行临界区代码。但是,与自旋锁不同的是,当获取不到 信号量时,进程不会原地打转而是进入休眠等待状态。

定义信号量: struct semaphore sem;

初始化信号量: void sema_init(struct semaphore *sem, int val);  val一般为0或1 #define init_MUTEX(sem)   sema_init(sem, 1) #define init_MUTEX_LOCKED(sem)   sema_init(sem, 0)

下面两个宏是定义并初始化信号量的“快捷方式”: DECLARE_MUTEX(name) DECLARE_MUTEX_LOCKED(name) 前者定义一个名为 name 的信号量并初始化为 1;后者定义一个名为 name 的信号量并初始化 为 0。

获取信号量:

int dowm(struct semaphore* sem); 该函数用于获得信号量 sem,它会导致睡眠,因此不能在中断上下文使用

int down_interrupttible(struct semaphore* sem); 该函数功能与 down 类似,不同之处为,因为 down()而进入睡眠状态的进程不能被信号打断, 但因为 down_interruptible()而进入睡眠状态的进程能被信号打断,信号也会导致该函数返回,这 时候函数的返回值非 0;

int dowm_trylock(struct semaphore* sem); 该函数尝试获得信号量 sem,如果能够立刻获得,它就获得该信号量并返回 0,否则,返回 非 0 值。它不会导致调用者睡眠,可以在中断上下文使用。

释放信号量: void up(struct semaphore* sem);

使用信号量实现设备只能被一个进程打开  范例: static DECLARE_MUTEX(xxx_lock);

static int xxx_open(struct inode *inode, struct file *filp) {  if(down_trylock(&xxx_lock))     return -EBUSY;       ....    return 0;    /*成功*/

}

static int xxx_release(struct inode* inode, struct file* filp) {   up(&xxx_lock);   return 0; }

信号量用于同步:

如果信号量被初始化为 0,则它可以用于同步,同步意味着一个执行单元的继续执行需等待 另一执行单元完成某事,保证执行的先后顺序。如图 7.4 所示,执行单元 A 执行代码区域 b 之前, 必须等待执行单元 B 执行完代码单元 c,信号量可辅助这一同步过程。

完成量用于同步:

1.定义完成量 下列代码定义名为 my_completion 的完成量: struct completion my_completion; 2.初始化 completion 下列代码初始化 my_completion 这个完成量: init_completion(&my_completion); 对 my_completion 的定义和初始化可以通过如下快捷方式实现: DECLARE_COMPLETION(my_completion); 3.等待完成量 下列函数用于等待一个 completion 被唤醒: void wait_for_completion(struct completion *c); 4.唤醒完成量 下面两个函数用于唤醒完成量: void complete(struct completion *c); void complete_all(struct completion *c); 前者只唤醒一个等待的执行单元,后者释放所有等待同一完成量的执行单元。

自旋锁VS信号量:

(1)当锁不能被获取到时,使用信号量的开销是进程上下文切换时间 Tsw,使用自旋锁的开 销是等待获取自旋锁(由临界区执行时间决定)Tcs,若 Tcs 比较小,宜使用自旋锁,若 Tcs 很大, 应使用信号量。 (2)信号量所保护的临界区可包含可能引起阻塞的代码,而自旋锁则绝对要避免用来保护包 含这样代码的临界区。因为阻塞意味着要进行进程的切换,如果进程被切换出去后,另一个进程 企图获取本自旋锁,死锁就会发生。 (3)信号量存在于进程上下文,因此,如果被保护的共享资源需要在中断或软中断情况下使 用,则在信号量和自旋锁之间只能选择自旋锁。当然,如果一定要使用信号量,则只能通过 down_trylock()方式进行,不能获取就立即返回以避免阻塞。

总结:

并发和竞态广泛存在,中断屏蔽、原子操作、自旋锁和信号量都是解决并发问题的机制。中 断屏蔽很少单独被使用,原子操作只能针对整数进行,因此自旋锁和信号量应用最为广泛。 自旋锁会导致死循环,锁定期间不允许阻塞,因此要求锁定的临界区小。信号量允许临界区 阻塞,可以适用于临界区大的情况。 读写自旋锁和读写信号量分别是放宽了条件的自旋锁和信号量,它们允许多个执行单元对共 享资源的并发读。

Smart210---学习记录 竞态与并发的更多相关文章

  1. linux设备驱动归纳总结(四):5.多处理器下的竞态和并发【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-67673.html linux设备驱动归纳总结(四):5.多处理器下的竞态和并发 xxxxxxxxxx ...

  2. linux设备驱动归纳总结(四):4.单处理器下的竞态和并发【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-67005.html linux设备驱动归纳总结(四):4.单处理器下的竞态和并发 xxxxxxxxxx ...

  3. 【Linux开发】linux设备驱动归纳总结(四):5.多处理器下的竞态和并发

    linux设备驱动归纳总结(四):5.多处理器下的竞态和并发 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  4. 【Linux开发】linux设备驱动归纳总结(四):4.单处理器下的竞态和并发

    linux设备驱动归纳总结(四):4.单处理器下的竞态和并发 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  5. Smart210学习记录------块设备

    转自:http://bbs.chinaunix.net/thread-2017377-1-1.html 本章的目的用尽可能最简单的方法写出一个能用的块设备驱动.所谓的能用,是指我们可以对这个驱动生成的 ...

  6. Smart210学习记录-----Linux i2c驱动

    一:Linux i2c子系统简介: 1.Linux 的 I2C 体系结构分为 3 个组成部分: (1) I2C 核心. I2C 核心提供了 I2C 总线驱动和设备驱动的注册.注销方法,I2C 通信方法 ...

  7. Smart210学习记录-------内存初始化

    买了Smart210的板子,开始学习中,,,,, 今天看了重定位DRAM ,然而内存需要初始化,早上信心满满的我到现在崩溃的我....也不知遭受了什么样的蹂躏 ,,还是记下一点学到的知识吧.. 数据手 ...

  8. LoadRunner11学习记录三 -- 迭代和并发

    LoadRunner中%d和%s是什么意思? %d 格式化输出短整形数据,TC环境中占用两个字节,输出整数范围为:32768~32767.Visual C++环境中占用四个字节,输出数据范围为:-21 ...

  9. Smart210学习记录-----SD/MMC/SDIO驱动

    转自:http://jingpin.jikexueyuan.com/article/23369.html http://blog.csdn.net/evilcode/article/details/7 ...

随机推荐

  1. 石子归并问题(nyoj737)

    石子合并(一) 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述     有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的 ...

  2. [整理]Svn常见问题汇总。

    1.’.’ is not a working copy.Can’t open file‘.svn/entries’: 系统找不到指定的路径. 解答:原因是输入的访问路径不正确,如svn://192.1 ...

  3. linux tar 增量备份命令

    tar --newer-mtime "2013-09-17 00:00:00"   -zcvf /var/www/good.tar.gz    spider/

  4. SendMessage函数的常用消息及其应用大全

    来源:http://www.360doc.com/content/09/0814/10/19147_4907488.shtml,非常全面的解释. 文本框控件通常用于输入和编辑文字.它属于标准 Wind ...

  5. 使用C#下载网络文件

    下载 /// <summary> /// 下载文件 /// </summary> /// <param name="URL">下载文件地址< ...

  6. java面向对象编程--第九章 多态和抽象

    9.1多态 在生物学中,是指一个生物或物种可以有多种不同的形式或阶段: 在OOP中,多态是指一个对象有多种形式的能力. 多态可以说是面向对象编程的精髓所在.java中之所以引入多态的概念,原因之一是它 ...

  7. xcode开发的6个小技巧

    Xcode是iPhone和iPad开发者用来编码或者开发iOS app的IDE.Xcode有很多小巧但很有用的功能,很多时候我们可能没有注意到它们,也或者我们没有在合适的水平使用这些功能简化我们的iO ...

  8. 登陆中session的处理

    在学校中的登陆注册使用的普通session存储信息,然后就是根据session中获取user是否拥有来判断是否登陆. 在一次面试中别人问到了我你们项目的登陆session是怎么一个情况,我这样答的话那 ...

  9. C# TCP实现多个客户端与服务端 数据 与 文件的传输

    C#菜鸟做这个东东竟然花了快三天的时间了,真是菜,菜,菜--- 下面是我用C#写的 一个简单的TCP通信,主要的功能有: (1) 多个客户端与服务器间的数据交流 (2)可以实现群发的功能 (3)客户端 ...

  10. hadoop创建两大错误:Bad connection to FS. command aborted. exception和Shutting down NameNod...

    我的hadoop启动后,各个节点都正常,但是无法查看hdfs目录,错误提示 Bad connection to FS. command aborted.  查了下网上的解决办法,主要是删除tmp下的所 ...