linux kernel同步技术

  • Post author:
  • Post category:linux




内核同步



为什么需要内核同步

内核在随时响应各种中断、异常、内核抢占、进程睡眠等事件,所以内核并不是严格的按照顺序执行的,而是采用交错的方式。交错执行意味着对资源有竞争,譬如进程A正在使用资源1的时候,因为发生某些事件导致程序切换到进程B执行,进行B也需要使用资源1,这时候进程A、B就同时竞争资源1。若没有同步机制,进程B等待进程A对资源1的释放,那么进程B将会改写资源1的数据,再切回进程A时,资源1就不是进程A想要的值了。这里同时体现了互斥性,也就是说进程A在访问资源1还没有释放的时候,进程B不能访问,只能等待起,当进程A访问完成之后,释放资源,同时告诉了进程B可以访问资源1了,同步了可以访问资源1的信息。



内核中使用的各种同步技术



每CPU变量(per-cpu var)

在CPU之间复制数据,每个CPU都维护一份属于自己的数据。



原子操作

读-修改-写 linux使用atomic_t来修饰原子操作



内存屏障

避免指令重新排布,保证了语句的串行性,优化屏障使用barrier()宏定义。



自旋锁

加锁时忙等;很多内核资源只锁比较短的时间,这样自旋锁的效率就会变得更高些;因为睡眠引起的进程切换效率更低。释放CPU之后,不知道多久才能再次获得CPU。自旋锁所保护的临界区(得到锁之后和释放锁之前的代码)是禁止内核抢占的,因为假如得到自旋锁的临界区被抢占调度走,切换到其他进程也需要这个锁时CPU就会忙等,然后又等不到锁,就会导致死锁;所以自旋锁在使用时候,要注意在临界区不能发生进程调度,典型的应用例子就是不能调用sleep睡眠函数。



读写自旋锁

读写自旋锁可以将读写获取锁的动作分开,一个CPU得到读锁时,另一个CPU也要读时,是被允许的,但是不允许写,想得到写锁是得不到的。这样能提高并发读的能力。读写锁是不是只对多核CPU有效?因为单核CPU时,读写锁保护的临界区是禁止内核抢占的,这样其他进程也得不到调度,那自然是不会再去获取读锁的吧?《深入理解Linux内核》给了答案:在单处理器系统上,这种锁本身起不到锁的作用,自旋锁原语仅仅是开启或者禁止内核抢占。不仅读写锁,所有自旋锁都是这样。



顺序锁

基于访问计数器的锁,同样是由自旋锁演变而来;为了提高写的并发能力而演变出来的锁,但是读的效率会变低,因为读的时候可以进行写,那么为了读到正确的值,需要进行多次读,读到的值是一样的才算读到正确的值;具体的实现,引用了访问的计数器变量,写的时候会修改这个计数器,当前后读到这个计数器的不一致时,就知道这个值被修改过了,需要继续读,才能读到正确的数据。



读-拷贝-更新(RCU)

通过指针而不是锁来访问共享数据结构;允许多进程同时读,读的时候也允许写,写时先拷贝一份,然后进行修改,修改完之后,确认没有进程在读时,通过修改指针使得刚刚修改的数据生效。RCU保护的临界区不能发生睡眠。



信号量

得不到锁时睡眠等待,进程被切走。



本地中断的禁止

主要针对单核CPU系统,单核CPU任务调度主要依赖于中断触发,当中断被禁止之后,那么此时系统就是串行运行的,自然就具有保护临界区的属性。



应用场景

自旋锁适用于保护比较短的临界区,假如保护的临界区比较长的情况下,产生竞争时,拿不到锁的CPU会自旋忙等的时间比较长,导致资源的浪费,降低性能。

信号量不能在中断服务函数中使用,因为信号量获取不到会引起睡眠,所以一般中断服务函数中都会使用自旋锁,或者不阻塞的信号量(得不到信号量不睡眠而是直接返回)

为什么中断上下文不能睡眠?

睡眠是进程来的一种状态,进程中使用task_struct结构体来记录这个状态,因为中断上下文不是一个进程,不存在task_struct,所以它是不可调度的。所以,在中断上下文就不能睡眠

参考书籍《深入理解linux内核》



版权声明:本文为qq_36413391原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。