第九章 内核同步介绍

临界区和竞争条件

临界区:指的是访问和操作共享数据的代码段。

竞争条件:两个或多个执行线程处于同一个临界区中同时执行,可能会发生各线程互相覆盖共享数据的情况,造成被访问数据处于不一致的状态。

原子操作:为了避免对临界区的并发访问,必须保证临界区代码原子地执行——操作在执行结束前不可被打断,就像整个临界区是一个不可分割地指令一样。

同步:避免并发和防止竞争条件

避免竞争条件:
对临界区操作前,需要加锁,保证操作是原子性的,临界区代码必须完整地执行,决不能被打断。

对简单的单个变量的读写:
内核提供了指令实现原子的读变量,增加变量,写回变量。

加锁

锁提供的一种机制:
在同一时刻只有一个线程持有锁,第二个线程必须等待第一个线程释放锁,才能继续运行。

各种锁机制的区别:
当锁被其他线程持有,因而不可用时的行为表现——有的锁会简单执行忙等待,即不断循环检测锁的状态,等待锁变为可用;有的锁会使当前任务睡眠直到锁可用为止。

本质:
将临界区缩小到加锁和开锁之间。锁是采用原子操作实现的。

应用:(生产者消费者模型)
有一个队列,一个函数将新请求添加到队列尾部,一个函数从队列首部删除请求并处理它。我们需要确保一次只有一个线程对数据结构操作,避免并发访问队列。
此时,可以使用锁对队列进行保护。当有一个新请求加入队列时,先占住锁,然后安全地将请求加入队列,再释放锁;当有线程想要删除请求时,也要先占住锁,然后读取和删除请求,最后释放锁。

造成并发执行的原因

用户空间之所以需要同步,是因为用户程序会被调度程序抢占和重新调度。

当一个进程处于临界区时,可能会被调度程序选择的另一个优先级高的进程抢占,如果新调度的进程也随后进入此临界区,那么前后两个进程就会产生竞争。

内核中可能造成并发执行的原因:

  • 中断:可以在任何异步时刻发生,随时打断当前的进程
  • 软中断和tasklet:内核可以在任何时刻唤醒软中断和tasklet,打断当前的进程
  • 内核抢占:内核中的任务被另一个任务抢占
  • 睡眠及用户空间的同步:在内核执行的进程可能会睡眠,会唤醒调度程序,从而导致调度一个新的用户程序运行
  • 对称多处理:两个或多个处理器同时执行代码

对应的能避免并发访问的安全代码:

  • 中断安全代码:在中断处理程序中能避免并发访问的安全代码
  • SMP安全代码:可在多处理器避免并发访问的安全代码
  • 抢占安全代码:在内核抢占时能避免并发访问的安全代码

需要加锁的数据:大部分内核数据

不需要保护的数据:局部数据

死锁

产生条件:
有一个或多个执行线程和一个或多个资源,每个线程都在等待其中的一个资源,但是所有的资源都被占用了。所有线程都在互相等待,但是永远不会释放已经占有的资源,于是任何线程都无法继续,因而产生了死锁。

常见的死锁例子:

  1. 自死锁:一个线程试图获取自己已经持有的锁,它将一直等待锁被释放,而由于它在等待这个锁,永远不会有机会释放锁,最终结果是死锁。
  2. 两个线程两个锁ABBA锁:线程1获得锁A,,等待锁B;线程2获得锁B,等待锁A。
  3. n个线程n个锁:每个线程都有一把其他线程等待的锁,而没有一个线程会释放已得到的锁。

避免死锁

  • 按顺序加锁:嵌套使用锁时,必须以相同的顺序获得锁。
  • 防止发生饥饿
  • 不要重复请求锁
  • 设计力求简单

争用和可扩展性

锁的争用:当锁被占用时,有其他线程试图获取该锁。

高度争用状态:有多个其他线程等待获得该锁。

加锁粒度用来形容加锁保护的数据规模。一个过粗的锁保护大块数据,一个过于精细的锁保护很小的一块数据。

提高扩展性,可以提高Linux在大型的、处理能力更强的系统上的性能;但是一味提高可扩展性,会导致Linux在小型机器上性能降低。

当锁争用严重时,加锁太粗会降低可扩展性;当锁争用不严重时,加锁太细会增大系统开销,带来浪费,这两种情况都会是系统性能下降。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值