赞
踩
说起Blackberry的QNX操作系统, 想必大家都听说过,但到底为什么QNX能如此有名?难道微软的Windows和Linux都不能与之抗衡?
美国NASA的太空接驳飞船也使用QNX操作系统
QNX采用微内核结构,也就是说,内核非常非常非常小。这样一方面启动速度非常快,另一方面安全性稳定性大大提高。
QNX构架是有一个微型内核,然后又包含许多相关进程。这样的好处是,即使有一个进程出错,也不会影响内核。
各个服务进程以及应用进程之间通过内部进程通信IPC的方式进行沟通,如下图:
QNX构架
那什么是进程(pid)呢?如下图:
Process的结构
进程包含自己的一些资源,比如说ID, 内存(代码和数据),计时器,等等..., 并且这些资源是被保护的,也就是说其他进程不能访问。
线程是什么?
一个线程就是一个执行流或者控制流。
它也有一些属性,比如: 优先级, 调度算法,寄存器集合,CPU掩码(用于多核应用),等......
而所有的这些属性都会作用在正在运行的代码上。
顾名思义,核心模块。因为它,系统的各个模块可以协作
其他程序可以通过kernel call的方式来调用核心模块,来执行kernel的代码
大部分的子系统,包括用户应用软件,互相通信都是通过kernel call的方式
kernel是系统的核心
kernel call是采用抢占的方式(pre-emptable)被调用的。好处是,响应新的事件速度会很快,不好是要花更多的时间去恢复到原来被打断的kernel call。
内核可以提供不同的服务,比如: 同步,时钟,进程间通信,调度等等
内核可以提供不同的服务
kernel提供的进程之间的通信种类有三种:
a. Messages, 进程间交换信息
b. Pulses,传递通知给进程
c. Signals, 中断进程,并让它做点别的事情
Messages
Pulses
Signals
事实上,kernel可以被想象成一个library, 并没有一直不停运行的循环进程(no while(1)). 只有在被调用的时候才运行。
2. Process Manager
procnto = Process manager + Micarokernel
Communication with the Process Manager
Process Manager提供服务包括:
a. 捆绑一组threads一起进入process
b. 内存保护,内存空间管理,QNX使用虚拟内存地址
c. 路径名管理
d. process创建和结束
e. 一个idle线程在cpu上运行,当cpu空闲的时候
虚拟地址,物理地址,共享内之间的关系
3. Scheduling
Thread都有两个状态: blocked和runnable.
Thread都有优先级(0-255),kernel总是选择优先级最高的thread来执行
Thread都有属于自己的调度算法,(Round-robin, FIFO等等)
Round-robin 该术语来源于含义为“ 带子”的 法语词 ruban,久而被 讹用并成为 惯用语。在17、18世纪时 法国 农民希望以请愿的方式抗议国王时,通常 君主的反应是将请愿书中最前面的两至三人逮捕并处决,所以很自然地没有人希望自己的名字被列在前面。为了对付这种专制的报复,人们在请愿书底部把名字签成一个圈(如同一条环状的带子),这样就找不出打头的人,于是只能对所有参与者进行同样的惩罚。
4. Resource manager
资源管理,顾名思义,就是提供POSIX规范的接口来管理资源。比如open文件,read文件,写文件...
稍微总结一下:
---QNX是一个微核架构
---进程拥有自己的资源,线程以及代码
---QNX采用抢占式调度策略
进程包含一个以上的线程和许多资源
举个形象的例子:
组装线
Process来控制所有的设备(钻孔机,传送带等),每一种设备就可以想象成一个个thread。
当然还有一些更多的层级关系:
接下来两个多线程的进程的例子(multithreaded processes):
1. 一个实时性要求很高的进程和硬件进行通信,其他的线程可以慢条斯理的和其他process进行通信
2. Pool of worker threads. 很多线程准备着,当其他线程都忙的时候,新的请求依旧可以有线程来满足
一个process里的threads都有属于自己的内存地址(虚拟地址),其他的资源都是共享的。
一个process的虚拟地址
每一个thread都有一个最大的体积,也不是每一个都需要分配物理内存。
1 进程process
fork(), exec*(), spawn(), spawn*(), posix_spawn()
举个例子fork():
fork() will create a copy of your process
fork 这个英文单词在英文里是"分叉"意思, fork() 这个函数作用也很符合这个意思. 它的作用是复制当前进程(包括进程在内存里的堆栈数据)为1个新的镜像. 然后这个新的镜像和旧的进程同时执行下去. 相当于本来1个进程, 遇到fork() 函数后就分叉成两个进程同时执行了. 而且这两个进程是互不影响.
fork
实际应用中, 单纯让程序分叉意义不大, 我们新增一个子程序, 很可能是为了让子进程单独执行一段代码. 实现与主进程不同的功能. 要实现上面所说的功能, 实际上就是让子进程和主进程执行不同的代码啊. 所以fork() 实际上有返回值, 而且在两条进程中的返回值是不同的, 在主进程里 fork()函数会返回主进程的pid, 而在子进程里会返回0! 所以我们可以根据fork() 的返回值来判断进程到底是哪个进程, 就可以利用if 语句来执行不同的代码了!
2 线程Thread
pthread_create()可以用来创建线程。
每个线程其实就是执行一个fun(). 每一个fun()就是一个thread。
pthread_create()会返回tid(thread ID).
pthread_attr_init()可以设置一个线程的default值。
如果你想设置thread优先级和调度算法:
param.sched_priority = 15; %优先级值为15
pthread_attr_setschedparam (&attr, ¶m); %给该thread设定优先级
pthread_attr_setschedpolicy (&attr, SCHED_RR); %设定调度算法为 Round- Robin
Process里面,第一个thread就是main thread, 因为它调用了整个process的main()函数. 如果exit()被调用,那么整个process就结束死亡了。
同样的道理,如果在一个thread里, 如果pthread_exit()被调用了,那么thread也就会结束死亡。
如果一个process里面,所有的threads都死亡了,那么这个process就会死亡。
无论process如何死亡的,所有的相应的资源(内存,channels等)都会被释放或清理。
3. 同步Synchronization
多threads却引入了新的问题,比如公用内存空间,多个writers可能会互相覆盖对方的值, readers也不知道什么时候数据是稳定有效地。
所以我们需要同步机制来协调管理。
3.1 Mutual exclusion
Mutual exclusion意味着只有一个thread在某一时间里可以执行某段重要的代码段,或者读写一些特别的数据。一个形象的例子:
把厕所空间比喻成内存空间,每次只能进去一个人,里面有人的时候,其他人就不能进去了。这代表一个thread使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存. 问题是如何防止别人也同时进去呢?
一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫"互斥锁"(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。
Mutex
其实我的理解就是Mutex就是一个process内的对于所有threads来说的全局01变量,你要锁的时候就把这个全局变量Mutex设置为1,其他的thread读到这个Mutex的时候就知道你在使用,就停下来等,知道你用完了把Mutex设置回0,然后别的进程才可以进去用。
当然这样又会产生新的问题,死锁问题(Dead lock). 对此,我们可以设计一些特殊的执行顺序来避免死锁,这里就不打算展开了。
当然一旦锁定了Mutex,该thread的优先级就会上升。因为系统当然希望这个thread赶紧运行完毕,毕竟不能占着茅坑不拉屎。
拥有Mutex的thread的优先级就会上升
3.2 条件锁Condvars
光有互斥锁还是不够,最好有一把聪明的互斥锁。比如说,只有满足了某种条件(收到某种信号)的情况下,才能解锁。这样就会更高效的执行程序功能。
其实总体看起来,线程的执行很像最近流行的宫廷剧里,一个皇上(CPU)拥有很多嫔妃(Threads),但是宗旨是雨露均沾。所以如何雨露均沾就是一件需要调度和协调的事情,最后还尽量要让嫔妃们都满意,苦了皇上了,哈哈。
既然有了进程process,那么不同进程间通信就很有必要了。两个进程之间要交换数据,控制,以及事件通知。
Message passing ---比较传统的IPC方式是基于主从式构架(client-server),并且是双向通信。
再仔细来看的话,就是每一个process里面都有一个thread来负责通信。当一个线程在等待回信的时候,就会傻傻的等待,什么都不做了。直到收到回复信息。
傻等
Servers收到信息在通道上,Clients通过connection连接上channel,来发送信息。
一个进程可以有多个connections连接到另一个进程的channel上,是个多对一的关系。
多connections和多channels
Server创建Channel:
chid = ChannelCreate (flags);
Client连接上Server的channel:
coid = ConnectAttach(nd, pid, chid, _NTO_SIDE_CHANNEL, flags);
信息的发送:
status = MsgSend (coid, smsg, sbytes, rmsg, rbytes);
信息的接收:
rcvid = MsgReceive (chid, rmsg, rbytes, info);
接下来来点干货,看一个demo代码:
Massage之间的通信数据总是通过拷贝,而不是指针的传递。
那么如何设计消息传递策略呢?一个例子看一下:
Pulses脉冲
脉冲的通信方式很特别,就像喊命令,不需要回应,执行就好了。便宜还快速,也不会发生blocking的现象。
Pulse命令
一个例子:
Event Delivery
Event是一种notification。可是从thread到thread,也可以从kernel到thread。比如硬件打断kernel的通知,或者timer到期的通知。
Shared Memory
如果通过设置shared memory, 同样的物理内存可以被多个进程访问。
After setting up a shared memory region, the samephysical memory is accessible to multipleprocesses:
preocess进程间通过shared memory通信同步策略:
IPC for synchronization
IPC for synchronization
Client先准备好共享内存的内容,然后告诉Server一切准备好了。接着Server读取共享内存内容,并做处理。最后Server回复Client说搞定了。这样Client就可以继续准备下一个任务了。
IPC想想也是很重要的,如果你设计的系统功能需要几个process的相互协作,你就绕不开IPC这个概念,下次准备有机会在一个实际的例子里看看IPC到底有什么用,怎么用。
QNX是一个微内核的实时操作系统。2021年,QNX已经不再是旧时王谢堂前燕,广泛应用于各种量产智能座舱和自动驾驶的域控制器中,和Android/linux一样,早已飞入寻常百姓家了。
在QNX的系统定义中,包含着以下两个概念:
微内核(Micro Kernel):是提供操作系统核心功能的内核精简版本。
实时操作系统(RTOS):是指当外界事件和数据产生时,能够接受并以足够快的速度予以处理,其处理的结果又能在规定的时间之内,来控制生成过程或对处理系统做出快速响应,调度一切可以利用的资源完成实时任务,并控制所有实时任务协调一致运行的操作系统。
QNX的核心提供4种服务:
因此QNX内核非常的精致小巧,比传统的宏内核(Linux)系统可靠性更高。
QNX 提供POSⅨ.1b标准进程调度:
- 255个进程优先级
- 抢占式的、基于优先级的正文切换
- 可选调度策略:FIFO、轮转策略、适应性策略
QNX的微内核结构及其与外部的联系
为什么各大主机厂和Tier1在量产自动驾驶的时候,会选择QNX而非Linux或其他宏内核系统呢?
随着汽车行业“四化”的发展,域控制器等ECU所承载的功能和算法呈几何倍数的增加。软件定义汽车使得整个车载ECU的软件变得非常的复杂和庞大,一个域控制器的软件代码可能有几万个文件,几百万行代码。在这样的代码量下,要保证每个组件正常工作,就需要一整套的维护/运行策略。汽车厂商及其供应商们为了设计出安全可靠的软件系统也做了很多的努力:比如在研发流程上执行ASPICE,强调设计的溯源和变更追踪;在架构设计上启用AutoSAR, 维护框架和接口的一致性;同时,使用QNX等具有功能安全的操作系统也是目前业界所广泛认同的一个策略。
那么为什么说QNX系统更安全呢?QNX的开发者们如何通过系统组件来进行软件设计/开发呢?下面是一些QNX特性的例子:
有时候我们会遇到这样一种问题,某进程里有个bug,调试的时候发现该进程一起来整个一个核的CPU都满了,我们猜测系统内可能是出现了死循环。
自适应分区是QNX的重要特性之一,也是一个在开发调试阶段很好用的一个工具。
在软件集成在QNX系统之后,开始优化整个系统之前,为了保护不同的应用群组/应用,独立运行而不被其他应用破坏或干扰,操作系统采用“虚拟墙(virtual walls)”将系统的共享资源(CPU执行时间/内存/存储空间等)以一定的比例划分,以确保每个分区都有一组经过工程设计的资源,每个分区内可以运行一个或多个线程。
ap分区概念
分区能够提供:
自适应的含义是:在运行时可以改变配置。例如,空闲时间被重新分配给其他调度程序分区,系统会使用一种机制,使得CPU可以在一个时间分区之间临时移动线程。
我们设计的软件系统,在很大的概率上是存在很多漏洞的,那么一旦发送故障,有没有什么办法能快速的使整个软件系统尽快恢复正常状态呢?
(上电重启不是一个系统持续运行的好办法)
QNX提供了一套高可靠性的软件框架,当系统内的部分进行或线程失效时,通过这一套框架来进行系统的处理。这一套框架被称为HAM。其基本思想是:
内核独立自处于一个被保护的地址空间;驱动程序、网络协议和应用程序处于程序空间中。
微内核结构的优点:
①驱动程序、网络协议、文件系统等操作系统模块和内核相互独立,任何模块的故障都不会导致内核的崩溃;
②驱动程序、网络协议、文件系统和应用程序都处于程序空间,都调用相同的内核API,开发与调试和应用程序没有区别;
③操作系统功能模块可以根据需要动态地加载或卸载,不需要编译内核。
在具有高可靠性内核的基础上,QNX的创新设计使它同样具有很高的效率。
QNX最为引人注目的地方是,它是UNⅨ的同胞异构体,保持了和UNⅨ的高度相似性,绝大多数UNⅨ或LINUX应用程序可以在QNX下直接编译生成。
这意味着为数众多的稳定成熟的UNⅨ、LINUX应用可以直接移植到QNX这个更加稳定高效的实时嵌入式平台上来。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。