欢迎光临散文网 会员登陆 & 注册

带你玩转Linux内核并发与同步- -原子操作和内存屏障(一篇搞定)

2022-03-19 20:55 作者:补给站Linux内核  | 我要投稿

并发访问:多个内核路径同时访问和操作数据,就有可能发生相互覆盖共享数据的情况,造成被访问数据的不一致。

  1. 临界区:访问和操作共享数据的代码段。

  2. 并发源:访问临界区的执行线程或代码路径。

在内核中产生并发访问的主要有如下4种:

  1. 中断和异常:中断发生后,中断处理程序和被中断的进程之间有可能产生并发访问。中断<==>被中断的线程

  2. 软中断和tasklet:软中断或者tasklet随时可能会被调度执行,从而打断当前正在执行的进程上下文。软中断<==>进程上下文

  3. 内核抢占:调度器支持可抢占特性,会导致进程和进程之间的并发访问。进程<==>进程

  4. 多处理器并发执行:多处理器上可以同时运行多个进程。A处理器进程<==>B处理器进程

对于单处理器系统,主要有一下并发源:(硬中断 > 软中断和tasklet > 进程上下文)

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

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

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

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

对于SMP系统,有如下并发情况:

  1. 同一类型的中断处理程序不会并发,但是不同类型的中断有可能送达到不同的CPU上,因此不同类型的中断处理程序可能会存在并发执行。

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

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

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

并发保护的是资源或者数据,而不是保护代码;包括静态局部变量、全局变量、共享的数据结构、缓存、链表、红黑树等各种形式的资源数据。

  1. 原子操作

  2. 一个原子操作例子思考

  3. 原子操作API atomic_t数据结构表示原子变量,它的实现依赖于不同的体系结构。

【文章福利】小编推荐自己的Linux内核技术交流群:【891587639】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!前100名进群领取,额外赠送一份价值699的内核资料包(含视频教程、电子书、实战项目及代码) 

Linux提供了很多操作原子变量的API。以arch/arm/include/asm/atomic.h为例。

  • 上面虽然有多种API但是基础的只有atomic_add()/atomic_sub()/atomic_add_return()/atomic_sub_return()四种。

  • 他们通过ATOMIC_OPS定义,产生atomic_add()/atomic_sub()/atomic_add_return()/atomic_sub_return()四个函数。

  • ARM使用ldrex和strex指令来保证add操作的原子性,指令后缀ex表示exclusive。

  • ldrex Rt, [Rn] - 把Rn寄存器只想内存地址的内容加载到Rt寄存器中。

  • strex Rd, Rt, [Rn] - 把Rt寄存器的值保存到Rn寄存器指向的内存地址中,Rd保存更新的结果,0表示更新成功,1表示失败。

GCC嵌入汇编的格式如下:

ARM32如何保证原子性

内存屏障

内存屏障3条指令DMB/DSB/ISB

  • ARM体系架构中常见的3条内存屏障指令:

  • 数据内存屏障DMB:Data Memory Barrier,它可确保会先检测到程序中位于DMB指令前的所有显示内存访问指令,然后再检测到程序中位于DMB指令后的显式内存访问指令。它不影响其它指令在处理器上的执行顺序。 数据同步屏障DSB:Data Synchronization Barrier,是一种特殊的内存屏障,只有当此指令执行完毕后,才会执行程序中位于此指令后的指令。

  • 当满足以下条件时,此指令才会完成:

  • 位于此指令前的所有显示内存访问均完成。

  • 位于此指令前的所有缓存、跳转预测和TLB维护操作全部完成。

  • 指令同步屏障ISB:Instruction Synchronization Barrier,可刷新处理器中的管道,因此可确保在ISB指令完成后,才从高速缓存或内存中提取位于该指令后的其他所有指令。

  • 这可确保提取时间晚于ISB指令的指令,能够检测到ISB指令执行前就已经执行的上下文更改操作的执行效果。

内存屏障API

内存屏障例子

一个网卡驱动中发送数据包

  • 网络数据包写入buffer后交给DMA负责发送,wmb()保证在DMA传输之前,数据被完全写入到buffer中。

睡眠唤醒API中的内存屏障

  • 通常一个进程因为等待某系时间需要睡眠,调用wait_event()。

  • 在wait_event()中有prepare_to_wait_event()调用了set_current_state()。

唤醒者通常调用__set_task_cpu()来设置当前进程对应的调度实体




带你玩转Linux内核并发与同步- -原子操作和内存屏障(一篇搞定)的评论 (共 条)

分享到微博请遵守国家法律