說起B(yǎng)lackberry的QNX操作系統(tǒng), 想必大家都聽說過,但到底為什么QNX能如此有名?難道微軟的Windows和Linux都不能與之抗衡?
美國NASA的太空接駁飛船也使用QNX操作系統(tǒng)
QNX采用微內(nèi)核結(jié)構(gòu),也就是說,內(nèi)核非常非常非常小。這樣一方面啟動(dòng)速度非???,另一方面安全性穩(wěn)定性大大提高。
QNX構(gòu)架是有一個(gè)微型內(nèi)核,然后又包含許多相關(guān)進(jìn)程。這樣的好處是,即使有一個(gè)進(jìn)程出錯(cuò),也不會(huì)影響內(nèi)核。
各個(gè)服務(wù)進(jìn)程以及應(yīng)用進(jìn)程之間通過內(nèi)部進(jìn)程通信IPC的方式進(jìn)行溝通,如下圖:
QNX構(gòu)架
那什么是進(jìn)程(pid)呢?如下圖:
Process的結(jié)構(gòu)
進(jìn)程包含自己的一些資源,比如說ID, 內(nèi)存(代碼和數(shù)據(jù)),計(jì)時(shí)器,等等..., 并且這些資源是被保護(hù)的,也就是說其他進(jìn)程不能訪問。
線程是什么?
一個(gè)線程就是一個(gè)執(zhí)行流或者控制流。
它也有一些屬性,比如: 優(yōu)先級, 調(diào)度算法,寄存器集合,CPU掩碼(用于多核應(yīng)用),等......
而所有的這些屬性都會(huì)作用在正在運(yùn)行的代碼上。
Kernel
顧名思義,核心模塊。因?yàn)樗?,系統(tǒng)的各個(gè)模塊可以協(xié)作
其他程序可以通過kernel call的方式來調(diào)用核心模塊,來執(zhí)行kernel的代碼
大部分的子系統(tǒng),包括用戶應(yīng)用軟件,互相通信都是通過kernel call的方式
kernel是系統(tǒng)的核心
kernel call是采用搶占的方式(pre-emptable)被調(diào)用的。好處是,響應(yīng)新的事件速度會(huì)很快,不好是要花更多的時(shí)間去恢復(fù)到原來被打斷的kernel call。
內(nèi)核可以提供不同的服務(wù),比如: 同步,時(shí)鐘,進(jìn)程間通信,調(diào)度等等
內(nèi)核可以提供不同的服務(wù)
kernel提供的進(jìn)程之間的通信種類有三種:
a. Messages, 進(jìn)程間交換信息
b. Pulses,傳遞通知給進(jìn)程
c. Signals, 中斷進(jìn)程,并讓它做點(diǎn)別的事情
Messages
Pulses
Signals
事實(shí)上,kernel可以被想象成一個(gè)library, 并沒有一直不停運(yùn)行的循環(huán)進(jìn)程(no while(1)). 只有在被調(diào)用的時(shí)候才運(yùn)行。
2. Process Manager
procnto = Process manager + Micarokernel
Communication with the Process Manager
Process Manager提供服務(wù)包括:
a. 捆綁一組threads一起進(jìn)入process
b. 內(nèi)存保護(hù),內(nèi)存空間管理,QNX使用虛擬內(nèi)存地址
c. 路徑名管理
d. process創(chuàng)建和結(jié)束
e. 一個(gè)idle線程在cpu上運(yùn)行,當(dāng)cpu空閑的時(shí)候
虛擬地址,物理地址,共享內(nèi)之間的關(guān)系
3. Scheduling
Thread都有兩個(gè)狀態(tài): blocked和runnable.
Thread都有優(yōu)先級(0-255),kernel總是選擇優(yōu)先級最高的thread來執(zhí)行
Thread都有屬于自己的調(diào)度算法,(Round-robin, FIFO等等)
Round-robin 該術(shù)語來源于含義為“ 帶子”的 法語詞 ruban,久而被 訛用并成為 慣用語。在17、18世紀(jì)時(shí) 法國 農(nóng)民希望以請?jiān)傅姆绞娇棺h國王時(shí),通常 君主的反應(yīng)是將請?jiān)笗凶钋懊娴膬芍寥舜恫⑻帥Q,所以很自然地沒有人希望自己的名字被列在前面。為了對付這種專制的報(bào)復(fù),人們在請?jiān)笗撞堪衙趾灣梢粋€(gè)圈(如同一條環(huán)狀的帶子),這樣就找不出打頭的人,于是只能對所有參與者進(jìn)行同樣的懲罰。
4. Resource manager
資源管理,顧名思義,就是提供POSIX規(guī)范的接口來管理資源。比如open文件,read文件,寫文件...
稍微總結(jié)一下:
---QNX是一個(gè)微核架構(gòu)
---進(jìn)程擁有自己的資源,線程以及代碼
---QNX采用搶占式調(diào)度策略
進(jìn)程包含一個(gè)以上的線程和許多資源
舉個(gè)形象的例子:
組裝線
Process來控制所有的設(shè)備(鉆孔機(jī),傳送帶等),每一種設(shè)備就可以想象成一個(gè)個(gè)thread。
當(dāng)然還有一些更多的層級關(guān)系:
接下來兩個(gè)多線程的進(jìn)程的例子(multithreaded processes):
1. 一個(gè)實(shí)時(shí)性要求很高的進(jìn)程和硬件進(jìn)行通信,其他的線程可以慢條斯理的和其他process進(jìn)行通信
2. Pool of worker threads. 很多線程準(zhǔn)備著,當(dāng)其他線程都忙的時(shí)候,新的請求依舊可以有線程來滿足
一個(gè)process里的threads都有屬于自己的內(nèi)存地址(虛擬地址),其他的資源都是共享的。
一個(gè)process的虛擬地址
每一個(gè)thread都有一個(gè)最大的體積,也不是每一個(gè)都需要分配物理內(nèi)存。
1 進(jìn)程process
fork(), exec*(), spawn(), spawn*(), posix_spawn()
舉個(gè)例子fork():
fork() will create a copy of your process
fork 這個(gè)英文單詞在英文里是"分叉"意思, fork() 這個(gè)函數(shù)作用也很符合這個(gè)意思. 它的作用是復(fù)制當(dāng)前進(jìn)程(包括進(jìn)程在內(nèi)存里的堆棧數(shù)據(jù))為1個(gè)新的鏡像. 然后這個(gè)新的鏡像和舊的進(jìn)程同時(shí)執(zhí)行下去. 相當(dāng)于本來1個(gè)進(jìn)程, 遇到fork() 函數(shù)后就分叉成兩個(gè)進(jìn)程同時(shí)執(zhí)行了. 而且這兩個(gè)進(jìn)程是互不影響.
fork
實(shí)際應(yīng)用中, 單純讓程序分叉意義不大, 我們新增一個(gè)子程序, 很可能是為了讓子進(jìn)程單獨(dú)執(zhí)行一段代碼. 實(shí)現(xiàn)與主進(jìn)程不同的功能. 要實(shí)現(xiàn)上面所說的功能, 實(shí)際上就是讓子進(jìn)程和主進(jìn)程執(zhí)行不同的代碼啊. 所以fork() 實(shí)際上有返回值, 而且在兩條進(jìn)程中的返回值是不同的, 在主進(jìn)程里 fork()函數(shù)會(huì)返回主進(jìn)程的pid, 而在子進(jìn)程里會(huì)返回0! 所以我們可以根據(jù)fork() 的返回值來判斷進(jìn)程到底是哪個(gè)進(jìn)程, 就可以利用if 語句來執(zhí)行不同的代碼了!
2 線程Thread
pthread_create()可以用來創(chuàng)建線程。
每個(gè)線程其實(shí)就是執(zhí)行一個(gè)fun(). 每一個(gè)fun()就是一個(gè)thread。
pthread_create()會(huì)返回tid(thread ID).
pthread_attr_init()可以設(shè)置一個(gè)線程的default值。
如果你想設(shè)置thread優(yōu)先級和調(diào)度算法:
param.sched_priority = 15; %優(yōu)先級值為15
pthread_attr_setschedparam (&attr, ¶m); %給該thread設(shè)定優(yōu)先級
pthread_attr_setschedpolicy (&attr, SCHED_RR); %設(shè)定調(diào)度算法為 Round- Robin
Process里面,第一個(gè)thread就是main thread, 因?yàn)樗{(diào)用了整個(gè)process的main()函數(shù). 如果exit()被調(diào)用,那么整個(gè)process就結(jié)束死亡了。
同樣的道理,如果在一個(gè)thread里, 如果pthread_exit()被調(diào)用了,那么thread也就會(huì)結(jié)束死亡。
如果一個(gè)process里面,所有的threads都死亡了,那么這個(gè)process就會(huì)死亡。
無論process如何死亡的,所有的相應(yīng)的資源(內(nèi)存,channels等)都會(huì)被釋放或清理。
3. 同步Synchronization
多threads卻引入了新的問題,比如公用內(nèi)存空間,多個(gè)writers可能會(huì)互相覆蓋對方的值, readers也不知道什么時(shí)候數(shù)據(jù)是穩(wěn)定有效地。
所以我們需要同步機(jī)制來協(xié)調(diào)管理。
3.1 Mutual exclusion
Mutual exclusion意味著只有一個(gè)thread在某一時(shí)間里可以執(zhí)行某段重要的代碼段,或者讀寫一些特別的數(shù)據(jù)。一個(gè)形象的例子:
把廁所空間比喻成內(nèi)存空間,每次只能進(jìn)去一個(gè)人,里面有人的時(shí)候,其他人就不能進(jìn)去了。這代表一個(gè)thread使用某些共享內(nèi)存時(shí),其他線程必須等它結(jié)束,才能使用這一塊內(nèi)存. 問題是如何防止別人也同時(shí)進(jìn)去呢?
一個(gè)防止他人進(jìn)入的簡單方法,就是門口加一把鎖。先到的人鎖上門,后到的人看到上鎖,就在門口排隊(duì),等鎖打開再進(jìn)去。這就叫"互斥鎖"(Mutual exclusion,縮寫 Mutex),防止多個(gè)線程同時(shí)讀寫某一塊內(nèi)存區(qū)域。
Mutex
其實(shí)我的理解就是Mutex就是一個(gè)process內(nèi)的對于所有threads來說的全局01變量,你要鎖的時(shí)候就把這個(gè)全局變量Mutex設(shè)置為1,其他的thread讀到這個(gè)Mutex的時(shí)候就知道你在使用,就停下來等,知道你用完了把Mutex設(shè)置回0,然后別的進(jìn)程才可以進(jìn)去用。
當(dāng)然這樣又會(huì)產(chǎn)生新的問題,死鎖問題(Dead lock). 對此,我們可以設(shè)計(jì)一些特殊的執(zhí)行順序來避免死鎖,這里就不打算展開了。
當(dāng)然一旦鎖定了Mutex,該thread的優(yōu)先級就會(huì)上升。因?yàn)橄到y(tǒng)當(dāng)然希望這個(gè)thread趕緊運(yùn)行完畢,畢竟不能占著茅坑不拉屎。
擁有Mutex的thread的優(yōu)先級就會(huì)上升
3.2 條件鎖Condvars
光有互斥鎖還是不夠,最好有一把聰明的互斥鎖。比如說,只有滿足了某種條件(收到某種信號(hào))的情況下,才能解鎖。這樣就會(huì)更高效的執(zhí)行程序功能。
其實(shí)總體看起來,線程的執(zhí)行很像最近流行的宮廷劇里,一個(gè)皇上(CPU)擁有很多嬪妃(Threads),但是宗旨是雨露均沾。所以如何雨露均沾就是一件需要調(diào)度和協(xié)調(diào)的事情,最后還盡量要讓嬪妃們都滿意,苦了皇上了,哈哈。
既然有了進(jìn)程process,那么不同進(jìn)程間通信就很有必要了。兩個(gè)進(jìn)程之間要交換數(shù)據(jù),控制,以及事件通知。
Message passing ---比較傳統(tǒng)的IPC方式是基于主從式構(gòu)架(client-server),并且是雙向通信。
再仔細(xì)來看的話,就是每一個(gè)process里面都有一個(gè)thread來負(fù)責(zé)通信。當(dāng)一個(gè)線程在等待回信的時(shí)候,就會(huì)傻傻的等待,什么都不做了。直到收到回復(fù)信息。
傻等
Servers收到信息在通道上,Clients通過connection連接上channel,來發(fā)送信息。
一個(gè)進(jìn)程可以有多個(gè)connections連接到另一個(gè)進(jìn)程的channel上,是個(gè)多對一的關(guān)系。
多connections和多channels
Server創(chuàng)建Channel:
chid = ChannelCreate (flags);
Client連接上Server的channel:
coid = ConnectAttach(nd, pid, chid, _NTO_SIDE_CHANNEL, flags);
信息的發(fā)送:
status = MsgSend (coid, smsg, sbytes, rmsg, rbytes);
信息的接收:
rcvid = MsgReceive (chid, rmsg, rbytes, info);
接下來來點(diǎn)干貨,看一個(gè)demo代碼:
Massage之間的通信數(shù)據(jù)總是通過拷貝,而不是指針的傳遞。
那么如何設(shè)計(jì)消息傳遞策略呢?一個(gè)例子看一下:
Pulses脈沖
脈沖的通信方式很特別,就像喊命令,不需要回應(yīng),執(zhí)行就好了。便宜還快速,也不會(huì)發(fā)生blocking的現(xiàn)象。
Pulse命令
一個(gè)例子:
Event Delivery
Event是一種notification??墒菑膖hread到thread,也可以從kernel到thread。比如硬件打斷kernel的通知,或者timer到期的通知。
Shared Memory
如果通過設(shè)置shared memory, 同樣的物理內(nèi)存可以被多個(gè)進(jìn)程訪問。
After setting up a shared memory region, the samephysical memory is accessible to multipleprocesses:
preocess進(jìn)程間通過shared memory通信同步策略:
IPC for synchronization
IPC for synchronization
Client先準(zhǔn)備好共享內(nèi)存的內(nèi)容,然后告訴Server一切準(zhǔn)備好了。接著Server讀取共享內(nèi)存內(nèi)容,并做處理。最后Server回復(fù)Client說搞定了。這樣Client就可以繼續(xù)準(zhǔn)備下一個(gè)任務(wù)了。
IPC想想也是很重要的,如果你設(shè)計(jì)的系統(tǒng)功能需要幾個(gè)process的相互協(xié)作,你就繞不開IPC這個(gè)概念,下次準(zhǔn)備有機(jī)會(huì)在一個(gè)實(shí)際的例子里看看IPC到底有什么用,怎么用。
QNX是一個(gè)微內(nèi)核的實(shí)時(shí)操作系統(tǒng)。2021年,QNX已經(jīng)不再是舊時(shí)王謝堂前燕,廣泛應(yīng)用于各種量產(chǎn)智能座艙和自動(dòng)駕駛的域控制器中,和Android/linux一樣,早已飛入尋常百姓家了。
在QNX的系統(tǒng)定義中,包含著以下兩個(gè)概念:
微內(nèi)核(Micro Kernel):是提供操作系統(tǒng)核心功能的內(nèi)核精簡版本。
實(shí)時(shí)操作系統(tǒng)(RTOS):是指當(dāng)外界事件和數(shù)據(jù)產(chǎn)生時(shí),能夠接受并以足夠快的速度予以處理,其處理的結(jié)果又能在規(guī)定的時(shí)間之內(nèi),來控制生成過程或?qū)μ幚硐到y(tǒng)做出快速響應(yīng),調(diào)度一切可以利用的資源完成實(shí)時(shí)任務(wù),并控制所有實(shí)時(shí)任務(wù)協(xié)調(diào)一致運(yùn)行的操作系統(tǒng)。
QNX的核心提供4種服務(wù):
進(jìn)程調(diào)度
進(jìn)程間通信
底層網(wǎng)絡(luò)通信
中斷處理
因此QNX內(nèi)核非常的精致小巧,比傳統(tǒng)的宏內(nèi)核(Linux)系統(tǒng)可靠性更高。
QNX 提供POSⅨ.1b標(biāo)準(zhǔn)進(jìn)程調(diào)度:
255個(gè)進(jìn)程優(yōu)先級
搶占式的、基于優(yōu)先級的正文切換
可選調(diào)度策略:FIFO、輪轉(zhuǎn)策略、適應(yīng)性策略
QNX的微內(nèi)核結(jié)構(gòu)及其與外部的聯(lián)系
為什么各大主機(jī)廠和Tier1在量產(chǎn)自動(dòng)駕駛的時(shí)候,會(huì)選擇QNX而非Linux或其他宏內(nèi)核系統(tǒng)呢?
隨著汽車行業(yè)“四化”的發(fā)展,域控制器等ECU所承載的功能和算法呈幾何倍數(shù)的增加。軟件定義汽車使得整個(gè)車載ECU的軟件變得非常的復(fù)雜和龐大,一個(gè)域控制器的軟件代碼可能有幾萬個(gè)文件,幾百萬行代碼。在這樣的代碼量下,要保證每個(gè)組件正常工作,就需要一整套的維護(hù)/運(yùn)行策略。汽車廠商及其供應(yīng)商們?yōu)榱嗽O(shè)計(jì)出安全可靠的軟件系統(tǒng)也做了很多的努力:比如在研發(fā)流程上執(zhí)行ASPICE,強(qiáng)調(diào)設(shè)計(jì)的溯源和變更追蹤;在架構(gòu)設(shè)計(jì)上啟用AutoSAR, 維護(hù)框架和接口的一致性;同時(shí),使用QNX等具有功能安全的操作系統(tǒng)也是目前業(yè)界所廣泛認(rèn)同的一個(gè)策略。
那么為什么說QNX系統(tǒng)更安全呢?QNX的開發(fā)者們?nèi)绾瓮ㄟ^系統(tǒng)組件來進(jìn)行軟件設(shè)計(jì)/開發(fā)呢?下面是一些QNX特性的例子:
AP(Adaptive Partitioning)
有時(shí)候我們會(huì)遇到這樣一種問題,某進(jìn)程里有個(gè)bug,調(diào)試的時(shí)候發(fā)現(xiàn)該進(jìn)程一起來整個(gè)一個(gè)核的CPU都滿了,我們猜測系統(tǒng)內(nèi)可能是出現(xiàn)了死循環(huán)。
自適應(yīng)分區(qū)是QNX的重要特性之一,也是一個(gè)在開發(fā)調(diào)試階段很好用的一個(gè)工具。
在軟件集成在QNX系統(tǒng)之后,開始優(yōu)化整個(gè)系統(tǒng)之前,為了保護(hù)不同的應(yīng)用群組/應(yīng)用,獨(dú)立運(yùn)行而不被其他應(yīng)用破壞或干擾,操作系統(tǒng)采用“虛擬墻(virtual walls)”將系統(tǒng)的共享資源(CPU執(zhí)行時(shí)間/內(nèi)存/存儲(chǔ)空間等)以一定的比例劃分,以確保每個(gè)分區(qū)都有一組經(jīng)過工程設(shè)計(jì)的資源,每個(gè)分區(qū)內(nèi)可以運(yùn)行一個(gè)或多個(gè)線程。
ap分區(qū)概念
分區(qū)能夠提供:
內(nèi)存保護(hù):提供內(nèi)存保護(hù),即每個(gè)分區(qū)是離散的,由內(nèi)存管理單元控制(MMU);
過載保護(hù):提供過載保護(hù),即根據(jù)系統(tǒng)設(shè)計(jì)人員的指定,每個(gè)分區(qū)都有一段執(zhí)行時(shí)間。
自適應(yīng)的含義是:在運(yùn)行時(shí)可以改變配置。例如,空閑時(shí)間被重新分配給其他調(diào)度程序分區(qū),系統(tǒng)會(huì)使用一種機(jī)制,使得CPU可以在一個(gè)時(shí)間分區(qū)之間臨時(shí)移動(dòng)線程。
2. HAM(High Availability Manager)
我們設(shè)計(jì)的軟件系統(tǒng),在很大的概率上是存在很多漏洞的,那么一旦發(fā)送故障,有沒有什么辦法能快速的使整個(gè)軟件系統(tǒng)盡快恢復(fù)正常狀態(tài)呢?
(上電重啟不是一個(gè)系統(tǒng)持續(xù)運(yùn)行的好辦法)
QNX提供了一套高可靠性的軟件框架,當(dāng)系統(tǒng)內(nèi)的部分進(jìn)行或線程失效時(shí),通過這一套框架來進(jìn)行系統(tǒng)的處理。這一套框架被稱為HAM。其基本思想是:
系統(tǒng)隔離:隔離系統(tǒng)中的問題區(qū)域,確保系統(tǒng)組件的獨(dú)立,每個(gè)組件(進(jìn)程)享有完全基于MMU的內(nèi)存保護(hù);
多級恢復(fù):HAM在系統(tǒng)出現(xiàn)問題是可以執(zhí)行多級恢復(fù),按特定的順序執(zhí)行多個(gè)操作(這在系統(tǒng)的各個(gè)進(jìn)程間存在各種嚴(yán)格依賴關(guān)系的時(shí)候非常有效,這樣系統(tǒng)就可以把自己恢復(fù)到bug前的狀態(tài))
內(nèi)核獨(dú)立自處于一個(gè)被保護(hù)的地址空間;驅(qū)動(dòng)程序、網(wǎng)絡(luò)協(xié)議和應(yīng)用程序處于程序空間中。
微內(nèi)核結(jié)構(gòu)的優(yōu)點(diǎn):
①驅(qū)動(dòng)程序、網(wǎng)絡(luò)協(xié)議、文件系統(tǒng)等操作系統(tǒng)模塊和內(nèi)核相互獨(dú)立,任何模塊的故障都不會(huì)導(dǎo)致內(nèi)核的崩潰;
②驅(qū)動(dòng)程序、網(wǎng)絡(luò)協(xié)議、文件系統(tǒng)和應(yīng)用程序都處于程序空間,都調(diào)用相同的內(nèi)核API,開發(fā)與調(diào)試和應(yīng)用程序沒有區(qū)別;
③操作系統(tǒng)功能模塊可以根據(jù)需要?jiǎng)討B(tài)地加載或卸載,不需要編譯內(nèi)核。
在具有高可靠性內(nèi)核的基礎(chǔ)上,QNX的創(chuàng)新設(shè)計(jì)使它同樣具有很高的效率。
QNX最為引人注目的地方是,它是UNⅨ的同胞異構(gòu)體,保持了和UNⅨ的高度相似性,絕大多數(shù)UNⅨ或LINUX應(yīng)用程序可以在QNX下直接編譯生成。
這意味著為數(shù)眾多的穩(wěn)定成熟的UNⅨ、LINUX應(yīng)用可以直接移植到QNX這個(gè)更加穩(wěn)定高效的實(shí)時(shí)嵌入式平臺(tái)上來。
聯(lián)系客服