竞态与并发

自旋锁

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

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. 5月25日 DOM

    练习一:输入答案,弹出是否正确. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "h ...

  2. 《Pro AngularJS》学习小结-02

    上一篇的项目只有一个单独的模板页面,加入了相应的controller,filter,使得页面上的数据能够动态的变化.现在我们开始建立并整合多个模板,加入购物车模块和结账checkout模块. 一.在页 ...

  3. Tomcat配置虚拟主机的两种方式

    1.基于主机名的虚拟主机配置 在随意盘符下建立一个目录作为虚拟地址的目录.例如:F:\virtualhost1,在其下建立 test1.html,写入内容例如:test 在tomcat/conf/se ...

  4. js克隆

    一.有什么用 不破坏原对象的属性 引入一些概念~ 原始数据类型(5种):undefined.null.number.string.boolean 引用数据类型(1种,也叫复合数据类型):object ...

  5. bzoj 2282: [Sdoi2011]消防

    #include<cstdio> #include<cstring> #include<iostream> #define N 600000 using names ...

  6. CNAPS Code 查询(招商银行)

    招商银行的妹子实在太傻了,根本不知道什么是CNAPS Code.联行号,完全答非所问. 最后还是自己搞定了,如图: 最后再看看招行人员的英语水平,真是不知道什么是东西:

  7. UVa 11427 - Expect the Expected

    http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&p ...

  8. 二模 (8) day1

    第一题: 题目大意: 梦幻城市每年为全市高中生兴办一次运动会.为促使各校同学之间的交流,采用特别的分队方式:每一个学校的同学,必须被均匀分散到各队,使得每一队中该校的人数皆相同.为增加比赛的竞争性,希 ...

  9. Qt之控件美化

    级联样式表 (CSS) 包含应用于网页中的元素的样式规则.CSS 样式定义元素的显示方式以及元素在页中的放置位置.可以创建一个通用规则,只要 Web 浏览器遇到一个元素实例,或遇到一个分配给某个特定样 ...

  10. Android模拟器配置选项说明

    Memory Options是模拟器的运行内存大小,类比电脑内存大小,就是在设置->应用程序中,正在运行标签页下面显示的那个大小Internal storage是模拟器内置存储空间大小,用于存放 ...