Linux内核学习之内核同步技术

注:本文含个人的理解和认知

1. 造成内核资源竞争的原因

其实根本原因是,内核允许交叉执行的内核控制路径(细节可以参考我之前Linux内核学习之内核控制路径文章),场景可以归纳成4种,都有可能对内核数据结构存在竞争。

  1. 系统调用
  2. 异常
  3. 中断
  4. 抢占

2. 不必要的同步

有一些场景,考虑到架构的特点,是没有必要做同步的。

  1. 中断不会被同优先级(或一下)的中断打断,因此在分析中断的同步场景时,没有必要考虑会被同优先级甚至低优先级的中断打断。
  2. 内核在设计阶段,就充分考虑了异常,保证除了少数几个被用作特殊功能的异常(如,缺页),在内核态是不会产生异常的,因此,没必要考虑这种场景的同步。
  3. 同一个tasklet不可能同时在几个CPU上执行。

3. 同步技术

  1. 原子操作
  2. 内核信号量
  3. 内核自旋锁
  4. 关中断
  5. 关抢占
  6. 大内核锁

3.1 原子操作

  • 这是最小的,也是代价最小的同步机制,它利用CPU指令集中部分指令的原子性,即,有些指令,它在执行完成前是不会被中断打断的,也就是说,在该CPU上(注意,这里强调是单核CPU),执行该指令时,是不会发生内核控制路径切换的,因为内核控制路径的切换需要中断的加持(不考虑主动切换,因为主动切换前,肯定自行保证了数据的一致性),进而保证了数据操作的一致性。这类指令包含一些读、改、写一体的指令。注意不是所有指令都支持原子操作,如有些微控器芯片,为了加快中断响应的及时性,有些指令是可以被打断的。
  • 现在说说原子操作的局限性,第一,因为很小,因此能做的事情也很有限,比如修改数据结构涉及多个字段,无法通过一条指令实现。第二,在多核系统上,原子操作仍无法保证数据的一致性,虽然总线一次只能有一个cpu访问,但是,CPU1上的指令执行完读时,总线可能先响应了CPU2的读,然后CPU1将结果通过总线写回内存,最后CPU2将结果写回内存,假设CPU1和CPU2执行的都是对变量a的加1操作,a最初值为0,那么经过这一番操作,a的值等于1,这显然不符合一致性要求,正确结果应为2。
内存 CPU1 CPU2 a=0 a=0 a=0+1=1 a=0+1=1 内存 CPU1 CPU2
  • 为了解决上面问题,在使用原子操作时,往往会结合总线锁使用,lock指令能够保证,在接下来的指令执行完成前,总线都被当前CPU独占。
内存 CPU1 CPU2 锁总线,a=0 总线被锁,阻塞 a=0+1=1,释放总线 锁总线,a=1 a=1+1=2,释放总线 内存 CPU1 CPU2

3.2 内核信号量

  • 与用户级信号量(IPC信号量)相似,内核信号量也会使进程挂起,部分会阻塞的系统调用就是通过内核信号量实现的,与用户级型号不同的地方在,内核信号量运行在内核态,用户级信号量则运行在用户态。
  • 内核信号量有一个子类是读写信号量,与用户态读写锁的作用类似。

PS:这里说一下读写锁一个有意思的地方,读锁可以重复获取,即,读操作允许多次进入临界区,但是写锁只有一个人获取,且必须等待所有读锁释放(即读锁的引用计数归零),才能进入临界区,为了避免读操作频繁,导致写操作永远无法进行(写饥渴),当有进程在等待写锁时,则禁止后续读锁的获取(不影响已经获取的读锁),但是频繁的写操作仍然会导致读饥渴(这种情况就不应该使用读写锁,本末倒置的用法错误)。

临界区 程序1 程序2 程序3 获取读锁 OK 获取读锁 OK 获取写锁 阻塞 释放读锁 获取读锁 阻塞(因为有写锁在阻塞) 释放读锁 获取写锁 OK 临界区 程序1 程序2 程序3

3.3 内核自旋锁

  • 与用户级自旋锁作用一样,不同的是这个运行在内核态。
  • 自旋锁也分几个子类,普通自旋锁,读写自旋锁,顺序自旋锁,前面两种比较熟悉,重点说下顺序锁,它与读写锁非常相似,不过赋予了写更高的权限,就算当前有读正在执行,仍然可以执行写操作,写只与写互斥。这个锁的缺点是,读操作需要重复操作几次以确认读到了有效的数据。
  • 顺序锁有个计数器,每次读操作前后两次检查该计数器,如果不相同,则说明数据有更新,需要重新读;计数器起始值为0,每次写操作,获取锁,会对计数器进行原子加1,释放锁,会再次原子加1,因此每次获取写锁,计数器是计数,以计数器奇数值表锁定状态,偶数值表释放状态。

3.4 关中断

  • 关中断,这种同步方式,只针对单核CPU有效,除非关全局中断(即关闭中断仲裁器),因为关一个CPU的中断,并不能影响其他CPU访问内核数据。当然对于每CPU变量(只有这个CPU自己会修改的全局变量),可以采用这种关本地中断的方式来实现保护。x86架构CPU的中断由名为eflags的cpu寄存器控制,关闭 eflags 的 IF 标志位,即可关本地中断

3.5 关抢占

  • 这里指关内核抢占,这中方法只能作用于两个处在内核控制路径的进程,对内核资源造成竞争的场景,这是代价较小的同步方法。

3.6 大内核锁

这个就大了,它保证一次只有一个进程进入内核态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值