竞态与并发

自旋锁

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

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. UML类图关系大全

    UML类图关系大全 1.关联 双向关联: C1-C2:指双方都知道对方的存在,都可以调用对方的公共属性和方法.在GOF的设计模式书上是这样描述的:虽然在分析阶段这种关系是适用的,但我们觉得它对于描述设 ...

  2. HDU 1864

    最大报销额 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  3. 为 Node.js 开发者准备的 8 本免费在线电子书(转)

    ode.js 是一套用来编写高性能网络服务器的JavaScript工具包,一系列的变化由此开始.比较独特的是,Node.js会假设你是在POSIX环境下运行它 Linux 或 Mac OS X.如果你 ...

  4. Go语言并发与并行学习笔记(一)

    转:http://blog.csdn.net/kjfcpua/article/details/18265441 如果不是我对真正并行的线程的追求,就不会认识到Go有多么的迷人. Go语言从语言层面上就 ...

  5. [转]JDE910--jas.ini参数说明

    配置 jas.ini 文件 jas.ini 文件可以使用 Java™ Application Server (JAS) 来提供 JDE 安装的配置.您必须针对特定于每个环境的 JDE 实例来配置 ja ...

  6. 通过计算机名访问linux

    1.安装samba 2.设置/etc/samba/smb.conf的 netbois name 配置节的值为你要设置的名称,如 netbois name = mylinux 也可以不设置此项,如果不设 ...

  7. 百度360争推1TB永久网盘

    导读:百度云.360云盘的互掐则将云盘空间战拉升到史无前例的高度——无论是国内还是国外,还没有哪家公司有过这样的手笔.      这几天百度热热闹闹开大会,会场外“小伙伴们”也不甘寂寞.       ...

  8. 4通用Makefile编写

    a.c #include<stdio.h> #include "a.h" int main() { printf("hello world\n"); ...

  9. limit 百万级数据分页优化方法

    mysql教程 这个数据库教程绝对是适合dba级的高手去玩的,一般做一点1万 篇新闻的小型系统怎么写都可以,用xx框架可以实现快速开发.可是数据量到了10万,百万至千万,他的性能还能那么高吗? 一点小 ...

  10. Python的排序

    1.reversed() 这个很好理解,reversed英文意思就是:adj. 颠倒的:相反的:(判决等)撤销的 print list(reversed(['dream','a','have','I' ...