操作系统哲学原理(21)多核原理-多核环境下进程同步与调度

本文探讨了多核环境下操作系统面临的修正,包括原语、调度和能耗管理的调整。重点在于进程同步与调度,介绍了硬件原子操作如总线锁和交换指令,以及软件同步原语如旋锁及其优缺点。队列旋锁作为解决竞争的策略,提供了更好的扩展性。此外,还讨论了多核环境下的进程调度策略、负载平衡和能耗管理技术,强调了在多核系统中性能和节能的平衡问题。
摘要由CSDN通过智能技术生成

说明:该系类文章更多的是从从哲学视角看 操作系统 这门学科。同时也是 操作系统的学习笔记总结。因为博主 这些年主要是以研究安卓系统和 嵌入式Linux为主,因此这个系类文章也是这两个领域不可或缺的基石之一,尤其是对操作系统感兴趣的伙伴可特别关注。


21 多核环境下进程同步与调度

21.1 多核环境下操作系统的修正

在多核环境下,原来基于单核的合理设计和实践无法适应多核环境;主要包括原语的修正、调度修正、能耗管理等。

21.2 多核环境下进程同步与调度

多核环境带来的最大变化是进程的同步与调度。由于进程运行在不同的CPU或执行核上,其同步就不仅仅是线程的同步,而有可能是执行核或CPU之间的同步。这时进程的调度也将涉及到将何进程分配到何CPU或执行核上。由于不同的核在内存的共享方式上有可能不同,其运行有数据共享的进程和没有数据共享的进程的效率就会有很大不同。这就需要调度策略的合理选择与执行来保证系统的整体运行效率。

21.3 多核进程同步

在单核情况下,一次只能有一个程序运行;但在多核环境下,多个程序可以真正地同时执行。因此,多核环境下的进程同步与单核环境有很大的不同。而这里面最需要的就是原子操作;这类似于锁的实现,需要硬件的支持,而多处理器原语原子操作也需要硬件的支持。

21.4 硬件原子操作

在单核环境下,硬件提供的原子操作有:中断的启用与禁止、加载存入指令、测试与设置。而这三种操作除中断启用与禁止不工作外,其他两种在多核环境下均可使用。

对于加载存入原子操作来说,下面的操作均是原子操作:

  • 读写一个字节。
  • 读写一个按16位对齐的16位的字。
  • 读写一个按32位对齐的32位的双字。

而测试与设置则需要针对共享内存单元进行。

21.5 总线锁

在多核环境下,一种硬件原子操作称为总线锁。总线锁就是将总线锁住,只有持有该锁的CPU才能使用总线。这样,由于所有CPU均需要使用共享总线来访问共享内存,而总线的锁住将使得其他CPU没有办法执行任何与共享内存有关的指令,从而保护数据的访问是排他的。除此之外,硬件提供的另一种同步原语是交换指令:以原子操作完成在寄存器和内存单元之间的内容交换。

21.6 多核环境下的软件同步

在硬件提供的同步原语基础上,我们可以构建软件同步原语。由于多核技术相对比较新,如何实现多CPU同步尚没有统一标准,这样造成不同的操作系统实现的软件同步原语不尽相同。Windows和Linux内核里提供的一些原子的操作。

Linux内核提供的原子操作包括如下几种:

  • 总线锁:置换,比较与置换,原子递增操作。
  • 原子算术操作:原子读、设置、加、减、递增、递减、递减与测试。
  • 原子位操作:位设置、位清除、位测试与设置、位测试与清除、位测试与改变。

Windows内核提供的原子操作包括如下几种:

  • 互锁操作(Inter_Locked_Operation)。
  • 执行体互锁操作(Executive_Inter_Locked_Operation)。

注意:目前操作系统还没有为多核环境提供锁操作,因为这种操作代价比较大。

21.7 旋锁

旋锁(spin_lock)是几乎所有多核操作系统均会提供的一种CPU互斥机制,是操作系统内核用于多处理器互斥的机制。(用户程序不能使用)

旋锁通常用于保护某个全局的数据结构,这里的互斥指的是多个处理器或执行核之间的互斥,即两个处理器或核不能在物理上同时访问同一个数据结构。对于局部数据结构来说,则因为只在一个CPU下而不需要使用旋锁。

旋锁通过获取和释放两个操作来保证任何时候只有一个拥有者。旋锁的状态有两种:要么是闲置的,要么被某个CPU所拥有。因此,如果一个CPU获得一个旋锁,那么运行在该CPU上的所有的线程都可以访问该旋锁所保护的寄存器和数据结构。

使用旋锁的过程如下:

  1. 等待旋锁变为闲置;
  2. 获得旋锁;
  3. 访问寄存器和全局数据结构;
  4. 释放旋锁;

Windows下使用旋锁保护对DPC队列的访问过程,如图所示。

两个处理器A和B均需要访问全局的DPC队列(延迟过程调用)。上要用于在中断时将那些不需要高优先级执行的代码放进一个队列,等有空时再执行的机制。因此我们用旋锁来进行处理器间的互斥。对于处理器A/B来说,如果要访问全局数据,就要先获得Spin_lock,直到成功,然后才访问。

21.7.1 旋锁的实现

旋锁的实现也必须在硬件提供的原子操作上进行。多处理器环境下的硬件原子操作有加载与存入、测试与设置。这两种方法皆可以用来实现旋锁。只是使用测试与设置更为简单。在使用测试与设置来实现旋锁时,旋锁是一个特定的内存单元。这个特定的内存单元必须位于整个系统的共享内存里面。这是旋锁的物理载体。如果一个处理器要使用旋锁,就必须检查这个特定内存单元的值。

  • 如果为0,则将其设置为1,表示获得该旋锁。
  • 如果为1,则表示该旋锁被其他处理器所占有,则在该旋锁上进行繁忙等待,即不停地循环。

  流程如图所示:

注意:获得和释放旋锁的代码是用汇编语言写的。如果使用高级语言,有的动作就无法执行,即使能够执行,也很可能速度缓慢。而且体系结构的一些优点也只有汇编语言能利用。为了提高速度并且最大限度地利用底层处理器结构提供的各种锁机制,用来获取和释放旋锁的代码通常用汇编语言来写。

21.7.2 旋锁的缺点

旋锁的缺陷:

  • 需要繁忙等待;(但是由于等待时间比较短且由于是CPU所以可以自动切换到别的线程,这反而不是问题)
  • 对总线的竞争;(由于要测试并设置这个全局内存单元,会用到内存总线,从而不断地向内存总线发信号,造成对内存总线的竞争,这将导致效率低下)

而解决的方案就是队列旋锁。

21.7.3 队列旋锁

  • 队列旋锁的中心思想:需要旋锁的CPU不需要到全局内存去SPIN,而是到自己的局部内存去SPIN。(这样就可以排除对总线的竞争);在旋锁上排一个队,就表示哪些CPU要这个旋锁,释放的时候就去检查这个队列,交给队列里的第一个CPU;就是把局部变最改成释放状态,因为释放旋锁的CPU会改变局部单元上的等待状态。
  • 使用队列旋锁的一个巨大优点是其扩展性。由于每个CPU等在自己的本地内存单元上,此种机制几乎可以无限扩展。

21.8 其他同步原语

在多核环境下使用的原语还有信号量与内核对象等。

21.9 多核环境下的进程调度

对于多线程和多进程,多核环境和单核环境的最大不同是可以有多个线程/进程可以真正的执行。在调度目标上,单核环境下调度应该达到的目标,多核环境也应该达到,如下所示:

  • 快速响应时间
  • 后台工作的高吞吐率
  • 防止进程饥饿
  • 协调高低优先级进程

对于多核,需要考虑的还有负载平衡问题。

21.9.1 调度策略

对单核有效的进程调度算法在多核上一样有效,多核多的一类算法就是线程迁移。

21.9.2 调度域

调度域主要针对linux下多核负载均衡问题,使得各CPU之间进行平衡,即繁忙程度类似。一个多核系统里面可以有多个调度域。所有的CPU均被映射到某个调度域。而调度域是一个层次架构,顶级调度域囊括所有的CPU,而子调度域则通常仅包括部分的CPU。

21.9.3 负载平衡

@1 负载平衡的目标:将进程均匀分配到每个CPU的就绪队列里面。一个负载均衡的系统的效能通常会优于一个负载不平衡的系统。对于Linux来说,负载平衡在调度域里面进行。

@2 负载平衡的方法有主动和被动两种:

  • 主动负载平衡:队列里面进程数多的CPU将某些进程推出去,即所谓的push。
  • 被动负载平衡:队列为空的CPU从别的CPU队列里面将进程拉出来,即所谓的Pull。

@3 由于一个系统里面有不同的调度域,不同的调度域其繁忙程度有可能不同。因此在调度域里面进行负载平衡的情况下,也可能需要在不同调度域之间进行平衡。因此,在负载平衡时,我们需要找出最繁忙的调度域,在每个调度域里面找出最繁忙的队列,然后将任务从一个队列移动到另一个队列,或者另一个调度域。

21.9.4 进程迁移

在判断需要进行负载平衡后,就需要将一个进程从一个处理器队列移动到另一个处理器队列。而这个移动包括了整个上下文的移动,如页表、TLB、缓存等。

21.9.5 钉子进程

有时候,因为一个进程的特殊性,我们需要让它在一个特定的CPU上执行;这在非对称多处理器的多核环境下非常普遍。在以下情况下会使用钉子进程:

  • 由于不同CPU的功能并不相同,而一个进程可能需要某个特定CPU的功能才能执行(即使这个CPU非常繁忙)。这时就将一个进程钉在一个CPU上,被钉住的进程是不能移动的。
  • 因为缓存而钉住一个进程。如果一个进程的许多信息已经缓存在一个CPU的缓存里面,我们可能也不太愿意迁移该进程到另一个的CPU上。

21.9.6 关联线程的调度

如果一个应用被分解为多个线程,山于多个线程需要共享许多资源,这个时候需要将这此线程尽货分配到同一个处理器核上执行,以提升缓存命中率。对于多核处理器系统的调度,目前还没有公认的或明确的标准。

21.10 多核环境下的能耗管理

对于多核计算机来说,降低能耗比在单核时更重要。通常情况下,CPU运行在正常状态,其主频时钟频率运行在最高值,此时的能耗也处于最高状态。除此之外还有C状态和P状态:

  • C状态:如果一个CPU没有任务可执行,我们可以令其执行一条所谓的终结(halt)指令,使其进人到C状态(一个耗能很低的状态)
  • P状态:如果一个CPU的工作量很少,我们可以降低其主频,使其运行在较低的速度上,从而降低能耗。这个低频率状态就是所谓的P状态;

不过,在进行状态改变的时候,需要考虑到许多因素,这些因素包括:逻辑CPU之间的依赖性、不同物理CPU之间的相关性。

21.10.1 超线程结构的能耗管理

对于超线程结构来说:

  • C状态是独立的。即一个逻辑CPU可以变为C状态,而不影响其他逻辑CPU的运行;即彻底关闭一个逻辑CPU,而将其所用资源全部移交给其他逻辑CPU。
  • P状态是不独立的。因为一个物理CPU里面的逻辑CPU共享同一个时钟,它们的P状态控制寄存器也是共享的;即所有共享P状态控制寄存器的逻辑CPU皆进人P状态。

21.a.2 多核结构的能耗管理

  • 对于多核结构来说,C状态是独立的。即一个核可以独立的进入到C状态而不影响另一个核的运转。如果两个核同时进入C状态,则两核之间的共享部分也可以进人C状态。
  • 多核之间有着独立的P状态控制寄存器,但是每个核之间的P状态是相互依赖的,它们之间的依赖关系由软件协调。

21.10.3 调度上的考虑

关闭一部分执行核可以降低系统的能耗。但关闭执行核或降低其执行频率有可能对与其有关联的其他执行核产生影响。因此,为了既可以节能,又不对其他执行核产生影响,可以从连个方面来考虑:

  • 多核的调度;尽量使得一个CPU忙碌而其他CPU因闲置而关闭。
  • 多核的电源优化;即耗电状态调整。

21.11 多核系统的性能

多核系统不一定能提高计算机的性能;

  • 适用于并行运算。(例如解微分,积分方程)
  • 不适用于海量信息筛选和数据挖掘。(这项技术在当前占有重要地位)

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图王大胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值