LKD 笔记:内核同步

介绍

在一个共享内存的应用程序中,开发者必须确保共享的资源不会被并发地访问。内核也不例外。共享的资源需要避免并发地访问是因为当有多个执行线程同时访问和修改数据时,一个线程可能会覆盖其他线程的修改或者造成访问的数据处于一个非一致的状态。

在 Linux 内核支持对称多处理器(SMP)之前,防止数据被并发地访问很简单。因为仅仅支持一个处理器,唯一能造成数据能够被并发地访问方式是中断发生或者内核代码显示地重新调度让另一个任务运行。

内核从 2.0 版本开始支持多处理器。这暗示着内核代码能够同时在多个处理器上运行。如果不对共享的数据保护,多个处理器上执行的内核代码能够同时(并行地)访问共享的数据。

内核从 2.6 版本开始支持可抢占。这暗示着(缺少保护的情况下)调度器可以在一个虚拟的任意时间点抢占内核代码然后重新调度另一个任务。

临界区和竞争条件

访问和操作共享数据的代码路径称作临界区。为了防止在临界区中的并发访问,程序员必须确保临界区中的代码原子地执行——代码执行过程中不会被中断就好像整个临界区的代码就是一条指令。如果多个线程能够同时在相同的临界区执行,那么这就是一个 bug。当这种情况发生时,我们说出现了一个竞争条件,因为线程要为先执行而竞争。确保不安全的并发访问被阻止并且竞争条件不会发生就称作同步

为什么需要保护?

参考之前一篇文章 互斥锁与条件变量

造成并发访问的原因

在用户空间中,需要同步是由于调度器的存在。因为一个进程能够在任意的时间点被调度器抢占,然后另一个进程被调度在处理器上执行,那么一个进程可能在访问临界区的过程中被抢占。如果新调度的进程也访问相同的临界区,那么竞争就会发生。相同的问题也会出现在多个共享文件的单线程进程中或者在一个处理信号的程序中,因为信号可以异步地出现。这种类型的并发——两种事情实际上不是同时发生而是交替地出现——称作伪并发。[书中称作 pseudo-concurrency,直译过来是伪并发,但个人认为还是称作伪并行恰当些]

如果你有一台多处理器机器,那么两个进程能够同时在临界区中执行。这称作真并发。[同理,个人认为称作(真)并行恰当些。后面还是尽量根据原文称作并发,但读者需要根据上下文判断是并发还是并行] 尽管造成真并发和伪并发的原因不同,但它们都会造成竞争条件,因此需要保护。

内核有相似的造成并发访问的原因:

  • 中断——一个中断能够在任意时间点异步地产生,中断当前执行的代码。

  • 软中断和 tasklets——内核能够在任意的时间点抛出或者调度一个软中断,中断当前执行的代码。

  • 内核抢占——因为内核是抢占的,一个在内核中的任务能够抢占其他的任务。

  • 睡眠和与用户空间同步——一个在内核中的任务能够睡眠因此会触发调度器,调度新的进程运行。

  • 对称多处理器——两个或多个处理器能够同时(并行地)执行内核代码。

内核开发者需要理解造成并发访问的这些原因,为应对它们做好准备。

要知道什么需要保护

简单一句话,需要保护数据而不是代码

当你写内核代码的时候,你应该问自己这些问题:

  • 数据是全局的吗?有其他的线程能够访问它吗?
  • 数据是在进程上下文和中断上下文共享的吗?是在两个不同的中断处理函数中共享的吗?
  • 如果一个进程在访问这个数据的过程中被抢占了,新调度的进程能够访问相同的数据吗?
  • 当前的进程能够睡眠吗?如果可以,共享的数据在睡眠时处于什么状态?
  • 数据会不会在内核中另一个执行路径中被释放?
  • 如果另一个处理器又调用这个函数会发生什么?

简而言之,在内核中几乎全部的全局和共享的数据都需要使用某种形式的同步方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值