QNX相關(guān)歷史文章:
介紹
QNX Neutrino微內(nèi)核procnto
實(shí)現(xiàn)了嵌入式實(shí)時(shí)系統(tǒng)中常用的核心POSIX功能,并提供基本的消息傳遞服務(wù)可训。而未實(shí)現(xiàn)的POSIX功能(比如文件沈矿、設(shè)備IO)則可以通過可選的進(jìn)程和共享庫(kù)來提供莉御。
微內(nèi)核包含了一些基本對(duì)象以及操作這些對(duì)象的例程馍佑,這些對(duì)象定義得很具體条舔,而且高度可重用毛肋,整個(gè)操作系統(tǒng)在此之上構(gòu)建的怨咪。
系統(tǒng)服務(wù)
QNX Neutrino微內(nèi)核提供了一系列系統(tǒng)調(diào)用來支持以下服務(wù):
- 線程
- 消息傳遞
- 信號(hào)
- 時(shí)鐘
- 定時(shí)器
- 中斷處理
- 信號(hào)量
- 互斥鎖
- 條件變量
-
屏障
整個(gè)系統(tǒng)都是基于這些系統(tǒng)調(diào)用來構(gòu)建的,QNX完全可搶占润匙,甚至在消息傳遞的過程時(shí)也能被搶占诗眨,并在搶占完成后恢復(fù)之前的消息傳遞狀態(tài)。
微內(nèi)核實(shí)現(xiàn)越簡(jiǎn)單孕讳,越有利于減少不可搶占區(qū)間的長(zhǎng)度匠楚,同時(shí)巍膘,代碼量少,讓解決復(fù)雜的多處理器問題也變得簡(jiǎn)單芋簿。將系統(tǒng)服務(wù)包含進(jìn)內(nèi)核的前提是峡懈,系統(tǒng)服務(wù)只有一個(gè)短的執(zhí)行路徑長(zhǎng)度。需要執(zhí)行很多工作的操作益咬,可以交給外部的進(jìn)程或線程去做逮诲。
嚴(yán)格按照上邊的規(guī)則來劃分內(nèi)核和外部進(jìn)程功能的話,微內(nèi)核的運(yùn)行時(shí)負(fù)載不見得就高于單內(nèi)核幽告。簡(jiǎn)單內(nèi)核的上下文切換的時(shí)間非趁佛校快,相比于在進(jìn)程間通過消息傳遞來服務(wù)請(qǐng)求的時(shí)間冗锁,上下文的切換開銷微不足道齐唆。
下圖演示了在非對(duì)稱多處理器內(nèi)核(X86實(shí)現(xiàn))搶占的細(xì)節(jié),其中冻河,中斷禁用或禁止搶占的時(shí)間非常短箍邮,通常為幾百納秒。
QNX Neutrino搶占
線程和進(jìn)程
在開發(fā)應(yīng)用程序時(shí)(實(shí)時(shí)叨叙、嵌入式锭弊、圖形等),通常會(huì)用到POSIX線程模型來實(shí)現(xiàn)多個(gè)算法同時(shí)執(zhí)行擂错。線程是微內(nèi)核中最小的執(zhí)行和調(diào)度單元味滞,進(jìn)程可以認(rèn)為是線程的“容器”,定義了線程將在其中執(zhí)行的“地址空間”钮呀,進(jìn)程會(huì)包含一個(gè)或多個(gè)線程剑鞍。
應(yīng)用程序中的線程有可能相互獨(dú)立,也可能緊密的聯(lián)系爽醋,QNX Neutrino提供了豐富的IPC和同步服務(wù)蚁署。
其中不涉及微內(nèi)核線程調(diào)用的POSIX接口有如下:
下表中的POSIX接口,微內(nèi)核中有對(duì)應(yīng)的接口實(shí)現(xiàn)一樣的功能蚂四,允許自己來選擇:
線程屬性
盡管進(jìn)程中的線程共享進(jìn)程地址空間中的所有內(nèi)容光戈,但每個(gè)線程仍然有一些“私有”數(shù)據(jù),在某些情況下遂赠,這些私有數(shù)據(jù)在內(nèi)核中受到保護(hù)田度,比如線程ID/進(jìn)程ID;而其他的可能不受保護(hù)解愤,比如線程的堆棧镇饺。
值得注意的私有數(shù)據(jù)有:
- tid,線程ID送讲,每個(gè)線程在進(jìn)程中都有唯一的ID奸笤,從1開始惋啃;
- priority,每個(gè)線程都有一個(gè)調(diào)度的優(yōu)先級(jí)监右,線程的初始優(yōu)先級(jí)是繼承而來边灭,并且可以根據(jù)調(diào)度策略進(jìn)行改變,進(jìn)程沒有優(yōu)先級(jí)健盒;
- name绒瘦,線程名字,可以通過
pthread_getname_np()
和pthread_setname_np()
來獲取和設(shè)置扣癣; - Register set惰帽,每個(gè)線程都有IP、SP以及處理器相關(guān)的寄存器上下文父虑;
- Stack该酗,線程在自己的堆棧上執(zhí)行,存儲(chǔ)在其進(jìn)程的地址空間中士嚎;
- Signal mask呜魄,信號(hào)掩碼;
- Thread local storage莱衩,線程本地存儲(chǔ)TLS爵嗅,用于存儲(chǔ)線程私有的數(shù)據(jù),用戶不需要直接訪問TLS笨蚁,線程可以通過線程特定的key與用戶自定義數(shù)據(jù)進(jìn)行綁定操骡,用到的接口有
pthread_key_create()
,pthread_key_delete()
,pthread_setspecific()
,pthread_getspecfic()
.其中線程對(duì)應(yīng)的key和線程ID是通過稀疏矩陣來映射的。 - Cancellation handlers赚窃,線程終止時(shí)執(zhí)行的回調(diào)函數(shù);
線程生命周期
線程是動(dòng)態(tài)創(chuàng)建的岔激,創(chuàng)建時(shí)涉及到資源分配和初始化勒极,銷毀時(shí)涉及到資源回收,當(dāng)線程執(zhí)行時(shí)虑鼎,它的狀態(tài)通常描述為“就緒”或“阻塞”辱匿,具體來說有以下狀態(tài):
- CONDVAR,阻塞在條件變量上炫彩,比如調(diào)用
pthread_cond_wait()
匾七; - DEAD,線程終止了江兢,等待被其他線程join昨忆;
- INTERRUPT,阻塞在等待中斷上杉允,比如調(diào)用
InterruptWait()
邑贴; - JOIN席里,線程阻塞在join另一個(gè)線程,比如調(diào)用
pthread_join()
拢驾; - MUTEX奖磁,線程阻塞在互斥鎖上,比如調(diào)用
pthread_mutex_lock()
繁疤; - NANOSLEEP咖为,線程休眠很短的時(shí)間,比如調(diào)用
nanosleep()
稠腊; - NET_REPLY躁染,線程正在等待通過網(wǎng)絡(luò)傳遞回復(fù),比如調(diào)用
MsgReply*()
麻养; - NET_SEND褐啡,線程正在等待通過網(wǎng)絡(luò)發(fā)送脈沖或信號(hào),比如調(diào)用
MsgSendPulse()
鳖昌,MsgDeliverEvent()备畦,
SignalKill()`等; - READY许昨,線程等待執(zhí)行懂盐,此時(shí)處理器可能正在執(zhí)行同級(jí)或更高優(yōu)先級(jí)的線程;
- RECEIVE糕档,線程阻塞在接收消息上莉恼,比如調(diào)用
MsgReceive()
; - REPLY速那,線程阻塞在消息回復(fù)上俐银,比如調(diào)用
MsgSend()
; - RUNNING端仰,線程正在執(zhí)行捶惜,內(nèi)核會(huì)使用一個(gè)數(shù)組(每個(gè)CPU上有一個(gè)入口)來跟蹤記錄所有運(yùn)行的線程;
- SEM荔烧,線程正在等待信號(hào)量的釋放吱七,比如調(diào)用
SyncSemWait()
; - SEND鹤竭,線程阻塞在信息發(fā)送上踊餐,比如調(diào)用
MsgSend()
,但服務(wù)器還沒收到消息臀稚; - SIGSUSPEND吝岭,線程阻塞在等待一個(gè)信號(hào)上,比如調(diào)用
sigsuspend()
; - SIGWAITINFO苍碟,線程阻塞在等待一個(gè)信號(hào)之上酒觅,比如調(diào)用
sigwaitinfo()
; - STACK微峰,線程正在等待虛擬堆棧地址空間分配舷丹,父進(jìn)程調(diào)用
ThreadCreate()
; - STOPPED蜓肆,線程阻塞在等待
SIGCONT
信號(hào)颜凯; - WAITCTX,線程在等待非整數(shù)上下文變得可用仗扬,比如浮點(diǎn)運(yùn)算症概;
- WAITPAGE,線程等待為虛擬地址分配物理地址早芭;
- WAITTHREAD彼城,線程等待子線程完成自我創(chuàng)建,比如調(diào)用
ThreadCreate()
退个;
線程調(diào)度
當(dāng)執(zhí)行內(nèi)核調(diào)用募壕、異常、硬件中斷時(shí)语盈,當(dāng)前的執(zhí)行線程會(huì)被掛起舱馅,每當(dāng)任何線程的執(zhí)行狀態(tài)發(fā)生改變時(shí),都會(huì)做出調(diào)度決策刀荒。通常被掛起的線程將會(huì)被恢復(fù)代嗤,這時(shí)線程調(diào)度器將進(jìn)行一次上下文切換。
有三種情況會(huì)發(fā)生上下文切換:
- 阻塞缠借,當(dāng)線程需要等待某些事件的發(fā)生時(shí)(比如響應(yīng)IPC請(qǐng)求干毅、等待互斥鎖等),就會(huì)阻塞等待泼返。線程被阻塞時(shí)硝逢,會(huì)從運(yùn)行隊(duì)列中移除,解阻塞時(shí)會(huì)移動(dòng)到同優(yōu)先級(jí)就緒隊(duì)列的尾部中符隙。
- 搶占,高優(yōu)先級(jí)線程會(huì)搶占低優(yōu)先線程垫毙;
- 主動(dòng)讓出CPU霹疫,比如調(diào)用
sched_yield()
等;
調(diào)度優(yōu)先級(jí)
每個(gè)線程都會(huì)分配一個(gè)優(yōu)先級(jí)综芥,QNX Neutrino支持256級(jí)優(yōu)先級(jí)丽蝎,non-root線程可以將優(yōu)先級(jí)設(shè)置為1-63,與調(diào)度策略無(wú)關(guān),root線程(有效uid為0)屠阻,能將優(yōu)先級(jí)設(shè)置為63之上红省。通常會(huì)采用優(yōu)先級(jí)繼承來應(yīng)對(duì)優(yōu)先級(jí)反轉(zhuǎn)的問題。
下圖中描述了一個(gè)就緒隊(duì)列中国觉,B-F是就緒吧恃,G-Z是阻塞,A正在運(yùn)行:
調(diào)度策略
QNX Neutrino支持三種調(diào)度策略麻诀,這個(gè)也跟Nuttx系統(tǒng)一樣:
-
FIFO調(diào)度
在FIFO調(diào)度下痕寓,線程會(huì)在兩種情況下放棄執(zhí)行:1)主動(dòng)放棄CPU;2)高優(yōu)先級(jí)線程搶占蝇闭;
FIFO調(diào)度 -
Round-Robin調(diào)度
在Round-Robin調(diào)度下呻率,線程會(huì)在三種情況下放棄執(zhí)行:1)主動(dòng)放棄CPU;2)高優(yōu)先級(jí)線程搶占呻引;3)時(shí)間片消耗完畢礼仗;
Round-Robin調(diào)度
時(shí)間片為4倍時(shí)鐘周期。與FIFO調(diào)度不同的是多了一個(gè)時(shí)間片的控制逻悠。
Sporadic調(diào)度
Sporadic調(diào)度策略通常用于在給定時(shí)間段內(nèi)提供線程執(zhí)行時(shí)間的上限元践,Sporadic調(diào)度會(huì)為線程執(zhí)行提供“預(yù)算”。與FIFO調(diào)度一樣蹂风,在阻塞或被搶占的情況下會(huì)放棄執(zhí)行卢厂。Sporadic調(diào)度會(huì)自動(dòng)降低線程的優(yōu)先級(jí),可以更精確的控制線程的行為惠啄。
Sporadic調(diào)度時(shí)慎恒,線程優(yōu)先級(jí)會(huì)在前臺(tái)正常優(yōu)先級(jí)N和后臺(tái)低優(yōu)先級(jí)L之間動(dòng)態(tài)調(diào)整,通過使用下列參數(shù)控制調(diào)度條件:
- Initial budget(C)撵渡,線程從正常優(yōu)先級(jí)調(diào)整到低優(yōu)先級(jí)前融柬,允許的執(zhí)行時(shí)間;
- Low priority(L)趋距,線程降到的優(yōu)先級(jí)粒氧,線程在后臺(tái)以L優(yōu)先級(jí)運(yùn)行,在前臺(tái)以N優(yōu)先級(jí)運(yùn)行节腐;
- Replenishment period(T)外盯,允許線程消耗執(zhí)行預(yù)算的時(shí)間段,對(duì)于Replenishment操作翼雀,POSIX實(shí)現(xiàn)時(shí)采用這個(gè)值作為線程變?yōu)镽eady狀態(tài)的時(shí)間段饱苟。
-
Max number of pending replenishment,Replenishment最大次數(shù)狼渊,決定了Sporadic調(diào)度策略的最大系統(tǒng)負(fù)載上限箱熬。
下圖所示,Sporadic調(diào)度策略建立了線程的初始化執(zhí)行budget(預(yù)算),線程執(zhí)行時(shí)會(huì)消耗這個(gè)budget城须,但這個(gè)值會(huì)周期性重復(fù)填滿蚤认。
Sporadic調(diào)度
在正常優(yōu)先級(jí)N時(shí),線程會(huì)執(zhí)行budget時(shí)間C糕伐,當(dāng)時(shí)間耗盡后砰琢,線程的優(yōu)先級(jí)會(huì)調(diào)整至L。當(dāng)Replenishment發(fā)生后又將恢復(fù)到原來的優(yōu)先級(jí)赤炒,在一個(gè)T的時(shí)間周期內(nèi)氯析,線程將會(huì)有機(jī)會(huì)最大去執(zhí)行C的運(yùn)行時(shí)間,也能保證一個(gè)線程在N優(yōu)先級(jí)的情況下只消耗C/T比例的系統(tǒng)資源莺褒。假設(shè)在一個(gè)系統(tǒng)中掩缓,線程不會(huì)被阻塞或搶占,運(yùn)行情況如下圖所示:
關(guān)于優(yōu)先級(jí)和調(diào)度策略的設(shè)置遵岩,有以下接口來實(shí)現(xiàn):
sched_getparam()/SchedGet()
sched_setparam()/SchedSet()
sched_getscheduler()/SchedGet()
sched_setscheduler()/SchedSet()
同步服務(wù)
QNX Neutrino提供POSIX標(biāo)準(zhǔn)線程級(jí)別的同步原語(yǔ):
上述同步機(jī)制中你辣,大部分都是由內(nèi)核直接實(shí)現(xiàn),除了以下幾種:
- 屏障尘执、睡眠鎖舍哄、讀寫鎖,這些是基于條件變量和互斥鎖實(shí)現(xiàn)的誊锭;
- 原子操作表悬,由處理器提供,或者在內(nèi)核中模擬實(shí)現(xiàn)丧靡;
Mutex
互斥鎖是最簡(jiǎn)單的同步服務(wù)蟆沫,用于對(duì)臨界區(qū)的互斥訪問,通常會(huì)用phtread_mutext_lock()
/pthread_mutex_timedlock()
來獲取鎖温治,使用phread_mutext_unlock()
來釋放鎖饭庞,當(dāng)獲取不到鎖的時(shí)候線程會(huì)阻塞等待,也可以使用非阻塞函數(shù)pthread_mutex_trylock()
來測(cè)試Mutex
是否已經(jīng)被鎖熬荆。Condvars
條件變量用于在臨界區(qū)來阻塞線程舟山,直到滿足某些條件,這些條件可以是任意復(fù)雜的卤恳,并且與Condvar
無(wú)關(guān)累盗。Condvar
必須始終與Mutex
一起使用。
條件變量支持三種操作:
- 等待突琳,
pthread_cond_wait()
- 發(fā)出信號(hào)若债,
pthread_cond_signal()
- 廣播,
pthread_cond_broadcast()
3.Barriers
屏障是一種同步機(jī)制本今,可以用于將多個(gè)協(xié)作線程阻塞在某個(gè)點(diǎn)等待拆座,直到所有線程都完成后才可以繼續(xù)。pthread_join()
函數(shù)是用于等待線程終止冠息,而屏障是用于等待多個(gè)線程在某個(gè)點(diǎn)“集合”挪凑,當(dāng)線程都達(dá)到后,就可以取消阻塞所有線程并繼續(xù)運(yùn)行了逛艰。有以下接口:
4.Sleepon locks
Sleepon locks
與Condvar
很像躏碳,也都是等待條件的滿足,不同的是Condvars
必須為每個(gè)檢查的condition
分配Mutex
散怖,而Sleepon locks
可以復(fù)用一個(gè)Mutex
菇绵。
Reader/writer locks
讀寫鎖通常用于“多個(gè)讀取者,單個(gè)寫入者”場(chǎng)景的同步镇眷,它的開銷遠(yuǎn)大于Mutex
咬最,但是在這種數(shù)據(jù)訪問模式下很有用。通常使用pthread_rwlock_rdlock()
/pthread_rwlock_wrlock()
/pthread_rwlock_unlock()
接口欠动。Semaphores
信號(hào)量是常用的同步形式永乌,允許線程在一個(gè)信號(hào)量上post
和wait
來控制線程何時(shí)喚醒和休眠。信號(hào)量與其他同步原語(yǔ)的一個(gè)顯著區(qū)別是信號(hào)量是異步安全的具伍,可以由信號(hào)處理程序操作翅雏。如果想讓一個(gè)信號(hào)處理程序喚醒一個(gè)線程,信號(hào)量是正確的選擇人芽。
對(duì)于單個(gè)進(jìn)程中的線程之間同步望几,互斥鎖比信號(hào)量更有效。通過調(diào)度策略來同步
可以使用FIFO調(diào)度策略來保證同一優(yōu)先級(jí)的線程不會(huì)在非SMP系統(tǒng)中并發(fā)運(yùn)行萤厅。通過消息傳遞來同步
Send/Receive/Reply
IPC消息傳遞天然就是一種同步機(jī)制橄抹,在很多情況下讓其他同步機(jī)制變得不必要。它們也是唯一可以跨網(wǎng)絡(luò)使用的同步和IPC原語(yǔ)祈坠。通過原子操作來同步
QNX Neutrino提供以下原子操作:
- adding a value
- subtracting a value
- clearing bits
- setting bits
- toggling (complementing) bits
可以在任何地方使用原子操作害碾,但原子操作在以下兩種情況下非常適用: - ISR和線程之間,ISR可以在任何時(shí)間點(diǎn)搶占線程赦拘,線程保護(hù)自己不被ISR打擾的唯一方法就是禁用中斷慌随,在實(shí)時(shí)系統(tǒng)中不建議禁用中斷,推薦使用QNX提供的原子操作躺同;
- 兩個(gè)線程之間阁猜,在SMP系統(tǒng)中,線程能做到真正并發(fā)蹋艺,使用原子操作來進(jìn)行保護(hù)剃袍;
時(shí)鐘和定時(shí)器服務(wù)
時(shí)鐘服務(wù)用于維護(hù)系統(tǒng)時(shí)間,同時(shí)內(nèi)核也會(huì)使用時(shí)間服務(wù)來完成定時(shí)器操作捎谨。
時(shí)鐘相關(guān)的接口如下:
QNX提供了POSIX定時(shí)器所有功能函數(shù)集民效,定時(shí)器模型很豐富憔维,有以下幾種timer類型:
- 絕對(duì)日期
- 相對(duì)日期
-
周期性的
周期模式非常重要,因?yàn)槎〞r(shí)器常用來作為事件周期性來源畏邢,觸發(fā)某些線程進(jìn)行處理业扒,處理完后睡眠,直到下一個(gè)事件觸發(fā)舒萎。定時(shí)器是OS中的另外一個(gè)事件源程储,所有定時(shí)器都能用作時(shí)間分發(fā)系統(tǒng)。應(yīng)用請(qǐng)求在定時(shí)器超時(shí)后臂寝,系統(tǒng)發(fā)送QNX支持的任意事件章鲤。
定時(shí)器有以下接口:
中斷處理
在實(shí)時(shí)系統(tǒng)中,減少不必要的CPU cycles是至關(guān)重要的咆贬,需要關(guān)注兩個(gè)latency:中斷l(xiāng)atency败徊,調(diào)度latency。
中斷l(xiāng)atency
中斷l(xiāng)atency指的是從硬件中斷觸發(fā)到執(zhí)行驅(qū)動(dòng)程序中中斷處理函數(shù)的第一條指令之間的時(shí)間間隔掏缎。在QNX中集嵌,一直維持中斷使能,但在某些特殊的代碼中需要關(guān)閉中斷御毅,可能會(huì)造成大的延遲根欧,在QNX中這個(gè)時(shí)間很短。
調(diào)度latency
調(diào)度latency指的是從中斷處理函數(shù)中返回后到驅(qū)動(dòng)線程第一條指令執(zhí)行的時(shí)間間隔端蛆,通常包括保存當(dāng)前執(zhí)行上下文凤粗,加載驅(qū)動(dòng)線程上下文。盡管這個(gè)時(shí)間大于中斷l(xiāng)atency今豆,但是QNX中調(diào)度延遲仍然很小嫌拣。
中斷嵌套
QNX支持中斷嵌套,中斷嵌套時(shí)序比較復(fù)雜呆躲,考慮以下這種情況:進(jìn)程A在運(yùn)行异逐,中斷IRQx觸發(fā)Intx運(yùn)行,在處理時(shí)又被IRQy搶占觸發(fā)Inty運(yùn)行插掂,Inty返回一個(gè)事件導(dǎo)致線程B運(yùn)行灰瞻,Intx返回一個(gè)事件導(dǎo)致線程C運(yùn)行,如下圖:
中斷接口