- 嵌入式實時操作系統:基于ARM Mbed OS的應用實踐
- 王宜懷等
- 4329字
- 2024-02-27 16:08:40
1.3 線程的三要素、四種狀態及三種基本形式
線程是完成一定功能的函數,但不是所有的函數都可以被稱為線程。一個函數只有在給出其線程描述符及線程堆棧的情況下,才可以被稱為線程,才能夠被調度運行。本節先介紹線程的三要素(線程函數、線程堆棧和線程描述符),然后介紹線程的四種狀態(終止態、阻塞態、就緒態和激活態),最后介紹線程的三種基本形式(單次執行、周期執行和資源驅動)。
1.3.1 線程的三要素
從線程的存儲結構上看,線程由線程函數、線程堆棧和線程描述符三個部分組成,這三個組成部分也稱為線程的三要素。線程函數就是線程要完成具體功能的程序;每個線程擁有自己獨立的線程堆棧空間,用于保存線程在調度時的上下文信息及線程內部使用的局部變量;線程描述符是關聯線程屬性的程序控制塊,用于記錄線程的各個屬性。
1.線程函數
一個線程對應一段函數代碼,完成一定功能,可被稱為線程函數。從代碼上看,線程函數與一般函數并無區別,被編譯鏈接生成機器碼之后,一般存儲在Flash中。但是從線程自身視角來看,線程認為CPU就是屬于它自己的,并不知道還有其他線程存在。線程函數也不是用來被其他函數直接調用的,而是由實時操作系統內核調度運行的。要使線程函數能夠被實時操作系統內核調度運行,必須先登記線程函數,設置線程優先級、堆棧大小,給線程編號等,不然當運行多個線程時,實時操作系統內核無法知道先運行哪個線程。由于任何時刻只能有一個線程在運行(處于激活態),因此當實時操作系統內核通過調度使一個線程運行時,之前運行的線程就會退出激活態。CPU被處于激活態的線程獨占,從這個角度來看,線程函數與無操作系統中的“main”函數性質相近,一般被設計為永久循環,認為線程一直在執行,永遠獨占處理器。但也有一些特殊性,這將在第7章中討論。
2.線程堆棧
線程堆棧是獨立于線程函數之外的RAM,是按照先進后出策略組織的一段連續存儲空間,是實時操作系統中線程概念的重要組成部分。在實時操作系統中,每個線程都有自己私有的堆棧空間。在線程的運行過程中,堆棧用于保存線程的上下文、線程運行過程中的局部變量。此外,當線程調用普通函數時,它還會為線程保存返回地址等參數變量。
雖然前面已經簡要描述過線程的上下文的概念,但是這里還要多說幾句,以便對線程堆棧用于保存線程上下文有充分的認識。在多線程系統中,每個線程都認為CPU寄存器是自己的。當一個線程正在運行,實時操作系統內核決定不讓該線程繼續運行,而轉去運行別的線程時,就要把CPU的當前狀態保存在屬于該線程的線程堆棧中;當實時操作系統內核再次決定讓其運行時,就從該線程的線程堆棧中恢復原來的CPU狀態,就像未被暫停過一樣。
在系統資源充裕的情況下,可分配盡量多的堆棧空間,可以是K數量級的(如常用1024字節),但若是系統資源受限,就得精打細算了,具體的數值要根據線程的執行內容確定。線程堆棧的組織及使用由系統維護,對于用戶而言,只要在創建線程時指定其大小即可。
3.線程描述符
在創建線程時,系統會為每個線程創建一個唯一的線程描述符(Task Descriptor,TD),它相當于線程在實時操作系統中的“身份證”,實時操作系統就是通過線程描述符來管理線程和查詢線程信息的。雖然在不同操作系統中,線程描述符的名稱不同,但含義相同,例如在Mbed OS中被稱為線程控制塊(Thread Control Block,TCB),在μC/OS中被稱任務控制塊(Task Control Block,TCB),在Linux中被稱為進程控制塊(Process Control Block,PCB)。線程函數只有配備了線程描述符才能被實時操作系統內核調度,未配備線程描述符、駐留在Flash中的線程函數代碼只是通常意義上的函數,不會被實時操作系統內核調度。
多個線程的線程描述符被組成鏈表,存儲在RAM中。每個線程描述符中都包含指向前一個節點的指針、指向后一個節點的指針、線程狀態、線程優先級、線程堆棧指針、線程函數指針(指向線程函數)等字段,實時操作系統內核通過線程描述符來執行線程。
在實時操作系統中,一般情況下使用列表來維護線程描述符,使用就緒列表管理就緒的線程,使用延時列表管理延時等待的線程,使用條件阻塞列表管理因等待事件、消息等而阻塞的線程。在Mbed OS中,還提供了一個等待列表來管理因等待事件、消息等而阻塞的線程。當實時操作系統內核調度線程時,可以通過就緒列表的頭節點查找鏈表,獲取就緒列表上所有線程描述符的信息。
1.3.2 線程的四種狀態
實時操作系統中的線程一般有四種狀態,分別為終止態、阻塞態、就緒態和激活態。在任一時刻,線程被創建后所處的狀態一定是以上四種狀態之一。
1.線程狀態的基本含義
(1)終止態(Terminated,Inactive):線程執行已經完成或被刪除,不再需要使用CPU。
(2)阻塞態(Blocked):又可稱為掛起態。線程未準備好,不能被激活,因為該線程需要等待一段時間或某些情況發生;當等待時間到或等待的情況發生時,該線程才變為就緒態。處于阻塞態的線程描述符存放于阻塞列表或延時列表中。
(3)就緒態(Ready):線程已經準備好可以被激活,但未進入激活態,因為其優先級等于或低于當前正在運行的線程,一旦獲取CPU的使用權就可以進入激活態。處于就緒態的線程描述符存放于就緒列表中。
(4)激活態(Active,Running):又稱為運行態,該線程正在運行中,線程擁有CPU使用權。
如果一個激活態的線程變為阻塞態,那么實時操作系統將執行線程切換操作,從就緒列表中選擇優先級最高的線程進入激活態,若有多個具有相同優先級的線程處于就緒態,則就緒列表中的首個線程先被激活。也就是說,每個就緒列表中相同優先級的線程是按先進先出(First In First Out,FIFO)的策略進行調度的。
在一些操作系統中,還把線程分為中斷態和休眠態。對于被中斷的線程,實時操作系統把它歸為中斷態;休眠態是指該線程的相關資源雖然仍駐留在內存中,但不被實時操作系統內核調度的一種狀態,其實它就是一種終止的狀態。
2.線程狀態之間的轉換
實時操作系統線程的四種狀態是動態轉換的,有的情況是由系統調度自動完成的,有的情況是由用戶調用某個系統函數完成的,還有的情況是等待某個條件滿足后完成的。線程的四種狀態轉換關系圖如圖1-1所示。

圖1-1 線程的四種狀態轉換關系圖
1)終止態轉為就緒態
終止態轉為就緒態(圖1-1中的①線):線程準備重新運行,根據線程優先級進入就緒態。例如,在Mbed OS中,調用svcRtxThreadNew()函數再次創建線程。
2)阻塞態轉為就緒態、終止態
阻塞態轉為就緒態(圖1-1中的②線):阻塞條件被解除,如中斷服務程序或其他線程運行時釋放了線程等待的信號量,從而使線程再次進入就緒態;延時列表中的線程延時到達喚醒的時刻。例如,在Mbed OS中,會自動調用svcRtxThreadResume()函數。
阻塞態轉為終止態(圖1-1中的⑥線):如在Mbed OS中,調用svcRtxThreadTerminate()函數。
3)就緒態轉為激活態、終止態
就緒態轉為激活態(圖1-1中的③線):就緒線程被調度而獲得了CPU資源進入運行;也可以直接調用函數進入激活態。例如,在Mbed OS中,調用svcRtxThreadYield()函數。
就緒態轉為終止態(圖1-1中的⑧線):如在Mbed OS中,調用svcRtxThreadTerminate()函數。
4)激活態轉為就緒態、阻塞態、終止態
激活態轉為就緒態(圖1-1中的④線):正在執行的線程被高優先級線程搶占后進入就緒列表;或使用時間片輪詢調度策略時,時間片耗盡,正在執行的線程讓出CPU;或被外部事件中斷。
激活態轉為阻塞態(圖1-1中的⑤線):正在執行的線程等待信號量、等待事件或者等待I/O資源等,如在Mbed OS中,調用svcRtxThreadSuspend()函數。
激活態轉為終止態(圖1-1中的⑦線):如在Mbed OS中,調用svcRtxThreadExit()函數。
1.3.3 線程的三種基本形式
線程函數一般分為兩個部分:初始化部分和線程體部分。初始化部分實現對變量的定義、初始化及設備的打開等,線程體部分負責完成該線程的基本功能。線程的一般結構如下:

線程的基本形式主要有單次執行線程、周期執行線程和資源驅動線程三種,下面介紹這三種線程的結構特點。
1.單次執行線程
單次執行線程是指線程在創建完之后只會被執行一次,執行完成后就會被銷毀或阻塞的線程。其線程函數結構如下:

單次執行線程由三部分組成:線程函數初始化、線程函數執行和線程函數銷毀或阻塞。線程函數初始化包括對變量的定義和賦值、打開需要使用的設備等;線程函數的執行是該線程的基本功能實現;線程函數的銷毀或阻塞,即調用線程銷毀或阻塞函數將自己從線程列表中刪除。銷毀與阻塞的區別在于銷毀除了停止線程的運行,還將回收該線程所占用的所有資源,如堆棧空間等;而阻塞只是將線程描述符中的狀態設置為阻塞態而已。舉例來說,在水質監測系統中,主線程需要創建傳感器采集數據線程和處理線程,需要對小燈、串口、傳感器等外設進行初始化,當啟動完傳感器采集數據線程和處理線程后,就阻塞主線程,然后實時操作系統內核開始線程調度,此時的主線程就是單次執行線程。
2.周期執行線程
周期執行線程是指需要按照一定周期執行的線程。其線程函數結構如下:

初始化部分同單次執行線程一樣,包括對變量的定義和賦值、打開需要使用的設備等。與單次執行線程不同的是,周期執行線程的函數體內存在永久循環部分,由于該線程需要按照一定周期執行,因此在該線程內一般會調用延時函數,使線程轉入阻塞態,進入延時列表中。當延時時間到時,線程就會轉入就緒態,進入就緒列表中。舉例來說,在水質監測系統中,我們需要得到被監測水域的酸堿度和各種離子的濃度,但是不需要時時刻刻都在監測數據,因為這些物理量的變化比較緩慢,所以使用傳感器采集數據時可以調用延時函數,每隔半個小時采集一次數據,此時的物理量采集線程就是典型的周期執行線程。
3.資源驅動線程
除了上面介紹的兩種線程類型,還有一種線程形式,那就是資源驅動線程。這里的資源主要指線程同步與通信中的事件、信號量、互斥量等。這種類型的線程比較特殊,它是操作系統特有的線程類型,因為只有在操作系統下才會出現資源共享使用的問題,同時引出操作系統中另一個主要問題,那就是線程同步與通信。該線程與周期執行線程的區別在于它的執行時間不是確定的,只有當它所要等待的資源可用時,它才會轉入就緒態,否則被加入等待該資源的阻塞列表中。資源驅動線程的函數結構如下:

初始化部分和線程體部分與之前兩種類型的線程類似,主要區別就是在線程體執行之前會調用等待資源函數,以等待資源實現線程體部分的功能。仍以水質監測系統為例,數據處理是在物理量采集完成后才能進行的操作,所以在系統中使用一個信號量用于兩個線程之間的同步,當物理量采集線程完成時就會釋放這個信號量,而數據處理線程一直在等待這個信號量,當等到這個信號量時,就可以進行下一步的操作。系統中的數據處理線程就是一個典型的資源驅動線程。
以上就是三種線程基本形式的介紹,其中周期執行線程和資源驅動線程從本質上來講可以歸結為一種,也就是資源驅動線程。因為時間也是操作系統的一種資源,只不過時間是一種特殊的資源,特殊在該資源是整個操作系統的實現基礎,系統中大部分函數都是基于時間這一資源的,所以在分類中將周期執行線程單獨作為一類。
- 樂學Windows操作系統
- Google系統架構解密:構建安全可靠的系統
- Linux自動化運維:Shell與Ansible(微課版)
- 無蘋果不生活 The New iPad隨身寶典
- 完美應用RHEL 8
- Linux內核設計的藝術:圖解Linux操作系統架構設計與實現原理
- Advanced Infrastructure Penetration Testing
- Learning Continuous Integration with Jenkins(Second Edition)
- VMware vSphere 5.1 Cookbook
- 辦公自動化教程(Windows7+Office2010)
- openEuler操作系統核心技術與行業應用實踐
- Serverless Architectures with Kubernetes
- Xamarin Mobile Application Development for Android
- Docker for Developers
- 鴻蒙操作系統開發入門經典