QNX为什么是安全的操作系统?牛喀网

QNX成立于1980年,是全世界第一个类UNIX的符合POSIX标准的微内核的硬实时操作系统,在过去的几十年中广泛的应用在汽车、工业自动化、国防、航空航天、医疗、核电和通信等领域,提供以嵌入式操作系统为核心的中间件和基础软件解决方案。

到目前为止,世界上几乎所有的主机厂都采用了基于QNX操作系统的软件技术。全球top25家电动汽车厂家,其中24家在使用QNX的软件操作系统,例如,中国的小鹏汽车自动辅助驾驶系统Xpilot3.0和Xpilot3.5基于QNX通过TUV莱茵ISO26262ASILD功能安全的硬实操作系统,合众新能源汽车的哪吒S采用QNXHypervisor打造其全新科技感智能座舱,并在其全栈自研的TAPILOT3.0智能驾驶系统中搭载QNXOSforSafety操作系统,实现多种场景下的智能辅助驾驶,又如零跑汽车在其量产的第三代高端纯电SUV—零跑C11和智能纯电桥车C01中均采用了QNXNeutrino实时操作系统和QNXHypervisor,旨在为中国消费者带来更个性化与舒适的驾驶体验。除此之外,高合即将发布的豪华纯电超跑HiPhiZ的自动辅助驾驶平台使用的是英伟达Orin-X芯片和QNX嵌入式硬实时操作系统。

QNX特点

QNX是嵌入式硬实时的微内核操作系统

有硬实时、微内核、模块化、弱耦合、分布式的特点,从1980年诞生之初就是基于SOA架构设计,基于Client-Server的模型,具体表现为:

微内核:除调度、进程管理、中断及操作系统核心的功能外,其余部分都处于用户态,包括驱动、协议栈、文件系统及功能模块等。

模块化:操作系统的各个功能单元都模块化设计,内存保护,并且相互隔离,可按照需要动态加载或卸载,基于消息机制通信,按照Client-Server的架构设计。

弱耦合:模块与模块之间互不影响,都在独立的虚拟地址空间运行。

分布式:局域网内的QNX系统对于用户角度可以认为是一台QNX系统,资源可以复用。

微内核则和宏内核结构相反,它提倡内核中的功能模块尽可能的少。内核只提供最核心的功能,比如任务调度,中断处理等等。其他实际的模块功能如进程管理、存储器管理、文件管理……这些则被移出内核,变成一个个服务进程,和用户进程同等级,只是它们是一种特殊的用户进程。

QNX是类UNIX操作系统

遵循POSIX的最高级别PSE54标准(注:POSIX标准有四个等级PSE51,PSE52,PSE53和PSE54,在RTOS实时操作系统的世界里,只有QNX操作系统是PSE54标准的,因为QNX诞生之初就是类UNIX系统按照POSIX标准编写),因此基于开源的应用程序以及一些开源的中间件都可以无缝的移植到QNX系统之上。QNXMicrokernel和ProcessManager组成QNX最小系统Procnto,其他如驱动程序、协议栈、文件系统、应用程序都作为一个独立的模块运行在QNX系统之上。

POSIX(PortableOperatingSystemInterface,可移植操作系统接口)是一个IEEE标准,旨在提高各种UNIX操作系统上运行的软件的可移植性和兼容性。它定义了操作系统应该为应用程序提供的接口标准,使得遵循POSIX标准的软件能够在不同的操作系统上运行,而无需进行大量的修改。

POSIX标准有四个等级:PSE51、PSE52、PSE53和PSE54。这些等级代表了不同的兼容性和功能级别,其中PSE54是最高级别。在RTOS(实时操作系统)的世界中,QNX操作系统是唯一一个符合PSE54标准的系统。这是因为QNX从诞生之初就是基于类UNIX系统并按照POSIX标准编写的,因此它具有高度的兼容性和可移植性。

遵循POSIX最高标准(PSE54)意味着QNX操作系统完全遵循了POSIX定义的所有接口和功能要求。这使得基于开源的应用程序以及一些开源的中间件,只要它们也遵循POSIX标准,就可以无缝地移植到QNX系统之上。换句话说,这些应用程序和中间件无需进行大量的修改或重新编写,就可以直接在QNX系统上运行,从而大大简化了移植过程并提高了效率。

QNXMicrokernel和ProcessManager组成QNX的最小系统Procnto,为系统提供了基本的内核和进程管理功能。其他如驱动程序、协议栈、文件系统、应用程序等都作为独立的模块运行在QNX系统之上。这种模块化设计使得QNX系统非常灵活和可扩展,可以根据不同的需求添加或删除模块。

总的来说,POSIX最高标准(PSE54)为QNX操作系统提供了高度的兼容性和可移植性,使得基于POSIX标准的应用程序和中间件可以无缝地移植到QNX系统上。这种特性使得QNX在RTOS领域具有独特的优势,能够满足各种复杂的实时应用需求。

QNX是功能安全和信息安全的操作系统

QNX通过功能安全TUV莱茵ISO26262ASILD最高等级道路车辆最高功能等级安全认证,包括QNX操作系统、QNXHypervisor虚拟化和GraphicMonitor图形监控子系统以及QNXIPC通讯机制blackchannel,同时黑莓是网络信息安全标准ISO/SAE21434委员会基础软件组唯一成员。

QNX其他特性

1.QNX调度算法及策略

2.QNXIPC通讯机制

QNX除了支持Native的IPC机制如Massagepassing、Signal等,同时还提供POSIX标准的IPC例如MessageQ、Piple、SharedMemory等IPC通讯方式,多种IPC方式供用户在不同的应用场景下进行选择。

3.QNX的IDE集成开发环境

QNX提供基于Eclipse的MomenticsIDE集成开发环境,供用户进行基于以太网SoftwareGDB的代码级的编译调试或系统性能分析,可实时以图形化的方式,查看进程资源、系统日志、CPU占用情况,内存使用情况,进程间通信以及Coredump等。

中国自动辅助驾驶领域基础平台软件所遇到的问题

近年来自动辅助驾驶领域非常火爆,许多国内外的主机厂都逐步在量产项目中开发以及发布L2+的功能,当我们回顾这几年来快速发展会发现,大多数的自动辅助驾驶的人才都来自于Robotaxi,自动驾驶算法初创公司或大学研究机构,特别是算法人才。这就有个显著的特点,在这些公司里面的大多数项目,最初都是基于工控机+英伟达显卡(大多数用英伟达的GPU,少数用AMD的)+开源的操作系统+来自于开源的算法,其实和汽车电子的安全性本身毫无关系,唯一的好处就是快,容易尽早演示,尽快融资。

这些算法人才加入主机厂之后,更倾向于用以前最熟悉的开发方式,这样好尽快的出演示成果,也就是英伟达的SOC+开源的操作系统+来自于开源的算法。另一方面,在自动辅助驾驶项目中,一般主机厂会把控制器平台即硬件和平台软件外包给外部的Tier1来做,类似于一台PC电脑,而自己开发应用和算法。

一般主机厂也有平台组,负责部分的驱动及驱动以上的中间件的整合,系统组负责系统设计统筹,功能安全团队负责整体的功能安全,而算法团队负责算法应用的开发和实现,那么问题就来了,除纯算法团队外,一般国外的主机厂都会有一个成建制的叫算法嵌入式工程实现的团队,负责算法在非工控机的嵌入式环境和实时操作系统的优化实现落地,这样的团队即要懂一点算法架构,又要懂嵌入式软件的开发和硬件特性,又要对操作系统有足够的理解。

QNX算法移植以及性能优化举例

QNX提供ADASreference平台产品,里面涵盖了SensorFramework,networking,opensourcemodules,第三方的SDK以及一些参考设计,其中sensorFramework提供了ADAS的一些基本库。

算法移植

自动辅助驾驶以开源的算法居多,由于QNX符合POSIXPSE54标准,API兼容基本一致,因此各类开源算法可以很方便的移植到QNX的平台上,使用QNX的工具链进行编译并运行,但是虽然API是一致的,但由于实时操作系统的特性,表现的行为会有所差异,需要对系统进行优化调整。

QNX有专门的team来根据roadmap以及客户需求移植一些开源软件,比如ROS/ROS2,比如OpenCV和vSomeIP,我们也会负责后期的维护。

IPC优化

QNX支持绝大部分主流POSIX系统常见的IPC方式,同时也有其独特的原生IPC方式,Message-passing。在自动辅助驾驶方案设计中,常有公司会将UDS、DDS做为软件通信总线的架构方案原封不动地从Linux照搬到QNX上。从功能上看,这样的跨平台方案可以使得代码重用并且功能没有区别。但从性能角度考虑,由于QNX独特内核架构,这并不是高效的解决方案。不同于Linux的宏内核架构,QNX为了安全性和实时性采用了微内核架构,绝大部分的系统服务,比如网络协议栈,它是完全运行在内核之外以服务(ResourceManager)的方式运行。如果采用UDS(UnixDomainSocket)这用基于网络服务(严格意义上讲,UDS并不需要经过网络协议栈,但也是需要经过QNX的网络服务io-pkt支持)的通讯方式,那么所有的数据报都需要经过网络服务中转,相比直接通讯多了一次IPC,这就带来了系统资源的浪费。建议的优化方案是采用更高效的IPC方式,一般情况下,中小量的数据量传输建议使用message-passing,特别大的数量使用sharedmemory方式。另外,一些开源软件也会大量使用FIFO,PIPE等IPC,尽管QNX支持这类使用,但是我们也建议改成更高效的messagepassing方式,以减少单次IPC的开销。

IPC(Inter-ProcessCommunication,进程间通信)是操作系统中不同进程之间共享信息和通信的机制。在自动辅助驾驶方案设计中,IPC的性能优化对于确保系统的实时性和效率至关重要。

QNX操作系统支持多种主流的POSIX系统常见的IPC方式,如UnixDomainSocket(UDS)、DDS等。同时,它还有自己独特的原生IPC方式,即消息传递(Message-passing)。在跨平台方案设计中,尽管将UDS、DDS等从Linux照搬到QNX上可以实现代码重用和功能无差异,但从性能角度来看,这并不是最优的选择。

针对IPC的优化,建议在QNX中采用更高效的通信方式。对于中小量的数据量传输,建议使用message-passing方式。Message-passing是QNX原生支持的IPC方式,具有高效、灵活的特点,能够减少单次IPC的开销。对于特别大的数据量传输,则建议使用共享内存(SharedMemory)方式。共享内存允许不同进程直接访问同一块内存区域,从而实现高效的数据共享和通信。

此外,尽管QNX支持FIFO、PIPE等IPC方式,但在实际应用中,也建议将这些方式替换为更高效的messagepassing方式。这样可以进一步减少单次IPC的开销,提高系统的整体性能。

编译选项优化

QNX采用GCC的编译框架,意味着其源代码通过GCC(GNUCompilerCollection)进行编译,生成可执行文件。GCC是一个广泛使用的开源编译器,支持多种编程语言和平台。

驱动级别优化

如网络/存储设备驱动,根据以往的经验,大部分的性能问题的瓶颈在设备驱动这层。特别是新的硬件、新的驱动,要注意根据QNX系统服务层做好适配,驱动的好坏,往往是除硬件本身之外最主要的性能影响因素。我们遇到非常多的来自驱动层面的空等,忙等,最终导致系统机能的冗余浪费。

网络协议栈优化

除了网络驱动的优化,QNX的网络协议栈io-pkt本身也提供了丰富的参数,可以根据具体使用的应用场景来达到性能的最优化。另外,使用QNXSDP7.1及后续版本的用户,可以使用最新的版本网络协议栈io-sock,它对多核CPU的利用和大并发小包数据的处理能力有显著地提升。两个协议栈各有千秋,实际上大量的案例证明,用户并没有达到io-pkt的性能瓶颈,socketbuffer不足导致丢包,typedmemorypool分配的不够导致收发阻塞等等,这些都可以通过配置以及API层面的优化达到性能提升。

系统API优化

如memoryallocation,memorycopy等,QNX提供jemalloc根据实际应用场景提供额外内存泄漏手段,提供更多的功能,jemalloc比default的malloc效率更高,特别是对于大量线程高并发调用的场景。

用户接口优化

QNX提供的底层接口,尤其是一些自有API,是有不少细微差别的,比如sendmsg()和sendmmsg(),用户往往会比较熟悉前者,用于socket的发包,但是后者提供了message队列来实现不增加IPC的基础上提高了整体的吞吐率。又比如mmap(),我们提供了一些QNX独有的flag来应对不同的memorymapping场景,如MAP_ANON与MAP_PHYS的配合,才代表申请物理连续memoryregion而MAP_LAZY更会延迟内存的申请分配。了解并熟悉每个接口的参数配置以及相近命名接口的应用场景会对开发帮助很大。

QNX提供MomenticsIDE环境对算法进行性能分析

QNX提供了onboarddebug也支持应用程序调用栈的实时保存及相应的GDB,在调查一些忙等的现场会有很大的帮助。

最后总结一下,即便作为ISO26262ASIL-D安全认证的硬实时性操作系统,QNX在系统性能上也并没有落后宏内核系统。只要合理地使用和优化,它的性能表现同样非常优秀,同时占用更低系统资源。QNX有着丰富的算法移植和优化经验能给到用户,同时QNX提供一系列的手段和工具去定位算法性能的瓶颈。

QNX调度算法

作为一个硬实时操作系统,QNX是一个基于优先级抢占的系统。这也导致其基本调度算法相对比较简单。因为不需要像别的通用操作系统考虑一些复杂的“公平性”,只需要保证“优先级最高的线程最优先得到CPU”就可以了。

基本调度算法

调度算法,是基于优先级的。QNX的线程优先级,是一个0-255的数字,数字越大优先级越高。所以,优先级0是内核中的idle线程。同时,优先级64是一个分界岭。就是说,优先级1–63是非特权优先级,一般用户都可以用,而64–255必须是有root权限的线程才以设。这个“优先级64”分界线,如果有必要,还可以通过启动Procnto时传–P来改变。

内核中的idle线程是Linux系统在初始化时为每个CPU创建的一个线程。idle线程的目的是在不影响性能的前提下,尽可能减少功耗。一般来说,idle线程的优先级被设置为很低,这是因为它只在没有其他进程需要运行时才运行,用以降低功耗

所以在调度算法看来,整个系统里的线程像这样:

图1有两个CPU的系统里的线程

这是一个有两个CPU的系统,所以可以看到有两个RUNNING线程;对于BLOCKTHREAD,它们不参于调度,所以不需要考虑它们的优先级。

调度策略

在QNX上实质上只有三种基本调度策略,“轮询”(RoundRobin),“先进先出”(Firstinfirstout)和"零星调度”(Sporadic)算法。虽然形式上还有一个“其他”,但“其他”跟“轮询”是一样的。这些调度策略,在/usr/include/sched.h里有定义。(SCHED_FIFO,SCHED_RR,SCHED_SPORADIC,SCHED_OTHER)

先进先出(FIFO)

在FIFO策略中,一个线程可以一直占用CPU,直到它执行完。这意味着如果一个线程正在做一个非常长的数学计算而且没有其他更高优先级的线程就绪,这个线程就会一直执行下去。拥有相同优先级的线程会怎么样呢?它们会一直等待,当然更低优先级的线程也得不到执行。如果运行中的线程退出或者自愿放弃CPU的使用权,此时内核会寻找其他拥有相同优先级的就绪线程。如果没有这样的线程,内核会继续寻找更低优先级的就绪线程。自愿放弃CPU有以下两种情况。如果线程进入sleep,或被信号量阻塞,此时更低优先级的线程可以运行。另外一个是一个系统调用sched_yield(),仅仅让渡CPU给相同优先级的线程。如果一个线程调用了sched_yield(),而且没有相同优先级的线程就绪,此时会继续执行调用sched_yield()的线程。

轮询(RoundRobin)

强调一下,调度策略只限于在READY队列里的线程,优线级最高的线程有不止一个时,才会用到。如果线程不再READY,或是有别的更高优先级的线程READY了,那就高优先级线程获取CPU,没有什么策略可言。

“轮询调度”(RoundRobin)跟平时生活里排队的情形差不多,晚到的人排在队尾,早到的人排在队首,等到叫号(调度)的时候,队首的人会被先叫到。如下图所示:

图2论询调度示意

首先在CPU1上运行的线程4,被挪入优先级15的队列末尾

然后重新搜索可执行的最高优先级线程,这里有优先级15队列上的线程3和4

线程3因为在队列最前端,它被选择得到CPU,线程3的状态变为RUNNING,在CPU1上执行

可以预期,当下一次调度发生时,线程3会被挪入优先级15队列末尾,而线程4会被调度执行,这样线程3和4会分别得到CPU1.

“先进先出”(Firstinfirstout)调度则刚好相反,后来的人插在队首,然后在叫号的时候被先叫到。看下图:

图3先进先出调度示意

首先在CPU1上运行的线程4,被挪入优先级15的队列队首

然后重新搜索可执行的最高优先级线程,这里有优先级15队列上的线程4和3

线程4因为在队列最前端,它被选择得到CPU,线程4的状态变为RUNNING,在CPU1上执行

可以看到,在这个调度算法下,如果没有别的状态发生,事实上线程4就会一直占据CPU1。

如果在优先级15上的线程3和线程4都是FIFO会怎样?按上面的描述,线程3还是始终无法获得CPU1,因为线程4每次都会插在3的前面,再调度就又是4获得CPU1。除非线层4进入了阻塞状态(从而不在READY队列里了),那么线程3才能获得CPU。

零星调度(Sporadic)

图4零星调度示意

上图是一个零星调度线程的示意。

这时,线程会被自动调整为低优先级L(sched_ss_low_priority);一旦被调低,线程也可能运行(如果优先级L依然是系统里最高优先级的线程),也可能无法运行呆在READY队列里(系统里有比L更高的优先级)

“零星调度”看上去比较“公平”,但是实际在用QNX的项目中,这个调度算法很少被用户用到。主要是因为一般来说在QNX上很少有线程能够“连续占用CPU”的。而且当系统变得复杂,线程数成百上千后,这种上下调优先级的做法,很容易出现别的后遗症。

什么时候会发生调度?

上面介绍了QNX支持的几个调度算法。那么,什么时候才会发生调度呢?

举个例子,哪怕程序里只写一个printf("HelloWorld!\n");可是在libc库里,最后这个会变成一个IO_WRITE消息,MsgSend()给控制台驱动;这时,在MsgSend()这个内核调用里,会把printf()的线程置为阻塞状态(REPLYBLOCK),同时会把控制台驱动的信息接收线程(从RECEIVEBLOCK)改到READY状态,并放入READY队列。当退出MsgSend()内核调用时,线程调度发生,通常情况下(如果没有别的线程READY的话)控制台驱动的信息接收线程被激活,并占据CPU.

另一种常见情况是,由于某些别的原因导致高优先级线程被激活,比如网卡驱动中断导致高优先级驱动线程READY,所设时钟到达导致高优先级线程从阻塞状态返回READY状态了,当前线程开放互斥锁之类的线程同步对象,导致别的线程返回READY状态了。这些,都会在从内核调用退出时,进行调度。

中断与优先级

上面提到如果用户线程长期占有CPU,时钟中断会打断用户线程。细心的读者或许会有疑问,那中断的优先级是多少呢?

答案是在QNX这样的实时操作系统里,“硬件中断”永远高于任何线程优先级,哪怕你的线程优先级到了255,只要有中断发生,都要让路,CPU会跳转去执行中断处理程序,执行完了再回归用户线程。事实上,能够快速稳定地响应中断处理,是一个实时操作系统的硬指标。

我们这里说的是“硬件中断”,就是说,当外部设备,通过中断控制器,向CPU发出中断请求时,无论当时CPU上执行的线程优先级是什么,都会先跳转到内核的中断处理程序;中断处理程序会去中断控制器找到具体是哪一个源发生了中断(中断号),并据此,跳转到该中断号的中断处理程序(通常是硬件驱动程序通过InterruptAttach()挂接的函数)。在这个过程中,如果当前CPU正在处理另一个中断,那么这时,会根据中断的优先级来决定是让CPU继续处理下去(当前中断进入等待);或者发生中断抢占,新中断的优先级比旧中断高,所以跳转新中断处理。

当然,实际应用中,特别是微内核环境下,考虑中断其实只是中断设备给出的一个通知,对这中断的响应并不需要真的在中断处理中进行,驱动程序可以选择在普通线程中处理,QNX上有InterruptAttachEvent()就是为了这个设计的。通常这里的“事件”会是一个“脉冲”,也就是说,当硬件中断发生,内核检查到相应中断绑定了事件。这时,不会跳转到用户中断处理程序,而是直接发出那个脉冲,以激活一个外部(驱动器中)线程,在这线程中,做设备中断所需要的处理。这样做,虽然稍微增加了一些中断延迟,但也带来了不少好处。首先,这个外部线程同普通的用户线程一样,所以可以调用任何库函数,而中断服务程序因为执行环境的不同,有好多限制。其次,因为是普通用户线程,就可以用线程调度的方法规定其优先级(脉冲事件是带优先级的),使不同的设备中断处理,跟正常业务逻辑更好地一起使用。

在QNX这样的微内核环境下,中断处理的方式确实有其独特之处。首先,我们要明确中断本质上只是设备向系统发出的一种通知,告知系统某个特定事件已经发生,需要相应的处理。但并不意味着中断处理必须在中断发生的即时上下文中完成。

QNX提供的InterruptAttachEvent()机制允许驱动程序选择在一个普通的用户线程中处理中断,而不是传统的在中断服务程序中直接处理。这种设计思路有几个关键的优势。

首先,当硬件中断发生时,内核会检测到相应的中断,并检查是否有与该中断绑定的事件(通常是一个“脉冲”)。此时,内核不会直接跳转到用户的中断处理程序,而是发出这个脉冲。这个脉冲的作用类似于一个信号,用于激活一个在驱动程序中定义的外部线程。这个外部线程随后会执行设备中断所需要的处理逻辑。

这样做虽然可能稍微增加了一些中断的延迟,但带来的好处是显著的。由于这个外部线程是一个普通的用户线程,它拥有与普通用户线程相同的执行环境和能力。这意味着它可以调用任何库函数,使用任何可用的系统资源,而不会受到中断服务程序通常存在的诸多限制。

此外,由于它是一个普通的用户线程,我们可以使用线程调度的方法来规定其优先级。这意味着不同的设备中断处理可以根据需要设置不同的优先级,从而更好地与正常的业务逻辑协同工作。例如,对于实时性要求较高的设备中断,我们可以将其处理线程的优先级设置得较高,以确保其得到及时响应。

多CPU上的线程调度

现在同步多处理器(SMP)已经相当普及了。在SMP上,也就是说当有多个CPU时,我们的调度算法有什么变化呢?比如一个有2个CPU的系统,首先肯定,系统上可执行线程中的最高优先级线程,一定在2个CPU上的某一个上执行;那,是不是第二高优先级的线程就在另一个CPU上执行呢?

虽然直觉上我们觉得应该是这样的(系统里的第一,第二高优先级的线程占据CPU1和CPU2),但事实上,第二高优先级的线程占据CPU2这件事,并不是必要的。实时抢占系统的要求是最高优先级”必须“能够抢占CPU,但对第二高优先级并没有规定。拿我们最开始的双CPU图再看一眼。

图5有两个CPU的系统里的线程

线程4以优先级15占据CPU1这是毫无疑问的,但线程5只有优先级12,为什么它可以占据CPU2,而线程3明明也有优先级15,但只能排队等候,这是不是优先级倒置了?其实并没有,如上所述,系统确实保证了“最高优先级占据CPU”的要求,但在CPU2上执行什么线程,除了线程本身的优先级以外,还有一些别的因素可以权衡,其中一个在SMP上比较重要的,就是“线程跃迁”。

“线程跃迁”指的是一个线程,一会儿在CPU1上执行,一会儿在CPU2上执行。在SMP系统上,线程跃迁而导致的缓存清除与重置,会给系统性能带来很大的影响。所以在线程调度时,尽量把线程调度到上次执行时用的CPU,是SMP调度算法里比较重要的一环。上述例子中,很有可能就是线程3上一次是在CPU1上执行的,而线程5虽然优先级比较低,很有可能上一次就是在CPU2上执行的。

实际应用中,因为QNX的易于阻塞的特性,其实大多数情况下,还是符合“第一,第二高优先级线程在CPU上执行”的。只是,如果你观察到了上述情形,也不需要担心,设计上确实有可能不是第二高优先级的线程在运行。

另一个多处理器上常见的应用,是线程绑定。在正常情况下,把可执行线程调度到哪一个CPU上,是由操作系统完成的。当然操作系统会考虑“线程跃迁”等情形来做决定。但是,QNX的用户也可以把线程绑定到某一个(或者某几个)CPU上,这样操作系统在调度时,会考虑用户的要求来进行。绑定是通过ThreadCtl()修改线程的“RUNMASK”来进行的,如果你有0,1,2,3总共4个CPU,那么0x00000003意味着线程可以在CPU0和CPU1上执行,具体例子可以参考ThreadCtl()函数说明。更简单的办法,是通过QNX特有的on命令的–C参数来指定,这个指定的runmask,还会自动继承。所以你可以简单的如下执行:

#on–C0x00000003Navigation&

#on–C0x00000004Media&

#on–C0x00000008System&

这样来把不同的系统部署到不同的CPU上。

这样做的好处当然是可以减少比如因为系统繁忙而对导航带来的影响,但不要忘了,另一面,如果所有Media线程都处于阻塞状态,上述绑定也限制了导航线程使用CPU2的可能,CPU2这时候就会空转(执行内核idle线程)。

自适应分区调度算法

前面我们提到过,在讨论优先级调度时,只是讨论当有多个优先级相同的线程时,系统怎样取舍。优先级不一样时,肯定是优先级高的赢。但是“高出多少”并不是一个考量因素。两个线程,一个优先级10,另一个优先级11的情况,和一个10,另一个40的情况是一样的。并不会因为10和40差距比较大而有什么不同。

也就是说,各占了50%的CPU。但只要把蓝色线程提高哪怕1,执行结果就成了下面这样。

这种“非黑即白”的情形,是实时系统的基本要求(高优先级抢占CPU)。但是当然,现实情况有时候比较复杂。比如“HMI渲染”是需要经常占据CPU的一个任务(这样画面才会顺畅),但“用户输入”也是需要响应比较快的(不然用户的点击就会没有反应)。如果“用户输入”的优先级太高的话,那用户拖拽时,画面就会卡顿甚至没有反应?反之,如果”HMI渲染“的优先级太高,那么有用户输入时,因为处理程序优先级低而造成用户输入反应慢。通常情况下,需要有经验的系统工程师不断调整这两个任务的优先级(因为优先级继承与传统,一个任务可能涉及到多个线程),来达到系统的最优。那么,有没有别的办法呢

分区调度

传统上,有一种“分区调度”的方法,今天还有一些Hypervisor采取这个办法。这个想法很简单,就是把CPU算力隔成几个分区,比如70%,30%这样,然后把不同线程分到这些分区里,当分区里的CPU预算被用完以后,那个分区里所有可执行线程都会被”停住“,直到预算恢复。

图6分区调度算力全满示意

综上,在任意一个滑动窗口中,蓝色分区总是只占30%,而红色分区却占了70%。QNX的自适应分区调度,跟上面这个是类似的。只是传统的分区调度,有一个明显的弱点。

想一下这个情况,如果红线程因为某些情况被阻塞了,会发生什么呢?

图7分区调度算力有富余示意

所以你也看到了,在传统的分区调度里,当一个分区的算力有富裕的时候,CPU就被浪费了。

自适应分区调度

QNX在传统的分区调度上,增加了“自适应”的部份。其基本思想是一样的,给算力加分区,然后把不同的线程分到分区里。这样,当所有的线程都忙起来时,你会发现情况跟图7是一样的。但是当分区算力有富裕时,“自适应“允许把多出来的算力”借“给需要更多算力的分区。

图8自适就分区算力有富裕示意

自适应分区似乎确实带来了好处,但是也带来了一些潜在的问题,需要在系统设计的时候做好决定。

自适应分区调度与线程优先级

你可能会好奇,在分区调度的系统里,线程的优先级代表了什么?

答案取决于各个分区对各自算力的消耗情况。我们假设蓝色分区里的线程优先级比较高,红色的优先级比较低,当两个分区都有预算时,内核会调度(所有分区里的)最高优先级线程执行。如果系统一直不是很忙,那么不论分区,永远是有最高优先级的线程得到CPU,这个,跟一个标准的实时操作系统是一致的。

当两个分区中某一个有预算时(意味着那个分区中所有的线程都不在执行状态),那么多出来的CPU算力会被分给另一个分区,另一个分区中的最高优先级线程(虽然用完了自己分区的预算,但得到了别的分区的算力),继续占据CPU。这个,也是跟实时操作系统是一致的。

比较特殊的情况是,当两个分区都没有预算,都需要占据CPU时,这时,蓝色线程虽然有较高的优先级,但因为分区算力(30%)被用完,面且没有别的算力可以“借”,所以它被留在READY队列中,而比它优先级低的红色线程得以占据CPU。

自适应分区调度富裕算力分配

我们上面的例子只有两个分区,考虑这样一个例子。假设我们现在有A(70%),B(20%),C(10%)三个分区,A分区没有可执行线程,B分区有个优先级为10的线程,C分区有个优先级为20的线程。我们知道A分区的70%会分配给B和C,但具体是怎么分配的呢?

如上所述,当预算有富裕时,系统挑选所有分区中,优先级最高的线程执行,也就是说C分区中的线程得到运行。在一个窗口以后,你会发现A的CPU使用率是0%,B是20%,C则达到了80%。也就是说A所有的富裕算力,都给了C分区(因为C中的线程优先级高)。

也许,在某些时候,这个不是你所期望的。也许C中有一些第三方程序你无法控制,你也不希望他们偷偷提高优先级而占用全部富裕算力。QNX提供了SchedCtl()函数,可以设SCHED_APS_FREETIME_BY_RATIO标志。设了这个标志后,富裕算力会按照各分区的预算比例分配给各分区。上面的例子下,最后的CPU使用率会变成A是0%,B是65%,而C是35%。A分区富裕的70%算力,按照大约2:1的比例,分给了分区B和C。

“关键线程”与“关键分区”

当一个“关键线程”需要执行时,如果线程所在分区有预算,它就直接使用所在分区预算就好,如同普通线程;如果所在分区没有预算了,但是别的分区还有预算,那么“自适应”部份会把别分区的预算拿过来,并用于关键线程,这个跟普通的自适应分区调度一样。

关键线程的破产

自适应分区继承

想像这个场景,文件系统在System分区里,但另一个Others分区里的第三方应用拼命调用文件系统,很有可能造成System分区的预算耗尽;这样,首先可能导致别的应用无法使用文件系统;更严重的,可能是System分区里别的系统,比如Audio也无法正常工作。这个,显然是自适应分区系统带来的安全隐患。

自适应分区的小结

自适应分区有一些有趣的用法,比如我们常常被要求“系统需要保留30%的算力”。有了自适应分区,就可以建一个有30%预算的分区,在里面跑一个for(;;);这样的死循环。这样,剩下的系统就只有70%的算力了,可以在这个环境下检验一下系统的性能和稳定性。

THE END
1.汽车也有操作系统吗?(上)——广义的汽车操作系统平常我们说操作系统,首先想起来的就是电脑上的操作系统windows,手机上的操作系统android,ios这些,那么汽车也有操作系统吗? 这里需要科普一下操作系统的概念,操作系统是用户和计算机的接口,同时也是计算机硬件和软件的接口,它能够让计算机系统所有资源最大限度地发挥作用,提供各种形式的用户界面,使用户有一个好的工作环境,https://www.yoojia.com/ask/17-12144509932127577235.html
2.一文读懂智能汽车操作系统汽车传输系统连接系统智能控制系统汽车操作系统是运行在异构分布硬件架构上的实时安全平台软件,提供整车及部件感知、规划、控制等功能框架并向上支撑智能网联驾驶生态的软件集合,是汽车智能计算基础平台安全、实时和高效运行的重要基础和核心支撑。 二、汽车操作系统分类 汽车操作系统包括安全车控操作系统、智能驾驶操作系统和智能座舱操作系统。 https://blog.csdn.net/Anghuikeji/article/details/143185641
3.汽车行业通用的软件架构汽车软件概念汽车行业通用的软件架构 汽车软件概念 一、前言: 一般看《计算机操作系统》的书籍,都会有进程(Process),线程(Thread)的概念。但是在嵌入式RTOS里面,比如应用于汽车软件的OSEK/VDX Operating System Specification 2.2.3规范里没有这两个概念,有的是任务(Task)。那么这三个概念是什么关系呢?本文阐述一下作者的理解。https://blog.51cto.com/u_16213714/11636521
4.《中国汽车基础软件发展报告5.0》解读本文节选自《中国汽车基础软件发展报告5.0》,本报告围绕汽车智能化发展趋势下的软件架构,探讨并关注如何在融入 AI 大模型的情况下,打造安全、可靠、稳定的开放式软件架构,以及该架构中的关键技术、实践案例与发展趋势,旨在为整车智能化软件产业链参与者提供有益的指导和参考。 https://www.eet-china.com/mp/a355280.html
5.车载电子电器架构——国产基础软件生态简介近几年,在汽车 “新四化” 趋势的带动下,软件对于汽车的重要性持续攀升。同时,汽车行业在新一代集中式 E/E 架构中的域控制器或车载计算平台所需的基础软件方面还存在空白,没有形成类似PC行业的 Windows 这种大一统,可供各家整车厂或 Tier1 使用的通用操作系统及中间件。 http://www.360doc.com/content/23/1024/09/78548535_1101431462.shtml
6.软件业务模式研究:汽车软件供应商的四种业务形态与收费模式作为国内最早研发国产车用基础软件的企业,普华基础软件于2010年发布了国内首个自主的汽车基础软件平台及工具链产品,并于2011年通过MBtech官方认证的OSEK标准;同时,在基础软件企业中还率先通过ASPICE3流程认证,拿到德国莱茵颁发的国内首张国产AUTOSAR车控操作系统平台ISO 26262 ASIL D产品认证。2013年,普华车用基础软件平https://www.dongchedi.com/article/7148300292948279823
7.软件定义汽车1—概述由于自身电子设计和机器视觉的背景,早期的项目经历,让我涉猎了各领域的技术,包括电子设计、嵌入式软件、互联网全栈、移动端 app、操作系统、渲染引擎、内核驱动、工业控制现场总线等,每一个部分都不敢说有多么精通,但都经历过实际的项目。对车这个领域,并不是专业出身,之前了解并不多,但为了能理解一帮传统汽车人在https://www.jianshu.com/p/120837904713