天天看點

[轉]QNX Neutrino微核心介紹介紹系統服務線程和程序線程排程同步服務時鐘和定時器服務中斷處理

如果你認為本系列文章對你有所幫助,請大家有錢的捧個錢場,點選此處贊助,贊助額0.1元起步,多少随意

聲明:本文隻用于個人學習交流,若不慎造成侵權,請及時聯系我,立即予以改正

鋒影

email:[email protected]

介紹

QNX Neutrino微核心

procnto

實作了嵌入式實時系統中常用的核心POSIX功能,并提供基本的消息傳遞服務。而未實作的POSIX功能(比如檔案、裝置IO)則可以通過可選的程序和共享庫來提供。

微核心包含了一些基本對象以及操作這些對象的例程,這些對象定義得很具體,而且高度可重用,整個作業系統在此之上建構的。

QNX微核心

系統服務

QNX Neutrino微核心提供了一系列系統調用來支援以下服務:

  • 線程
  • 消息傳遞
  • 信号
  • 時鐘
  • 定時器
  • 中斷處理
  • 信号量
  • 互斥鎖
  • 條件變量
  • 屏障

    整個系統都是基于這些系統調用來建構的,QNX完全可搶占,甚至在消息傳遞的過程時也能被搶占,并在搶占完成後恢複之前的消息傳遞狀态。

    微核心實作越簡單,越有利于減少不可搶占區間的長度,同時,代碼量少,讓解決複雜的多處理器問題也變得簡單。将系統服務包含進核心的前提是,系統服務隻有一個短的執行路徑長度。需要執行很多工作的操作,可以交給外部的程序或線程去做。

    嚴格按照上邊的規則來劃分核心和外部程序功能的話,微核心的運作時負載不見得就高于單核心。簡單核心的上下文切換的時間非常快,相比于在程序間通過消息傳遞來服務請求的時間,上下文的切換開銷微不足道。

    下圖示範了在非對稱多處理器核心(X86實作)搶占的細節,其中,中斷禁用或禁止搶占的時間非常短,通常為幾百納秒。

    QNX Neutrino搶占

線程和程序

在開發應用程式時(實時、嵌入式、圖形等),通常會用到POSIX線程模型來實作多個算法同時執行。線程是微核心中最小的執行和排程單元,程序可以認為是線程的“容器”,定義了線程将在其中執行的“位址空間”,程序會包含一個或多個線程。

應用程式中的線程有可能互相獨立,也可能緊密的聯系,QNX Neutrino提供了豐富的IPC和同步服務。

其中不涉及微核心線程調用的POSIX接口有如下:

下表中的POSIX接口,微核心中有對應的接口實作一樣的功能,允許自己來選擇:

線程屬性

盡管程序中的線程共享程序位址空間中的所有内容,但每個線程仍然有一些“私有”資料,在某些情況下,這些私有資料在核心中受到保護,比如線程ID/程序ID;而其他的可能不受保護,比如線程的堆棧。

值得注意的私有資料有:

  • tid,線程ID,每個線程在程序中都有唯一的ID,從1開始;
  • priority,每個線程都有一個排程的優先級,線程的初始優先級是繼承而來,并且可以根據排程政策進行改變,程序沒有優先級;
  • name,線程名字,可以通過

    pthread_getname_np()

    pthread_setname_np()

    來擷取和設定;
  • Register set,每個線程都有IP、SP以及處理器相關的寄存器上下文;
  • Stack,線程在自己的堆棧上執行,存儲在其程序的位址空間中;
  • Signal mask,信号掩碼;
  • Thread local storage,線程本地存儲TLS,用于存儲線程私有的資料,使用者不需要直接通路TLS,線程可以通過線程特定的key與使用者自定義資料進行綁定,用到的接口有

    pthread_key_create()

    ,

    pthread_key_delete()

    ,

    pthread_setspecific()

    ,

    pthread_getspecfic()

    .其中線程對應的key和線程ID是通過稀疏矩陣來映射的。
  • Cancellation handlers,線程終止時執行的回調函數;

線程生命周期

線程是動态建立的,建立時涉及到資源配置設定和初始化,銷毀時涉及到資源回收,當線程執行時,它的狀态通常描述為“就緒”或“阻塞”,具體來說有以下狀态:

[轉]QNX Neutrino微核心介紹介紹系統服務線程和程式線程排程同步服務時鐘和定時器服務中斷處理

線程狀态

  • CONDVAR,阻塞在條件變量上,比如調用

    pthread_cond_wait()

  • DEAD,線程終止了,等待被其他線程join;
  • INTERRUPT,阻塞在等待中斷上,比如調用

    InterruptWait()

  • JOIN,線程阻塞在join另一個線程,比如調用

    pthread_join()

  • MUTEX,線程阻塞在互斥鎖上,比如調用

    pthread_mutex_lock()

  • NANOSLEEP,線程休眠很短的時間,比如調用

    nanosleep()

  • NET_REPLY,線程正在等待通過網絡傳遞回複,比如調用

    MsgReply*()

  • NET_SEND,線程正在等待通過網絡發送脈沖或信号,比如調用

    MsgSendPulse()

    MsgDeliverEvent(),

    SignalKill()`等;
  • READY,線程等待執行,此時處理器可能正在執行同級或更高優先級的線程;
  • RECEIVE,線程阻塞在接收消息上,比如調用

    MsgReceive()

  • REPLY,線程阻塞在消息回複上,比如調用

    MsgSend()

  • RUNNING,線程正在執行,核心會使用一個數組(每個CPU上有一個入口)來跟蹤記錄所有運作的線程;
  • SEM,線程正在等待信号量的釋放,比如調用

    SyncSemWait()

  • SEND,線程阻塞在資訊發送上,比如調用

    MsgSend()

    ,但伺服器還沒收到消息;
  • SIGSUSPEND,線程阻塞在等待一個信号上,比如調用

    sigsuspend()

  • SIGWAITINFO,線程阻塞在等待一個信号之上,比如調用

    sigwaitinfo()

  • STACK,線程正在等待虛拟堆棧位址空間配置設定,父程序調用

    ThreadCreate()

  • STOPPED,線程阻塞在等待

    SIGCONT

    信号;
  • WAITCTX,線程在等待非整數上下文變得可用,比如浮點運算;
  • WAITPAGE,線程等待為虛拟位址配置設定實體位址;
  • WAITTHREAD,線程等待子線程完成自我建立,比如調用

    ThreadCreate()

線程排程

當執行核心調用、異常、硬體中斷時,目前的執行線程會被挂起,每當任何線程的執行狀态發生改變時,都會做出排程決策。通常被挂起的線程将會被恢複,這時線程排程器将進行一次上下文切換。

有三種情況會發生上下文切換:

  • 阻塞,當線程需要等待某些事件的發生時(比如響應IPC請求、等待互斥鎖等),就會阻塞等待。線程被阻塞時,會從運作隊列中移除,解阻塞時會移動到同優先級就緒隊列的尾部中。
  • 搶占,高優先級線程會搶占低優先線程;
  • 主動讓出CPU,比如調用

    sched_yield()

    等;

排程優先級

每個線程都會配置設定一個優先級,QNX Neutrino支援256級優先級,non-root線程可以将優先級設定為1-63,與排程政策無關,root線程(有效uid為0),能将優先級設定為63之上。通常會采用優先級繼承來應對優先級反轉的問題。

下圖中描述了一個就緒隊列中,B-F是就緒,G-Z是阻塞,A正在運作:

[轉]QNX Neutrino微核心介紹介紹系統服務線程和程式線程排程同步服務時鐘和定時器服務中斷處理

就緒隊列

排程政策

QNX Neutrino支援三種排程政策,這個也跟Nuttx系統一樣:

  1. FIFO排程

    在FIFO排程下,線程會在兩種情況下放棄執行:1)主動放棄CPU;2)高優先級線程搶占;

    [轉]QNX Neutrino微核心介紹介紹系統服務線程和程式線程排程同步服務時鐘和定時器服務中斷處理
    FIFO排程
  2. Round-Robin排程

    在Round-Robin排程下,線程會在三種情況下放棄執行:1)主動放棄CPU;2)高優先級線程搶占;3)時間片消耗完畢;

    [轉]QNX Neutrino微核心介紹介紹系統服務線程和程式線程排程同步服務時鐘和定時器服務中斷處理

    Round-Robin排程

    時間片為4倍時鐘周期。與FIFO排程不同的是多了一個時間片的控制。

  3. Sporadic排程

    Sporadic排程政策通常用于在給定時間段内提供線程執行時間的上限,Sporadic排程會為線程執行提供“預算”。與FIFO排程一樣,在阻塞或被搶占的情況下會放棄執行。Sporadic排程會自動降低線程的優先級,可以更精确的控制線程的行為。

    Sporadic排程時,線程優先級會在前台正常優先級N和背景低優先級L之間動态調整,通過使用下列參數控制排程條件:

  • Initial budget(C),線程從正常優先級調整到低優先級前,允許的執行時間;
  • Low priority(L),線程降到的優先級,線程在背景以L優先級運作,在前台以N優先級運作;
  • Replenishment period(T),允許線程消耗執行預算的時間段,對于Replenishment操作,POSIX實作時采用這個值作為線程變為Ready狀态的時間段。
  • Max number of pending replenishment,Replenishment最大次數,決定了Sporadic排程政策的最大系統負載上限。

    下圖所示,Sporadic排程政策建立了線程的初始化執行budget(預算),線程執行時會消耗這個budget,但這個值會周期性重複填滿。

    [轉]QNX Neutrino微核心介紹介紹系統服務線程和程式線程排程同步服務時鐘和定時器服務中斷處理
    Sporadic排程

在正常優先級N時,線程會執行budget時間C,當時間耗盡後,線程的優先級會調整至L。當Replenishment發生後又将恢複到原來的優先級,在一個T的時間周期内,線程将會有機會最大去執行C的運作時間,也能保證一個線程在N優先級的情況下隻消耗C/T比例的系統資源。假設在一個系統中,線程不會被阻塞或搶占,運作情況如下圖所示:

[轉]QNX Neutrino微核心介紹介紹系統服務線程和程式線程排程同步服務時鐘和定時器服務中斷處理

image.png

關于優先級和排程政策的設定,有以下接口來實作:

  • sched_getparam()/SchedGet()

  • sched_setparam()/SchedSet()

  • sched_getscheduler()/SchedGet()

  • sched_setscheduler()/SchedSet()

同步服務

QNX Neutrino提供POSIX标準線程級别的同步原語:

[轉]QNX Neutrino微核心介紹介紹系統服務線程和程式線程排程同步服務時鐘和定時器服務中斷處理

上述同步機制中,大部分都是由核心直接實作,除了以下幾種:

  • 屏障、睡眠鎖、讀寫鎖,這些是基于條件變量和互斥鎖實作的;
  • 原子操作,由處理器提供,或者在核心中模拟實作;
  1. Mutex

    互斥鎖是最簡單的同步服務,用于對臨界區的互斥通路,通常會用

    phtread_mutext_lock()

    /

    pthread_mutex_timedlock()

    來擷取鎖,使用

    phread_mutext_unlock()

    來釋放鎖,當擷取不到鎖的時候線程會阻塞等待,也可以使用非阻塞函數

    pthread_mutex_trylock()

    來測試

    Mutex

    是否已經被鎖。
  2. Condvars

    條件變量用于在臨界區來阻塞線程,直到滿足某些條件,這些條件可以是任意複雜的,并且與

    Condvar

    無關。

    Condvar

    必須始終與

    Mutex

    一起使用。

    條件變量支援三種操作:

  • 等待,

    pthread_cond_wait()

  • 發出信号,

    pthread_cond_signal()

  • 廣播,

    pthread_cond_broadcast()

3.Barriers

屏障是一種同步機制,可以用于将多個協作線程阻塞在某個點等待,直到所有線程都完成後才可以繼續。

pthread_join()

函數是用于等待線程終止,而屏障是用于等待多個線程在某個點“集合”,當線程都達到後,就可以取消阻塞所有線程并繼續運作了。有以下接口:

[轉]QNX Neutrino微核心介紹介紹系統服務線程和程式線程排程同步服務時鐘和定時器服務中斷處理

4.Sleepon locks

Sleepon locks

Condvar

很像,也都是等待條件的滿足,不同的是

Condvars

必須為每個檢查的

condition

配置設定

Mutex

,而

Sleepon locks

可以複用一個

Mutex

  1. Reader/writer locks

    讀寫鎖通常用于“多個讀取者,單個寫入者”場景的同步,它的開銷遠大于

    Mutex

    ,但是在這種資料通路模式下很有用。通常使用

    pthread_rwlock_rdlock()

    /

    pthread_rwlock_wrlock()

    /

    pthread_rwlock_unlock()

    接口。
  2. Semaphores

    信号量是常用的同步形式,允許線程在一個信号量上

    post

    wait

    來控制線程何時喚醒和休眠。信号量與其他同步原語的一個顯著差別是信号量是異步安全的,可以由信号處理程式操作。如果想讓一個信号處理程式喚醒一個線程,信号量是正确的選擇。

    對于單個程序中的線程之間同步,互斥鎖比信号量更有效。

  3. 通過排程政策來同步

    可以使用FIFO排程政策來保證同一優先級的線程不會在非SMP系統中并發運作。

  4. 通過消息傳遞來同步

    Send/Receive/Reply

    IPC消息傳遞天然就是一種同步機制,在很多情況下讓其他同步機制變得不必要。它們也是唯一可以跨網絡使用的同步和IPC原語。
  5. 通過原子操作來同步

    QNX Neutrino提供以下原子操作:

  • adding a value
  • subtracting a value
  • clearing bits
  • setting bits
  • toggling (complementing) bits

    可以在任何地方使用原子操作,但原子操作在以下兩種情況下非常适用:

  • ISR和線程之間,ISR可以在任何時間點搶占線程,線程保護自己不被ISR打擾的唯一方法就是禁用中斷,在實時系統中不建議禁用中斷,推薦使用QNX提供的原子操作;
  • 兩個線程之間,在SMP系統中,線程能做到真正并發,使用原子操作來進行保護;

時鐘和定時器服務

時鐘服務用于維護系統時間,同時核心也會使用時間服務來完成定時器操作。

時鐘相關的接口如下:

[轉]QNX Neutrino微核心介紹介紹系統服務線程和程式線程排程同步服務時鐘和定時器服務中斷處理

QNX提供了POSIX定時器所有功能函數集,定時器模型很豐富,有以下幾種timer類型:

  • 絕對日期
  • 相對日期
  • 周期性的

    周期模式非常重要,因為定時器常用來作為事件周期性來源,觸發某些線程進行處理,處理完後睡眠,直到下一個事件觸發。定時器是OS中的另外一個事件源,所有定時器都能用作時間分發系統。應用請求在定時器逾時後,系統發送QNX支援的任意事件。

    定時器有以下接口:

    [轉]QNX Neutrino微核心介紹介紹系統服務線程和程式線程排程同步服務時鐘和定時器服務中斷處理

中斷處理

在實時系統中,減少不必要的CPU cycles是至關重要的,需要關注兩個latency:中斷latency,排程latency。

中斷latency

中斷latency指的是從硬體中斷觸發到執行驅動程式中中斷處理函數的第一條指令之間的時間間隔。在QNX中,一直維持中斷使能,但在某些特殊的代碼中需要關閉中斷,可能會造成大的延遲,在QNX中這個時間很短。

[轉]QNX Neutrino微核心介紹介紹系統服務線程和程式線程排程同步服務時鐘和定時器服務中斷處理

中斷latency

排程latency

排程latency指的是從中斷處理函數中傳回後到驅動線程第一條指令執行的時間間隔,通常包括儲存目前執行上下文,加載驅動線程上下文。盡管這個時間大于中斷latency,但是QNX中排程延遲仍然很小。

[轉]QNX Neutrino微核心介紹介紹系統服務線程和程式線程排程同步服務時鐘和定時器服務中斷處理

排程latency

中斷嵌套

QNX支援中斷嵌套,中斷嵌套時序比較複雜,考慮以下這種情況:程序A在運作,中斷IRQx觸發Intx運作,在處理時又被IRQy搶占觸發Inty運作,Inty傳回一個事件導緻線程B運作,Intx傳回一個事件導緻線程C運作,如下圖:

[轉]QNX Neutrino微核心介紹介紹系統服務線程和程式線程排程同步服務時鐘和定時器服務中斷處理

中斷嵌套

中斷接口

[轉]QNX Neutrino微核心介紹介紹系統服務線程和程式線程排程同步服務時鐘和定時器服務中斷處理

中斷API

繼續閱讀