QNX 多核处理
俗话说,“三个臭皮匠,顶个诸葛亮”,这句话同样适用于计算机系统,因为在计算机系统中,两个或多个处理器可以极大地提高性能。多处理系统可以采用以下形式:
- 离散或传统:在板级总线上以多处理模式连接的独立物理处理器的系统
- 多核心:一种芯片,它有一个物理处理器,多个cpu通过芯片级总线互连。多核处理器通过并发提供更强大的计算能力,提供更大的系统密度,并且比单处理器芯片运行的时钟速度更低。多核处理器还减少了散热、功耗和板面积(从而降低了系统的成本)。
多处理包括以下几种工作模式: - 非对称多处理(AMP) :一个独立的操作系统或同一个操作系统的单独实例化,运行在每个CPU上。
- 对称多处理(SMP):一个操作系统的单一实例可以同时管理所有的cpu,并且应用程序可以浮动到其中的任何一个。
- 混合多处理(BMP):一个操作系统的单一实例同时管理所有的CPU,但是每个应用程序都被锁定到一个特定的CPU。
Asymmetric multiprocessing (AMP)
非对称多处理提供了与传统单处理器系统类似的执行环境。它为移植遗留代码提供了一种相对简单的路径,并为控制cpu的使用方式提供了一种直接的机制。在大多数情况下,它允许您使用标准的调试工具和技术。
AMP 能够:
- 同构性——每个CPU运行相同类型相同版本的操作系统
- 非均匀性——每个CPU运行不同的操作系统或不同版本的相同的操作系统
QNX中微子的分布式编程模型使您能够充分利用同构环境中的多个cpu。在一个CPU上运行的应用程序可以与在其他CPU上运行的应用程序和系统服务(例如,设备驱动程序,协议栈)透明的通信,并且没有传统处理器间通信形式所带来的高CPU占用率。
在异构系统中,您必须实现一个专有的通信方案,或者选择两个共享公共基础设施(可能是基于IP的)的操作系统来进行处理器间通信。为了避免资源冲突,OSs还应该提供访问共享硬件组件的标准化机制。
使用AMP,您可以决定应用程序使用的共享硬件资源如何在cpu之间分配。通常,这种资源分配在引导期间静态发生,包括物理内存分配、外围设备使用和中断处理。虽然系统可以动态地分配资源,但是这样做需要在cpu之间进行复杂的协调。
在AMP系统中,一个进程总是在同一个CPU上运行,即使其他CPU处于空闲状态。结果,单个CPU可能会被过度使用或使用不足。为了解决这个问题,系统可以允许应用程序动态地从CPU迁移到另一个CPU。但是,这样做可能涉及复杂的状态信息检查点,或者当应用程序在一个CPU上停止而在另一个CPU上重新启动时可能出现服务中断。而且,如果cpu运行不同OS,这种迁移就算是可能的,但也是困难的。
Symmetric multiprocessing (SMP)
在多核设计中分配资源可能很困难,特别是当多个软件组件不知道其他组件如何使用这些资源时。对称多处理通过只运行QNX RTOS的一个副本在系统的所有CPU上,来解决这个问题。因为操作系统在任何时候都能洞察所有的系统元素,所以它可以在多个cpu上分配资源,而很少或根本不需要应用程序设计人员的输入。此外,QNX Neutrino提供了内置的标准化原语,如pthread_mutex_lock()、pthread_mutex_unlock()、pthread_spin_lock()和pthread_spin_unlock(),这些原语让多个应用程序可以安全、轻松地共享这些资源。
通过只运行一个QNX中微子副本,SMP可以动态地将资源分配给特定的应用程序,而不是分配给cpu,从而更好地利用可用的处理能力。多处理系统作为一个整体,它还允许系统跟踪工具聚焦收集统计数据和应用程序交互,使您能够深入了解如何优化和调试应用程序。
例如,IDE中的系统分析器可以跟踪从一个CPU到另一个CPU的线程迁移,以及操作系统的基本原语、调度事件、应用程序到应用程序的消息传递和其他事件,所有这些都具有高精度的时间戳。由于使用标准的OS原语而不是复杂的IPC机制,应用程序同步也变得容易得多。
QNX中微子允许应用程序内的执行线程在任何CPU上并发运行,从而使整个芯片的计算能力始终对应用程序可用。QNX中微子的抢占和线程优先级功能帮助您确保CPU周期到达最需要它们的应用程序。
QNX中微子RTOS的微核方法
SMP通常与高端操作系统相关联,像Unix和widow NT 运行在高端服务器上一样。这些大型的系统往往非常复杂,是许多人多年开发的结果。由于这些大型内核包含所有OS服务的大部分,因此支持SMP所需的更改非常广,通常需要大量修改并在整个代码中使用专门的自旋锁。
另一方面,QNX微核包含一个非常小的微内核,内核周围的进程充当资源管理器,提供文件系统、字符I/O和网络等服务。通过单独修改微内核,所有其他OS服务都可以充分利用SMP,而不需要修改代码。如果这些提供服务的进程是多线程的,那么它们的许多线程将被调度到可用的处理器中。甚至单线程服务器也将从中受益,因为它的线程可以被调度在其他服务器和客户端进程旁边的可用处理器上。
作为这种微内核方法的一个证明,启用smp的QNX中微子内核/进程管理器只增加了几kb的额外代码。SMP版本是为这些主要处理器家族设计的:
- ARM (procnto-smp)
- x86 (procnto-smp)
x86版本可以在任何符合Intel多处理器规范的系统上引导。QNX中微子还支持英特尔的超线程技术(在P4和Xeon处理器)。
procnt-smp管理器还可以在单个非smp系统上运行。由于构建一个双处理器奔腾主板的成本与构建一个单处理器主板的成本非常接近,因此有可能通过简单地添加第二个CPU来交付具有成本效益的解决方案。事实是操作系统本身只增加了几千字节,使得SMP可以被认真考虑用于小型cpu密集型嵌入式系统,而不仅仅是高端服务器。
Booting an x86 SMP system
微内核本身只包含很少的特定于硬件或系统的代码。确定系统功能的代码被隔离在启动程序中,启动程序负责初始化系统、确定可用内存等。收集到的信息被放入微核和进程可用的内存表中(只读)。
startup-bios程序被设计去兼容Intel MP规范(1.4版本及以后)。这个程序负责:
- 确定处理器的数量
- 确定本地和I/O APIC的地址
- 初始化每个附加的处理器
复位后,只有一个处理器将执行复位代码。这个处理器称为引导处理器(BP)。对于找到的每个额外的处理器,运行startup-bios代码的BP将:
- 初始化处理器
- 切换到32位保护模式
- 分配处理器自己的页面目录
- 设置处理器旋转,禁用中断,等待内核释放
How the SMP microkernel works
一旦释放并运行了额外的处理器,所有处理器都被视为线程调度的对等点。
- Scheduling
调度策略遵循与单处理器系统相同的规则。也就是说,最高优先级的线程将在可用的处理器上运行。如果一个新线程已经准备好作为系统中优先级最高的线程运行,那么它将被分配给适当的处理器。如果选择了多个处理器作为潜在目标,那么微内核将尝试将线程分派到它最后运行的处理器。此关联用于尝试减少从一个处理器到另一个处理器的线程迁移,提高缓存性能。
在SMP系统中,调度器在确定如何调度其他线程方面具有一定的灵活性,其目的是优化缓存使用和最小化线程迁移。这可能意味着一些处理器将运行低优先级的线程,而高优先级的线程正在等待在它上次运行的处理器上运行。下一次,那个运行低优先级线程的处理器做调度决策时,它将选择高优先级线程。在任何情况下,在单处理器系统上的实时调度规则在SMP系统上得到了保证。 - Kernel locking
在单处理器系统中,一次只允许一个线程在微内核中执行。大多数内核操作的持续时间都很短(通常在奔腾类处理器上只有几微秒)。微内核还被设计成完全可抢占的,并且对于那些需要更多时间的操作可以重新启动。这种设计保持了微内核的精简和快速,而不需要大量的细粒度锁。令人关注的是,在通过内核的主代码路径中放置许多锁将显著降低内核的速度。每个锁通常涉及处理器总线事务,这可能导致处理器停机。
在SMP系统中,QNX中微子总保持一个理念:只有一个线程可抢占可重启内核。微内核可以被任何处理器访问,但是每次只允许一个处理器访问。
对于大多数系统,花在微内核上的时间只占处理器工作负载的一小部分。因此,当冲突发生时,它们应该是例外而不是常态。这对于微内核尤其正确,因为微内核中,操作系统的服务是独立进程,而不是内核本身的一部分。 - 处理器间中断 Interprocessor interrupts (IPIs)
处理器之间通过IPIs(处理器间中断)进行通信。IPIs可以在多个处理器上有效地调度和控制线程。例如,在以下情况下常常需要向另一个处理器发送IPI:
- 高优先级线程准备就绪
- 在另一个处理器上运行的线程被一个信号命中
- 在另一个处理器上运行的线程被取消
- 在另一个处理器上运行的线程被销毁
Critical sections
为了控制对它们之间共享的数据结构的访问,线程和进程使用标准的POSIX原语的互斥、控制器和信号量。这些在SMP系统中无需更改即可工作。
许多实时系统还需要保护中断处理程序和拥有该处理程序的线程之间对共享数据结构的访问。中断处理程序不能使用线程之间使用的传统POSIX原语。这里有两个解决方案:
- 一种方法是从中断处理程序中删除所有的工作,取而代之的是在线程执行所有的工作。考虑到我们的快速线程调度,这是一个非常可行的解决方案。
- 在运行QNX中微子RTOS的单处理器系统中,中断处理程序可能抢占一个线程,但线程永远不会抢占中断处理程序。这允许线程通过在非常短的时间内禁用和启用中断来保护自己不受中断处理程序的影响。
非smp系统上的线程使用下面的代码保护自己:
InterruptDisable()
// critical section
InterruptEnable()
Or:
InterruptMask(intr)
// critical section
InterruptUnmask(intr)
不幸的是,这段代码在SMP系统上会失败,因为线程可能在一个处理器上运行,而中断处理程序在另一个处理器上并发运行!一种解决方案是将线程锁定到特定的处理器(BMP)。更好的解决方案是使用线程和中断处理程序都可用的新排除锁。这是由以下原语提供的,它们可以在单处理器和SMP机器上工作:
- InterruptLock(intrspin_t* spinlock )
尝试获取一个自旋锁(这是中断处理程序和线程共享的一个变量)。代码将在一个紧密的循环中旋转,直到获得锁为止。禁用中断之后,代码将获得锁(假设被一个线程申请获取)。锁必须尽快打开(通常在几行C代码中,且没有任何循环)。 - InterruptUnlock(intrspin_t* spinlock )
释放锁并重新启用中断
在非smp系统中,不需要自旋锁。
Bound multiprocessing (BMP)
绑定多处理提供了非对称多处理模型的调度控制,同时保留了对称多处理的硬件抽象和管理。BMP类似于SMP,但是您可以指定一个线程可以在哪个处理器上运行。您可以在同一个系统上同时使用SMP和BMP,从而允许一些线程从一个处理器迁移到另一个处理器,而其他线程只能迁移到一个或多个处理器。
与SMP一样,操作系统的单一副本维护所有系统资源的整体视图,允许在应用程序之间动态分配和共享这些资源。但是,在应用程序初始化期间,系统设计人员确定的设置将强制应用程序的所有线程仅在指定的CPU上执行。与完全的浮动SMP操作相比,这种方法有以下几个优点:
- 通过允许共享相同数据集的应用程序仅在相同的CPU上运行,它消除了SMP系统中可能降低性能的缓存抖动。
- 它提供了比SMP更简单的应用程序调试,因为应用程序中的所有执行线程都在一个CPU上运行。
- 它帮助使用糟糕的同步共享数据技术的遗留应用程序正确地运行(通过让它们在单个CPU上运行)。
使用BMP,锁定到一个CPU的应用程序不能使用其他CPU,即使它们是空闲的。但是,QNX中微子RTOS允许您动态地更改指定的RTOS CPU,无需检查点,然后停止并重新启动应用程序。
QNX中微子通过运行掩码(runmask)支持硬处理器亲和的概念。runmask中设置的每个位代表一个线程可以在其上运行的处理器。默认情况下,一个线程的runmask被设置为所有的,允许它在任何处理器上运行。0x01的值将允许线程仅在第一个处理器上执行。
默认情况下,进程和线程的子类不继承runmask;存在一个独立的继承mask。
通过谨慎地使用这些掩码,系统设计人员可以进一步优化系统的运行时性能(例如,通过将非实时进程转移到特定的处理器)。但是,通常不需要这样做,因为当高优先级线程准备就绪时,我们的实时调度器总是会立即抢占一个低优先级线程。处理器锁定可能只会影响缓存的效率,因为可以防止线程迁移。
你可以指定一个新的线程或进程的运行掩码: - 当你调用spawn()时,设置inheritance结构的runmask成员且指定SPAWN_EXPLICIT_CPU flag
- 当你launch一个程序时,用-C和-R 选项在on 工具上。这也将进程的继承掩码设置为相同的值。
你可以改变一个现有的线程或进程的运行掩码: - 在内核调用ThreadCtl()上使用使用_NTO_TCTL_RUNMASK或_NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT。
- 使用-C或-R选项在slay 工具。如果您也加上-i选项,那么slay将inherit mask设置为相同的值。
可行的迁移策略
作为AMP和SMP之间的中间点,BMP提供了一个可行的迁移策略,如果您希望迁移到完整的SMP,但是您担心现有代码可能在真正的并发执行模型中运行不正确。
您可以将遗留代码移植到多核系统,并最初将其绑定到单个CPU,以确保正确的运行。通过明智地将应用程序(以及可能的单线程)绑定到特定的cpu,您可以将潜在的并发问题隔离到应用程序和线程级别。解决这些问题将允许应用程序完全并发运行,从而最大化多处理器提供的性能收益。
选择AMP,SMP,和BMP
在AMP, SMP和BMP之间的选择取决于你试图解决的问题:
- AMP可以很好地与遗留应用程序一起工作,但是跨CPU的可伸缩性有限。
- SMP提供了透明的资源管理,但是那些没有为并发性做适当设计的软件可能会有问题。
- BMP提供了许多与SMP相同的优点,但它保证了单处理器应用程序将正确地运行,极大地简化了遗留软件的迁移。
灵活选择各个模型,需要平衡,性能,可扩展性,易迁移性,如下表展示:
Feature | SMP | BMP | AMP |
---|---|---|---|
资源无缝共享 | Yes | Yes | — |
跨双CPU的扩展性 | Yes | Yes | Limited |
遗留程序的运行 | In most cases | Yes | Yes |
混合操作系统环境(e.g. QNX and Linux) | — | — | Yes |
功能性的专用处理器 | — | Yes | Yes |
多核间的消息收发 | 快(操作系统原语) | 快(操作系统原语) | 慢(app) |
CPU间线程同步 | YES | YES | — |
负载平衡 | YES | YES | — |
系统范围的调试和优化 | YES | YES | — |