Linux内核同步机制

基本概念

临界区(critical section):对某个共享的数据结构(共享资源) 进行操作的程序片段

临界资源(critical resource):系统中某些资源一次只允许一 个进程使用,称这样的资源为临界资源或互斥资源或共享 变量

竞争条件(race condition):两个执行线程同时处于同一个临界区中。

同步(synchronization):避免并发和防止竞争条件被称为同步。

同步机制

per-cpu

per-CPU variable

an array of data structures, one element per each CPU in the system
per-CPU就是每个CPU中的一个数组

a CPU should not access the elements of the array corresponding to the other CPUs; on the other hand, it can freely read and modify its own element
per-CPU数组内的才能访问特定CPU

the per-CPU variables can be used only when it makes sense to logically split the data across the CPUs of the system
当按照CPU分配数据的时候,per-CPU才会有意义

per-CPU variables provide protection against concurrent accesses from several CPUs, they do not provide protection against accesses from asynchronous functions (interrupt handlers and deferrable functions). In these cases, additional synchronization primitives are required.
保证一定程度并发

per-CPU variables are prone to race conditions caused by kernel preemption, both in uniprocessor and multiprocessor systems. As a general rule, a kernel control path should access a per-CPU variable with kernel preemption disabled.

a kernel control path gets the address of its local copy of a per-CPU variable, and then it is preempted and moved to another CPU: the address still refers to the element of the previous CPU

同CPU访问者,非内核抢占。可以将访问者移到其他CPU

原子操作(Atomic operations)

自旋锁(Spin locks )

Linux内核中最常见的锁:自旋锁(spin lock)

如果一个线程试图获得一个被争用的自旋锁,那 么该线程就会一直进行忙循环—旋转—等待锁重新可用
锁未被争用,请求锁的执行线程便立即得到它, 继续执行

普通的锁忙,会自己等会再去访问。而自旋锁会一直在原地等着
适合于加锁时间短的情况。

在中断处理程序中使用自旋锁(不能使用信号量, 因为信号量会导致睡眠)
自旋锁针对 SMP以及 本地内核抢占

顺序锁(seqlock)

写者优先:读时,写者可以进入;

其它与读写锁一样,写不阻塞读;读读ok, 写写no

优点:写者不必等待,读也不会被写阻塞

缺点:读者需要检查读到数据的有效性

使用场景:

  1. 数据存在很多读者,数据写者很少
  2. 希望写优先于读,而且不允许写者饥饿
  3. 数据很简单

读-拷贝-更新(RCU)

允许多个读者和写者并发

不使用锁

性能好,但是对内存有一定开销,用在网 络层和虚拟文件系统中

适用于读多写少情况,如果写过多,写操作之间的同步开销会很大

就是写操作时,把要写的部分复制出来修改,然后找个合适的时间放回去

信号量(semaphore)

Linux提供两种信号量:

  1. 内核信号量:由内核控制路径使用
  2. System Ⅴ IPC信号量:由用户态进程使用

Linux中信号量是一种睡眠锁

使用信号量时锁的时间长一点是可以忍受的

持有信号量的代码可以被抢占

可以同时允许任意数量的锁持有者

信号量支持两个原子操作P() 和 V()

乱入的总结

如果代码需要睡眠,使用信号量是唯一的选择。由于不 受睡眠的限制,使用信号量通常来说更加简单一些。

如果需要在自旋锁和信号量中作选择,应该取决于锁被 持有的时间长短。

需求 建议的加锁方法
低开销加锁 优先使用自旋锁
短期锁定 优先使用自旋锁
长期加锁 优先使用信号量
中断上下文中加锁 使用自旋锁
持有锁是需要睡眠 使用信号量

屏障( Memory Barriers )

所谓屏障,从处理器角度来说,是用来串行化读写操作的,从软 件角度来讲,就是用来解决顺序一致性问题

为什么使用屏障

  • 当处理多处理器之间或硬件设备之间的同步问题时,有时需要在程序代码中以指定的顺序发出读内存和写内存指令
  • 在和硬件交互时,时常需要确保一个给定的读操作发生在其它读或写操作之前

Linux提供了确保顺序的指令称做屏障

猜你喜欢

转载自blog.csdn.net/jh_zhai/article/details/80234933