啓動工做原理html
剛接觸操做系統的時候以爲這個最神祕,到底裏面作了什麼,怎麼就成了個操做系統,它到底有什麼用,爲何要引進來着個東東。學了以後才知道,原來最根本的思想仍是源於彙編裏面的跳轉和壓棧,以調用一個函數爲例,編譯後的彙編確定是先經過SP壓入當前代碼段地址而後就是保存一些寄存器的值放棧裏面(51單片機好像不是這樣),而後執行程序,完了以後,出棧把寄存器恢復,最後把原來存的代碼段地址付給PC而後回到原來的程序,這是彙編執行函數的作法,而操做系統人爲強行的模擬這樣操做,把代碼寫成不一樣代碼塊假定爲A、B,要相互之間執行彷佛不受影響,是這麼實現的,假設首先代碼在A中執行,代碼段一直往下指,若是我要執行B,首先把A的代碼段壓入棧,全部的寄存器值存入A的棧,而後讓SP強行指到B的棧執行出棧操做,把寄存器恢復成B的,B的代碼段地址放入PC,這樣就B在執行,若是又要A接着上次執行,又強行把B的寄存器、當前代碼段地址壓入B的棧,而後SP強行指到A的棧恢復的時候恢復成A最近一次保存的東西(和函數調用不一樣每次切換棧裏面的東西是隨機的),這樣,能夠很自由的在A、B中切換,若是切換足夠快,A、B看以來好像同時在執行,這就是並行,A、B就是任務。若是這個切操做放到定時器函數中來作,就能夠嚴格按照時間來切換,這就是操做系統雛形。另外,各個任務之間有存在必定的關係,有邏輯上的前後等,必須引進全局的結構體、變量來標記一些信息,全局的這些數據是不會被釋放的,因此全部的任務能夠去經過讀、寫這些數據來實現各個程序塊交流信息,實現所謂的同步、互斥。這就是操做系統的原理,而這些不一樣的通訊方式按功能細分就成事件管理、內存管理啥玩意。有這些基本的管理就是一個只有內核操做系統了。配上文件系統、圖形界面這些個模塊功能就能作出想Window這樣的東東。只有引入操做系統才能更好的寫程序,才能讓性能發揮到極致。具體到uCOSII也是這樣:首先是主函數,而後是OSInit(),這個函數就是對那些全局的數據結構初始化,創建但願的鏈表等數據結構,爲後面全局變量通訊作好準備,而且建立了1-2個系統任務(空閒任務必須,統計任務可選),而所謂的建立任務OSTaskCreate(另外在這個系統裏還有個OSTaskCreateExt也是一種建立任務函數,只不過多了些檢測棧、清楚棧的功能而已)就是把一個函數,的函數地址、本身的棧創建聯繫、優先級啥弄好爲任務切換作好準備。設置好定時切換的相關信息相似定時器,按照節拍在中斷中進行任務切換判斷、發生切換,這個時候尚未開啓開關,因此的任務建立完成後,啓動多任務函數OSStart(),這個函數是讓SP指到其中的一個站而後出棧就跳到一個任務函數裏去了,接下來就是正常的任務運行了。node
函數名數組 |
功能簡介數據結構 |
OS_MemInit();函數 |
初始化分區控制塊OS_MEM構建空閒內存控制塊MCB空閒鏈表,OSInit()內部調用。性能 |
OSMemCreate();spa |
其實是定義一個二維數組,一維也是能夠的只是沒那麼直觀方便而已,經過一個從空閒內存控制塊鏈表上取一個OS_MEM來管理這塊內存,作法是按用戶參數分紅小塊內存(通常大小爲第二維大小,直觀),填寫控制塊的相關信息,用戶調用操作系統 |
OSMemGet()指針 |
從指定的分區上取一個內存塊htm |
OSMemPut(); |
釋放一個內存塊到指定的分區 |
OSMemQuery(); |
查詢某個指定的分區信息 |
OSMemNameSet(); |
設置某個指定分區的名 |
OSMemNameGet(); |
獲得某個指定分區的名 |
函數名 |
功能簡介 |
OS_InitEvenList() |
初始化事件控制塊OS_EVENT鏈表,OSInit()內部調用 |
OS_EventWaitListInit() |
僅僅清零指定事件控制塊的等待任務表,等待任務組,實際上我以爲這個函數沒有必要,由於建立鏈表的時候全部ECB所有清0,被系統用事後返回回去時都是調用OS_EventTaskRdy或刪除事件函數,也都進行過清0,因此從空閒ECB鏈表取下來的ECB確定等待表、組都是0。 |
OS_EventTaskWait() |
阻塞當前任務,登記就緒表、組,等待任務表、組,TCB指向ECB |
OS_EventTaskRemove() |
僅僅清指定ECB的等待任務表、組 |
OS_EventTaskRdy() |
選出指定事件最高等待任務,讓其就緒,TCB域設置,若是沒有掛起的標誌還要清就緒組、表,ECB清相應的等待任務表、組 |
普通訊號量:
函數名 |
功能簡介 |
OSSemCreate() |
從ECB鏈表上取一個ECB,設置事件類型、信號量數,清ECB指針域爲0,OS_EventWaitListInit()清掉等待任務表、組。返回ECB* |
OSSemSet() |
通常不用,特殊狀況非得用,必需要求沒有任務被阻塞該事件上。 |
OSSemDel() |
有個op參數爲OS_DEL_NO+PEND 只有在沒有任務等待是才容許 OS_DEL_ALWAYS即便有任務在等待都要清掉 首先標記是否有任務被阻塞,再判斷是那種操做類型 第一種:沒有任務等待,返回ECB到空閒鏈表,不然,錯誤返回 第二種:若是有任務等待時,OS_EventTaskRdy()讓全部任務以等到該事件方式所有就緒,而後返回ECB到空閒鏈表,調OS_Sched()。若是沒有任務等待,直接返回ECB到空閒鏈表。 |
OSSemPend() |
請求信號量,若是該ECB有信號量,直接減1,返回; 若是沒有,設置TCB的3個域,超時(若是是0,就不存在時間滴答1-0的過程因此死等到有事件發生爲止),OSTCBCur->Stat標誌標記上信號量,OSTCBCur->StatPend默認設置爲OS_STAT_PEND_OK,調用OS_EventTaskWait(); 而後掉OS_Sched()任務切換 切換回來的時候:作個判斷是那種緣由切換回來的 若是是超時到,必定是OSTimeDlyResume或中斷中的 OSTimeTick 2個函數作過處理,設置 OSTCBCur->Stat(被清掉)和OSTCBCur->StatPend=OS_STAT_PEND_TO,可是ECB中的任務等待表、組還沒請因此調用OS_EventTaskRemove() 若是是放棄等待或正常等到了事件,那麼只須要標記返回信息,在切換回來的時候都已經被OS_EventTaskRdy()恢復過了。 最後統一把當前TCB的3個相關域設置爲OS_STAT_RDY、OS_STAT_PEND_OK、指向ECB的指針變量清0,退出該函數 |
OSSemAccept() |
無等待的請求信號量,若是有信號量減1,沒有的話就直接退出,返回值是作減以前的信號量值,很明顯若是不是0說明有信號量。 |
OSSemPendAbort() |
放棄信號量等待,參數是某個信號量類型ECB指針,若是有效的ECB沒有任務被阻塞,那麼直接退出,若是有任務被阻塞,參數opt 2中取值決定操做: OS_PEND_OPT_BROADCASE 廣播方式,全部被阻塞的任務都被以放棄等待的方式就緒(實現方式若是就緒組不爲0就一直循環執行OS_EventTaskRdy函數,裏面有選最高優先級的功能)。 OS_PEND_OPT_NONE非廣播方式,OS_EventTaskRdy函數只被執行一次,選當前被該事件阻塞的最高優先級任務就緒。 由於可能有比當前任務優先級更高的任務就緒因此調OSSched()。 |
OSSemPost() |
提交信號量,若是就緒組不爲0(有任務被阻塞)以等到事件的方式執行OS_EventTaskRdy,就緒被阻塞最高優先級的任務;若是爲0(沒有任務被阻塞),單純的讓信號量數加1就好了。 |
OSSemQuery() |
和內存管理查詢相似,有個OS_SEM_DATA類型結構體,主要是存一下指定的某個信號量類型的ECB的就緒組、表和信號量數3個信息。 |
互斥信號量:
函數名 |
功能簡介 |
OSMutexCreate() |
建立帶升級優先級的互斥類型信號量,若是指定的優先級未被佔用那麼把OSTCBPrioTbl[Pric]=1,佔着防止別人搶,爲了後面升級用。另外ECB的域OSEventCnt意義不一樣了,16位高8位爲待升級到的優先級,低8爲默認0Xff,表示尚未任務佔用該互斥信號量,不是0xFF表示佔用該ECB的任務的優先級。另外ECB的域(void *)OSEventPtr能夠指向佔用他的任務的TCB指針了,源代碼中請求事件成功後只有ECB指向TCB,TCB沒有指向ECB,TCB中ECB指針要指向ECB必須該任務被ECB事件阻塞起來,在該域初始化後默認爲0. |
OSMutexDel |
刪除指定互斥類型的ECB,首先判斷並標記是否有任務被阻塞因該事件,參數opt決定2種操做方式: OS_DEL_NO_PEND(沒有被阻塞時才進行刪除操做)若是有事件被阻塞錯誤退出;若是沒有,首先找到升級優先級的TCB指針OSTCBPrioTbl[],內容從1清爲0(是1的緣由:建立時若是成功就被1佔着,即便該ECB被某任務佔着,若是升級了必定會有阻塞任務,固然阻塞了若是比佔着的優先級低就只阻塞不升級,由於沒有阻塞的任務因此必定不會升級使用,因此仍是1),而後,初始化ECB的域後放到空閒的ECB鏈表中。 OS_DEL_ALWAYS任何狀況都刪除該ECB 首先取得升級優先級和實際優先級(可能爲0xFF未用),若是確實發生了升級經過OSMutex_RdyAtPro(ptcb,prio)恢復爲prio優先級,而後若是有被阻塞的任務讓全部因該事件被阻塞的任務所有就緒, 讓升級優先級的OSTCBPrioTbl[]的TCB指針清0,而後設置ECB的域,返回給ECB空閒鏈表,調用OSSched() |
OSMutex_RdyAtPro() |
讓升級了的任務優先級恢復原優先級並使之就緒,首先清0這個任務在就緒表、組,而後重新設置TCB中的相關域,設置全局變量 就緒表、組標記爲原優先級的就緒狀態,優先級指針表存TCB * |
OSMutexPend()
爲何要優先級升級? 假如較低優先級的任務50請求互斥信號量A,請求到了,較高優先級3去請求發現被佔,只能掛起,必須等待優先級爲50的任務釋放,可是若是這個過程當中像20、30優先級的任務發生就緒,50的優先級必須讓出CPU,若是20、30執行還未完,七、8優先級又就緒,致使50號任務遲遲得不到CPU,高優先級3相對重要的任務一直得不到執行,這是不容許的,因此採用優先級升級。優先級升級思想就是,建立互斥信號量時指定一個優先級2(假如),若是被低優先級50佔,有高優先級3請求,50的優先級會升到2,待釋放互斥信號量、或要刪除該信號量時才恢復成50,這樣能夠避免20、30這樣的任務搶CPU |
請求互斥信號量,若是域OSEventCnt低8位位0xFF表示未被任務佔用,設置ECB的相關域,返回。若是被其餘任務佔用,從ECB中取得佔用的TCB指針和優先級,判斷只有當佔用ECB的優先級比升級優先級和當前請求優先級都低的時候才進行升級,而後就是升級操做(不須要升級就不操做這塊),而後操做和普通訊號量相似都是設置TCB的3個域,超時(若是是0,就不存在時間滴答1-0的過程因此死等到有事件發生爲止),OSTCBCur->Stat標誌標記上互斥信號量,OSTCBCur->StatPend默認設置爲OS_STAT_PEND_OK, 調用OS_EventTaskWait();而後掉OS_Sched()任務切換 切換回來的時候:作個判斷是那種緣由切換回來的, 若是是超時到,必定是OSTimeDlyResume或中斷中的 OSTimeTick 2個函數作過處理,設置 OSTCBCur->Stat(被清掉)和OSTCBCur->StatPend=OS_STAT_PEND_TO,可是ECB中的任務等待表、組還沒請因此調用OS_EventTaskRemove() 若是是放棄等待或正常等到了事件,那麼只須要標記返回信息,在切換回來的時候都已經被OS_EventTaskRdy()恢復過了。 最後統一把當前TCB的3個相關域設置爲OS_STAT_RDY、OS_STAT_PEND_OK、指向ECB的指針變量清0,退出該函數 升級操做具體: 首先判斷升級後的任務是否就緒並標記,若是就緒直接清就緒表、組,若是被掛起的,判斷TCB中ECB指針是否爲0推斷是否因事件而掛起,是0表示不是等待事件而掛起(有多是被掛起函數直接掛起),非0表示因事件而掛起(若是是因事件而掛起,要清該事件的等待任務表、組),而後設置其TCB中相關域,按照原來的任務狀態設置就緒表、組,ECB任務等待表、組的值。恢復後的優先級的TCB指針表存該TCB指針。 |
OSMutexPost() |
判斷是否進行了優先級升級若是有恢復,判斷是否有任務在等待該互斥信號量: 沒有的話就設置該ECB 2個參數OSEventCnt低8位爲0xFF,OSEventPtr=0,而後退出;若是有任務在等待,那麼就執行一次事件恢復函數OS_EventTaskRdy(),設置ECB 2 2個參數OSEventCnt低8位爲任務優先級,OSEventPtr指向其TCB,而後發生任務切換,切換回來退出。 |
OSMutexAccept() |
無等待請求,判斷是否互斥信號量被佔,若是沒被佔就使用,若是被佔就標記錯誤返回信息,直接退出,不須要升級操做。 |
OSMutexQuery() |
查詢互斥信號,OS_MUTEX_DATA結構體中存5個域,等待任務表、組,原來優先級、升級優先級、邏輯變量是否進行過升級。 |
郵箱
函數名 |
功能簡介 |
OSMboxCreate() |
建立郵箱,很簡單,取一個空閒的ECB,而後,標記事件類型,OSEvenPtr域爲存消息的地方,建立時參數pmsg做爲參數傳進,因此建立時能夠有消息也可沒有,其他域清0。 |
OSMboxPend() |
請求指定消息類型ECB消息,若是消息域OSEventPtr不爲空,取出消息,清零,消息做爲返回值返回;若是爲0,處理和普通訊號量相似,設置當前TCB 3個域,等待時間域(若是是0,就不存在時間滴答1-0的過程因此死等到有事件發生爲止),OSTCBCur->Stat標誌標記上郵箱,OSTCBCur->StatPend默認設置爲OS_STAT_PEND_OK,調用OS_EventTaskWait(); 而後掉OS_Sched()任務切換 切換回來的時候:作個判斷是那種緣由切換回來的 若是是超時到,必定是OSTimeDlyResume或中斷中的 OSTimeTick 2個函數作過處理,設置 OSTCBCur->Stat(被清掉)和OSTCBCur->StatPend=OS_STAT_PEND_TO,可是ECB中的任務等待表、組還沒請因此調用OS_EventTaskRemove(),注意設置返回存消息的變量pmsg爲0。 若是是放棄等待,設置返回存消息的變量pmsg爲0。 若是是正常等到了事件,從當前TCB中OSTCBMsg域取消息存入返回值,和普通訊號量不一樣吧,由於提交信號量的函數把消息已經存入了當時處於等待任務表中最高優先級的該任務的TCB消息域中。 最後統一把當前TCB的4個相關域設置爲OS_STAT_RDY、OS_STAT_PEND_OK、指向ECB的指針變量清0,TCB存消息的域清0(這是比普通訊號量多出的操做),而後返回存消息的變量pmsg(有消息返回的就是消息,沒有返回的就是0),而後退出函數,究竟是3種緣由的那種緣由經過傳入的參數變量來存儲信息,外部知道。 信號量和郵箱請求的區別:主要是返回處理,郵箱要多出對TCB的消息域清空處理,填寫存返回消息的變量。 |
OSMboxPost() |
往指定郵箱類型ECB存消息,返回結果信息,分3種狀況 狀況1:等待任務表不爲0,表示有任務被事件掛起,要調用事件恢復函數OS_EventTaskRdy(),傳入的參數包括消息pmsg,以等到了消息的方式就緒。而後掉OS_Sched()任務切換,回來時退出 狀況2:等待任務表爲0,但ECB存消息的域OSEventPtr不爲0,表示有消息在ECB,只能標記郵箱已滿,退出 狀況3:沒有消息、也沒有任務在等,直接投到ECB中,狀況一、3都是正常的提交,而狀況2爲郵箱已滿錯誤退出 |
OSMboxDel() |
經過參數opt判斷是無任務等待才刪除OS_DEL_NO_PEND 仍是OS_DEL_ALWAYS強行刪除。首先判斷ECB的等待任務組是否爲0來知道是否有任務被掛起並經過變量標記,而後 第一種狀況:判斷標記變量爲沒有任務等待就初始化ECB而後返還給ECB空閒鏈表,若是有就錯誤退出。 第二種狀況:只要等待任務組不爲0就循環執行事件恢復函數OS_EventTaskRdy(),傳入的參數包括消息pmsg=0,以等到了消息的方式就緒。以後也是初始化ECB返還給空閒的鏈表,作個判斷若是標記變量代表確實以前有任務時被掛起的,那麼剛纔確定有新任務被就緒了,因此執行OSSched()調度下。 |
OSMboxPendAbort() |
講指定郵箱類型的處於等待被掛起的任務就緒,和普通訊號量操做很是相似,首先經過該ECB的等待任務組判斷有沒有任務在等待,若是沒有就不須要回復直接退出。 若是有任務被掛起那麼經過操做變量opt(連操做碼都同樣)判斷: OS_PEND_OPT_BROADCASE 廣播方式,全部被掛起的任務都被以放棄等待的方式就緒(實現方式若是就緒組不爲0就一直循環執行OS_EventTaskRdy函數,裏面有選最高優先級的功能),注意消息參數傳0。 OS_PEND_OPT_NONE非廣播方式,OS_EventTaskRdy函數只被執行一次,選當前被該事件阻塞的最高優先級任務就緒,注意消息參數傳0。 由於可能有比當前任務優先級更高的任務就緒因此調OSSched()。 |
OSMboxAccept() |
無等待請求郵箱消息,很簡單若是沒有就錯誤退出,若是有就取ECB消息,並將ECB存消息的域清0,正常退出 |
OSMboxQuery() |
OS_MBOX_DATA的填寫信息域3個存消息void* OSMsg,其實是個地址,真正的消息在這個地址所指向的地方,任務等待表、組。 |
注意: 消息這裏實際上只是void*指針,真正的消息是這個指針所指向的內容,具體存消息地方本身去定義啦,另外選void*好能夠存任意類型的消息地址,若是信息量比較大可消息多是結構體,使用消息的時候強制轉換爲該類型結構體地址。 |
消息隊列:
函數名 |
功能簡介 |
OS_QInit() |
初始化消息隊列,和前面3種事件管理不一樣,消息隊列引入了新的數據結構隊列控制塊QCB,這些結構體也要創建空閒連,每一個QCB能夠管理一個消息隊列,OS_Q OSQTbl[OS_MAX_OS]在ucos_ii。中定義,這個函數主要作的事是:把定義的這個結構體數組內容所有清0,而後構建鏈表,OSQFreeList指向空閒隊列鏈表表頭。 |
OSQCreate() |
傳入的參數是用戶定義的存消息指針的地址的一塊內存區地址(可見是指針的指針類型,若是作加法操做實際地址浮動4個本身(32位CPU,32位地址線))和存消息地址的尺寸。 建立消息隊列函數,首先要從ECB空閒鏈表中取一個ECB, 從空閒的隊列鏈表中取一個QCB,ECB中類型設置爲消息隊列OS_EVENT_TYPE_Q,ECB的指針域指向QCB,,QCB中設置相關域有: OSQStart域賦值用戶定義的存消息指針的地址,第一個參數 OSQEnd域存經過第1、二參數算出來的存消息地址的末地址 OSQIn下次插入消息地址的地址 OSQOut下次取消息地址的地址 OSQSize隊列的尺寸,就是第二個參數 OSQEnteries以及當前存了多小個消息地址 固然是用ECB仍是調用了OS_EventWaitListInit()清ECB等待任務組、表 |
OSQPost() |
提交消息到隊列,首先經過ECB判斷等待任務組是否爲0來得知是否有任務被該事件掛起: 若不爲0不須要把消息投到隊列而是直接投給當前等待任務表中最高優先級的任務,調用事件就緒函數 OS_EventTaskRdy(),傳入的參數包括消息pmsg,以等到了消息的方式就緒,而後就是調用OSSched()任務切換,返回來時正常退出。 若爲0,經過ECB的指針域得到QCB指針來操做,若是當前的隊列有的消息數大於等於尺寸數(實際上最多就是等於)就錯誤返回,返回錯誤爲,隊列已經滿了,若是消息隊列未滿,就以下代碼: 很是經典的代碼: *pq->OSQIn++=pmsg 消息存入插入消息的位置 *pq->OSQEntries++ 隊列消息數目加1 而後是if(pq->OSQIn==pq->OSQEnd) { pq->OSQIn=pq->OSQSart }這段代碼很關鍵,說明是先進先出的循環隊列,只有插入點到了隊列地址的末尾就插到最前面去。最後退出 |
OSQPend() |
請求指定消息隊列的消息,首先經過參數ECB指針得到QCB指針, 判斷隊列中是否有消息,若是有取一個消息地址,存入返回變量中,而後對隊列調整,QCB相關參數設置,正常退出。 若是發現消息隊列沒有消息,處理和郵箱、普通訊號量很相似了, 設置當前TCB 3個域,等待時間域(若是是0,就不存在時間滴答1-0的過程因此死等到有事件發生爲止),OSTCBCur->Stat標誌標記上隊列,OSTCBCur->StatPend默認設置爲OS_STAT_PEND_OK,調用OS_EventTaskWait(); 而後掉OS_Sched()任務切換 切換回來的時候:和郵箱處理如出一轍,下面爲粘貼過來的 若是是超時到,必定是OSTimeDlyResume或中斷中的 OSTimeTick 2個函數作過處理,設置 OSTCBCur->Stat(被清掉)和OSTCBCur->StatPend=OS_STAT_PEND_TO,可是ECB中的任務等待表、組還沒請因此調用OS_EventTaskRemove(),注意設置返回存消息的變量pmsg爲0。 若是是放棄等待,設置返回存消息的變量pmsg爲0。 若是是正常等到了事件,從當前TCB中OSTCBMsg域取消息存入返回值,和普通訊號量不一樣吧,由於提交信號量的函數把消息已經存入了當時處於等待任務表中最高優先級的該任務的TCB消息域中。 最後統一把當前TCB的4個相關域設置爲OS_STAT_RDY、OS_STAT_PEND_OK、指向ECB的指針變量清0,TCB存消息的域清0(這是比普通訊號量多出的操做),而後返回存消息的變量pmsg(有消息返回的就是消息,沒有返回的就是0),而後退出函數,究竟是3種緣由的那種緣由經過傳入的參數變量來存儲信息,外部知道。 信號量和郵箱請求的區別:主要是返回處理,郵箱要多出對TCB的消息域清空處理,填寫存返回消息的變量。 |
OSQDel() |
刪除消息隊列中當前被掛起的任務,也是首先經過ECB的任務等待組來判斷並標記是否有任務在等待, 經過參數opt(和郵箱、互斥信號量掩碼都同樣)判斷是否無任務等待才刪除OS_DEL_NO_PEND 仍是OS_DEL_ALWAYS強行刪除。 對於操做碼OS_DEL_NO_PEND: 若是標記變量表面有任務在等待錯誤退出,若是沒有,初始化該QCB,,和ECB分別放入各類的空閒鏈表中。 對於操做碼OS_DEL_ALWAYS: 只要等待任務組不爲0就循環執行事件恢復函數OS_EventTaskRdy(),傳入的參數包括消息pmsg=0,以等到了消息的方式就緒,時間類型爲消息隊列這和郵箱幾乎如出一轍。以後就初始化該QCB,,和ECB分別放入各類的空閒鏈表中。 作個判斷若是標記變量代表確實以前有任務時被掛起的,那麼剛纔確定有新任務被就緒了,因此執行OSSched()調度下(若是沒有就不須要了)。如出一轍和郵箱操做。
比較郵箱和消息隊列刪除的不一樣: 惟一操做上的不一樣就是釋放是郵箱僅僅是講ECB放回ECB空閒鏈表,而隊列多了一個將QCB放回QCB空閒鏈表,其他如出一轍。 |
OSQQuery() |
查詢指定消息隊列信息,OS_Q_DATA的信息域5個 即將被取到的消息 void *OSMsg 消息隊列中消息數目 消息隊列尺寸 事件的任務等待表、組 |
事件標誌組:
數據結構介紹:事件標誌組這個東東比較怪,雖然掛着事件2個事,執行的是事件管理,但壓根就沒有用事件控制塊ECB來管理,徹底是本身的一套數據結構,數據結構不一樣了,天然那些公用函數用不到了,本身有本身的掛起函數OS_FlagBlock() 和就緒函數OS_FlagTaskRdy() 關鍵數據結構式事件標誌組OS_FLAG_GRP,包含4個域, INT8U OSFlagType事件標誌類型 用的是填宏 OS_EVEN_TYPE_FLAG Void *OSFlgWaitList 這個參數當在事件標誌組空閒鏈表時指向下一事件標誌組, 當在做爲真正管理事件時指向事件標誌節點鏈表的表頭 OS_FLAGS OSFlagFlags 事件標誌實際上,OS_FLAGS就是8位、16位或32位類型重定義 具體是那種取決你想怎麼管理,事件多小。 關鍵數據結構式事件標誌節點OS_FLAG_NODE包含6個域 節點的先後驅鏈指針,void *OSFlagNodeNext void *OSFlagNodePrev 指向等待任務的TCB void *OSFlagNodeTCB; 指向被管理的時間標誌組 Void *OSlagNodeFlagGrp OS_FLAGS OSFlagNodeFlags;該任務須要等待的哪些事件標誌組合 INT8U OSFlagNodeWaitType 取值是4種宏 OS_FLAG_WAIT_SET_ALL 表示請求的事件標誌位所有置1才能算等到事件 OS_FLAG_WAIT_ SET_ANY 表示請求的事件標誌位只要有任意一個1就算等到事件 OS_FLAG_WAIT_CLR_ALL 表示請求的事件標誌位所有爲0才能算等到事件 OS_FLAG_WAIT_CLR_ANY 表示請求的事件標誌位只要有任意一個0就算等到事件 |
|
函數名 |
功能簡介 |
OSFlagInit() |
在OSInit中被調用屬於內部函數,主要作的事就是清0全部實際標誌組數組內存,構建空閒事件標誌組鏈表,而後讓OSFlagFreeList指向鏈首。 |
OSFlagCreate() |
從時間標誌組空閒鏈表表頭去一個GRP而後設置類型和初始事件 |
OS_FlagBlock() |
內部函數,功能是設置TCB中相關域 3+1:OSTCBCur->OSTCBStat|=OS_STAT_FLAG OSTCBCur->OSTCBStatPend=OS_STAT_PEND_OK OSTCBCur->OSTCBDly=timeout 這三個域設置和之前事件請求函數中如出一轍 OSTCBCur->OSTCBFlagNode=pnode指向標誌節點 這個功能相似於之前的TCB指向ECB域OSEventPtr,之前是劃分到OS_EventTaskWait()事件掛起函數中的。另外就是清就緒組、表中的標誌。 可見這部分功能劃分不同了把原來的事件請求函數中的部分劃到了標誌組事件掛起函數中來了。 還有部分操做就是事件標誌節點的域設置,是節點指向TCB,實現真正的互指。
與OS_EventTaskWait()比較:OS_EventTaskWait清就緒表、組,設置ECB的等待任務表,任務組,完成TCB到ECB的指向; 而OS_FlagBlock進行TCB的本該在請求事件函數中設置的三項,然清就緒表、組,完成TCB中到事件標誌節點的指向, 並無清等待任務表任務組操做(由於壓根就沒有用ECB),可是它還有其餘操做,就是對事件標誌節點設置,使標誌節點插入事件標誌節點鏈表,而且同時指向TCB和事件標誌組GRP。
問題來了既然沒有像ECB同樣的等待任務表、組,他是怎麼實現選擇、查找人任務並讓其就緒的,原來它是經過循環遍歷時間標誌節點,符合條件的任務都就緒,還有問題若是是消費類型的事件標誌在後面查找到的任務豈非很不公平? |
OSFlagPend() |
事件標誌組請求函數,首先經過判斷等待類型參數是否有清除標誌並記錄在另外定義的局部變量中,若是有消費類型的標誌在標記變量後要清掉,swich分支判斷: A若是匹配OS_FLAG_WAIT_SET_ALL: 用請求的flags事件組合同GPR事件標記作與運算,若是結果和flags相同(即GPR包含flags的所有事件),等到了事件組,若是有清除標誌就清了GPR中的相應事件組,並把flags存入當前TCB的域OSTCBFlagsRdy中,而後正常退出。但與結果同flags不相等,那麼調用OS_FlagBlock(pgrp,&node,flags,wait_type,timeout),該任務就被掛起來了。 B若是匹配OS_FLAG_WAIT_SET_ANY 做與運算只要不爲0,就算成功,若是有消費標誌,把GPR中共同部分標誌事件記錄到當前TCB的域OSTCBFlagsRdy中,同時清掉它在GPR中的共同位清掉,退出。若是沒有匹配成功也該任務就被掛起來了OS_FlagBlock(pgrp,&node,flags,wait_type,timeout),。 C若是匹配OS_FLAG_WAIT_CLR_ALL 用GPR的事件標誌位取反同flags做與運算,若是和flags相同,表面匹配成功,若是又有消費標誌就把GPR中flags的項所有置1,也記錄到TCB中,正常退出。若是匹配失敗也是任務就被掛起來了OS_FlagBlock(pgrp,&node,flags,wait_type,timeout),。 D若是匹配OS_FLAG_WAIT_CLR_ANY 用GPR的事件標誌位取反同flags做與運算,若是結果不爲0,說明原來GPR中對於flags至少有1位爲0,表示匹配成功,若是有消費標誌把共同的部分位置1,退出。若是匹配失敗,也是任務就被掛起來了OS_FlagBlock(pgrp,&node,flags,wait_type,timeout),。 這樣switch的4種狀況就匹配完了,出來的時候一定是沒有匹配成功,將當前任務掛起來了,因此調用OSSched() 切換回來的時候:判斷是什麼緣由切換回來,狀況一:若是是超時到或放棄等待那麼,對OSTCBCur->OSTCBStat=OS_STAT_RDY OSTCBCur->OSTCBStatPend=OS_STAT_PEND_OK 調用OS_FlagUnlink()使得OSTCBCur 的OSTCBFlagNode若是作了記錄就會清0,最後退出,退出返回的錯誤碼錶示究竟是超時到仍是放棄等待。狀況二:正常等待了知足條件的標誌,若是不是消費類型就,返回正常退出信息碼退出,若是有消費類型標誌,還得從TCB中域OSTCBFlagsRdy存的那個請求組合,該清0清0該置1置1。 |
OS_FlagUnlink() |
參數是處於鏈表中的事件標誌節點地址,功能是把該節點移除鏈表,若是使能了刪除事件標誌組的宏OS_TASK_DEL_EN,那麼還要把TCB指向該節點的域OSTCBFlagNode清0。 |
OSFlagDel() |
首先就經過GPR的域OSFlagWaitList是否爲0來判斷有沒有任務處於等待狀態並經過標誌變量標記下。 經過參數opt(和郵箱、互斥信號量掩碼都同樣)判斷是否無任務等待才刪除OS_DEL_NO_PEND 仍是OS_DEL_ALWAYS強行刪除。 對於操做碼OS_DEL_NO_PEND: 若是標誌變量代表有任務在等待就退出,返回錯誤碼信息;若是沒有任務等待就把該GPR初始化而後放回到事件標誌組空閒鏈表中。 對於操做碼OS_DEL_ALWAYS: 首先無論三七二十一,有沒有任務等待都取GPR域OSFlagWaitList 存入臨時變量,只要不爲0就循環遍歷下去,遍歷到節點就調用 OS_FlagTaskRdy(pnode,(OS_FLAGS)0);就緒完全部的任務,以後初始化GPR,返回給空閒GPR鏈表,若是有就緒的任務就調用函數 OS_SChed(); |
OS_FlagTaskRdy() |
通常用在提交事件標誌組合刪除事件標誌組中經常使用,清楚該節點對於的任務等待事件標誌組這樣操做,並不表示他就沒有等待其餘的操做,首先設置TCB的4個相關域 OSTCBCur->OSTCBStat&=~OS_STAT_FLAG OSTCBCur->OSTCBStatPend=OS_STAT_PEND_OK OSTCBCur->OSTCBDly=0 賦值給域OSTCBFlagsRdy,顯然刪除的時候直接賦0的。 實際上有5個域還有TCB對節點指針在OS_FlagUnlink()清0 若是發現清掉等待事件標誌組的實際狀態就變成OS_STAT_RDY 那麼真的要設置就緒表、組,讓其調用一次OS_SChed();和事件就緒函數徹底不相同 |
OSFlagAccept() |
無等待請求,這個函數看起來複雜實際很簡單,首先標記是不是消費類型,而後按照4中匹配方式去請求事件,若是成功就按照OSFlagPend()中A、B、C、D4種方法去消費處理,若是沒有固然就直接返回錯誤碼退出撒。 |
OSFlagPost() |
參數4個,指定事件標誌組GPR指針,OS_FLAG_GPR *pgrp 提交的位組合OS_FLAGS flags 提交方式INT8U opt 是清除OS_FLAG_CLR 仍是置位OS_FLAG_SET 若是是前者,那麼就把GPR中OSFlagFlags域同~flags與清除 後者,就同flags或運算置位 定義標記變量導致是否切換任務,默認不切換,經過GPR遍歷事件標誌節點,若是知足條件就就緒,就緒函數只要調用過就把標誌變量標記爲要切換,出來的時候就經過標誌判斷需不須要發生切換,若是要OS_Sched(),以後退出。 |
文章轉載自:http://blog.sina.com.cn/s/blog_a193c7a90101at44.html