- VxWorks設(shè)備驅(qū)動(dòng)開發(fā)詳解
- 曹桂平等編著
- 57字
- 2019-01-09 15:53:24
第2章 VxWorks操作系統(tǒng)的基本組件
本章將對(duì)VxWorks操作系統(tǒng)中的任務(wù)、任務(wù)調(diào)度、任務(wù)間通信、內(nèi)存管理、中斷處理幾方面知識(shí)進(jìn)行介紹。
2.1 VxWorks任務(wù)
VxWorks是實(shí)時(shí)操作系統(tǒng),這決定了它的任務(wù)調(diào)度必須是基于優(yōu)先級(jí)的,而且必須是可搶占式調(diào)度方式,這樣才能夠區(qū)分實(shí)際情況下不同狀態(tài)的處理級(jí)別,對(duì)高優(yōu)先級(jí)的情況進(jìn)行優(yōu)先響應(yīng)。
2.1.1 內(nèi)核實(shí)現(xiàn)基本原理
VxWorks內(nèi)核維護(hù)三個(gè)隊(duì)列:tick隊(duì)列、ready隊(duì)列、active隊(duì)列。另外還有一個(gè)隊(duì)列涉及任務(wù),即任務(wù)等待資源時(shí)所處的隊(duì)列,這個(gè)隊(duì)列可以是VxWorks內(nèi)核提供的,也可以是用戶提供的,此處令其為pend隊(duì)列。
所謂tick隊(duì)列,即當(dāng)調(diào)用taskDelay函數(shù)讓任務(wù)延遲一段固定的時(shí)間時(shí),任務(wù)所處的隊(duì)列,此時(shí)任務(wù)被設(shè)置為Delay狀態(tài),無資格競爭使用CPU;ready隊(duì)列即有資格競爭使用CPU的所有任務(wù),該隊(duì)列以優(yōu)先級(jí)為序排列任務(wù),隊(duì)列頭部是除了當(dāng)前運(yùn)行任務(wù)外,系統(tǒng)中最高優(yōu)先級(jí)的任務(wù);active隊(duì)列有些誤導(dǎo),實(shí)際上稱之為task隊(duì)列更合適,因?yàn)橄到y(tǒng)中所有的任務(wù)無論當(dāng)前狀態(tài)如何,都將在這個(gè)隊(duì)列中,這個(gè)隊(duì)列維護(hù)著系統(tǒng)中當(dāng)前所有的任務(wù),即通過該隊(duì)列可以查找到當(dāng)前系統(tǒng)中的所有任務(wù),在Shell下運(yùn)行“i”命令,顯示系統(tǒng)中所有的任務(wù),就是通過遍歷active隊(duì)列完成的;pend隊(duì)列即當(dāng)任務(wù)競爭使用某資源,而資源當(dāng)前不可得時(shí),任務(wù)就被設(shè)置為pend狀態(tài),進(jìn)入pend隊(duì)列。
函數(shù)taskSpawn創(chuàng)建一個(gè)新的任務(wù)。首先,其創(chuàng)建一個(gè)任務(wù)控制結(jié)構(gòu),對(duì)該結(jié)構(gòu)進(jìn)行初始化后,將結(jié)構(gòu)加入active隊(duì)列以作為系統(tǒng)任務(wù)管理之用。此時(shí)任務(wù)仍無資格競爭使用CPU,taskSpawn函數(shù)的最后一步就是將這個(gè)任務(wù)結(jié)構(gòu)再加入到ready隊(duì)列,此時(shí)這個(gè)任務(wù)才真正可以稱為已經(jīng)在競爭使用CPU了。當(dāng)系統(tǒng)中所有的優(yōu)先級(jí)高于這個(gè)任務(wù)的其他任務(wù)運(yùn)行完畢或者由于等待資源而處于阻塞時(shí),這個(gè)新創(chuàng)建的任務(wù)就將被調(diào)度運(yùn)行。所以,在VxWorks下,如果一個(gè)新創(chuàng)建的任務(wù)優(yōu)先級(jí)不高,創(chuàng)建后將等待一段時(shí)間才能被真正執(zhí)行。在實(shí)際項(xiàng)目中,有時(shí)需要一個(gè)新的任務(wù)被創(chuàng)建后立刻得到執(zhí)行,那么就需要在創(chuàng)建任務(wù)時(shí),指定一個(gè)較高的任務(wù)優(yōu)先級(jí)。VxWorks內(nèi)核將任務(wù)分為256個(gè)優(yōu)先級(jí),標(biāo)號(hào)從0到255。其中0表示最高優(yōu)先級(jí),255表示最低優(yōu)先級(jí)。任務(wù)在調(diào)用taskSpawn函數(shù)進(jìn)行創(chuàng)建時(shí)就指定了優(yōu)先級(jí),當(dāng)然任務(wù)的優(yōu)先級(jí)并非在創(chuàng)建后就無法改變,用戶可以通過調(diào)用taskPrioritySet函數(shù)在任務(wù)創(chuàng)建后重新設(shè)定優(yōu)先級(jí),taskPrioritySet專門針對(duì)嵌入式平臺(tái)下不同的情況對(duì)同一任務(wù)不同運(yùn)行級(jí)別的需求進(jìn)行設(shè)置。值得注意的是,taskPrioritySet函數(shù)不同于通用操作系統(tǒng)提供的類似函數(shù),taskPrioritySet函數(shù)可以提高或者降低任務(wù)的運(yùn)行級(jí)別,而不是通用操作系統(tǒng)下在任務(wù)創(chuàng)建后就只能動(dòng)態(tài)地降低任務(wù)優(yōu)先級(jí)。
VxWorks下對(duì)于應(yīng)用層任務(wù),推薦使用100~250 之間的優(yōu)先級(jí),驅(qū)動(dòng)層任務(wù)可以使用51~99之間的優(yōu)先級(jí)。要特別注意的是,內(nèi)核網(wǎng)絡(luò)數(shù)據(jù)包收發(fā)任務(wù)tNetTask的優(yōu)先級(jí)為50,如果使用網(wǎng)口進(jìn)行調(diào)試,則一定注意不要?jiǎng)?chuàng)建任務(wù)優(yōu)先級(jí)高于50 的任務(wù),否則tNetTask任務(wù)將無法得到運(yùn)行,表現(xiàn)形式是死機(jī),因?yàn)橄到y(tǒng)將無法再從網(wǎng)口接收調(diào)試命令,無法響應(yīng)Tornado Shell或者Telnet下輸入的任何命令。以上只是推薦值,事實(shí)上,由于嵌入式系統(tǒng)下的特殊應(yīng)用,對(duì)于某個(gè)任務(wù)優(yōu)先級(jí)的設(shè)置需要根據(jù)該任務(wù)完成的具體工作而定,而不可一味地遵循推薦值。如筆者曾在項(xiàng)目中為了與Shell“爭讀”從串口發(fā)送的命令,就將一個(gè)應(yīng)用層任務(wù)優(yōu)先級(jí)設(shè)置為0。要謹(jǐn)記在任務(wù)達(dá)到某一特殊目的后,必須將任務(wù)優(yōu)先級(jí)設(shè)置回正常(推薦)值。
無論何種操作系統(tǒng),任務(wù)在設(shè)計(jì)中都由一個(gè)數(shù)據(jù)結(jié)構(gòu)表示。這個(gè)數(shù)據(jù)結(jié)構(gòu)包含一個(gè)任務(wù)運(yùn)行時(shí)需要的所有信息,我們一般將這些信息稱為任務(wù)上下文。具體的任務(wù)上下文(廣義上)包括以下內(nèi)容:
1)所在平臺(tái)CPU內(nèi)部所有的寄存器值,特別是指令寄存器,這代表了任務(wù)當(dāng)前的執(zhí)行點(diǎn)。這一般是狹義上的任務(wù)上下文。除了寄存器值,每個(gè)任務(wù)有自己的內(nèi)存映射空間、任務(wù)名稱、任務(wù)優(yōu)先級(jí)值、任務(wù)入口函數(shù)地址、打開文件句柄數(shù)組、信號(hào)量和用于各種目的的隊(duì)列等。
2)任務(wù)運(yùn)行時(shí)暫時(shí)存放函數(shù)變量以及函數(shù)調(diào)用時(shí)被傳遞參數(shù)的棧。從操作系統(tǒng)底層實(shí)現(xiàn)來看,很多操作系統(tǒng)將表示任務(wù)的數(shù)據(jù)結(jié)構(gòu)和任務(wù)棧統(tǒng)一管理,如Linux下在分配任務(wù)結(jié)構(gòu)的同時(shí)分配任務(wù)的內(nèi)核棧,這兩者作為一個(gè)整體進(jìn)行內(nèi)存分配,通常將一頁(如4KB)的開始部分作為任務(wù)結(jié)構(gòu),用以存儲(chǔ)任務(wù)關(guān)鍵信息,而將頁的末尾作為任務(wù)內(nèi)核棧的頂部。如此,實(shí)際上用一頁頁面的大小(通常為4KB)減去任務(wù)信息占據(jù)的空間,剩下的空間都作為任務(wù)的內(nèi)核棧在使用。VxWorks與Linux在棧的分配和管理上基本類似,不過VxWorks區(qū)分于Linux的一個(gè)最大不同是VxWorks下所有的代碼都運(yùn)行在一個(gè)狀態(tài)下,不區(qū)分內(nèi)核態(tài)和用戶態(tài)(當(dāng)然,VxWorks下也有內(nèi)核態(tài)的概念,但含義完全不同,見下文分析)。所以,不存在Linux下的內(nèi)核態(tài)棧和用戶態(tài)棧。VxWorks下的任務(wù)自始至終都在使用同一個(gè)棧,不論這個(gè)任務(wù)在運(yùn)行過程中調(diào)用了任何VxWorks內(nèi)核函數(shù),都不存在棧的切換。正因如此,VxWorks對(duì)棧的大小無法預(yù)先進(jìn)行把握,棧的大小將由被創(chuàng)建的任務(wù)決定,而且不同于通用操作系統(tǒng),VxWorks下任務(wù)棧在任務(wù)創(chuàng)建時(shí)就被確定,而且此后不可以改變棧的大小。所以,對(duì)于一個(gè)存在很多遞歸調(diào)用的任務(wù),必須在任務(wù)創(chuàng)建時(shí)指定一個(gè)較大的任務(wù)棧,防止在后續(xù)的運(yùn)行中造成棧的溢出,導(dǎo)致任務(wù)異常退出。
3)各種定時(shí)信息。這些信息實(shí)際上都作為任務(wù)結(jié)構(gòu)中的一個(gè)字段而存在。任何操作系統(tǒng)都必須有一個(gè)系統(tǒng)時(shí)鐘進(jìn)行驅(qū)動(dòng),該系統(tǒng)時(shí)鐘通常稱為系統(tǒng)的脈搏。系統(tǒng)時(shí)鐘一定與一個(gè)高優(yōu)先級(jí)的中斷聯(lián)系,這樣,每當(dāng)時(shí)鐘前進(jìn)一個(gè)滴答(Tick),操作系統(tǒng)就會(huì)響應(yīng)一次中斷,該中斷通常就被作為操作系統(tǒng)進(jìn)程調(diào)度的觸發(fā)點(diǎn)。每次滴答,操作系統(tǒng)都會(huì)增加內(nèi)核維護(hù)的一個(gè)全局變量(如VxWorks操作系統(tǒng)維護(hù)的vxTick變量),通過該變量為系統(tǒng)各種定時(shí)器提供定時(shí)依據(jù)。每個(gè)任務(wù)都有一個(gè)內(nèi)部固定的定時(shí)器,用于任務(wù)內(nèi)部特定的需求,每次系統(tǒng)時(shí)鐘產(chǎn)生一個(gè)中斷,操作系統(tǒng)都會(huì)對(duì)當(dāng)前系統(tǒng)內(nèi)所有需要關(guān)注的任務(wù)定時(shí)器進(jìn)行處理,從而完成任務(wù)定時(shí)器特殊的用途。定時(shí)器的一個(gè)特殊變相應(yīng)用即Round-Robin任務(wù)調(diào)度(簡稱RR調(diào)度)。RR調(diào)度實(shí)際上對(duì)每個(gè)支持RR調(diào)度的任務(wù)內(nèi)部都維護(hù)有一個(gè)定時(shí)器。當(dāng)一個(gè)支持RR調(diào)度的任務(wù)被調(diào)度進(jìn)入運(yùn)行狀態(tài)時(shí),在任務(wù)運(yùn)行期間,每次系統(tǒng)時(shí)鐘前進(jìn)一個(gè)滴答時(shí),該定時(shí)器指針都會(huì)前進(jìn)一個(gè)單位。當(dāng)?shù)竭_(dá)預(yù)定的值時(shí),該任務(wù)就要主動(dòng)讓出CPU,以便讓相同優(yōu)先級(jí)的其他任務(wù)運(yùn)行。定時(shí)器可以以加法或者減法運(yùn)算運(yùn)行。對(duì)于減法運(yùn)算,一般根據(jù)定時(shí)時(shí)間計(jì)算出一個(gè)Tick數(shù),每次系統(tǒng)前進(jìn)一個(gè)滴答,Tick數(shù)減一,當(dāng)?shù)竭_(dá)0時(shí),表示定時(shí)器到期。而對(duì)于加法運(yùn)算,則一般需要使用操作系統(tǒng)維護(hù)的全局Tick變量。VxWorks下,如設(shè)置定時(shí)時(shí)間間隔為N,則定時(shí)器到期時(shí)間為vxTick+N=T0,每次系統(tǒng)前進(jìn)一個(gè)滴答,操作系統(tǒng)會(huì)對(duì)當(dāng)前系統(tǒng)內(nèi)維護(hù)的所有定時(shí)器進(jìn)行檢查,判斷T0是否大于vxTick,一旦T0小于或等于vxTick,則表示該定時(shí)器到期,此時(shí)將根據(jù)定時(shí)器的目的做出相應(yīng)的響應(yīng)。
4)信號(hào)處理函數(shù)。事實(shí)上,操作系統(tǒng)的每個(gè)信號(hào)都有一個(gè)默認(rèn)的響應(yīng)方式,如用戶在命令行按“Ctrl+C”組合鍵時(shí),則系統(tǒng)默認(rèn)響應(yīng)方式是中止當(dāng)前前臺(tái)任務(wù)。每個(gè)任務(wù)可以根據(jù)自身情況定制對(duì)某個(gè)信號(hào)的響應(yīng)方式。如一個(gè)任務(wù)可以將用戶的“Ctrl+C”組合鍵操作響應(yīng)為打印輸出當(dāng)前任務(wù)中某個(gè)變量的值。每個(gè)任務(wù)內(nèi)部對(duì)每個(gè)信號(hào)都維護(hù)一個(gè)響應(yīng)函數(shù)句柄,操作系統(tǒng)在創(chuàng)建任務(wù)時(shí)已經(jīng)將所有的句柄設(shè)置為系統(tǒng)默認(rèn)方式,用戶在創(chuàng)建任務(wù)后,可以針對(duì)某個(gè)信號(hào)安裝自己的信號(hào)響應(yīng)句柄。
5)其他輔助信息。這些信息包括統(tǒng)計(jì)上的一些數(shù)據(jù),如任務(wù)運(yùn)行總時(shí)間、任務(wù)最終返回值等。
2.1.2 任務(wù)操作函數(shù)
前文中說到VxWorks是基于優(yōu)先級(jí)方式的搶占式任務(wù)調(diào)度操作系統(tǒng),同時(shí)對(duì)于相同優(yōu)先級(jí)的任務(wù),支持Round-Robin循環(huán)調(diào)度方式(以下簡稱RR調(diào)度)。RR調(diào)度方式通過kernelTimeSlice函數(shù)使能。該函數(shù)調(diào)用原型如下。
STATUS kernelTimeSlice ( int ticks /* time-slice in ticks or 0 to disable round-robin */ )
RR調(diào)度在默認(rèn)情況下是禁用的。當(dāng)用戶以非零參數(shù)明確調(diào)用kernelTimeSlice函數(shù)后,RR調(diào)度方才使能,此時(shí)相同優(yōu)先級(jí)的任務(wù)可以輪流使用CPU,但是整個(gè)系統(tǒng)仍然是按優(yōu)先級(jí)方式進(jìn)行調(diào)度的。換句話說,只有在當(dāng)前系統(tǒng)中最高優(yōu)先級(jí)下存在多個(gè)任務(wù)運(yùn)行時(shí),RR調(diào)度才有效,如果存在多個(gè)低優(yōu)先級(jí)的任務(wù),那么系統(tǒng)仍然運(yùn)行著高優(yōu)先級(jí)的任務(wù),這些低優(yōu)先級(jí)任務(wù)根本無法使用CPU。注意,kernelTimeSlice函數(shù)參數(shù)為tick系統(tǒng)時(shí)鐘嘀答數(shù),一般情況下,VxWorks下一個(gè)滴答為1ms,故很多時(shí)候直接將參數(shù)作為以ms為單位的時(shí)間值。如果在調(diào)用kernelTimeSlice函數(shù)使能RR調(diào)度方式后,又想禁用RR調(diào)度方式,則只需要以參數(shù)0再次調(diào)用kernelTimeSlice函數(shù)即可。
控制任務(wù)調(diào)度的另一個(gè)函數(shù)是taskPrioritySet,該函數(shù)通過改變?nèi)蝿?wù)優(yōu)先級(jí)來控制調(diào)度。注意,taskPrioritySet函數(shù)可以任意改變?nèi)蝿?wù)的優(yōu)先級(jí),該函數(shù)的調(diào)用原型如下。
STATUS taskPrioritySet ( int tid, /* task ID */ int newPriority /* new priority */ )
第一個(gè)參數(shù)(tid)為需要改變優(yōu)先級(jí)的任務(wù)的ID號(hào),這是一個(gè)整型值,是調(diào)用taskSpawn創(chuàng)建任務(wù)時(shí)的返回值。第二個(gè)參數(shù)(newPriority)即對(duì)應(yīng)任務(wù)需要設(shè)置的優(yōu)先級(jí),該參數(shù)范圍為0~255,即可以改變某個(gè)任務(wù)到任意優(yōu)先級(jí),而不是只能降低任務(wù)的優(yōu)先級(jí)。
注意
一個(gè)任務(wù)可以在運(yùn)行過程中通過taskPrioritySet函數(shù)按照需要自己改變自己的優(yōu)先級(jí),此時(shí)只需要將第一個(gè)參數(shù)設(shè)置為0即可。這種可以動(dòng)態(tài)改變?nèi)蝿?wù)自身優(yōu)先級(jí)的方式給任務(wù)的運(yùn)行提供了極大的靈活性,特別是當(dāng)任務(wù)需要在某個(gè)特定時(shí)刻從特殊渠道獲取數(shù)據(jù)時(shí),必須提高自身的優(yōu)先級(jí)才能讀取到數(shù)據(jù),而在讀取數(shù)據(jù)后,又必須退回到正常優(yōu)先級(jí)進(jìn)行數(shù)據(jù)的處理,taskPrioritySet函數(shù)就是針對(duì)這種情況專門設(shè)計(jì)的。通常,任務(wù)不需要使用taskPrioritySet函數(shù)。
另外一對(duì)控制任務(wù)調(diào)度的關(guān)鍵函數(shù)是taskLock和taskUnlock。函數(shù)調(diào)用原型分別如下:
STATUS taskLock (void) STATUS taskUnlock (void)
taskLock函數(shù)即關(guān)閉任務(wù)調(diào)度,taskUnlock函數(shù)即重新開啟任務(wù)調(diào)度,這兩個(gè)函數(shù)必須成對(duì)使用。這兩個(gè)函數(shù)通常用于系統(tǒng)級(jí)的資源共享上,是一種變相的互斥機(jī)制。不過需要注意的是,這兩個(gè)函數(shù)并不禁止中斷,所以并不能與在中斷上下文中運(yùn)行的函數(shù)形成互斥。另外要注意的是,taskLock并不是信號(hào)量,它僅僅是暫時(shí)禁止了任務(wù)調(diào)度機(jī)制,保證了接下來的一段時(shí)間內(nèi)(直到調(diào)用taskUnlock),當(dāng)前任務(wù)一直主動(dòng)占用CPU。taskLock主要使用在需要以原子方式執(zhí)行一段代碼的情況下,如對(duì)某個(gè)變量進(jìn)行某種操作(如加1或減1)。剛才說到任務(wù)主動(dòng)占用CPU,即沒有其他外界手段可以將任務(wù)調(diào)度出去,但是任務(wù)自身可以進(jìn)入阻塞狀態(tài)(如阻塞于某個(gè)資源,雖然不應(yīng)在此種方式下調(diào)用taskLock),此時(shí)taskLock應(yīng)有的作用將被取消,任務(wù)調(diào)度機(jī)制重新工作,直到該任務(wù)又重新被調(diào)度運(yùn)行。換句話說,只要調(diào)用taskLock的任務(wù)處于運(yùn)行狀態(tài),則任務(wù)調(diào)度機(jī)制一直被禁止,而一旦該任務(wù)由于某種原因主動(dòng)讓出CPU,則任務(wù)調(diào)度機(jī)制重新啟動(dòng)。taskLock的作用對(duì)于某個(gè)任務(wù)而言,只有taskUnlock可以取消它。
VxWorks提供任務(wù)創(chuàng)建函數(shù)taskSpawn,該函數(shù)的調(diào)用原型如下。
int taskSpawn ( char * name, /* name of new task (stored at pStackBase) */ int priority, /* priority of new task */ int options, /* task option word */ int stackSize, /* size (bytes) of stack needed plus name */ FUNCPTR entryPt, /* entry point of new task */ int arg1, /* 1st of 10 req'd task args to pass to func */ int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10 )
① 參數(shù)1:char * name
任務(wù)名。可以為NULL,此時(shí)系統(tǒng)將使用默認(rèn)的任務(wù)名。默認(rèn)任務(wù)名形式為“tN”,其中,“t”為前綴,“N”是一個(gè)數(shù)字,系統(tǒng)將對(duì)所有的任務(wù)創(chuàng)建時(shí)未提供任務(wù)名的任務(wù)依次進(jìn)行編號(hào),從1開始。
② 參數(shù)2:int priority
任務(wù)優(yōu)先級(jí)。該優(yōu)先級(jí)決定了任務(wù)的調(diào)度級(jí)別。在任務(wù)創(chuàng)建完畢后,仍然可以使用taskPrioritySet函數(shù)對(duì)任務(wù)優(yōu)先級(jí)進(jìn)行動(dòng)態(tài)改變。
③ 參數(shù)3:int options
任務(wù)選項(xiàng)。用于控制任務(wù)的某些行為。任務(wù)可用選項(xiàng)如表2-1所示,一般情況下,將該參數(shù)設(shè)置為0。
表2-1 任務(wù)可用選項(xiàng)

④ 參數(shù)4:int stackSize
任務(wù)棧大小。注意,任務(wù)棧在創(chuàng)建任務(wù)時(shí)指定,此后不可更改。任務(wù)從創(chuàng)建到結(jié)束自始至終都在使用這個(gè)棧。VxWorks下任務(wù)棧和表示任務(wù)的結(jié)構(gòu)連續(xù)存放,在內(nèi)存分配時(shí)是作為一個(gè)整體進(jìn)行分配的。
⑤ 參數(shù)5:FUNCPTR entryPt
任務(wù)開始執(zhí)行時(shí)入口函數(shù)的地址。當(dāng)任務(wù)被調(diào)度運(yùn)行時(shí),從該參數(shù)指定的地址開始執(zhí)行。
⑥ 參數(shù)6~15:int arg1~int arg10
入口函數(shù)參數(shù),最多可同時(shí)傳遞10個(gè)參數(shù),多于10個(gè)時(shí),可以通過指針的方式傳遞。
⑦ 返回值
taskSpawn函數(shù)返回一個(gè)整型數(shù)據(jù),表示剛創(chuàng)建任務(wù)的ID。實(shí)際上,這個(gè)ID是一個(gè)內(nèi)存地址,指向這個(gè)被創(chuàng)建任務(wù)的TCB(Task Control Block)。故,原則上可以通過以下方式通過任務(wù)ID得到任務(wù)的TCB:
WIND_TCB *tPcb; tPcb = ((WIND_TCB *) tid); …
但是并不建議這樣做,因?yàn)閷?duì)于任務(wù)ID的解釋可以隨著VxWorks內(nèi)核版本的不同而改變,故要從任務(wù)ID得到任務(wù)對(duì)應(yīng)的TCB,建議使用內(nèi)核提供的函數(shù)taskTcb。taskTcb函數(shù)調(diào)用原型如下。
WIND_TCB *taskTcb ( int tid /* task ID */ )
使用方式的代碼如下。
WIND_TCB *tPcb; tPcb = taskTcb(0);
注意
taskTcb要求傳入任務(wù)ID,當(dāng)以0作為參數(shù)傳遞給該函數(shù)時(shí),taskTcb將返回當(dāng)前任務(wù)的TCB結(jié)構(gòu)地址。參數(shù)0表示得到當(dāng)前任務(wù)的某種信息,這種處理方式在VxWorks下很常見。
taskSpawn調(diào)用舉例:
taskSpawn ("demo", 20, 0, 2000, (FUNCPTR)usrDemo, 0,0,0,0,0,0,0,0,0,0); void usrDemo (void) { …… }
taskSpawn函數(shù)調(diào)用完畢后,任務(wù)就進(jìn)入運(yùn)行狀態(tài),即與其他任務(wù)競爭使用CPU。有時(shí)創(chuàng)建一個(gè)任務(wù)后,需要暫時(shí)使其處于掛起狀態(tài),不具有調(diào)度運(yùn)行的資格,這可以通過調(diào)用taskCreate函數(shù)完成。taskCreate調(diào)用原型如下。
int taskCreate ( char * name, /* name of new task */ int priority, /* priority of new task */ int options, /* task option word */ int stackSize, /* size (bytes) of stack needed */ FUNCPTR entryPt, /* entry point of new task */ int arg1, /* 1st of 10 req'd args to pass to entryPt */ int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10 )
taskCreate函數(shù)參數(shù)與taskSpawn完全一致,其區(qū)別于taskSpawn函數(shù)之處就在于taskCreate創(chuàng)建后的任務(wù)暫不具有運(yùn)行的資格,需要另一個(gè)函數(shù)taskActivate來“激活”。taskActivate函數(shù)將任務(wù)結(jié)構(gòu)加入到ready隊(duì)列,從而使得任務(wù)有資格競爭使用CPU。至于任務(wù)確切的運(yùn)行時(shí)間,則根據(jù)系統(tǒng)當(dāng)前任務(wù)情況決定。一般而言,某個(gè)任務(wù)結(jié)構(gòu)被加入到系統(tǒng)ready隊(duì)列,我們就認(rèn)為其已經(jīng)在運(yùn)行狀態(tài),因?yàn)橛嘞碌那闆r已經(jīng)無法由系統(tǒng)和用戶掌控。如果用戶需要在任務(wù)被激活后立刻得到運(yùn)行,可以設(shè)置任務(wù)為較高優(yōu)先級(jí)。任務(wù)激活函數(shù)taskActivate調(diào)用原型如下。
STATUS taskActivate ( int tid /* task ID of task to activate */ )
該函數(shù)接收taskCreate函數(shù)返回的任務(wù)ID,對(duì)指定ID的任務(wù)進(jìn)行激活,即將任務(wù)結(jié)構(gòu)添加到系統(tǒng)任務(wù)ready隊(duì)列中,使得任務(wù)成為競爭使用CPU的一員。
2.1.3 深入了解任務(wù)棧
任務(wù)在VxWorks內(nèi)核中作為調(diào)度的基本單元,每個(gè)任務(wù)是程序的一個(gè)實(shí)例,程序可以由單個(gè)或多個(gè)函數(shù)構(gòu)成。任務(wù)在運(yùn)行這些函數(shù)的過程中,不可避免地使用一些函數(shù)變量(定義在函數(shù)內(nèi)部的變量),這些函數(shù)變量的存放地就在任務(wù)棧中;當(dāng)存在函數(shù)間調(diào)用時(shí),需要某種機(jī)制傳遞參數(shù),使用較多的就是棧機(jī)制。棧實(shí)際上就是一塊地址連續(xù)的內(nèi)存塊,只不過由于其特殊的使用方式,從而被封裝成一個(gè)特殊的結(jié)構(gòu)類型。一般而言,棧的使用以向下遞減地址方式,即棧頂位于內(nèi)存高地址處,棧底位于內(nèi)存低地址處,使用時(shí)從棧頂開始使用,逐漸向棧底逼近。當(dāng)然也有相反方向的工作模式。無論方向如何,其目的都是一樣的,只要在某個(gè)平臺(tái)保持全局范圍內(nèi)的統(tǒng)一性,就可以保證系統(tǒng)工作的正常性。注意:棧的方向一般是由CPU硬件決定的,更進(jìn)一步地說,是由指令集決定的。如Intel x86系列提供的push、pop指令就是向下遞減棧地址的,操作系統(tǒng)編寫者對(duì)此必須了解。
通用操作系統(tǒng)進(jìn)程棧(通用操作系統(tǒng)一般稱每個(gè)調(diào)度單元為進(jìn)程)分為兩個(gè)層次,這是由于通用操作系統(tǒng)一般是由兩個(gè)運(yùn)行態(tài)(內(nèi)核態(tài)和用戶態(tài))決定的。通用操作系統(tǒng)需要提供高安全性的運(yùn)行環(huán)境,它必須明確地隔離兩個(gè)不同層次上的操作,從而知道哪些操作是被允許的,哪些是被禁止的,當(dāng)然安全措施不僅僅通過運(yùn)行態(tài)來實(shí)現(xiàn),運(yùn)行態(tài)的區(qū)分只是其中重要的措施之一。通用操作系統(tǒng)進(jìn)程在兩個(gè)不同運(yùn)行態(tài)下具有不同的棧,當(dāng)進(jìn)程運(yùn)行在用戶態(tài)時(shí),其使用用戶態(tài)棧,當(dāng)發(fā)生系統(tǒng)調(diào)用或由于中斷進(jìn)入到內(nèi)核態(tài)時(shí),其切換到進(jìn)程的內(nèi)核態(tài)棧。
一般而言,用戶態(tài)棧是使用不盡的,由平臺(tái)內(nèi)存空間決定,地址空間一般不造成限制(如Linux下用戶態(tài)棧原則上有接近3GB的空間可用),而內(nèi)核態(tài)棧大小是存在很大限制的,如Linux下內(nèi)核態(tài)棧一般只有大約4KB的大小。所以,進(jìn)程運(yùn)行在內(nèi)核態(tài)時(shí),要求大空間的結(jié)構(gòu)必須使用堆的方式分配,不要使用棧進(jìn)行分配。事實(shí)上,由于運(yùn)行在內(nèi)核態(tài)的代碼都是嚴(yán)格限制的,所以一般都不會(huì)發(fā)生內(nèi)核態(tài)棧溢出的問題,而由于用戶態(tài)棧可使用的空間極大,雖然其先分配給用戶態(tài)棧的實(shí)際內(nèi)存較小,但是可以根據(jù)需要?jiǎng)討B(tài)地增加用戶態(tài)棧內(nèi)存,所以很難使得用戶態(tài)棧溢出(你能想象一個(gè)應(yīng)用程序可以使用完3GB的棧嗎?)。
VxWorks內(nèi)核不使用通用操作系統(tǒng)下的運(yùn)行態(tài),雖然VxWorks下也有內(nèi)核態(tài)的概念,不過其本質(zhì)上就是一個(gè)內(nèi)核數(shù)據(jù)結(jié)構(gòu)的簡單保護(hù)機(jī)制,僅僅由一個(gè)全局變量kernelState表示是否在內(nèi)核態(tài),當(dāng)用戶進(jìn)行內(nèi)核函數(shù)調(diào)用或者中斷發(fā)生進(jìn)入到VxWorks內(nèi)核運(yùn)行時(shí),其并不發(fā)生運(yùn)行態(tài)的本質(zhì)切換,而任務(wù)自始至終都在使用同一個(gè)棧。換句話說,VxWorks下的任務(wù)棧既被應(yīng)用函數(shù)(用戶編寫的函數(shù))使用,也被內(nèi)核函數(shù)(VxWorks內(nèi)核提供)使用,其VxWorks下任務(wù)棧在初始創(chuàng)建后,不可以根據(jù)需要在后期動(dòng)態(tài)地增加內(nèi)存容量。這就對(duì)VxWorks任務(wù)棧的初始創(chuàng)建大小提出了一個(gè)要求,即其在創(chuàng)建棧(即創(chuàng)建任務(wù))時(shí)指定的棧大小必須足夠大,以滿足任務(wù)后續(xù)運(yùn)行期間對(duì)棧容量的需求,而這一點(diǎn)是很難由用戶在創(chuàng)建時(shí)進(jìn)行判斷的。故實(shí)際上,用戶在創(chuàng)建任務(wù)時(shí)會(huì)指定一個(gè)比實(shí)際需要大得多的棧容量,這對(duì)于一個(gè)嵌入式系統(tǒng)而言,對(duì)寶貴的內(nèi)存資源造成了極大的浪費(fèi)。VxWorks官方文檔的建議是通過試驗(yàn)法,在程序開發(fā)期間,在任務(wù)創(chuàng)建時(shí),指定一個(gè)比較大的棧,在任務(wù)運(yùn)行期間,不斷地使用checkStack函數(shù)查看任務(wù)的棧使用情況,從而得到一個(gè)任務(wù)棧使用數(shù)據(jù)的大致統(tǒng)計(jì)情況。其后,用這個(gè)得到的統(tǒng)計(jì)值作為任務(wù)棧的實(shí)際容量。checkStack函數(shù)的調(diào)用原型如下。
void checkStack ( int taskNameOrId /* task name or task ID; 0 = summarize all */ )
參數(shù)(taskNameOrId)指定要檢查的棧對(duì)應(yīng)任務(wù)的ID。以參數(shù)0調(diào)用該函數(shù)時(shí),會(huì)打印出系統(tǒng)內(nèi)所有任務(wù)的棧的使用情況。checkStack函數(shù)的一個(gè)使用實(shí)例如下。
-> checkStack tShell NAME ENTRY TID SIZE CUR HIGH MARGIN ------------ ------------ -------- ----- ----- ----- ------ tShell _shell 23e1c789208832 36325576
2.1.4 任務(wù)名長度問題
當(dāng)在Shell下使用“i”命令查看當(dāng)前系統(tǒng)中的任務(wù)時(shí),對(duì)于任務(wù)名的顯示有些令人誤解:任務(wù)名長度較長的字符串(如大于11B)都發(fā)生了截?cái)唷_@讓很多人誤解為任務(wù)名最大只能有10B的長度。故在創(chuàng)建任務(wù)時(shí)刻意減少任務(wù)名的長度。其實(shí)這只是顯示上的一些問題,taskShow函數(shù)(“i”命令的底層調(diào)用函數(shù))在打印任務(wù)名時(shí)進(jìn)行了截?cái)嗖僮鳎窃趖askSpawn函數(shù)中對(duì)傳入的任務(wù)名參數(shù)進(jìn)行截?cái)唷J聦?shí)上,VxWorks支持任意長度的任務(wù)名。換句話說,在調(diào)用taskSpawn或者taskCreate創(chuàng)建任務(wù)時(shí),用戶可以指定一個(gè)其需要的任何名稱。VxWorks內(nèi)核在內(nèi)部以無損方式保存了這個(gè)用戶傳入的任意名稱。只是在調(diào)用taskShow對(duì)任務(wù)信息進(jìn)行顯示時(shí),在打印格式上的一些操作只打印出了任務(wù)名前11B。用戶可以根據(jù)需要自編寫一個(gè)任務(wù)信息顯示函數(shù),將任務(wù)名稱全部打印出來。
VxWorks下并不要求任務(wù)名的全局唯一性,兩個(gè)完全不同的任務(wù)可以使用相同的名稱而不會(huì)造成底層(內(nèi)核)使用上的任何問題,但是這會(huì)讓查看任務(wù)信息的用戶產(chǎn)生疑問。同樣,上文中討論到的taskShow對(duì)于任務(wù)名的截?cái)囡@示也會(huì)造成同樣的問題。故如果仍然使用內(nèi)核提供的taskShow顯示任務(wù)信息,則建議用戶在任務(wù)創(chuàng)建時(shí)盡量將任務(wù)限制在11B之內(nèi),或者兩個(gè)不同任務(wù)的名稱最好在前11B內(nèi)有所區(qū)別。任務(wù)信息顯示函數(shù)taskShow調(diào)用原型如下。
STATUS taskShow ( int tid, /* task ID */ int level /* 0 = summary, 1 = details, 2 = all tasks */ )
參數(shù)1(tid):需要顯示信息的任務(wù)ID。
參數(shù)2(level):任務(wù)信息開放程度。
注意
taskShow根據(jù)第2 個(gè)參數(shù)值對(duì)任務(wù)信息和任務(wù)范圍做不同程度的信息顯示。當(dāng)參數(shù)2傳入的值為2時(shí),即對(duì)系統(tǒng)內(nèi)所有的任務(wù)進(jìn)行信息顯示,此時(shí)將忽略第1個(gè)參數(shù)值;否則將只顯示參數(shù)1指定的任務(wù)信息。而參數(shù)2的值表示顯示任務(wù)信息的詳細(xì)程度。0表示只顯示大概信息,這些信息包括函數(shù)名稱、入口函數(shù)、任務(wù)棧使用情況總結(jié);1表示顯示任務(wù)的詳細(xì)信息,這些信息除了以上所說的大概信息外,還包括任務(wù)棧的詳細(xì)信息(棧基址、棧容量)、任務(wù)所包含的事件、任務(wù)當(dāng)前寄存器值。
2.1.5 正確結(jié)束任務(wù)
除了極少數(shù)幾個(gè)任務(wù)自始至終保持存活外,大多數(shù)任務(wù)都只是在一段時(shí)間內(nèi)保持活動(dòng),最后都不可避免地要消亡。消亡有兩種方式:一是任務(wù)正常運(yùn)行到結(jié)束,通過exit函數(shù)結(jié)束;二是被直接刪除,即非正常方式結(jié)束。一般情況下,用戶創(chuàng)建一個(gè)任務(wù),完成某個(gè)特定的功能,最后“功成身退”,從系統(tǒng)中消失,這是一般任務(wù)的工作方式。例如,任務(wù)執(zhí)行一個(gè)main函數(shù),最后通過reture返回。代碼在編譯時(shí),編譯環(huán)境實(shí)際上在函數(shù)最后加上了一個(gè)exit函數(shù),這是對(duì)用戶透明的,用戶只需要調(diào)用reture語句或者直接退出都可以。但是從底層代碼運(yùn)行來看,在運(yùn)行完所有的用戶代碼后,還會(huì)執(zhí)行一個(gè)exit函數(shù),這個(gè)函數(shù)是由編譯環(huán)境在編譯用戶程序時(shí)自動(dòng)加入的,與在函數(shù)結(jié)束時(shí)加上一個(gè)exit函數(shù)的道理相同。實(shí)際上,編譯環(huán)境在調(diào)用用戶提供的入口函數(shù)之前,也加上了一個(gè)類似于init的函數(shù),創(chuàng)建程序的運(yùn)行環(huán)境,進(jìn)而調(diào)用用戶提供的入口函數(shù),真正執(zhí)行用戶的代碼。這些機(jī)制有些類似于C++中自動(dòng)加入的初始化(constructor)和消亡(destructor)函數(shù)。
VxWorks下exit函數(shù)用于結(jié)束一個(gè)任務(wù)的壽命,其底層調(diào)用windDelete函數(shù),完成表示任務(wù)的數(shù)據(jù)結(jié)構(gòu)的釋放和任務(wù)棧的釋放。注意:windDelete只釋放任務(wù)棧和表示任務(wù)本身的結(jié)構(gòu),并不負(fù)責(zé)釋放用戶在任務(wù)運(yùn)行過程中分配的任何內(nèi)存空間。exit函數(shù)用于一個(gè)任務(wù)正常的自動(dòng)消亡。內(nèi)核同樣提供了一個(gè)非正常結(jié)束任務(wù)的方式:taskDelete函數(shù),該函數(shù)的調(diào)用原型如下。
STATUS taskDelete ( int tid /* task ID of task to delete */ )
注意
當(dāng)以參數(shù)0調(diào)用taskDelete時(shí),表示刪除當(dāng)前運(yùn)行的任務(wù)自身。此時(shí)的功能類似于被動(dòng)調(diào)用exit函數(shù)退出。一般而言,如果一個(gè)任務(wù)需要?jiǎng)h除自身,那么直接調(diào)用exit即可,當(dāng)然也可以以參數(shù)0調(diào)用taskDelete。但是taskDelete更多地被用于刪除一個(gè)其他正常運(yùn)行的任務(wù),而非自身。該函數(shù)提供的功能必須慎用。通常對(duì)于普通的用戶不需要調(diào)用這么一個(gè)任務(wù)刪除函數(shù),這個(gè)函數(shù)極容易造成內(nèi)核的不一致性,從而導(dǎo)致內(nèi)核崩潰。設(shè)想一個(gè)任務(wù)剛剛獲取一個(gè)資源的使用權(quán),正在所謂的“關(guān)鍵區(qū)域”執(zhí)行代碼,而另外一個(gè)任務(wù)卻“霸道”地將其直接刪除,此時(shí)被刪除任務(wù)將停止運(yùn)行余下的代碼,表示任務(wù)的結(jié)構(gòu)和任務(wù)棧被釋放,進(jìn)而造成內(nèi)核資源狀態(tài)的不一致性,因?yàn)槿蝿?wù)退出之前沒有相應(yīng)地釋放其已獲取資源的使用權(quán),導(dǎo)致其他任何競爭使用該資源的任務(wù)處于無限等待狀態(tài)。以一個(gè)簡單的例子說明,如多個(gè)函數(shù)需要競爭使用同一項(xiàng)資源,該資源有一個(gè)變量進(jìn)行保護(hù),任何使用該資源的任務(wù)在使用之前必須檢查該變量的值,如果該變量為1,則表示資源可用;若為0,則表示資源已被其他的任務(wù)使用,所有其他的任務(wù)必須等待,直到該變量重新變?yōu)?。為了保證對(duì)變量本身操作的原子性,一般會(huì)使用某種特定編碼方式,如在Intel x86結(jié)構(gòu)下,會(huì)使用lock指令修改該變量的值。
某個(gè)競爭使用資源的任務(wù)準(zhǔn)備使用資源時(shí),其檢查該變量的值,如果為0,則等待,等待一段時(shí)間后,其重新檢查該變量的值,此時(shí)該變量值為1,表示資源可以訪問,那么任務(wù)將會(huì)將該變量的值設(shè)置為0,表示“我”這個(gè)任務(wù)現(xiàn)在正在使用資源,其他任務(wù)必須等待。而在該任務(wù)使用資源期間,一個(gè)“外來不明是非者”將這個(gè)任務(wù)進(jìn)行了刪除,即中斷了正在使用資源的任務(wù)的執(zhí)行,致使其非正常消亡,即系統(tǒng)中已經(jīng)不存在這個(gè)任務(wù)了,那么這個(gè)任務(wù)原先在使用完資源后將該變量重新設(shè)置為1,表示資源可用的代碼永遠(yuǎn)也無法得到運(yùn)行。換句話說,該資源被一個(gè)“死亡”的任務(wù)無限期地“使用”,所有其他競爭使用該資源的任務(wù)都無限期地處于等待狀態(tài)。所以,不正當(dāng)?shù)貏h除一個(gè)正在運(yùn)行的其他任務(wù)是非常危險(xiǎn)的一個(gè)操作,除非明確知道被刪除的任務(wù)當(dāng)時(shí)所處的狀態(tài),如被刪除任務(wù)已經(jīng)設(shè)置了一個(gè)標(biāo)志位,表示它可被刪除,因?yàn)槠湟呀?jīng)釋放了它所使用的一切資源,正在處于“無為”狀態(tài),但是在外界其他某個(gè)條件滿足之前,其又不能提前結(jié)束,故等待其他任務(wù)對(duì)其進(jìn)行刪除。但是這種情況除了某個(gè)特殊情況,一般都不會(huì)發(fā)生。所以,taskDelete函數(shù)的使用場合有限,用戶只需對(duì)其進(jìn)行了解即可,建議不要使用。
與taskDelete“分庭抗禮”的一對(duì)函數(shù)就是taskSafe和taskUnsafe。一個(gè)任務(wù)為了防止在預(yù)先無任何提示的情況下被其他任務(wù)刪除,其可以調(diào)用taskSafe函數(shù)進(jìn)行自身的保護(hù),一個(gè)任務(wù)在調(diào)用taskSafe函數(shù)后,則其他任何任務(wù)都不可對(duì)其進(jìn)行刪除操作,這個(gè)函數(shù)就是為了應(yīng)對(duì)taskDelete而設(shè)計(jì)的,以防止上文所述的一個(gè)處于“關(guān)鍵區(qū)域”的任務(wù)被野蠻刪除的行為發(fā)生。taskSafe和taskUnsafe函數(shù)調(diào)用原型如下。
STATUS taskSafe (void) STATUS taskUnsafe (void)
如下代碼列舉了這兩個(gè)函數(shù)的典型應(yīng)用環(huán)境。
taskSafe (); semTake (semId, WAIT_FOREVER); /* Block until semaphore available */ . . /* critical region code */ . semGive (semId); /* Release semaphore */ taskUnsafe ();
VxWorks內(nèi)核還提供如下三個(gè)函數(shù)對(duì)運(yùn)行中的任務(wù)施加影響。第一個(gè)是taskSuspend,即掛起一個(gè)正在執(zhí)行的函數(shù),該函數(shù)調(diào)用原型如下。
STATUS taskSuspend ( int tid /* task ID of task to suspend */ )
當(dāng)以參數(shù)0調(diào)用taskSuspend時(shí),表示掛起當(dāng)前正常執(zhí)行的任務(wù)。而以非0任務(wù)ID方式指定一個(gè)其他被掛起的任務(wù)。這個(gè)其他被掛起的任務(wù)原先可以是由于優(yōu)先級(jí)較低而等待運(yùn)行,也可以是一個(gè)處于延遲(delay)睡眠的任務(wù)或者是一個(gè)已被掛起的任務(wù)。
第二個(gè)函數(shù)taskResume的功能是取消taskSuspend的作用,其將一個(gè)被掛起的任務(wù)重新設(shè)置為可運(yùn)行狀態(tài)。當(dāng)然,此時(shí)被恢復(fù)的任務(wù)一定是一個(gè)其他任務(wù),因?yàn)橐粋€(gè)任務(wù)是不可能Resume其自身的。taskResume函數(shù)的調(diào)用原型如下。
STATUS taskResume ( int tid /* task ID of task to resume */ )
第三個(gè)比較重要的控制任務(wù)運(yùn)行的函數(shù)是taskDelay,即置當(dāng)前正在運(yùn)行的任務(wù)為睡眠狀態(tài),睡眠時(shí)間長度以調(diào)用taskDelay時(shí)輸入的參數(shù)為據(jù)。taskDelay函數(shù)的調(diào)用原型如下。
STATUS taskDelay ( int ticks /* number of ticks to delay task */ )
注意
參數(shù)以系統(tǒng)滴答數(shù)為單位,即以系統(tǒng)時(shí)鐘的時(shí)間間隔為單位。一般而言,這個(gè)間隔為1ms,所以可以簡單地認(rèn)為是毫秒級(jí)的任務(wù)延遲。任務(wù)調(diào)用taskDelay進(jìn)行延遲在代碼中使用較多,特別是以輪詢方式對(duì)某個(gè)設(shè)備進(jìn)行操作的任務(wù)一般都是以while循環(huán)加taskDelay的方式工作。
另外要注意taskDelay的一種特殊工作方式,就是以NO_WAIT參數(shù)調(diào)用taskDelay,此種方式主要是在RR調(diào)度禁止使用的前提下,給相同優(yōu)先級(jí)的其他任務(wù)一個(gè)使用CPU的機(jī)會(huì)。當(dāng)以NO_WAIT調(diào)用taskDelay,VxWorks內(nèi)核將當(dāng)前任務(wù)置于ready隊(duì)列中所有相同優(yōu)先級(jí)的任務(wù)之后,從而給相同優(yōu)先級(jí)的其他任務(wù)提供一個(gè)運(yùn)行的機(jī)會(huì)。注意,這種方式一般使用在RR調(diào)度被禁用的情況下。如果使能了RR調(diào)度,則一般無須使用此種方式,因?yàn)镽R調(diào)度會(huì)循環(huán)讓相同優(yōu)先級(jí)的任務(wù)使用CPU資源的。
2.1.6 任務(wù)的鉤子函數(shù)——黑客機(jī)制
涉及VxWorks任務(wù)一個(gè)重要的使用是可以在任務(wù)創(chuàng)建、消亡、調(diào)度之時(shí)調(diào)用用戶注冊(cè)的鉤子函數(shù),這在某些情況下會(huì)特別有意義。VxWorks提供一種機(jī)制可以讓用戶注冊(cè)一種鉤子函數(shù),當(dāng)系統(tǒng)中有新的任務(wù)被創(chuàng)建,一個(gè)任務(wù)消亡以及任務(wù)調(diào)度時(shí),可以執(zhí)行用戶注冊(cè)的這些函數(shù),從而達(dá)到用戶的特殊目的。如對(duì)于低功耗的實(shí)現(xiàn),可以利用VxWorks內(nèi)核提供的這種機(jī)制,雖然這不是很優(yōu)美的一種實(shí)現(xiàn),但可以用于說明鉤子函數(shù)的作用。低功耗要求當(dāng)CPU中沒有用戶任務(wù)運(yùn)行時(shí),將整個(gè)平臺(tái)盡量置于低功耗狀態(tài)。當(dāng)VxWorks下沒有用戶任務(wù)時(shí),VxWorks將執(zhí)行其自身提供的一個(gè)Idle任務(wù),Idle任務(wù)具有最低優(yōu)先級(jí)。換句話說,只要存在一個(gè)可運(yùn)行的用戶任務(wù),無論這個(gè)任務(wù)的優(yōu)先級(jí)是什么,其都將被調(diào)度運(yùn)行,如此,我們可以使用一個(gè)最低優(yōu)先級(jí)的后臺(tái)任務(wù)加上一個(gè)任務(wù)調(diào)度鉤子函數(shù)實(shí)現(xiàn)平臺(tái)低功耗目的。在系統(tǒng)初始化階段,創(chuàng)建一個(gè)最低優(yōu)先級(jí)的后臺(tái)任務(wù),這個(gè)任務(wù)的優(yōu)先級(jí)要低于所有的有“責(zé)任”在身的其他任務(wù)。換句話說,當(dāng)這個(gè)后臺(tái)任務(wù)被調(diào)度運(yùn)行時(shí),就表示當(dāng)前系統(tǒng)可以處于低功耗模式。我們同時(shí)注冊(cè)一個(gè)調(diào)度任務(wù)時(shí)被調(diào)用的鉤子函數(shù),后臺(tái)任務(wù)實(shí)現(xiàn)代碼就是一個(gè)FOREVER語句,其偽代碼如下。
FOREVER //等同于for(;;) { if(powerdown==FALSE){ put board to power down state; powerdown=TRUE; } }
鉤子函數(shù)實(shí)現(xiàn)如下。
void taskSwitchHook(WIND_TCB *pOldTcb, WIND_TCB *pNewTcb){ if(old task is our daemon task){ /*即后臺(tái)任務(wù)被其他任務(wù)替代了*/ put board back to Normal state; powerdown=FALSE; } }
一般而言,將平臺(tái)從低功耗模式喚醒的方式有很多種,如可以使用一個(gè)外部中斷專門用于喚醒平臺(tái),使脫離低功耗模式,上述代碼只是一個(gè)簡單示例,要使用到實(shí)際中需要很多考慮,但是這給我們提供了鉤子函數(shù)的一種使用方式。
VxWorks提供的鉤子函數(shù)注冊(cè)和注銷如下。
① 任務(wù)創(chuàng)建鉤子函數(shù)注冊(cè)和注銷。
STATUS taskCreateHookAdd ( FUNCPTR createHook /* routine to be called when a task is created */ )
參數(shù)為注冊(cè)的鉤子函數(shù),其將在任何一個(gè)新任務(wù)創(chuàng)建時(shí)被調(diào)用,它必須具有的定義形式如下:即以新創(chuàng)建任務(wù)的結(jié)構(gòu)為參數(shù)。鉤子函數(shù)可以這個(gè)代表新創(chuàng)建任務(wù)的結(jié)構(gòu)做一些個(gè)性化的處理,如檢查優(yōu)先級(jí)使其限定在特定的范圍等。
void createHook ( WIND_TCB *pNewTcb /* pointer to new task's TCB */ )
taskCreateHookDelete用以注銷之前注冊(cè)的鉤子函數(shù),其調(diào)用原型如下。
STATUS taskCreateHookDelete ( FUNCPTR createHook /* routine to be deleted from list */ )
② 任務(wù)調(diào)度鉤子函數(shù)注冊(cè)和注銷。
STATUS taskSwitchHookAdd ( FUNCPTR switchHook /* routine to be called at every task switch */ )
taskSwitchHookAdd用于注冊(cè)一個(gè)發(fā)生任務(wù)調(diào)度時(shí)調(diào)用的鉤子函數(shù),該鉤子函數(shù)必須具有如下的定義形式。(其中,pOldTcb表示被調(diào)度出去的任務(wù)結(jié)構(gòu),而pNewTcb則表示被調(diào)度進(jìn)來成為CPU新的使用者的任務(wù)結(jié)構(gòu)。)
void switchHook ( WIND_TCB *pOldTcb, /* pointer to old task's WIND_TCB */ WIND_TCB *pNewTcb /* pointer to new task's WIND_TCB */ )
taskSwitchHookDelete用以注銷通過taskSwitchHookAdd添加的鉤子函數(shù)。其調(diào)用原型如下。
STATUS taskSwitchHookDelete ( FUNCPTR switchHook /* routine to be deleted from list */ )
③ 任務(wù)消亡(刪除)鉤子函數(shù)注冊(cè)和注銷。
STATUS taskDeleteHookAdd ( FUNCPTR deleteHook /* routine to be called when a task is deleted */ )
上述代碼用以注冊(cè)在任何一個(gè)任務(wù)消亡時(shí)被調(diào)用的鉤子函數(shù)。鉤子函數(shù)deleteHook必須具有如下的定義形式,參數(shù)是消亡的任務(wù)結(jié)構(gòu)。
void deleteHook ( WIND_TCB *pTcb /* pointer to deleted task's WIND_TCB */ )
taskDeleteHookDelete用以注銷之前通過taskDeleteHookAdd注冊(cè)的鉤子函數(shù)。其調(diào)用原型如下。
STATUS taskDeleteHookDelete ( FUNCPTR deleteHook /* routine to be deleted from list */ )
除了這些注冊(cè)和注銷函數(shù)外,VxWorks同時(shí)還提供了三個(gè)信息顯示函數(shù)用于顯示當(dāng)前注冊(cè)到系統(tǒng)中的所有鉤子函數(shù)。這三個(gè)函數(shù)的調(diào)用原型如下。
void taskCreateHookShow (void)
顯示當(dāng)前注冊(cè)到系統(tǒng)的所有在任務(wù)創(chuàng)建時(shí)被調(diào)用的鉤子函數(shù)列表。
void taskDeleteHookShow (void)
顯示當(dāng)前注冊(cè)到系統(tǒng)中所有在發(fā)生任務(wù)切換時(shí)被調(diào)用的鉤子函數(shù)列表。
void taskSwitchHookShow (void)
顯示當(dāng)前注冊(cè)到系統(tǒng)中的在任務(wù)消亡時(shí)被調(diào)用的鉤子函數(shù)列表。
2.1.7 任務(wù)小結(jié)
至此,我們完成對(duì)VxWorks任務(wù)的說明,VxWorks下的任務(wù)即通用操作系統(tǒng)下所說的進(jìn)程是內(nèi)核的基本運(yùn)行單元,也是內(nèi)核調(diào)度算法的處理單元。縱觀所有的操作系統(tǒng),進(jìn)程或者任務(wù)在底層上都由一個(gè)數(shù)據(jù)結(jié)構(gòu)表示,這個(gè)數(shù)據(jù)結(jié)構(gòu)一般稱為任務(wù)(或者進(jìn)程)控制塊TCB(或者PCB),用以保存一個(gè)任務(wù)(或者CPU執(zhí)行單元)的所有信息,這些信息中有些是保證一個(gè)任務(wù)可以被CPU運(yùn)行的基本關(guān)鍵信息(如所有的寄存器值、內(nèi)存映射),但很多是為管理需要而存在的“輔助”信息,這些“輔助”信息構(gòu)成了某個(gè)操作系統(tǒng)的實(shí)現(xiàn)方式。
VxWorks任務(wù)給用戶提供了極大的靈活性,如果深入到任務(wù)控制結(jié)構(gòu)這一層面(即內(nèi)核編程和驅(qū)動(dòng)編程),那么可以對(duì)任務(wù)執(zhí)行其任何需要的操作,即便讓系統(tǒng)立刻崩潰也在所不惜。這也是底層編程提供的極大“娛樂性”。
VxWorks任務(wù)的幾個(gè)重要方面總結(jié)如下:
● 任務(wù)具有優(yōu)先級(jí),是任務(wù)調(diào)度運(yùn)行的依據(jù)和獲取CPU資源的“競爭卡”,優(yōu)先級(jí)越高,其獲得CPU資源的可能性就越大,VxWorks以0~255的數(shù)字表示優(yōu)先級(jí),數(shù)值越大,優(yōu)先級(jí)越小,0表示最高優(yōu)先級(jí)。任務(wù)優(yōu)先級(jí)可以在任務(wù)運(yùn)行過程中動(dòng)態(tài)地改變,可以改變一個(gè)任務(wù)到任意優(yōu)先級(jí)。
● 任務(wù)具有任務(wù)棧。任務(wù)棧的容量在任務(wù)創(chuàng)建時(shí)就被確定,而且其后不可進(jìn)行更改。任務(wù)棧與表示任務(wù)的內(nèi)核結(jié)構(gòu)作為一段連續(xù)的區(qū)域進(jìn)行分配。任務(wù)棧是任務(wù)運(yùn)行過程中各種局部函數(shù)變量的存放地和函數(shù)調(diào)用參數(shù)傳遞的渠道。由于VxWorks下任務(wù)棧容量不可動(dòng)態(tài)改變,故在創(chuàng)建任務(wù)時(shí)必須指定一個(gè)足夠大的容量,通常可以在函數(shù)調(diào)試階段通過checkStack統(tǒng)計(jì)出一個(gè)任務(wù)的棧使用情況,進(jìn)而指定一個(gè)合理的任務(wù)棧容量,避免浪費(fèi)較多的內(nèi)存空間。
● VxWorks提供了一種機(jī)制可以在涉及任務(wù)的三個(gè)關(guān)鍵狀態(tài)變化出調(diào)用用戶注冊(cè)的鉤子函數(shù),這三個(gè)狀態(tài)變化為任務(wù)初始創(chuàng)建時(shí)、任務(wù)被調(diào)度使用或放棄CPU時(shí)、任務(wù)消亡時(shí)。通過利用VxWorks提供的這種靈活性可以實(shí)現(xiàn)一些嵌入式平臺(tái)下有意義的策略。
● 每個(gè)任務(wù)在操作系統(tǒng)底層的實(shí)現(xiàn)上都是由一個(gè)數(shù)據(jù)結(jié)構(gòu)表示的,通過直接更改這個(gè)數(shù)據(jù)結(jié)構(gòu)中的某些字段值,可以控制任務(wù)某些運(yùn)行行為,這在內(nèi)核層編程時(shí)十分有效。VxWorks任務(wù)結(jié)構(gòu)定義在taskLib.h頭文件中。感興趣的用戶可以查看VxWorks任務(wù)結(jié)構(gòu)WIND_TCB的具體定義。
- ANSYS Workbench基礎(chǔ)教程與工程分析詳解
- FPGA嵌入式項(xiàng)目開發(fā)三位一體實(shí)戰(zhàn)精講
- TinyML:基于TensorFlow Lite在Arduino和超低功耗微控制器上部署機(jī)器學(xué)習(xí)
- 基于HCS12的嵌入式系統(tǒng)設(shè)計(jì)
- 現(xiàn)代嵌入式系統(tǒng)開發(fā)專案實(shí)務(wù)
- DSP技術(shù)與應(yīng)用實(shí)例
- 嵌入式產(chǎn)品分析與設(shè)計(jì)
- 嵌入式系統(tǒng)Linux內(nèi)核開發(fā)實(shí)戰(zhàn)指南(ARM平臺(tái))
- 51單片機(jī)C語言編程一學(xué)就會(huì)
- 單片機(jī)原理與接口技術(shù)
- 單片微型計(jì)算機(jī)原理及應(yīng)用
- 愛上單片機(jī)(第4版)
- AVR單片機(jī)很簡單:C語言快速入門及開發(fā)實(shí)例
- 基于Quartus II的FPGA/CPLD設(shè)計(jì)實(shí)例精解
- 零基礎(chǔ)學(xué)西門子S7- 200 SMART PLC編程及應(yīng)用