tag 标签: Linux内核详解

相关博文
  • 热度 3
    2023-5-18 21:51
    1923 次阅读|
    0 个评论
    【Linux内核锁】一、内核锁的由来 在 Linux 设备驱动中,我们必须要解决的一个问题是: 多个进程对共享资源的并发访问,并发的访问会导致竞态。 1、并发和竞态 并发 (Concurrency) :指的是多个执行单元同时、并行的被执行。 竞态 (RaceConditions) :并发执行的单元对共享资源的访问,容易导致竞态。 共享资源:硬件资源和软件上的全局变量、静态变量等。 解决竞态的途径是:保证对共享资源的互斥访问。 互斥访问:一个执行单元在访问共享资源的时候,其他执行单元被禁止访问。 临界区 (Critical Sections) :访问共享资源的代码区域成为临界区。临界区需要以某种互斥机制加以保护。 常见的互斥机制包括 :中断屏蔽,原子操作,自旋锁,信号量,互斥体等。 2、竞态发生的场合 多对称处理器(SMP)的多个CPU之间 多个CPU使用共同的系统总线,可以访问共同的外设和存储器。在 SMP 的情况下,多核 (CPU0、CPU1) 的竞态可能发生于: CPU0 的进程和 CPU1 的进程之间 CPU0 的进程和 CPU1 的中断之间 CPU0 的中断和 CPU1 的中断之间 单CPU内,该进程与抢占它的进程之间 在单CPU内,多个进程并发执行,当一个进程执行的时间片耗尽,也有可能被另一个高优先级进程打断,会发生竞态。 中断(软中断、硬中断、Tasklet、底半部)与进程之间 当一个进程正在执行,一个外部/内部中断(软中断、硬中断、Tasklet等)将其打断,会导致竞态发生。 3、编译乱序和执行乱序 除了并发访问导致的竞态外,还需要了解编译器和处理器的一些特点所引发的一些问题。 3.1 编译乱序 现代的高性能编译器 在目标代码优化上都具有 乱序优化 的能力,编译器为了尽量 提高Cache命中率以及CPU的Load/Store单元的工作效率 ,可以对访存的指令进行乱序,减少逻辑上不必要的访存。 因此,在打开编译器优化后,生成的汇编码并没有严格按照代码的逻辑顺序执行,这是正常的。 为了解决编译乱序的问题,可以加入 barrier() 编译屏障 , 该屏障可以阻挡编译器的优化。设置屏障的前后,可以保证执行的语句不乱。 加入 barrier() 编译屏障,即可保证正确的执行顺序。 例子: #define barrier() __asm__ __volatile__("": : :"memory") ​ int main ( int argc , char * argv , e ; e = d ; barrier (); b = a ; c = a ; return 0 ; } 3.2 执行乱序 编译乱序是编译器的行为,而执行乱序就是处理器运行时的行为。 高级的 CPU 往往会根据自身的缓存特性,将访存指令重新排序执行! 这样就导致了多个顺序的指令,后发的指令仍有可能先执行完毕。 这种执行乱序,在多个 CPU 之间,以及单个 CPU 内部,都是非常常见的。 3.2.1 多CPU之间 处理器为了解决多核之间,一个 CPU 的行为对另一个 CPU 可见的情况, ARM 处理器引入了内存屏障指令: DMB(数据内存屏障),保证在该指令前的所有指令,内存访问完成,再去访问该指令之后的访存动作 DSB(数据同步屏障),保证在该指令前的所有访存指令执行完毕(访存,缓存,跳转预测,TLB维护等)完成 ISB(指令同步屏障), Flush 流水线,保证所有 在ISB之后执行 的指令都是从缓存或者内存中获得。 3.2.2 单CPU内部 在单 CPU 中,我们常遇到访问外设寄存器时,某些外设寄存器就对读写顺序有很高的要求,为了避免执行乱序的发生,这时候就需要 CPU 的一些内存屏障指令了。 CPU 内部,为了解决这种问题, CPU 提供了一些内存屏障指令: 可以参考 Documentation/memory-devices.txt 和 Documentation/io_ordering.txt 读写屏障: mb() 读屏障: rmb() 写屏障: wmb() 寄存器读屏障 __iormb()__ 寄存器写屏障 __iowmb()__ #define writeb_relaxed(v,c) __raw_writeb(v,c) #define writew_relaxed(v,c) __raw_writew((__force u16) cpu_to_le16(v),c) #define writel_relaxed(v,c) __raw_writel((__force u32) cpu_to_le32(v),c) ​ #define readb(c) ({ u8 __v = readb_relaxed(c); __iormb(); __v; }) #define readw(c) ({ u16 __v = readw_relaxed(c); __iormb(); __v; }) #define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; }) ​ #define writeb(v,c) ({ __iowmb(); writeb_relaxed(v,c); }) #define writew(v,c) ({ __iowmb(); writew_relaxed(v,c); }) #define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); }) writel 与 writel_relaxed 的区别就在于有无屏障。 4、总结 由上文可知,为了解决 并发导致的竞态问题 高性能的编译器编译乱序问题 高性能的 CPU 带来的执行乱序问题 CPU 和 ARM 处理器提供的内存屏障指令等,这也是内核锁存在的意义。