Linux 设备驱动并发控制简述

零: 问题 :为什么要有并发控制机制?

 答案 :在Linux中会遇到多个进程对共享资源的并发访问,并发访问会导致竞态的发生,所以需要并发控制机制。

一:概念:

并发与竞态是指多个执行单位同时并行的被执行,而并发的执行单位对共享资源的访问很容易导致竞态

二:竞态发生的条件:

2.1 对称多处理器 (SMP系统) 的多个CPU
    :多个CPU共用同一条系统总线,因此可以访问共同的外设和存储器,此时多个核的进程、中断可能交叉实现并发竞态度。
2.2 单CPU内进程与抢占它的进程
    :Linux 2.6 之后内核支持内核抢占调度。
2.3 中断与进程之间
    :单核 多核 的进程、中断相互竞争

三:并发控制机制

访问共享资源的代码区叫 临界区,临界区需要被 并发控制机制加以保护,也叫互斥机制,包括:中断屏蔽、原子操作、自旋锁、信号量、互斥体等。也就是在一个执行单元在访问共享资源的时候,其他的执行单元禁止访问该共享资源。

四 中断屏蔽:

4.1 概念:
        使得中断与进程之间的并发不再发生,并且,因为Linux 内核之间进程调度都是依赖中断实现的,因此,也会使得内核中进程之间并发不再发生。
4.2 使用:
        local_irq_disable();
        ...
        临界区
        ...
        local_irq_enable();
4.3 注意:因为在Linux中的异步I/O,进程调度等操作都依赖于中断,中断对内核
          运行十分重要,在屏蔽中断期间,内核中所有的中断都无法得到处理,所以长时间的屏蔽中断时不可取的。

五:原子操作:

5.1 概念:可以使得对一个整型数据的修改是排他性的,原子操作分别针对 位 和
          整型变量,位和整型变量原子操作都以来于底层CPU的原子操作,对于ARM处理器而言,底层使用 LDTEX 和 STREX指令。原子操作就是将LDTEX 和 STREX搭配使用,使得某些变量或者位同一时间只能有一个实体操作,
5.2 LDTEX 和 STREX:
        LDREX Rx ,[Ry] : 读取寄存器Ry指向的4字节内存值,将其保存到Rx寄存器中,同时标记Ry指向的内存为 独占访问,

        STREX Rx ,Ry ,[Rz]: 更新内存数值               

六 自旋锁:

6.1 概念:
          自旋锁操作是基于原子操作的,某个执行单位的代码需要先执行一个原子操作,该操作测试并设置了某个内存变量(锁),然后访问临界区资源,在该执行单位释放释放前面设置的内存变量(锁)之前,其他的执行单位不能访问获得这个内存变量(锁),自然就不能访问临界区资源,其他执行单位会一直测试并设置 该锁 ,直到

可以获取该锁为止。

6.2 使用:
          spinlock_t lock; 定义一个自旋锁
          spin_lock_init(&lock); 初始化自旋锁
          spin_lock(&lock);  获得自旋锁
          ...临界区...
          spin_unlock(&lock); 释放自旋锁
6.3 注意:
    6.3.1 初级的自旋锁可以保证临界区不受其他CPU和本CPU内的抢占进程的打扰,但是无法避免中断和BH的影响,此时需要衍生的自旋锁,大致就是 开启自旋锁+开启中断屏蔽、释放自旋锁+关闭中断屏蔽。。。等等
6.3.2
          cpu在不停的尝试获取自旋锁期间不做其他工作,只是等待获取锁,因此,只有在占用锁时间很短的情况下使用自旋锁才更加合理,当临界区很大时,需要较长时间的占用锁,会降低系统速度。
6.3.3
          当一个执行单位持有锁期间,在释放锁之前,递归再一次的尝试获取锁,将会导致CPU锁死
6.3.4
          在自旋锁锁定期间不可以调用 可能引起进程调度的函数,因为进程调度有阻塞操作,如果获得自旋锁之后再阻塞,可能会导致内核崩溃。如调用 copy_to_user copy_from_user kmalloc msleep 等等。 

七 互斥体:

7.1 概念:
          和信号量类似,信号量的值可以是0、1或者n 如果信号量的值大于0 该进程继续执行,如果信号量的值等于0,那个该进程设置为等待状态,排入信号等到队列,知道系统唤醒
7.2 使用:struct mutex my_mutex;
          定义mutex
          mutex_init(&my_mutex);初始化mutex
          mutex_lock(&my_mutex);获取mutex
          ...临界区...
          mutex_unlock(&my_mutex);释放mutex
7.3 注意:
          互斥体是进程级,用于多个进程之间对资源的互斥,如果竞争失败,会发生上下文的 切换,当前进程进入睡眠状态,CPU运行其他进程,因为进程上下文切换开销很大,所以,只有当进程占用资源较长的时候,用互斥体才是较好的选择。

八 互斥体与自旋锁的区分:

8.1
    当锁锁不能被获取的时候,使用互斥体的开销是进程上下文切换的时间,使用自旋锁的开销时间是等待获取自旋锁(由临界区执行时间决定),如果临界区较小,宜使用自旋锁,反之宜使用互斥体
8.2
    互斥体所保护的临界区可以包含引起阻塞的代码,但是自旋锁必须要绝对避免用来保护包含可能引起阻塞的代码临界区,因为阻塞导致进程的切换,如果进程被切换出去,另一个进程企图获取本自旋锁,会导致死锁。(还没有释放锁,递归获取锁)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Linux老A

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值