嵌入式实践教程--【Linux驱动】Linux驱动开发基于Linux4.0+(二)——并发与同步

本文深入探讨了操作系统内核中的并发问题,包括中断、软中断、内核抢占和多处理器并发的影响。详细解释了原子操作、内存屏障、自旋锁、信号量和Mutex互斥体的概念及其在解决并发问题中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、概念

所谓并发,是指多个内核路径同时访问和操作数据,可能发生 覆盖共享数据的情况,造成被访问数据的不一致。

在内核中发生并发访问并发源主要有以下4种。

  1. 中断和异常
  2. 软中断和tasklet:软中断和tasklet可能随时会被调度运行,从而打断当前正在执行进程的上下文。
  3. 内核抢占:调度器支持内核抢占。
  4. 多处理器并发运行

上述情况需要针对单核和多核系统进行区别对待。

对于单处理器的系统有以下并发源:

1. 中断处理程序可以打断软中断、tasklet和进程上下文的执行

2. 软中断和tasklet并不会并发,但是可以打断进程上下文的执行

3. 在支持抢占的内核中,进程上下文会并发

4. 在不支持抢占的内核中,进程的上下文不会产生并发





对于SMP系统:

1. 同类型的中断并不会并发,但是不同类型的中断源可能会被送到不同的CPU上,因此可能会存在并发

2. 同类型的软中断会在不同的CPU上并发执行

3. 同类型的tasklet是串行执行,不会在多个CPU上并发

4. 不同CPU的进程上下文会并发

记住临界区的保护原则:是保护资源或者数据,而不是保护代码。(静态局部变量,全局变量,共享的数据结构,Buffer缓存,链表,红黑树等)

二、原子操作和内存屏障

1.ARM处理器中如何实现独占访问内存??

处理器中有Local monitorGlobal monitor来实现ldrexstrex指令的独占访问,并且ldrexstrex保证的add操作的原子性。

i++用原子操作还是加锁的方式来保证它的原子性??

采用原子操作,加锁开销太大!

2.内存屏障

程序实际运行时内存访问顺序和程序代码编写的访问顺序不一致,会导致内存乱序访问.因此引入内存屏障以防止内存乱序访问.

  • 数据存储屏障DMB(Data Memory Barrier)
  • 数据同步屏障DSB(Data Sync Barrier)
  • 指令同步屏障ISB(Instruction Sync Barrier)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IOvOiXLP-1635519975557)(https://i.imgur.com/W9oqnhp.jpg)]

内存屏障的使用场景举例:

  1. 在网卡驱动程序中发送数据包,网络数据包写入buffer后交给DMA引擎发送,wmb()保证在DMA传输前,数据被完全写入buffer中.
  2. 在内核里的睡眠和唤醒API也用到了内存屏障,在set_current_state()修改进程状态时插入内存屏障函数smp_wmb().唤醒时会调用wake_up(),在修改task状态之前也会隐式的插入smp_wmb()

3.自旋锁spinlock

1.spinlock的性质:

  1. 忙等待的所机制
  2. 同一时刻只能有一个代码路径获得该锁
  3. 锁持有者必须尽快完成临界区的任务

2.存在的问题:

在很多CPU争用同一个spinlock时,会导致严重的不公平性和性能下降。当该锁释放时,事实上可能刚刚释放该锁的CPU又会马上获得该锁的使用权,没有考虑那些已经在锁外面等待了很久的CPU。因为刚刚释放锁的CPU的L1 cache中存储了该锁,它比其他锁更快的获得自旋锁。



3spinlock锁实现的关键:

关闭内核抢占!!!

如果临界区允许内核抢占,那么如果临界区发生中断,中断返回时回去检查抢占调度。
因此就有两个问题:
①抢占调度相当于使得持有锁的进程睡眠,违背了spinlock不允许睡眠和快速执行的设计初衷;
②抢占调度进程也可能去申请获得spinlock锁,于是死锁就产生了。


4.使用spinlock的重要原则:

拥有spinlock锁的临界区必须是原子执行,不能休眠和主动调度。


5.spin_lockraw_spin_lock的区别

在绝对不允许被抢占和睡眠的临界区,应该使用raw_spin_lock,否则使用spin_lock

4.信号量

信号量的可以同时允许任意数量的锁持有者,sema_init(struct *sem,int count),其中count大于1,可以允许多个持有者,计数信号量;count等于1,只允许一人持有锁,互斥信号量。信号量允许睡眠。可以用于并行处理环境。

5.Mutex互斥体

Linux内核已经有了信号量机制,为何还要单独设置一个Mutex机制呢??

信号量相当于多个厕所;Mutex相当于一个厕所,一次只允许一个人进去。Mutex比信号量执行速度快,可扩展性更好,Mutex数据结构的定义比信号量小。

Mutex实现了自旋等待的机制,更准确的说,他比读写信号量更早的实现了自旋等待机制。在实现自旋等待机制时,内核实现了一套MCS锁机制(一种自旋锁优化方案)来保证只有一个人自旋等待持有者释放锁。

MCS避免多个CPU争用锁导致CPU高速缓存行颠簸现象

1.Mutex锁的实现

Mutex锁的初始化有两种方式:

  1. 静态使用DEFINE_MUTEX
  2. 动态使用mutex_init()函数

小结
Mutex使用场景:

  1. 同一时刻只有一个线程可以持有Mutex
  2. 只有锁持有者可以解锁.
  3. 不允许递归加锁和解锁
  4. 进程持有Mutex不能退出
  5. 必须使用官方API来初始化
  6. 可以睡眠,但是不允许在中断处理程序或中断下半部使用.

在实际工程中,如何使用spinlock和Mutex???

中断上下文,毫不犹豫地使用spinlock,临界区含有睡眠,隐含睡眠的动作及内核API,避免使用spinlock.信号量和Mutex,优先使用Mutex.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

漫游嵌入式

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

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

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

打赏作者

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

抵扣说明:

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

余额充值