深入理解Linux内核-内核同步

内核抢占(kernel preemption)(Linux 2.6)

抢占内核的主要特点:一个在内核态运行的进程,可能在执行内核函数期间被另外一个进程取代.
抢占条件:
1.只有当内核正在执行异常处理程序(尤其是系统调用),而且内核抢占没有被显式的禁用时,才可能抢占内核
2.本地CPU必须打开本地中断,否则无法完成内核抢占.
 
临界区是一段代码,在其他的内核控制路径能够进入临界区前,进入临界区的内核控制路径必须全部执行完这段代码
 
内核同步技术:
1,每CPU变量
把内核变量声明为每CPU变量(per-cpu variable).每CPU变量主要是数据结构的数组,系统的每个CPU对应数组的一个元素.
一个CPU不应访问其他CPU对应的数组元素,另外它可以随意读或修改它自己的元素而不用担心出现竞争条件.这意味着,每CPU变量只在当它确定在系统的CPU上的数据在逻辑上是独立的时候才能使用.
2,原子操作(atomic operations)
若干汇编语言指令具有"读-修改-写"类型,也就是说,它们访问存储器两次,第一次读原值,第二次写新值.避免由于"读-修改-写"指令引起的竞争条件的最简单办法,就是确保这样的操作在芯片级是原子的.任何一个这样的操作都必须以单个指令执行,中间不能间断,且避免其他的CPU访问同一存储器单元.
Linux内核提供了atomic_t类型(一个原子访问计数器).
typedef  struct {      
int counter; 
 } atomic_t; 
3,优化和内存屏障
事实上,所有的同步原语起优化和内存屏障的作用.
优化屏障(optimization barrier)原语保证编译程序不会混淆放在原语操作之前的汇编语言指令和放在原语操作之后的汇编语言指令.在Linux中,优化屏障就是barrier()宏,它展开为 asm volatile("":::"memory").优化屏障并不保证不使当前CPU把汇编语言指令混在一起执行---这是内存屏障的工作.
内存屏障(memory barrier)原语确保,在原语之后的操作开始执行之前,原语之前的操作已经完成.因此,内存屏障类似于防火墙,让任何汇编语言指令都不能通过.
在多处理器系统中,所有原子操作都起内存屏障的作用,因为它们都使用了lock字节.
4, 自旋锁(spin lock):用于多处理器环境中的一种特殊的锁,如果内核控制路径发现自旋锁开着,就获取锁并继续自己的执行.相反,如果内核控制路径发现锁由运行在另一个CPU上的内核控制路径锁着,就在周围旋转,反复执行一条紧凑的循环指令,直到锁被释放.
读/写自旋锁:
顺序锁(seqlock):与读/写自旋锁类似,只是为写者赋予较高优先级,即使读者正在读的时候也允许写者继续运行,优点写者永远不会等待(除非另一个写者正在写),缺点是有时候读者不得不反复读相同的数据直到它获得有效的副本.
5, 读-拷贝-更新(RCU)
RCU是为了保护在多数情况下被多个CPU读的数据结构而设计的同步技术。RCU允许多个读者和写者并发执行(相对于只允许一个写者执行的顺序锁有了改进)。而且,RCU是不使用锁的。
关键思想是限制RCP的范围,如下所述:
(1)RCU只保护被动态分配并通过指针引用的数据结构。
(2)在被RCU保护的临界区种,任何内核控制路径都不能睡眠。
RCU是Linux 2.6种新增的功能,用在网络层和虚拟文件系统中。
6, 信号量
Linux提供两种信号量:
(1) 内核信号量,由内核控制路径使用
(2) System V IPC 信号量,由用户态进程使用。
读/写信号量:
7, 禁止本地中断
确保一组内核语句被当做一个临界区处理的主要机制之一就是中断禁止,即使当硬件设备产生了一个IRQ信号时,中断禁止也让内核控制路径继续执行,这就提供了一种有效的方式,确保中断处理程序访问的数据结构也受到保护。然而,禁止本地中断并不保护运行在另一个CPU上的中断处理程序对数据结构的并发访问, 因此,在多处理器系统中,禁止本地中断通常与自选锁结合使用。
 8, 禁止和激活可延迟函数
 
KEY:不同类型的竞争条件采用不同的同步方法......
 
避免竞争条件的实例:
1, 引用计数器(reference counter)
广泛用于内核中以避免由于资源的并发分配和释放而产生的竞争条件。
 
 
 
 
 
 
 
 
 
 
发布了11 篇原创文章 · 获赞 2 · 访问量 672

猜你喜欢

转载自blog.csdn.net/liheng301/article/details/42786993