多核技术导论之操作系统对多核处理器的支持方法

1.4 操作系统对多核处理器的支持方法

1.4.1 调度与中断

随着多核处理器的发展,对软件开发有非常大的影响,而且核心的瓶颈在软件上。软件开发在多核环境下的核心是多线程开发。这个多线程不仅代表了软件实现上多线程,要求在硬件上也采用多线程技术。可以说多核提供了可以大幅提升性能的机制,多核软件就是可以真正利用这一特点的策略。只有与多核硬件相适应的软件,才能真正地发挥多核的性能。多核对软件的要求包括对多核操作系统的要求和对应用软件的要求。

多核操作系统的关注点在于进程的分配和调度。进程的分配将进程分配到合理的物理核上,因为不同的核在共享性和历史运行情况都是不同的。有的物理核能够共享二级cache,而有的却是独立的。如果将有数据共享的进程分配给有共享二级cache的核上,将大大提升性能;反之,就有可能影响性能。进程调度会涉及到比较广泛的问题,比如负载均衡、实时性等。

面向多核体系结构的操作系统调度目前多核软件的一个热点,其中研究的热点主要有下面几方面:程序的并行研究;多进程的时间相关性研究;任务的分配与调度;缓存的错误共享;一致性访问研究;进程间通信;多处理器核内部资源竞争等等。这些探讨相互独立又相互依赖。考虑一个系统的性能时必须将其中的几点同时加以考虑,有时候对一些点的优化会造成另一些点的性能下降,需要用程序进行性能优化评测,所以合适的多核系统软件方案正在形成过程中。

任务的分配是多核时代提出的新概念。在单核时代,没有核的任务分配的问题,一共只有一个核的资源可被使用。而在多核体系下,有多个核可以被使用。如果系统中有几个进程需要分配,是将他们均匀地分配到各个处理器核,还是一起分配到一个处理器核,或是按照一定的算法进行分配。并且这个分配还受底层系统结构的影响,系统是SMP构架还是CMP构架,在CMP构架中会共享二级缓存的核的数量,这是影响分配算法的因子。任务分配结束后,需要考虑任务调度。对于不同的核,每个处理器核可以有自己独立的调度算法来执行不同的任务(实时任务或者交互性任务),也可以使用一致的调度算法。此外,还可以考虑一个进程上一个时间运行在一个核上,下一个时间片是选择继续运行在这个核上,还是进行线程迁移;怎样直接调度实时任务和普通任务;系统的核资源是否要进行负载均衡等等。任务调度是目前研究的热点之一。

在单核处理器中,常见的调度策略有先到先服务(FCFS),最短作业调度(SJF),优先级调度(Priority-scheduling algorithm),轮转法调度(round-robin RR),多级队列调度(multilevel queue-schedule algorithm)等。例如在Linux操作系统中对实时任务采取FCFS和RR两种调度,普通任务调度采取优先级调度。

对于多核处理器系统的调度,目前还没有明确的标准与规范。由于系统有多个处理器核可用,必须进行负载分配,有可能为每个处理器核提供单独的队列。在这种情况下,一个具有空队列的处理器就会空闲,而另一个处理器会很忙。所以如何处理好负载均衡问题是这种调度策略的关键问题所在。为了解决这种情况,可以考虑共同就绪队列,所有处理器公用一个就绪队列。但是这无疑对进程上下文切换、锁的转换增加了执行时间,降低了性能。另外一种想法就是选择一个处理器来为其他处理器调度,因而创建了主从结构。有的系统将主从结构作进一步扩展,采用单一处理器来处理所有调度的调度策略、I/O处理和其他系统活动只有一个处理器处理访问系统数据,减轻了数据共享需要,然而它的执行效率并不高,I/O的执行和处理系统事务的主处理器成为瓶颈。

目前为止,世界上还没有对CMP体系结构多核操作系统一个成熟的调度算法的实现。虽然Windows与Linux操作系统可以作为CMP多核体系结构的操作系统,但是它们对多核的支持并不是直接对CMP多核体系结构的支持,实际上都是针对SMP体系的支持。

Linux操作系统是开源操作系统中的主流,在2.6内核发布之前,CMP体系结构并不是市场中体系结构的主体,但是Linux2.6内核的调度策略对SMP支持得很好。Linux SMP的调度算法已经较为全面,很大程度上解决了资源利用不充分、调度时间长、解内核锁、加内核锁而引起的过多资源消耗等问题,可作为多核调度的借鉴。

2.6内核的调度算法的时间复杂度为O(1),只需要常数时间就能够完成任务的调度。具体实现的原理简要而言是这样的:

(1)系统为每个处理器都维持一个单独的就绪队列,就绪队列包括活动的就绪队列和扩展的就绪队列。活动的就绪队列包含当前时间片还有剩余的就绪任务。而扩展的就绪队列包含的是那些时间片已经用完的,重新分配时间片的就绪任务。

(2)任务的调度是基于优先级调度的。每个处理器上的任务共有140个优先级(图1-7),而每个就绪任务的优先级通过散列函数直接映射到处理器的位图这个数据结构上,通过位图的find-first-bit可以找到优先级最高的任务执行。

图1-7 Linux2.6内核的就绪

(3)当活动就绪队列中的任务全部时间片结束或者等待IO挂起,只要进行简单的指针操作,就可以相互转换活动就绪队列和扩展就绪队列。

(4)负载均衡的实现的方式有两种:当一个处理器上的任务全部结束之后,每1ms,它会使用系统调用到最忙处理器上获得任意一个任务执行;而在全部处理器都有任务在执行时,则由时钟每200ms唤起系统调用去检查,若发现在Linux标准下不均衡,则会发生处理器之间的就绪任务迁移。

但是Linux没有考虑让一个应用程序的任务尽量在同一个处理器核上执行,尽量降低cache的缺失率;当负载不均衡发生时,并未作出迁移任务的具体决策;在调度之前并未采取合适的任务分配决策等等。

下面是几个具有代表性的多核调度算法:

(1)对任务的分配进行优化。使同一应用程序的任务尽量在一个核上执行,以便达到有共享数据的任务能够尽量在一个核上进行,而共享数据量少或者没有的任务尽量在不同核上进行。这样,可以显著得降低cache的缺失率,进而很大程度上提升了系统的整体性能。

(2)对任务的共享数据优化。由于CMP体系结构共享二级缓存,可以考虑改变任务在内存中的数据分布,使任务在执行时尽量增加二级缓存的命中率。

(3)对任务的负载均衡优化。当任务在调度时,出现了负载不均衡,考虑将较忙处理器中与其他任务最不相关的任务迁移,以达到数据的冲突量小。


 

1.4.2 输入输出系统

高级编程中断控制器(APIC)是基于中断控制器分散在两个基础功能单元--本地单元和I/O单元的分布式体系结构。本地和I/O单元能够通过一个叫中断控制通信(Interrupt Controller Communication即ICC)的总线互相之间通信。在多核系统中,多个本地和I/O APIC单元能够作为一个整体通过ICC总线互相操作。APIC单元主要用于从中断源传送中断到中断目标。

APIC具有如下功能:

  1. 减缓与中断相关的内存总线传输压力,从而似的内存总线可用程度更高。
  2. 帮助核之间更好的处理来自别的核的中断。

APIC发挥如下基本功能:

  1. APIC接收来自处理器中断引脚的来自内部或外部I/O APIC的中断。然后将这些中断发送过处理器核处理。
  2. 在多核处理器系统中,APIC还能够接受发送核内中断(Interprocessor interrupt即IPI)消息。APIC能将IPI消息发送或接收系统总线上其他的处理器核的IPI消息。IPI消息能够用来在系统时或者系统执行的广泛功能单元中分发中断到不同的处理器核。IPI消息可以用来启动处理器核或者分配工作任务到不同的处理器核。

多核体系处理器中,必须将中断处理分发给一组核处理。当系统中有多个核在并行执行时,必须有一个能够接收中断并将接收到的中断分发给能够提供服务的核的机制。

多核处理中断如同在其他的MP系统一样。外部中断通过I/O APIC接收,并被作为中断信息分配给特定的核。处理器核也能够通过写本地APIC的ICR寄存器来发送IPI(Interprocessor Interrupt)给其他的核。高级编程中断控制器(APIC)最早由IA-32奔腾体系处理器引进。APIC也被引入P6系列,奔腾4,Intel Xeon处理器核其他最新的Intel 64核IA-32处理器。

APIC能够通过中断命令寄存器(Interrupt Command Register即ICR)来接收和发送IPI消息。ICR提供如下功能:

  1. 发送中断到其他处理器核
  2. 允许处理器核转发接收到的其不服务的中断到其他处理器核来服务。
  3. 给处理器核自身发送中断(一次自中断)
  4. 传递特殊IPI到其他处理器核,比如启动IPI(Start-up IPI)消息。

由IPI消息产生的中断通过系统总线传送给其他处理器核。通过这种方式发送最低优先级的中断需要特别建造而且必须避免BIOS和操作系统软件干预。


 

1.4.3 存储管理与文件系统

多核环境下,存储管理相对变化较小。其主要的一些改进包括:

为了充分使用多核的运算能力,很多的库函数(memory allocation )都要做成non-blocking的,但这样会导致数据冲突或不同步的问题,所以需要有能够保证数据同步的机制。

事务内存管理(transactional memory management)就是这样的机制,能够协作程序,在并行运行的同时,保证数据的同步。

同时,为了提高内存分配(memory allocation)的效率,可以使用多线程内存分配,这样就可以提高效率,降低 cache冲突,特别有利于空间和时间关联性强的内存操作。


 

1.4.4 典型支持多核的操作系统

虽然多核技术是近两年才兴起,但是操作系统对多核的基本支持并不难解决。传统的通用的操作系统都是支持多任务执行的,对唯一的一个计算核心通过分时处理,即把CPU的运算时间划分成长短基本相同的时间片,轮流分配给各个任务使用,从而实现单个CPU执行多个任务的能力。

对于多核(片上多处理器)这种新的体系架构,操作系统并不需要多少修改就已经能够很好支持了,从软件角度来看,多核处理器和多路处理器(SMP)是一样的,所有针对单核多处理器的软件优化方式都可以用在多核处理器系统上。比如windows NT之后的Windows系列操作系统,其中可以支持SMP的都可以支持多核。而对于Linux操作系统内核,其SMP版本的内核能够很好的支持多核CPU,Linux 2.0内核是第一个支持对称多处理器硬件的内核,在近10年的发展进程中,尤其从1999年到现在,Linux对多处理器的支持越来越受到重视。

在早些时候,Linux2.0内核通过使用一种粗粒度的锁来保证系统的完整性,其原则就是:一个正在内核态运行的进程除非交出控制权或者要求进入睡眠,否则不能被另一个欲进入内核态的进程打断。也就是说在任意时刻只能有一个处理器是运行内核态的操作系统代码。这是一种安全而易于实现的方式,不过这种方式对于充分利用多处理器的性能存在着很明显的不足。在Linux以后改进的版本中,操作系统内核采用了一种细粒度的锁来支持这种多处理器的体系机构。通过把操作系统的内核代码划分为临界区的方式,在保证系统完整性的前提下,提升了操作系统利用多处理器的性能优势。

不管是WindowsNT操作系统还是Unix的一些商业版本,如Solaris,AIX等以及开源的Linux操作系统,都采用一种核心级线程和用户级线程两种线程的模型,操作系统的调度在核心态完成,这样调度器可以专注于核心态线程在处理器上的调度。

在2.6版本的Linux中,提出了一种新的O(1)的进程调度器,此调度器可以更好的支持SMP系统。它的优势就是在平衡了多个CPUs的负载均衡的同时又能有效的兼顾cache的有效性。在传统的调度过程中,当某进程从一个CPU迁移到另一个CPU上的时候,在原CPU上的cache内容即为失效,这将延迟任务在新的CPU上访问内存的时间延迟。

Linux 2.6内核设计为给每个CPU建立一个就绪运行队列,并把就绪任务划分为140个优先级,高100个优先级用于实时任务,低40个优先级用户普通任务。并给每个就绪任务一段时间片,当任务把时间片用完后转移到另一个期满就绪队列并重新分配时间片,等到原活动就绪队列的所有任务的时间片均用完之后启用期满就绪队列来体会活动就绪队列。这样,调度器提供了一个各个任务公平使用CPU的机会,并实现了任务和CPU的绑定。通过给每一个CPU设立单独的任务队列,可以有效的实现系统中各个CPUs的负载均衡。

为了支持SMP系统,在操作系统启动阶段,把CPUs分为BSP和AP两种类型,由于BIOS代码并不是支持多线程的,所以在SMP中,系统必须让所有AP进入中断屏蔽状态,不与BSP一起执行BIOS代码。为了达到这一目的,可以利用两种手段:

  1. 利用系统硬件本身进行处理;
  2. 系统硬件与BIOS程序一起处理。

在后一种方法中,BIOS程序将其它AP置于中断屏蔽状态,使其休眠,只选择BSP执行BIOS代码中的后继部分。BIOS要同时完成对APIC以及其他与MP相关的系统组件初始化过程,并建立相应的系统配置表格,以便操作系统使用。操作系统在BSP上完成内核加载,操作基本初始化工作后,通过使用处理器间中断系统(IPI)BSP初始化各个AP,最后让各个AP运行idle进程。最后BSP创建1号init进程,由init进程完成最后的系统启动工作。

在中断处理上,为了支持SMP,在硬件上需要APIC中断控制系统的支持。Linux操作系统的SMP版本和UP版本有所不同,对于UP版本,宏Cli(关中断)和Sti(开中断)就是简单的ch和sti指令;而对于SMP版本,内核不仅要禁止本地CPU,还要暂时避免其它CPU处理IRQ(中断请求),宏cli和sti实际上是对函数_global_cli()和_global_sti()的调用。_global_sti()是开中断函数,它首先判断CPU是否正在处理IRQ,如果没有,则释放全局IRQ锁,然后允许在此CPU上继续进行中断。

微软的windows操作系统对于多核的支持也非常容易解决,微软的操作系统支持多任务操作,支持IA架构32位与64位处理器。Windows目前能够在多内核处理器上运行,但并没有针对这类处理器进行优化。

如果要将多核架构的处理器的潜能完全释放,需要开发一个新的操作系统和一批相应的工具,而这样的操作系统和工具现在是没有的,需要操作系统厂家继续努力研发。

操作系统对多核的支持,只能提供任务一级的并行,而多核平台上的应用软件开发思路将大大不同于以前的软件编写思想。过去一个软件只是对应一个进程,在一个处理器上运行。即使是多进程的软件,也只是为了清晰性的考虑而为之,最终在底层仍是由操作系统分时完成。而在多核系统的时代,软件的设计思路将发生很大的改变。设计者必须认识到底层多核的存在,为了使软件发挥最大的性能,必须把软件设计成多进程或多线程,并将这些进程或线程与底层的硬件处理器绑定,真正地使程序的不同部分同时在运行。如何将软件分为多个进程或线程,发挥多核的性能将是本书的重点所在。相对于多核程序的设计,多核程序的编写是等同于多进程或多线程程序的编写,已经有很成熟的库函数支持,比如linux的系统调用fork、clone;libc里的posix pthread;更简单的调用接口OpenMP。以下章节将就多核软件开发展开介绍。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值