嵌入式操作系統內核原理和開發

嵌入式操作系統內核原理和開發(開篇)

操作系統是很多人每天必須打交道的東西,因為在你打開電腦的一剎那,隨著bios自檢結束,你的windows系統已經開始運行了。如果問大家操作系統是什么?可能有的人會說操作系統就是windows,就是那些可以放大、縮小、移動的窗口。對曾經是計算機專業的朋友來說,這個答案還要稍微復雜一些,操作系統可能還有linux、unix、ios、sun solaris、aix等。如果再細化一點,對嵌入式工具比較解的朋友還會有新的補充,因為在他們看來,vxworks、eCos、ucos也都是操作系統,雖然它們好多系統連界面都沒有。

? ? 既然操作系統稱之為一個系統,那么它必然是由好多的部件組成的。有過linux嵌入式開發經驗的朋友都知道,要想使一個linux在arm芯片上真正跑起來,它必須有三個部分組成,即boot + 內核 + 文件系統。而真正內核的東西其實很少,也就是cpu初始化、線程調度、內存分配、文件系統、網絡協議棧、驅動這些部分組成。那么是不是所有的芯片都需要跑操作系統呢?我們可以舉個例子。

? ? 現在有一個簡單的溫度測量電路,它由三部分組成:1、單片機;2、溫度傳感器模塊;3、無線發射模塊。我們設計這么一個溫度測量電路其實就是一個目的,那就是為了實時獲取當前的溫度信息。那么,這么一個簡單的電路應該怎么設計程序呢?其實很簡單。

[cpp] view plaincopy
  1. void?sleep(int?value)??
  2. {??
  3. ????int?outer;??
  4. ????int?inner;??
  5. ??????
  6. ????for(;?outer?<?value;?outer++)??
  7. ????{??
  8. ????????for(inner?=?0;?inner?<?1000;?inner++)??
  9. ????????????;??
  10. ????}??
  11. }??
  12. ??
  13. ??
  14. void?main()??
  15. {??
  16. ????while(1)??
  17. ????{??
  18. ????????/*?read?temperature?from?port*/??
  19. ????????sleep(1000);??
  20. ????????/*?send?temperature?to?wireless?module?*/??
  21. ????????sleep(1000);??
  22. ????}??
  23. }??
? ? 如果我們需要cpu干的事情很少,甚至極端一點說只有一件事情,那么根本沒有設計操作系統的必要。我們設計出操作系統,主要是想在單位時間內完成幾件事情。打個比方來說,你完全可以在工作的時候一遍寫文檔、一遍收發電子郵件,偶爾還能開個小差休息一會。?所以操作系統就是為了共享資源而存在的。

? ? 認識操作系統的用途不難,關鍵是如何把操作系統用代碼寫出來。也許有人會跟你說,免費的代碼一大堆,Linux就不錯,你下載下來直接讀就好了。但是我告訴你,最新的Linux內核版本已經輕松的越過了3.0,整個代碼的長度遠在千萬行之上,你可能從哪看起都不知道。可能此時又有人不同意了,看不懂高版本的linux,可以看看linux低版本的代碼,0.11版本的代碼就不錯,因為趙炯就是怎么推薦的。我要說的是,0.11的代碼固然好,但是怎么編譯版本、怎么修改代碼、怎么構造文件系統、怎么跑起來是我們繞不過的一道難題。對于很多朋友來說,閱讀linux代碼尚且困難,更不要說后面還需要完成的一大攤子爛事了。

? ? 說了這么多,我們需要的的內核代碼是什么樣的?其實在我看來,很簡單。它只要滿足下面兩個條件就可以了,
(1)像用戶軟件一樣可以運行;
(2)像用戶軟件一樣可以單步調試。 ? ?

? ? 要解決這些問題,對linux系統來說上不難解決。要解決os的運行和調試問題,關鍵就在于如何仿真中斷和實現os的任務切換。至于任務的開啟、運行和掛起,內存分配,互斥量,信號量,文件系統,tcp/ip協議棧,GUI操作,這些其實都是可以在linux上進行仿真和操作的,朋友們可以盡請放心。這部分的內容,我們會在以后的博客中陸續展開。

? ? 為了能夠更好地閱讀后面發表的博文,我建議你鞏固一下下面這些知識,這樣會對你的理解有很大的裨益。
(1)cpu 結構,了解中斷流程就行;
(2)linux 匯編語言;
(3)函數堆棧格式和內容;
(4)互斥量、信號量的使用方法;
(5)調度的基本策略;
(6)內存分配的基本方法;
(7)tcp/ip socket編程;
(8)gui編程方法,可以參考windows的方法;
(9)系統中的內存布局、編譯原理等等。


嵌入式操作系統內核原理和開發(cpu的那些事)

cpu是數字處理系統中的一個重要環節。在我看來,單片機、微處理器、dsp都可以稱作是cpu,只是它們的側重點有所不同罷了。具體來說,傳統意義上的單片機更偏重于嵌入式的計算,比如說我們經常使用的51、avr、arm芯片中不僅僅含有了運算和控制功能,它還涵蓋了定時器、串口、并口、usb、i2c總線等外部資源。dsp呢,cpu一般只是作為dsp的一個核存在,它通常還會包含另外一個核,專門用于數字信號的處理工作。而微處理器,也就是我們經常說的pc上的處理器,它的工作比較單一,專注于計算和控制功能的處理,因此一般來說在這方面的性能上面,單片機和dsp都是不能和它相比的,有了南橋芯片和北橋芯片的幫助,pc的微處理器就可以專注于自己的本職工作了,效率上面也會有一個很大的提高。


? ? 對于朋友們來說,生活中遇到的最多的cpu其實是x86的cpu。當然,如果有哪位朋友喜歡apple之類的玩具,也會知道一些arm的相關事情。剩下的就是一些專用領域的cpu了,比如說在通信行業用到的比較多的powerpc芯片,在高性能服務器用的到的sun sparc芯片,在科學計算領域使用到的mips芯片。所以,無論遇到什么芯片,對于應用層開發的朋友都是一樣的,只是在一些小地方需要還有一些注意的地方。比如說,

(1)數據的對齊方式

(2)數據的字節序問題

(3)函數參數的壓棧問題

(4)cpu的亂序執行問題

(5)cpu中cache和內存一致性的問題


? ? 當然,如果我們所要思考只是簡單的應用層設計,考慮到這些內容本身已經實屬不易了。然而,我們考慮的是如何設計嵌入式操作系統的問題,所以接下來還要看看一般cpu下面都包含了那些內容。這樣,只要熟練掌握了一款cpu的設計和實現,對其他cpu的知識也會觸類旁通了。


? ? 任何一款cpu,不管是完成的功能是什么樣的,通常都會有這樣一些基本設計:

(1)寄存器

? ? 堆棧寄存器 ? ?

? ? pc寄存器

? ? 狀態寄存器

? ? 運算寄存器

? ? 寄存器是cpu內部的基本資源。不管cpu的代碼執行到什么時候,這些資源都是共享的,所以在cpu發生中斷、函數調用、線程切換的時候,都需要對這些寄存器進行保護,常用的基本措施就是把把它們保存到臨時堆棧當中去。堆棧寄存器記錄了當前堆棧使用到了什么地方,pc寄存器則記錄當前代碼跑到了什么地方,下一條指令在什么地方等。狀態寄存器則保存了當前cpu的執行情況,包括計算有沒有溢出、中斷是關還是開、有沒有o除數異常等等。至于運算寄存器就因cpu而異了,有的cpu寄存器比較少,比如說x86的寄存器;有的cpu寄存器就比較多,比如說powerpc。運算寄存器的用途很多,比如說數據訪問、計算、移位、返回計算結果等等。


(2)指令集

? ? 尋址指令

? ? 數學運算指令

? ? 邏輯運算指令

? ? 軟中斷指令

? ? 跳轉指令

? ? 遠程調用指令

? ? io訪問指令

? ? 棧操作指令

? ? 指令集在某種程度上直接決定了某一種cpu的類型。就像intel和amd生產的cpu雖然有差別,但是它們的cpu使用的都是x86的指令集,而marwell、samsung和高通生產的cpu當然也不同,但是它們的指令集都是arm指令集。所以,如果軟件在marwell上跑,一般來說也可以在Samsung上跑起來。指令集很復雜,內容很多。但是通常來說,上面這些內容都是cpu所必須要完成的幾種指令。當然重中之重的還是中斷和棧處理指令。


(3)中斷、異常處理機制

? ? 不管是什么cpu,中斷部分的內容都是少不了的。試想一想,如果一顆cpu只知道不停地運行,那么它的執行效率實際上是很低的。擁有了中斷的cpu不僅使得cpu可以和更多的外設io打交道,還能極大地提高自身運行的效率。不同的cpu處理中斷的方法其實都差不多,在整個cpu的地址空間里面,通常在低地址區域會有一張中斷向量表,表中每一項記錄了每一個中斷對應的處理函數。所以,只要中斷發生時,cpu就會首先將下一條pc地址壓入堆棧,然后跳轉到中斷向量表中對應的中斷地址處執行的相應的處理函數。這個過程是cpu自動完成的,不需要我們關心。這樣對我們來說,它和cpu中的函數調用其實沒有什么區別。等待中斷處理結束后,我們使用ret指令返回到之前壓入的那個ip地址處,繼續下面的代碼。整個過程就好像中斷根本沒有發生過一樣。


? ? 所以,對于cpu的了解其實主要就是對寄存器、指令集和中斷的了解。有了對中斷和堆棧的深入理解,其實也就沒有什么困難的了。在這里我們大家可以考慮一個問題,如何在Windows或者linux下仿真中斷完成我們的操作系統開發呢?大家可以自己先思考一下,我們會在隨后的博客中繼續介紹。整篇文章,我們沒有介紹編碼的相關內容,其實只要把這里的基本概念弄清楚了,剩下來其實就是一些流程性的工作了。在軟件開發中,設計其實是最難的,剩下的才是開發和調試。

嵌入式操作系統內核原理和開發(中斷)

? 在我個人看來,中斷是cpu最重要的特色。從某種意義上來說,沒有中斷就沒有嵌入式操作系統。一旦你明白了中斷的真正含義,你對操作系統的了解就算真正入門了。什么是中斷呢?我們可以看看單片機下面是怎么做的。
[cpp] view plaincopy
  1. #include?<REG51.h>??
  2. ??
  3. sbit?LED?=?P1?^?6;??
  4. unsigned?int?led_enable?=?0;??
  5. ??
  6. void?Delay(unsigned?int?a)??
  7. {??
  8. ????unsigned?int?i;??
  9. ??
  10. ????while(a)??
  11. ????{??
  12. ????????a?--;??
  13. ????????for(i?=?0;?i?<?1200;?i++);??
  14. ????}??
  15. }??
  16. ??
  17. void?led_switch(void)?interrupt?0?using?1??
  18. {??
  19. ????if(0?==?led_enable)??
  20. ????{??
  21. ????????led_enable?=?1;??
  22. ????}??
  23. ????else??
  24. ????{??
  25. ????????led_enable?=?0;??
  26. ????}??
  27. ??
  28. ????EX0?=?1;??
  29. }??
  30. ??
  31. void?main(void)??
  32. {??
  33. ????EA?=?1;??
  34. ????EX0?=?1;??
  35. ??
  36. ????while(1){??
  37. ????????if(led_enable)??
  38. ????????{??
  39. ????????????LED?=?0;??
  40. ????????????Delay(100);??
  41. ????????????LED?=?1;??
  42. ????????????Delay(100);??
  43. ????????}??
  44. ????}??
  45. }??

? ? 上面的代碼是一段真實的51單片機代碼。它完成的功能很簡單,就是對led燈進行點亮處理。怎么解釋呢?在單片機上電后,我們發現一開始led二極管沒有發生閃爍。在我們單擊按鍵之后,led開始出現間隙性閃爍的現象,之后再一次單擊按鍵,又可以發現led的閃爍現象消失了。為什么會出現這種現象?主要是因為我們單擊按鍵的時候,在單片機的引腳處產生了中斷。查看到中斷的單片機此時就會跳轉到中斷向量表里面查找中斷處理函數。這里的按鍵中斷處理函數就是led_switch。處理完led_switch之后,單片機又會回到原來的main函數繼續執行,所以整個中斷的過程就像沒有發生過一樣。因為在led_switch中我們對led_enable進行了處理,所以就出現了我們在前面說過的各種現象。


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
?
? ? 說到這,也許有的朋友會說,cpu的這種中斷屬性怎么才能在pc上面仿真出來呢?其實很簡單。linux系統本身就有一個優秀的特性,那就是信號。只要我們設定相應的信號和處理函數,那么linux系統就會在系統調度返回之前調用相應的信號函數來處理。整個信號處理的過程和中斷是一模一樣的。因為在處理中斷的時候,我們需要對cpu的現場進行保存和恢復處理,而信號的處理也是一樣。在信號處理前,系統肯定是處于內核態,那么linux系統肯定已經為我們做好了現場的保護工作,處理完信號之后,系統本身又會恢復到原來的用戶態,繼續執行下面的代碼。所以linux自身也會默認對原來的場景進行恢復處理,就好象中斷返回一樣。

[cpp] view plaincopy
  1. #include?<stdio.h>??
  2. #include?<time.h>??
  3. #include?<sys/time.h>??
  4. #include?<stdlib.h>??
  5. #include?<signal.h>??
  6. ??
  7. static?int?count?=?0;??
  8. static?struct?itimerval?oldtv;??
  9. ??
  10. void?set_timer()??
  11. {??
  12. ????struct?itimerval?itv;??
  13. ????itv.it_interval.tv_sec?=?1;??
  14. ????itv.it_interval.tv_usec?=?0;??
  15. ????itv.it_value.tv_sec?=?1;??
  16. ????itv.it_value.tv_usec?=?0;??
  17. ????setitimer(ITIMER_REAL,?&itv,?&oldtv);??
  18. }??
  19. ??
  20. void?signal_handler(int?m)??
  21. {??
  22. ????count?++;??
  23. ????printf("%d\n",?count);??
  24. }??
  25. ??
  26. int?main()??
  27. {??
  28. ????signal(SIGALRM,?signal_handler);??
  29. ????set_timer();??
  30. ????while(count?<?10000);??
  31. ????exit(0);??
  32. ????return?1;??
  33. }??
? ? 大家可以把這樣一段代碼在Linux編譯一下,然后使用gdb調試即可。查看整個signal_handler在被斷點斷住的時候,本身線程是不是只有一個?函數堆棧是不是在一個線程里面。如果不出意外,整個信號的處理過程應該是這樣的,
[cpp] view plaincopy
  1. (gdb)?bt??
  2. #0?signal_handler(m=14)?at?code.c:?23??
  3. #1?<signal?handler?caller>??
  4. #2?main()?at?code.c:32??
? ? 到了這里,相應大家應該還有一個疑問,既然可以利用linux的signal對cpu的中斷進行仿真,那么能不能利用windows的signal對中斷進行仿真呢。因為Windows下面的沒有SIGALRM這個信號,所以我們可以重新編寫一段代碼,然后利用visual studio 6.0進行編譯,看看對應的情況。
[cpp] view plaincopy
  1. #include?<stdio.h>??
  2. #include?<stdlib.h>??
  3. #include?<signal.h>??
  4. #include?<tchar.h>??
  5. #include?<Windows.h>??
  6. ??
  7. void?SignalHandler(int?signal)??
  8. {??
  9. ????printf("Application?over..\n");??
  10. }??
  11. ??
  12. int?main()??
  13. {??
  14. ????typedef?void?(*SignalHandlerPointer)(int);??
  15. ??
  16. ????SignalHandlerPointer?previousHandler;??
  17. ????previousHandler?=?signal(SIGINT,?SignalHandler);??
  18. ????while(1)??
  19. ????{??
  20. ????Sleep(0);??
  21. ????}??
  22. ??????
  23. ????exit(1);??
  24. }??

? ?下面,我們首先編譯這一段代碼。接著在程序run之后,我們可以在SignalHandler之處設置一個斷點。一切就緒完畢,再按下ctrl+c之后,系統就會在SignalHandler之處斷住。此時單擊【Debug】-> 【Threads】,就可以看到這個情況。


? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

? ?相信看到這里,大家應該看明白了。其實在Windows下面,信號是專門有一個線程來完成的,和原來的main函數不是同一個線程。既然線程都不是一樣的,而中斷本身是必須在一個thread中完成的。我們怎么能利用windows來仿真cpu的中斷處理流程呢。

嵌入式操作系統內核原理和開發(地址空間)

不管是什么樣的嵌入式cpu,它必然有自己的訪問地址空間。至于這個具體的訪問空間是什么,那cpu就不知道了。它可以是ram,當然也可以是flash、uart、ide、i2c等。當然cpu不僅需要地址總線,它還需要數據總線和控制總線。這三個總線的目的都非常明確,控制總線主要是為了實現cpu對外設接口的控制,地址總線是為了實現地址的輸出,數據總線則是為了實現數據內容的獲取或者設置。所以,對于一般的嵌入式cpu來說,它的基本架構應該是這樣的,


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?


? ? 在x86的cpu上,很多對外設的操作是需要通過北橋或者通過南橋芯片完成的。而在嵌入式硬件中,我們就把經常使用到的接口芯片集成到了cpu里面。所以在嵌入式cpu功能上面,你除了看到cpu的字長、時鐘、指令集、運算速率這些通常的數據之外,你還會看到很多的接口控制寄存器,比如說定時器寄存器,lcd寄存器,uart寄存器,i2c寄存器。這些都表明了此時的cpu完成的不僅僅是簡單的計算功能,它還需要完成對外設接口的設置。通過對應的寄存器設置,我們就可以對外設的接口狀態進行控制。


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?


? ? 說了這么多,我們接下來要看看嵌入式系統在地址空間里面是怎么設計的啊?其實一個完整的嵌入式軟件系統并不復雜。一般來說,一個完整的系統需要有boot、kernel、文件系統三部分。其中boot主要放在norflash里面,而kernel和文件系統是存放在nandflash里面。在系統上電之后,cpu會有一個初始地址,這個地址要么是0x00000001,或者是0xFFFF0000。通常這個地址會指向Norflash,下面開始執行的代碼當然就是boot代碼。因為Norflash的訪問速度要比Ram速度慢很多,所以boot代碼很快會把自己拷貝到Ram中,然后跳到Ram中繼續運行。boot的功能比較簡單,主要就是為了獲取芯片參數,設置芯片屬性,設置堆棧空間,加載操作系統內核等。在boot完成自己的功能之后,它會把系統內核加載到Ram中,然后jump到系統的運行首地址處運行。系統內核主要完成整個系統的初始化工作,比如說內存分配,信號量初始化,net初始化,驅動結構初始化等工作。在內核即將完成初始化的時候,它會進行最后一步操作,mount一個文件系統,加載文件系統的腳本數據,開啟相關的系統進程,最后一步就是開啟shell進程,接受用戶的命令輸入。至此,一個系統算是真正跑起來了。


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?


? ? 前面,我們說過cpu需要數據總線、地址總線和控制總線才能與外設接口打交道。既然cpu是通過狀態寄存器設置外設接口的狀態的,那么cpu是如何通過地址總線與外界聯系的呢?這里面就涉及到片選信號的問題。我們知道,一個32位的cpu有32條地址線和外界相連,那么也就是說如果沒有其他的方法,它只能外設32個接口。那么有沒有什么辦法可以擴大外設接口呢?說到這里,你應該知道了,它就是解碼器和片選信號了。比如說,現在有4個外設接口,我們可以怎么從地址總線中挑出兩條線,00表示外設1,01表示外設2,10表示外設3,11表示外設4。這樣有了解碼器的幫助,我們就可以用兩個地址線實現對4個外設接口的控制了。

? ? 有了cpu狀態寄存器,我們可以設置當前外設接口的執行狀態。如果是讀命令,首先設置外設接口的狀態模式為讀狀態,然后發送地址,此時片選信號選中的芯片就會處于使能狀態,一會cpu就可以從數據總線上獲得數據,存儲在寄存器或者是內存當中;如果是寫命令,那么cpu首先設置外設接口為寫模式,然后在地址總線上輸出地址,在收到芯片ready信號后,cpu再將數據從寄存器上傳輸到數據總線上,在等到外設芯片的ack信號后,整個數據的傳輸過程才算完成。我們看到,一個匯編指令的操作竟然涉及到這么多信號的操作,可見cpu的處理過程還是很復雜的。有的時候,中間還會涉及到信號完整性或者是時序的問題,那么這時候邏輯分析儀就可以派上用場了。

嵌入式操作系統內核原理和開發(基礎)

? 在編寫我們的操作系統之前,我們需要明確一些事情。比如說,這個系統的運行環境是什么?怎么編譯?基本中斷環境是什么?上下文怎么切換?準備實現那些內容?基本數據類型是什么?等等。


(1)你的嵌入式操作系統準備叫什么名稱?運行環境是什么?可以在實際環境上面運行嗎?

? ? 我們準備把這個嵌入式操作系統稱之為MiniOS。雖然這個操作系統實現的功能不多,但是麻雀雖小,五臟俱全。一般操作系統該有的功能,MiniOS都會有這個功能。起初,我們會在linux上運行MiniOS,之后我們會把MiniOS移植到51單片機上去。?


(2)操作系統怎么編譯?

? ? MiniOS系統采用基本的C語言進行編寫,沒有匯編語言,但是移植到51上面需要一些匯編語言。編寫完C語言文件后,我們需要一個簡單的makefile文件,這樣就可以實現自動化編譯,進一步提高我們開發的效率。


(3)基本中斷環境是什么?

? ? 因為MiniOS起初是在linux實現運行的,所以它是依靠SIGALRM實現中斷調度的。當然在OS中還會有I/O操作,這里我們用信號進行仿真。SIGINT就是一種方法,每當我們使用鍵盤輸入ctrl+C的時候,就會調用相應的函數。這和外設的中斷是一模一樣的。


(4)上下文怎么切換?

? ? 上下文的切換就是堆棧的切換。要弄清楚堆棧的切換,首先你要明白函數,函數里面的數據是怎么安排的,壓棧是怎么回事,退棧是怎么回事。這些知識點,我們在后面都會一一介紹。


(5)MiniOS要實現哪些內容?

? ? MiniOS雖然比較小巧,但是操作系統該有的功能它都會有。因此,我們準備實現的下面這些內容,比如說中斷開關、互斥量、定時器、線程調度、內存分配都會擁有。


(6)基本數據類型是什么?

? ? 為了移植的方便,我們需要對基本數據進行重新定義一下基本數據類型。

[cpp] view plaincopy
  1. #define?UINT32?unsigned?int??
  2. #define?INT32??signed?int??
  3. #define?UINT16?unsigned?short??
  4. #define?INT16??signed?short??
  5. #define?UINT8??unsigned?char??
  6. #define?INT8???signed?char??
  7. ??
  8. #define?TRUE?1L??
  9. #define?FALSE?0L??
  10. #define?OK???0L??
  11. #define?ERROR?~0L??
  12. ??
  13. #define?BOOLEAN?UINT32??
  14. #define?STATUS??UINT32??

(7)把MiniOS移植到51單片機需要做些什么?

? ? 其實把MiniOS移植到51上面,其實不困難,只要做到這幾個方面就可以了。a)基本數據類型重新定義;b)中斷開關重新進行設置;c)任務切換的壓棧、出棧處理。要是做到這里,我們就可以在51單片機上面把自己的OS跑起來了,那是多么開心的一件事情啊。


嵌入式操作系統內核原理和開發(系統中斷仿真)

在嵌入式操作系統中,最難模仿的是系統中斷的問題。在windows下面,這是沒有辦法的事情。但是在linux下面,卻有一個非常便利的條件,那就是linux的信號處理。因為用戶程序處理的時候,signal的處理和用戶線程的處理堆棧是一致的,這和中斷是完全吻合的。所以,如果你需要關閉中斷,可以這么寫,
[cpp] view plaincopy
  1. sigprocmask(SIG_BLOCK,?&new_set,?&old_set);??
??? 當然,如果需要再次打開中斷了,還可以這么寫,
[cpp] view plaincopy
  1. sigprocmask(SIG_UNBLOCK,?&new_set,?NULL);??
??? 所以,這里我們可以編寫一個完整的程序說明問題。
[cpp] view plaincopy
  1. #include?<stdio.h>??
  2. #include?<time.h>??
  3. #include?<sys/time.h>??
  4. #include?<stdlib.h>??
  5. #include?<signal.h>??
  6. ??
  7. static?int?count?=?0;??
  8. static?struct?itimerval?oldtv;??
  9. ??
  10. void?set_timer()??
  11. {??
  12. ????struct?itimerval?itv;??
  13. ????itv.it_interval.tv_sec?=?1;??
  14. ????itv.it_interval.tv_usec?=?0;??
  15. ????itv.it_value.tv_sec?=?1;??
  16. ????itv.it_value.tv_usec?=?0;??
  17. ????setitimer(ITIMER_REAL,?&itv,?&oldtv);??
  18. }??
  19. ??
  20. void?signal_handler(int?m)??
  21. {??
  22. ????count?++;??
  23. ????printf("%d\n",?count);??
  24. }??
  25. ??
  26. void?signal_int()??
  27. {??
  28. ????printf("hello!\n");??
  29. }??
  30. ??
  31. int?main()??
  32. {??
  33. ????sigset_t?new_set;??
  34. ????sigset_t?old_set;??
  35. ??
  36. ????signal(SIGALRM,?signal_handler);??
  37. ????signal(SIGINT,??signal_int);??
  38. ????set_timer();??
  39. ??
  40. ????sigemptyset(&new_set);??
  41. ????sigaddset(&new_set,?SIGALRM);??
  42. ????sigaddset(&new_set,?SIGINT);??
  43. ????sigprocmask(SIG_BLOCK,?&new_set,?&old_set);??
  44. ????sleep(10);??
  45. ??????
  46. ????sigprocmask(SIG_UNBLOCK,?&new_set,?NULL);??
  47. ????while(count?<?10?)?sleep(1);??
  48. ????return?0;??
  49. }??
??? 基本說明如下:

??? (1)我們的系統暫時只負責SIGINT和SIGALRM,前者負責I/O的仿真,后則負責線程的調度;

??? (2) sigprocmask函數用來實現中斷的打開和關閉;

??? (3)程序首先關閉中斷,然后打開中斷,打印數據完即結束,僅作為示范使用。

嵌入式操作系統內核原理和開發(線程切換)

在操作系統中,線程切換是很重要的一個環節。如果沒有線程的切換,我們如何才能實現多線程的并發運行呢?既然要實現切換,那么一方面,我們需要對原來的寄存器進行保存,另外一方面我們還要壓入新堆棧的寄存器,這樣才能實現線程切換的效果。在x86下面,因為切換線程的ip地址是固定的,所以切換所需要的寄存器也是固定的,一般來說保存eax、ebx、ecx、edx、esi、edi、ebp和esp即可。比如說,像這樣,
[cpp] view plaincopy
  1. void?swap(UINT32*?prev,?UINT32*?next)??
  2. {??
  3. ????__asm("push?%%eax\n\t"??
  4. ??????????"push?%%ebx\n\t"??
  5. ??????????"push?%%ecx\n\t"??
  6. ??????????"push?%%edx\n\t"??
  7. ??????????"push?%%esi\n\t"??
  8. ??????????"push?%%edi\n\t"??
  9. ??????????"push?%%ebp\n\t"??
  10. ??????????"push?%%esp\n\t"??
  11. ??
  12. ??????????"lea?0x8(%%ebp),?%%eax\n\t"??
  13. ??????????"mov?(%%eax),?%%eax\n\t"??
  14. ??????????"mov?%%esp,?(%%eax)\n\t"??
  15. ??
  16. ??????????"lea?0xc(%%ebp),?%%eax\n\t"??
  17. ??????????"mov?(%%eax),?%%eax\n\t"??
  18. ??????????"mov?(%%eax),?%%esp\n\t"??
  19. ??
  20. ??????????"pop?%%esp\n\t"??
  21. ??????????"pop?%%ebp\n\t"??
  22. ??????????"pop?%%edi\n\t"??
  23. ??????????"pop?%%esi\n\t"??
  24. ??????????"pop?%%edx\n\t"??
  25. ??????????"pop?%%ecx\n\t"??
  26. ??????????"pop?%%ebx\n\t"??
  27. ??????????"pop?%%eax\n\t"??
  28. ??????????::);??
  29. }??
? ? 上面說的都是對已經運行的線程進行切換。那么剛剛創建的線程怎么進行切換呢?一個不錯的方法就是仿真出棧的處理流程。把初始狀態的寄存器放在堆棧里面,模仿線程的出棧過程,設置好線程的初始寄存器數值即可。比如說,像這樣,
[cpp] view plaincopy
  1. void?signal_handler(int?m)??
  2. {??
  3. ????UINT32*?data;??
  4. ????UINT32?unit;??
  5. ??
  6. ????if(count?!=?0)??
  7. ????{??
  8. ????????printf("count?=?%d\n",?count++);??
  9. ????????return;??
  10. ????}??
  11. ??
  12. ????printf("count?=?%d\n",?count++);??
  13. ????data?=?(UINT32*)malloc(STACK_LENGTH);??
  14. ????unit?=?STACK_LENGTH?>>?2;??
  15. ??
  16. ????if(NULL?==?data)??
  17. ????????return;??
  18. ??
  19. ????memset(data,?0,?STACK_LENGTH);??
  20. ????data[unit?-1]?=?(UINT32)?hello;??
  21. ????data[unit?-2]?=?0;??
  22. ????data[unit?-3]?=?0;??
  23. ????data[unit?-4]?=?0;??
  24. ????data[unit?-5]?=?0;??
  25. ????data[unit?-6]?=?0;??
  26. ????data[unit?-7]?=?0;??
  27. ????data[unit?-8]?=?0;??
  28. ????data[unit?-9]?=?0;??
  29. ????data[unit?-10]?=?(UINT32)?&data[unit?-?9];??
  30. ??
  31. ????new?=?(UINT32)?&data[unit?-10];??
  32. ????swap(&old,?&new);??
  33. ????free(data);??
  34. }??
? ? 最后,我們給出一份完整的代碼。在程序收到第一個signal的時候,我們發現代碼不僅申請了內存,還初始化成了堆棧的格式,完美地解決了堆棧切換的問題。當然在hello處理結束后,代碼又恢復成了原來的格式,而且內存正常釋放,一切就像沒有發生過一樣。試想,如果每一次處理的都是一個function和stack,那基本上就可以模仿線程的運行過程了。
[cpp] view plaincopy
  1. #include?<stdio.h>??
  2. #include?<time.h>??
  3. #include?<sys/time.h>??
  4. #include?<stdlib.h>??
  5. #include?<signal.h>??
  6. ??
  7. #define?UINT32?unsigned?int??
  8. #define?STACK_LENGTH?1024??
  9. ??
  10. static?struct?itimerval?oldtv;??
  11. UINT32?old?=?0;??
  12. UINT32?new?=?0;??
  13. UINT32?count?=?0;??
  14. ??
  15. void?set_timer()??
  16. {??
  17. ????struct?itimerval?itv;??
  18. ????itv.it_interval.tv_sec?=?1;??
  19. ????itv.it_interval.tv_usec?=?0;??
  20. ????itv.it_value.tv_sec?=?1;??
  21. ????itv.it_value.tv_usec?=?0;??
  22. ????setitimer(ITIMER_REAL,?&itv,?&oldtv);??
  23. }??
  24. ??
  25. void?swap(UINT32*?prev,?UINT32*?next)??
  26. {??
  27. ????__asm("push?%%eax\n\t"??
  28. ??????????"push?%%ebx\n\t"??
  29. ??????????"push?%%ecx\n\t"??
  30. ??????????"push?%%edx\n\t"??
  31. ??????????"push?%%esi\n\t"??
  32. ??????????"push?%%edi\n\t"??
  33. ??????????"push?%%ebp\n\t"??
  34. ??????????"push?%%esp\n\t"??
  35. ??
  36. ??????????"lea?0x8(%%ebp),?%%eax\n\t"??
  37. ??????????"mov?(%%eax),?%%eax\n\t"??
  38. ??????????"mov?%%esp,?(%%eax)\n\t"??
  39. ??
  40. ??????????"lea?0xc(%%ebp),?%%eax\n\t"??
  41. ??????????"mov?(%%eax),?%%eax\n\t"??
  42. ??????????"mov?(%%eax),?%%esp\n\t"??
  43. ??
  44. ??????????"pop?%%esp\n\t"??
  45. ??????????"pop?%%ebp\n\t"??
  46. ??????????"pop?%%edi\n\t"??
  47. ??????????"pop?%%esi\n\t"??
  48. ??????????"pop?%%edx\n\t"??
  49. ??????????"pop?%%ecx\n\t"??
  50. ??????????"pop?%%ebx\n\t"??
  51. ??????????"pop?%%eax\n\t"??
  52. ??????????::);??
  53. }??
  54. ??
  55. void?hello()??
  56. {??
  57. ????printf("hello!\n");??
  58. ????swap(&new,?&old);??
  59. }??
  60. ??
  61. void?signal_handler(int?m)??
  62. {??
  63. ????UINT32*?data;??
  64. ????UINT32?unit;??
  65. ??
  66. ????if(count?!=?0)??
  67. ????{??
  68. ????????printf("count?=?%d\n",?count++);??
  69. ????????return;??
  70. ????}??
  71. ??
  72. ????printf("count?=?%d\n",?count++);??
  73. ????data?=?(UINT32*)malloc(STACK_LENGTH);??
  74. ????unit?=?STACK_LENGTH?>>?2;??
  75. ??
  76. ????if(NULL?==?data)??
  77. ????????return;??
  78. ??
  79. ????memset(data,?0,?STACK_LENGTH);??
  80. ????data[unit?-1]?=?(UINT32)?hello;??
  81. ????data[unit?-2]?=?0;??
  82. ????data[unit?-3]?=?0;??
  83. ????data[unit?-4]?=?0;??
  84. ????data[unit?-5]?=?0;??
  85. ????data[unit?-6]?=?0;??
  86. ????data[unit?-7]?=?0;??
  87. ????data[unit?-8]?=?0;??
  88. ????data[unit?-9]?=?0;??
  89. ????data[unit?-10]?=?(UINT32)?&data[unit?-?9];??
  90. ??
  91. ????new?=?(UINT32)?&data[unit?-10];??
  92. ????swap(&old,?&new);??
  93. ????free(data);??
  94. }??
  95. ??
  96. int?main()??
  97. {??
  98. ????set_timer();??
  99. ????signal(SIGALRM,?signal_handler);??
  100. ????while(count?<?10);??
  101. ????exit(0);??
  102. ????return?1;??
  103. }?

嵌入式操作系統內核原理和開發(任務創建和堆棧溢出檢查)

雖然寫操作系統的博客要比寫普通的技術點要麻煩一些,但是心中還是挺開心的。一方面,通過幾行代碼就可以說明一些問題,把理論實踐化,這本身就很具有挑戰性;另外一方面還鍛煉自己的溝通能力,讓更多的人明白你的想法,認可你的想法。

??? 其實,通過上面一篇博客,我們就已經清楚任務的創建是怎么一回事,但是我們還是愿意就這個問題講得更細一點,說得更多一點。系統本身是多線程的,那說明所有線程的地址空間都是共享的。由于資源都是操作系統本身提供的,所以線程本身的要求就很低,函數名、堆棧、入口點、堆棧大小、優先級,大體上也就是這么多。至于這個堆棧是哪里的內存,其實已經不太重要了。為了簡單起見,我們對原來的初始化函數 稍微修改了一下,

[cpp] view plaincopy
  1. void?task_init()??
  2. {??
  3. ????UINT32?unit?=?STACK_LENGTH;??
  4. ??
  5. ????memset((void*)data,?0,?STACK_LENGTH?*?sizeof(UINT32));??
  6. ????data[unit?-1]?=?(UINT32)?hello;??
  7. ????data[unit?-2]?=?0;??
  8. ????data[unit?-3]?=?0;??
  9. ????data[unit?-4]?=?0;??
  10. ????data[unit?-5]?=?0;??
  11. ????data[unit?-6]?=?0;??
  12. ????data[unit?-7]?=?0;??
  13. ????data[unit?-8]?=?0;??
  14. ????data[unit?-9]?=?0;??
  15. ????data[unit?-10]?=?(UINT32)?&data[unit?-?9];??
  16. ????new?=?(UINT32)?&data[unit?-10];??
  17. }??
??? 上面的操作比較簡陋,只是對堆棧進行了設置。這是線程初始化的時候必須要做的一步。當然,這里的hello就是我們的函數入口點。因為這里用SIGALRM代替的時鐘中斷是沒有辦法做到搶占的,所以我們可以人為多設置一些調度點,比如象這樣,
[cpp] view plaincopy
  1. void?hello()??
  2. {??
  3. ????printf("count?=?%d?in?sub!\n",?count?++);??
  4. ????swap(&new,?&old);??
  5. ????printf("count?=?%d?in?sub!\n",?count?++);??
  6. ????swap(&new,?&old);??
  7. ????printf("count?=?%d?in?sub!\n",?count?++);??
  8. ????swap(&new,?&old);??
  9. ????printf("count?=?%d?in?sub!\n",?count?++);??
  10. ????swap(&new,?&old);??
  11. ????printf("count?=?%d?in?sub!\n",?count?++);??
  12. ????quit?=?1;??
  13. ????swap(&new,?&old);??
  14. }??
??? 在編寫程序的時候,最恐怖的事情就是堆棧溢出了。但是在操作系統中,我們完全可以自己判斷當前的堆棧是否已經溢出。因為我們知道,在線程調度的時候,保存的堆棧esp永遠指向最低的那個地址。
[cpp] view plaincopy
  1. int?check_stack_overflow(unsigned?int?base,?unsigned?int?current)??
  2. {??
  3. ????assert(0?!=?base?&&?0?!=?current);??
  4. ??
  5. ????return?(current?<?base)???1?:0;??
  6. }??
??? 當然,這些說的都是線程調度的事,你也可以編寫輸入輸出命令,實現對嵌入式操作系統的某種控制。要打印什么,設置什么,保存什么,都可以通過你的輸入命令來解析執行,這些都是和signal處理是分開來的。后面這部分還要詳細討論,這里可以稍微添加一下,
[cpp] view plaincopy
  1. int?main()??
  2. {??
  3. ????char?val;??
  4. ??
  5. ????task_init();??
  6. ????set_timer();??
  7. ????signal(SIGALRM,?signal_handler);??
  8. ??
  9. ????while(1)??
  10. ????{??
  11. ????????scanf("%c",?&val);??
  12. ????}??
  13. ??
  14. ????exit(0);??
  15. ????return?1;??
  16. }??
??? 最后,還是老規矩,附上詳細的代碼。雖然這一過程有點繁瑣和冗余,但是至少看上去更完整一些。
[cpp] view plaincopy
  1. #include?<stdio.h>??
  2. #include?<time.h>??
  3. #include?<stdlib.h>??
  4. #include?<signal.h>??
  5. #include?<assert.h>??
  6. #include?<sys/time.h>??
  7. ??
  8. #define?UINT32?unsigned?int??
  9. #define?STACK_LENGTH??512??
  10. ??
  11. static?struct?itimerval?oldtv;??
  12. UINT32?old?=?0;??
  13. UINT32?new?=?0;??
  14. UINT32?count?=?0;??
  15. UINT32?data[STACK_LENGTH]?=?{0};??
  16. UINT32?quit?=?0;??
  17. ??
  18. void?set_timer()??
  19. {??
  20. ????struct?itimerval?itv;??
  21. ????itv.it_interval.tv_sec?=?1;??
  22. ????itv.it_interval.tv_usec?=?0;??
  23. ????itv.it_value.tv_sec?=?1;??
  24. ????itv.it_value.tv_usec?=?0;??
  25. ????setitimer(ITIMER_REAL,?&itv,?&oldtv);??
  26. }??
  27. ??
  28. void?swap(UINT32*?prev,?UINT32*?next)??
  29. {??
  30. ????__asm("push?%%eax\n\t"??
  31. ??????????"push?%%ebx\n\t"??
  32. ??????????"push?%%ecx\n\t"??
  33. ??????????"push?%%edx\n\t"??
  34. ??????????"push?%%esi\n\t"??
  35. ??????????"push?%%edi\n\t"??
  36. ??????????"push?%%ebp\n\t"??
  37. ??????????"push?%%esp\n\t"??
  38. ??????????"lea?0x8(%%ebp),?%%eax\n\t"??
  39. ??????????"mov?(%%eax),?%%eax\n\t"??
  40. ??????????"mov?%%esp,?(%%eax)\n\t"??
  41. ??
  42. ??????????"lea?0xc(%%ebp),?%%eax\n\t"??
  43. ??????????"mov?(%%eax),?%%eax\n\t"??
  44. ??????????"mov?(%%eax),?%%esp\n\t"??
  45. ??????????"pop?%%esp\n\t"??
  46. ??????????"pop?%%ebp\n\t"??
  47. ??????????"pop?%%edi\n\t"??
  48. ??????????"pop?%%esi\n\t"??
  49. ??????????"pop?%%edx\n\t"??
  50. ??????????"pop?%%ecx\n\t"??
  51. ??????????"pop?%%ebx\n\t"??
  52. ??????????"pop?%%eax\n\t"??
  53. ??????????::);??
  54. }??
  55. ??
  56. void?hello()??
  57. {??
  58. ????printf("count?=?%d?in?sub!\n",?count?++);??
  59. ????swap(&new,?&old);??
  60. ????printf("count?=?%d?in?sub!\n",?count?++);??
  61. ????swap(&new,?&old);??
  62. ????printf("count?=?%d?in?sub!\n",?count?++);??
  63. ????swap(&new,?&old);??
  64. ????printf("count?=?%d?in?sub!\n",?count?++);??
  65. ????swap(&new,?&old);??
  66. ????printf("count?=?%d?in?sub!\n",?count?++);??
  67. ????quit?=?1;??
  68. ????swap(&new,?&old);??
  69. }??
  70. ??
  71. void?task_init()??
  72. {??
  73. ????UINT32?unit?=?STACK_LENGTH;??
  74. ??
  75. ????memset((void*)data,?0,?STACK_LENGTH?*?sizeof(UINT32));??
  76. ????data[unit?-1]?=?(UINT32)?hello;??
  77. ????data[unit?-2]?=?0;??
  78. ????data[unit?-3]?=?0;??
  79. ????data[unit?-4]?=?0;??
  80. ????data[unit?-5]?=?0;??
  81. ????data[unit?-6]?=?0;??
  82. ????data[unit?-7]?=?0;??
  83. ????data[unit?-8]?=?0;??
  84. ????data[unit?-9]?=?0;??
  85. ????data[unit?-10]?=?(UINT32)?&data[unit?-?9];??
  86. ????new?=?(UINT32)?&data[unit?-10];??
  87. }??
  88. ??
  89. int?check_stack_overflow(unsigned?int?base,?unsigned?int?current)??
  90. {??
  91. ????assert(0?!=?base?&&?0?!=?current);??
  92. ??
  93. ????return?(current?<?base)???1?:0;??
  94. }??
  95. ??
  96. void?signal_handler(int?m)??
  97. {??
  98. ????if(0?==?quit)?????
  99. ????{??
  100. ????????swap(&old,?&new);??
  101. ????????assert(0?==?check_stack_overflow(data,new));??
  102. ????????return;??
  103. ????}??
  104. ??
  105. ????printf("count?=?%d?in?main!\n",?count?++);??
  106. }??
  107. ??
  108. int?main()??
  109. {??
  110. ????char?val;??
  111. ??
  112. ????task_init();??
  113. ????set_timer();??
  114. ????signal(SIGALRM,?signal_handler);??
  115. ??
  116. ????while(1)??
  117. ????{??
  118. ????????scanf("%c",?&val);??
  119. ????}??
  120. ??
  121. ????exit(0);??
  122. ????return?1;??
  123. }?

嵌入式操作系統內核原理和開發(多線程輪轉)

之前我們也談到了線程創建,基本上簡單的系統就可以跑起來了,但是還沒有到多線程運行的地步。所以,我們下面試圖所要做的工作就是創建更多的線程,讓更多的線程運行起來。為了做好這一點,首先我們需要對task_init重新修整一下,
[cpp] view plaincopy
  1. void?task_init(int?index,?UINT32?data[],?int?size,?void?(*func)())??
  2. {??
  3. ????????UINT32?unit?=?size;??
  4. ??
  5. ????????memset((void*)data,?0,?size?*?sizeof(UINT32));??
  6. ????????data[unit?-1]?=?(UINT32)?func;??
  7. ????????data[unit?-2]?=?0;??
  8. ????????data[unit?-3]?=?0;??
  9. ????????data[unit?-4]?=?0;??
  10. ????????data[unit?-5]?=?0;??
  11. ????????data[unit?-6]?=?0;??
  12. ????????data[unit?-7]?=?0;??
  13. ????????data[unit?-8]?=?0;??
  14. ????????data[unit?-9]?=?0;??
  15. ????????data[unit?-10]?=?(UINT32)?&data[unit?-?9];??
  16. ????????new[index]?=?(UINT32)?&data[unit?-10];??
  17. }??

??? 這是一個創建線程的函數,有堆棧、大小、函數入口。那么,我們的函數什么時候創建呢,其實就是在系統的開始位置就可以,

[cpp] view plaincopy
  1. void?set_all_task()??
  2. {??
  3. ????????int?index;??
  4. ??
  5. ????????for(index?=?0;?index?<?THREAD_MAX_NUMBER;?index?++)??
  6. ????????????task_init(index,?task_stack[index],?STACK_LENGTH,?hello);??
  7. }??

??? 既然任務創建沒有問題,那么下面就會涉及到簡單輪轉的問題。其實我們的方法特別簡單,就是根據current_thread_id疊加,每一個thread都有自己的運轉機會。代碼如下所示,

[cpp] view plaincopy
  1. void?signal_handler(int?m)??
  2. {??
  3. ????????current_thread_id?=?current_thread_id?%?THREAD_MAX_NUMBER;??
  4. ??
  5. ????????if(0?==?quit[current_thread_id])??
  6. ????????{??
  7. ????????????swap(&old,?&new[current_thread_id]);??
  8. ????????}??
  9. ??
  10. ????????printf("count?=?%d?in?main!\n\n",??count?++);??
  11. ????????current_thread_id?++;??
  12. }??

??? 當然,為了要實現真正的多線程運行,我們還要保證線程始終在運行。要達到這一點也不是很復雜,只需要把子函數設計為while(1)即可,

[cpp] view plaincopy
  1. void?hello()??
  2. {??
  3. ????????while(1)?{??
  4. ????????????printf("id?=?%i,?count?=?%d?in?thread!\n",current_thread_id,??count?++);??
  5. ????????????swap(&new[current_thread_id],?&old);??
  6. ??
  7. ????????????printf("id?=?%i,?count?=?%d?in?thread!\n",current_thread_id,??count?++);??
  8. ????????????swap(&new[current_thread_id],?&old);??
  9. ????????}??
  10. }??

??? 基本上要做到以上幾點就可以實現了,最后給出完整的代碼,大家可以在linux系統好好試試這個代碼。

[cpp] view plaincopy
  1. #include?<stdio.h>??
  2. #include?<time.h>??
  3. #include?<stdlib.h>??
  4. #include?<signal.h>??
  5. #include?<assert.h>??
  6. #include?<sys/time.h>??
  7. ??
  8. #define?UINT32?unsigned???int??
  9. #define?STACK_LENGTH??????512??
  10. #define?THREAD_MAX_NUMBER?10??
  11. ??
  12. struct?itimerval?oldtv;??
  13. UINT32?old???=?0;??
  14. UINT32?count?=?0;??
  15. UINT32?task_stack[THREAD_MAX_NUMBER][STACK_LENGTH]?=?{0};??
  16. UINT32?new[THREAD_MAX_NUMBER]??=?{0};??
  17. UINT32?quit[THREAD_MAX_NUMBER]?=?{0};??
  18. UINT32?current_thread_id?=?0;??
  19. ??
  20. void?set_timer()??
  21. {??
  22. ????????struct?itimerval?itv;??
  23. ????????itv.it_interval.tv_sec?=?1;??
  24. ????????itv.it_interval.tv_usec?=?0;??
  25. ????????itv.it_value.tv_sec?=?1;??
  26. ????????itv.it_value.tv_usec?=?0;??
  27. ????????setitimer(ITIMER_REAL,?&itv,?&oldtv);??
  28. }??
  29. ??
  30. void?swap(UINT32*?prev,?UINT32*?next)??
  31. {??
  32. ????__asm("push?%%eax\n\t"??
  33. ??????????"push?%%ebx\n\t"??
  34. ??????????"push?%%ecx\n\t"??
  35. ??????????"push?%%edx\n\t"??
  36. ??????????"push?%%esi\n\t"??
  37. ??????????"push?%%edi\n\t"??
  38. ??????????"push?%%ebp\n\t"??
  39. ??????????"push?%%esp\n\t"??
  40. ??????????"lea?0x8(%%ebp),?%%eax\n\t"??
  41. ??????????"mov?(%%eax),?%%eax\n\t"??
  42. ??????????"mov?%%esp,?(%%eax)\n\t"??
  43. ??
  44. ??????????"lea?0xc(%%ebp),?%%eax\n\t"??
  45. ??????????"mov?(%%eax),?%%eax\n\t"??
  46. ??????????"mov?(%%eax),?%%esp\n\t"??
  47. ??????????"pop?%%esp\n\t"??
  48. ??????????"pop?%%ebp\n\t"??
  49. ??????????"pop?%%edi\n\t"??
  50. ??????????"pop?%%esi\n\t"??
  51. ??????????"pop?%%edx\n\t"??
  52. ??????????"pop?%%ecx\n\t"??
  53. ??????????"pop?%%ebx\n\t"??
  54. ??????????"pop?%%eax\n\t"??
  55. ??????????::);??
  56. }??
  57. ??
  58. void?hello()??
  59. {??
  60. ????????while(1)?{??
  61. ????????????printf("id?=?%i,?count?=?%d?in?thread!\n",current_thread_id,??count?++);??
  62. ????????????swap(&new[current_thread_id],?&old);??
  63. ??
  64. ????????????printf("id?=?%i,?count?=?%d?in?thread!\n",current_thread_id,??count?++);??
  65. ????????????swap(&new[current_thread_id],?&old);??
  66. ????????}??
  67. }??
  68. ??
  69. void?task_init(int?index,?UINT32?data[],?int?size,?void?(*func)())??
  70. {??
  71. ????????UINT32?unit?=?size;??
  72. ??
  73. ????????memset((void*)data,?0,?size?*?sizeof(UINT32));??
  74. ????????data[unit?-1]?=?(UINT32)?func;??
  75. ????????data[unit?-2]?=?0;??
  76. ????????data[unit?-3]?=?0;??
  77. ????????data[unit?-4]?=?0;??
  78. ????????data[unit?-5]?=?0;??
  79. ????????data[unit?-6]?=?0;??
  80. ????????data[unit?-7]?=?0;??
  81. ????????data[unit?-8]?=?0;??
  82. ????????data[unit?-9]?=?0;??
  83. ????????data[unit?-10]?=?(UINT32)?&data[unit?-?9];??
  84. ????????new[index]?=?(UINT32)?&data[unit?-10];??
  85. }??
  86. ??
  87. void?signal_handler(int?m)??
  88. {??
  89. ????????current_thread_id?=?current_thread_id?%?THREAD_MAX_NUMBER;??
  90. ??
  91. ????????if(0?==?quit[current_thread_id])??
  92. ????????{??
  93. ????????????swap(&old,?&new[current_thread_id]);??
  94. ????????}??
  95. ??
  96. ????????printf("count?=?%d?in?main!\n\n",??count?++);??
  97. ????????current_thread_id?++;??
  98. }??
  99. ??
  100. void?set_all_task()??
  101. {??
  102. ????????int?index;??
  103. ??
  104. ????????for(index?=?0;?index?<?THREAD_MAX_NUMBER;?index?++)??
  105. ????????????task_init(index,?task_stack[index],?STACK_LENGTH,?hello);??
  106. }??
  107. ??
  108. int?main()??
  109. {??
  110. ????????char?val;??
  111. ??
  112. ????????set_all_task();??
  113. ????????set_timer();??
  114. ????????signal(SIGALRM,?signal_handler);??
  115. ??
  116. ????????while(1)??
  117. ????????{??
  118. ????????????scanf("%c",?&val);??
  119. ????????}??
  120. ??
  121. ????????exit(0);??
  122. ????????return?1;??
  123. }?

嵌入式操作系統內核原理和開發(通用優先級調度)

相比較其他調度算法而言,時間片的輪轉更多的注重公平性。但是,任務與任務之間也是有先后之分的,有的任務我們希望多安排一些時間片,而有的任務我們則希望少安排一些時間片。比較說,如果我們在上網的話,我們就希望上網的操作響應的更快一些;如果我們在進行GUI操作,我們當然就希望圖形響應更快一些。這些都是可以理解的,下面我們就緒要對數據結構進行一些修改。
[cpp] view plaincopy
  1. typedef?struct?_TASK_INFO??
  2. {??
  3. ????UINT32?id;??
  4. ????UINT32*?stack;??
  5. ????UINT32?size;??
  6. ????UINT32?context;??
  7. ????UINT32?priority;??
  8. ????UINT32?time_slice;??
  9. ????void?(*func)();??
  10. ??
  11. }TASK_INFO;??
??? 這里的priority就是當前線程的優先級,所以最簡單的方法就是根據priority直接分配對應的time_slice。也就是這個函數,
[cpp] view plaincopy
  1. void?reset_time_slice?()??
  2. {??
  3. ????int?index;??
  4. ??
  5. ????for(index?=?0;?index?<?THREAD_MAX_NUMBER;?index++)??
  6. ????????gAllTask[index].time_slice?=?gAllTask[index].priority?+?1;??
  7. }??
??? 所以,以后每次調度的時候,我們就首先尋找當前最高優先級的任務,看看當前任務安排的時間片是否用完了,沒有用完就繼續運行。如果當前優先級的任務已經沒有時間片了,那么此時就可以安排低優先級的任務進行調度了。
[cpp] view plaincopy
  1. void?signal_handler(int?m)??
  2. {??
  3. ????????int?index;??
  4. ??
  5. start:??
  6. ????????index?=?find_next_thread();??
  7. ????????if(-1?==?index)??
  8. ????????{??
  9. ????????????reset_time_slice();??
  10. ????????????goto?start;??
  11. ????????}??
  12. ??
  13. ????????gAllTask[index].time_slice?--;??
  14. ????????current_thread_id?=?index;??
  15. ????????swap(&old,?&gAllTask[current_thread_id].context);??
  16. }??
??? 下面,我們就根據任務優先級挑選下一個需要運行的thread了,
[cpp] view plaincopy
  1. int?find_next_thread()??
  2. {??
  3. ????int?index;??
  4. ??
  5. ????for(index?=?THREAD_MAX_NUMBER?-1;?index?>=0;?index?--)??
  6. ????{??
  7. ????????if(0?!=?gAllTask[index].time_slice)??
  8. ????????????break;??
  9. ????}??
  10. ??
  11. ????return?index;????????
  12. }??
??? 整個代碼的流程也不復雜,大家可以運行、單步調試一把,試試看。
[cpp] view plaincopy
  1. #include?<stdio.h>??
  2. #include?<time.h>??
  3. #include?<stdlib.h>??
  4. #include?<signal.h>??
  5. #include?<assert.h>??
  6. #include?<string.h>??
  7. #include?<sys/time.h>??
  8. ??
  9. #define?UINT32?unsigned???int??
  10. #define?STACK_LENGTH??????512??
  11. #define?THREAD_MAX_NUMBER?10??
  12. ??
  13. typedef?struct?_TASK_INFO??
  14. {??
  15. ????UINT32?id;??
  16. ????UINT32*?stack;??
  17. ????UINT32?size;??
  18. ????UINT32?context;??
  19. ????UINT32?priority;??
  20. ????UINT32?time_slice;??
  21. ????void?(*func)();??
  22. ??
  23. }TASK_INFO;??
  24. ??
  25. static?struct?itimerval?oldtv;??
  26. UINT32?old???=?0;??
  27. UINT32?count?=?0;??
  28. UINT32?task_stack[THREAD_MAX_NUMBER][STACK_LENGTH]?=?{0};??
  29. TASK_INFO?gAllTask[THREAD_MAX_NUMBER]?=?{0};??
  30. UINT32?current_thread_id?=?0;??
  31. ??
  32. void?set_timer()??
  33. {??
  34. ????????struct?itimerval?itv;??
  35. ????????itv.it_interval.tv_sec?=?1;??
  36. ????????itv.it_interval.tv_usec?=?0;??
  37. ????????itv.it_value.tv_sec?=?1;??
  38. ????????itv.it_value.tv_usec?=?0;??
  39. ????????setitimer(ITIMER_REAL,?&itv,?&oldtv);??
  40. }??
  41. ??
  42. void?swap(UINT32*?prev,?UINT32*?next)??
  43. {??
  44. ????__asm("push?%%eax\n\t"??
  45. ??????????"push?%%ebx\n\t"??
  46. ??????????"push?%%ecx\n\t"??
  47. ??????????"push?%%edx\n\t"??
  48. ??????????"push?%%esi\n\t"??
  49. ??????????"push?%%edi\n\t"??
  50. ??????????"push?%%ebp\n\t"??
  51. ??????????"push?%%esp\n\t"??
  52. ??????????"lea?0x8(%%ebp),?%%eax\n\t"??
  53. ??????????"mov?(%%eax),?%%eax\n\t"??
  54. ??????????"mov?%%esp,?(%%eax)\n\t"??
  55. ??
  56. ??????????"lea?0xc(%%ebp),?%%eax\n\t"??
  57. ??????????"mov?(%%eax),?%%eax\n\t"??
  58. ??????????"mov?(%%eax),?%%esp\n\t"??
  59. ??????????"pop?%%esp\n\t"??
  60. ??????????"pop?%%ebp\n\t"??
  61. ??????????"pop?%%edi\n\t"??
  62. ??????????"pop?%%esi\n\t"??
  63. ??????????"pop?%%edx\n\t"??
  64. ??????????"pop?%%ecx\n\t"??
  65. ??????????"pop?%%ebx\n\t"??
  66. ??????????"pop?%%eax\n\t"??
  67. ??????????::);??
  68. }??
  69. ??
  70. void?hello()??
  71. {??
  72. ????????int?temp?=?0;??
  73. ??
  74. ????????while(1)?{??
  75. ????????????printf("id?=?%d,?temp?=?%d,?count?=?%d?in?thread!\n",current_thread_id,??temp?++,?count?++);??
  76. ????????????swap(&gAllTask[current_thread_id].context,?&old);??
  77. ??
  78. ????????????printf("id?=?%d,?temp?=?%d,?count?=?%d?in?thread!\n",current_thread_id,??temp?++,?count?++);??
  79. ????????????swap(&gAllTask[current_thread_id].context,?&old);??
  80. ????????}??
  81. }??
  82. ??
  83. int?find_next_thread()??
  84. {??
  85. ????int?index;??
  86. ??
  87. ????for(index?=?THREAD_MAX_NUMBER?-1;?index?>=0;?index?--)??
  88. ????{??
  89. ????????if(0?!=?gAllTask[index].time_slice)??
  90. ????????????break;??
  91. ????}??
  92. ??
  93. ????return?index;????????
  94. }??
  95. ??
  96. void?reset_time_slice?()??
  97. {??
  98. ????int?index;??
  99. ??
  100. ????for(index?=?0;?index?<?THREAD_MAX_NUMBER;?index++)??
  101. ????????gAllTask[index].time_slice?=?gAllTask[index].priority?+?1;??
  102. }??
  103. ??
  104. void?task_init(int?index)??
  105. {??
  106. ????????UINT32?unit?=?gAllTask[index].size;??
  107. ????????UINT32*?pData?=?gAllTask[index].stack;??
  108. ??
  109. ????????memset((void*)pData,(int)?0,?unit?*?sizeof(UINT32));??
  110. ????????pData[unit?-1]?=?(UINT32)?gAllTask[index].func;??
  111. ????????pData[unit?-2]?=?0;??
  112. ????????pData[unit?-3]?=?0;??
  113. ????????pData[unit?-4]?=?0;??
  114. ????????pData[unit?-5]?=?0;??
  115. ????????pData[unit?-6]?=?0;??
  116. ????????pData[unit?-7]?=?0;??
  117. ????????pData[unit?-8]?=?0;??
  118. ????????pData[unit?-9]?=?0;??
  119. ????????pData[unit?-10]?=?(UINT32)?&pData[unit?-?9];??
  120. ????????gAllTask[index].context?=?(UINT32)?&pData[unit?-10];??
  121. }??
  122. ??
  123. void?signal_handler(int?m)??
  124. {??
  125. ????????int?index;??
  126. ??
  127. start:??
  128. ????????index?=?find_next_thread();??
  129. ????????if(-1?==?index)??
  130. ????????{??
  131. ????????????reset_time_slice();??
  132. ????????????goto?start;??
  133. ????????}??
  134. ??
  135. ????????gAllTask[index].time_slice?--;??
  136. ????????current_thread_id?=?index;??
  137. ????????swap(&old,?&gAllTask[current_thread_id].context);??
  138. }??
  139. ??
  140. void?set_all_task()???
  141. {??
  142. ????????int?index;??
  143. ????????memset(gAllTask,?0,?sizeof(gAllTask));??
  144. ??
  145. ????????for(index?=?0;?index?<?THREAD_MAX_NUMBER;?index?++)??
  146. ????????{??
  147. ????????????gAllTask[index].id?=?index;??
  148. ????????????gAllTask[index].stack?=?task_stack[index];??
  149. ????????????gAllTask[index].size?=?STACK_LENGTH;??
  150. ????????????gAllTask[index].context?=?0;??
  151. ????????????gAllTask[index].func?=?hello;??
  152. ????????????gAllTask[index].priority?=?index;??
  153. ????????????gAllTask[index].time_slice?=?index?+?1;??
  154. ??
  155. ????????????task_init(index);??
  156. ????????}??
  157. }??
  158. ??
  159. int?main()??
  160. {??
  161. ????????char?val;??
  162. ??
  163. ????????set_all_task();??
  164. ????????set_timer();??
  165. ????????signal(SIGALRM,?signal_handler);??
  166. ??
  167. ????????while(1)??
  168. ????????{??
  169. ????????????scanf("%c",?&val);??
  170. ????????}??
  171. ??
  172. ????????exit(0);??
  173. ????????return?1;??
  174. }?

嵌入式操作系統內核原理和開發(改進型優先級調度)

上面的一篇博客說到了優先級調度,但是那個優先級調度算法比較極端。打個比方說,現在王先生有三個小孩,分別是老大、老二、老三。假設現在到了飯點,王先生需要給三個小孩喂飯。此時如果是時間片輪轉的話,那么就是絕對公平,王先生每人一口不停地進行喂飯。如果是優先級調度,那么王先生首先自己有一個優先級考量,比如說三個小孩按照年齡順序優先級是逐漸提高的,畢竟小孩需要更多的照顧嘛。這個時候如果需要進行喂飯的話,那么王先生需要首先伺候好最小的那個小孩老三,才會有時間照顧老二,至于老大什么時候才能得到照顧那就看造化了。


??? 現在,我們打算重新換一種方法。假設三個小孩的優先級分別是1、2、3,其中年齡越小優先級越高,3代表高優先級。接著,我們按照優先級給三個小孩安排時間片,分別是1、2、3。同時,這個時間片不光代表了當前可用的剩余時間,還代表了小孩此時的臨時優先級。

??? (1)首先王先生給老三喂飯,時間片降低1,即臨時優先級為2;

??? (2)接著王先生判斷當前優先級最高的仍為老三,畢竟老二的優先級也沒有超過老三,所以老三的時間片降1,臨時優先級為1;

??? (3)王先生獲知當前優先級最高的為老二,老二獲得時間片;

??? (4)此時王先生發現三個孩子的臨時優先級都一樣,那么就會按照固定優先級的大小依次對老三、老二、老大進行喂飯。


??? 我們發現,這中間受益最大的就是老二。當然,我們可以做進一步推論,如果老王的孩子越多,那么優先級處于中間的孩子在時間片的分配上將更加均勻,響應也會更加及時,交互性也會變得很好。


??? 根據以上的想法,我們重新改寫了優先級調度算法,修改為改進型優先級調度算法,

[cpp] view plaincopy
  1. int?find_next_thread()??
  2. {??
  3. ????int?index;??
  4. ????int?choice?=?THREAD_MAX_NUMBER?-1;??
  5. ????int?value?=?gAllTask[choice].time_slice;??
  6. ??
  7. ????for(index?=?choice?-1;?index?>=?0;?index?--)??
  8. ????{??
  9. ????????if(value?<?gAllTask[index].time_slice)??
  10. ????????{??
  11. ????????????choice?=?index;??
  12. ????????????value?=?gAllTask[index].time_slice;??
  13. ????????}??
  14. ????}??
  15. ??
  16. ????if(0?==?value)??
  17. ????????choice?=?-1;??
  18. ??
  19. ????return?choice;????????
  20. }??
??? 當然,加上原來的時間片輪轉調度、通用優先級調度方法,此時就存在三種調度方法了。我們可以自己設置宏,通過宏的設置靈活選用調度算法,
[cpp] view plaincopy
  1. #define?TIME_ROUND_SCHEDULE?????0??
  2. #define?HARD_PRIORITY_SCHEDULE??0??
  3. #define?SOFT_PRIORITY_SCHEDULE??1?????

??? 這些代碼都是可以在系統中共存的。選用什么算法,取決于實際情況是什么樣的情形。
[cpp] view plaincopy
  1. #include?<stdio.h>??
  2. #include?<time.h>??
  3. #include?<stdlib.h>??
  4. #include?<signal.h>??
  5. #include?<assert.h>??
  6. #include?<string.h>??
  7. #include?<sys/time.h>??
  8. ??
  9. #define?UINT32?unsigned?????????int??
  10. #define?STACK_LENGTH????????????512??
  11. #define?THREAD_MAX_NUMBER???????10??
  12. #define?TIME_ROUND_SCHEDULE?????0??
  13. #define?HARD_PRIORITY_SCHEDULE??0??
  14. #define?SOFT_PRIORITY_SCHEDULE??1?????
  15. ??
  16. typedef?struct?_TASK_INFO??
  17. {??
  18. ????UINT32?id;??
  19. ????UINT32*?stack;??
  20. ????UINT32?size;??
  21. ????UINT32?context;??
  22. ????UINT32?priority;??
  23. ????UINT32?time_slice;??
  24. ????void?(*func)();??
  25. ??
  26. }TASK_INFO;??
  27. ??
  28. static?struct?itimerval?oldtv;??
  29. UINT32?old???=?0;??
  30. UINT32?count?=?0;??
  31. UINT32?task_stack[THREAD_MAX_NUMBER][STACK_LENGTH]?=?{0};??
  32. TASK_INFO?gAllTask[THREAD_MAX_NUMBER]?=?{0};??
  33. UINT32?current_thread_id?=?0;??
  34. ??
  35. void?set_timer()??
  36. {??
  37. ????????struct?itimerval?itv;??
  38. ????????itv.it_interval.tv_sec?=?1;??
  39. ????????itv.it_interval.tv_usec?=?0;??
  40. ????????itv.it_value.tv_sec?=?1;??
  41. ????????itv.it_value.tv_usec?=?0;??
  42. ????????setitimer(ITIMER_REAL,?&itv,?&oldtv);??
  43. }??
  44. ??
  45. void?swap(UINT32*?prev,?UINT32*?next)??
  46. {??
  47. ????__asm("push?%%eax\n\t"??
  48. ??????????"push?%%ebx\n\t"??
  49. ??????????"push?%%ecx\n\t"??
  50. ??????????"push?%%edx\n\t"??
  51. ??????????"push?%%esi\n\t"??
  52. ??????????"push?%%edi\n\t"??
  53. ??????????"push?%%ebp\n\t"??
  54. ??????????"push?%%esp\n\t"??
  55. ??????????"lea?0x8(%%ebp),?%%eax\n\t"??
  56. ??????????"mov?(%%eax),?%%eax\n\t"??
  57. ??????????"mov?%%esp,?(%%eax)\n\t"??
  58. ??
  59. ??????????"lea?0xc(%%ebp),?%%eax\n\t"??
  60. ??????????"mov?(%%eax),?%%eax\n\t"??
  61. ??????????"mov?(%%eax),?%%esp\n\t"??
  62. ??????????"pop?%%esp\n\t"??
  63. ??????????"pop?%%ebp\n\t"??
  64. ??????????"pop?%%edi\n\t"??
  65. ??????????"pop?%%esi\n\t"??
  66. ??????????"pop?%%edx\n\t"??
  67. ??????????"pop?%%ecx\n\t"??
  68. ??????????"pop?%%ebx\n\t"??
  69. ??????????"pop?%%eax\n\t"??
  70. ??????????::);??
  71. }??
  72. ??
  73. void?hello()??
  74. {??
  75. ????????int?temp?=?0;??
  76. ??
  77. ????????while(1)?{??
  78. ????????????printf("id?=?%d,?temp?=?%d,?count?=?%d?in?thread!\n",current_thread_id,??temp?++,?count?++);??
  79. ????????????swap(&gAllTask[current_thread_id].context,?&old);??
  80. ??
  81. ????????????printf("id?=?%d,?temp?=?%d,?count?=?%d?in?thread!\n",current_thread_id,??temp?++,?count?++);??
  82. ????????????swap(&gAllTask[current_thread_id].context,?&old);??
  83. ????????}??
  84. }??
  85. ??
  86. #if?HARD_PRIORITY_SCHEDULE??
  87. int?find_next_thread()??
  88. {??
  89. ????int?index;??
  90. ??
  91. ????for(index?=?THREAD_MAX_NUMBER?-1;?index?>=0;?index?--)??
  92. ????{??
  93. ????????if(0?!=?gAllTask[index].time_slice)??
  94. ????????????break;??
  95. ????}??
  96. ??
  97. ????return?index;????????
  98. }??
  99. ??
  100. #endif??
  101. ??
  102. ??
  103. #if?SOFT_PRIORITY_SCHEDULE??
  104. int?find_next_thread()??
  105. {??
  106. ????int?index;??
  107. ????int?choice?=?THREAD_MAX_NUMBER?-1;??
  108. ????int?value?=?gAllTask[choice].time_slice;??
  109. ??
  110. ????for(index?=?choice?-1;?index?>=?0;?index?--)??
  111. ????{??
  112. ????????if(value?<?gAllTask[index].time_slice)??
  113. ????????{??
  114. ????????????choice?=?index;??
  115. ????????????value?=?gAllTask[index].time_slice;??
  116. ????????}??
  117. ????}??
  118. ??
  119. ????if(0?==?value)??
  120. ????????choice?=?-1;??
  121. ??
  122. ????return?choice;????????
  123. }??
  124. ??
  125. #endif??
  126. ??
  127. void?reset_time_slice?()??
  128. {??
  129. ????int?index;??
  130. ??
  131. ????for(index?=?0;?index?<?THREAD_MAX_NUMBER;?index++)??
  132. ????????gAllTask[index].time_slice?=?gAllTask[index].priority?+?1;??
  133. }??
  134. ??
  135. void?task_init(int?index)??
  136. {??
  137. ????????UINT32?unit?=?gAllTask[index].size;??
  138. ????????UINT32*?pData?=?gAllTask[index].stack;??
  139. ??
  140. ????????memset((void*)pData,(int)?0,?unit?*?sizeof(UINT32));??
  141. ????????pData[unit?-1]?=?(UINT32)?gAllTask[index].func;??
  142. ????????pData[unit?-2]?=?0;??
  143. ????????pData[unit?-3]?=?0;??
  144. ????????pData[unit?-4]?=?0;??
  145. ????????pData[unit?-5]?=?0;??
  146. ????????pData[unit?-6]?=?0;??
  147. ????????pData[unit?-7]?=?0;??
  148. ????????pData[unit?-8]?=?0;??
  149. ????????pData[unit?-9]?=?0;??
  150. ????????pData[unit?-10]?=?(UINT32)?&pData[unit?-?9];??
  151. ????????gAllTask[index].context?=?(UINT32)?&pData[unit?-10];??
  152. }??
  153. ??
  154. #if?TIME_ROUND_SCHEDULE??
  155. void?signal_handler(int?m)??
  156. {??
  157. ????????current_thread_id?=?current_thread_id?%?THREAD_MAX_NUMBER;??
  158. ????????swap(&old,?&gAllTask[current_thread_id].context);??
  159. ????????current_thread_id?++;??
  160. }??
  161. ??
  162. #else??
  163. void?signal_handler(int?m)??
  164. {??
  165. ????????int?index;??
  166. ??
  167. start:??
  168. ????????index?=?find_next_thread();??
  169. ????????if(-1?==?index)??
  170. ????????{??
  171. ????????????reset_time_slice();??
  172. ????????????goto?start;??
  173. ????????}??
  174. ??
  175. ????????gAllTask[index].time_slice?--;??
  176. ????????current_thread_id?=?index;??
  177. ????????swap(&old,?&gAllTask[current_thread_id].context);??
  178. }??
  179. #endif??
  180. ??
  181. ??
  182. void?set_all_task()???
  183. {??
  184. ????????int?index;??
  185. ????????memset(gAllTask,?0,?sizeof(gAllTask));??
  186. ??
  187. ????????for(index?=?0;?index?<?THREAD_MAX_NUMBER;?index?++)??
  188. ????????{??
  189. ????????????gAllTask[index].id?=?index;??
  190. ????????????gAllTask[index].stack?=?task_stack[index];??
  191. ????????????gAllTask[index].size?=?STACK_LENGTH;??
  192. ????????????gAllTask[index].context?=?0;??
  193. ????????????gAllTask[index].func?=?hello;??
  194. ????????????gAllTask[index].priority?=?index;??
  195. ????????????gAllTask[index].time_slice?=?index?+?1;??
  196. ??
  197. ????????????task_init(index);??
  198. ????????}??
  199. }??
  200. ??
  201. int?main()??
  202. {??
  203. ????????char?val;??
  204. ??
  205. ????????set_all_task();??
  206. ????????set_timer();??
  207. ????????signal(SIGALRM,?signal_handler);??
  208. ??
  209. ????????while(1)??
  210. ????????{??
  211. ????????????scanf("%c",?&val);??
  212. ????????}??
  213. ??
  214. ????????exit(0);??
  215. ????????return?1;??
  216. }?

嵌入式操作系統內核原理和開發(頭文件調整)

很長一段時間,我個人對頭文件的功能了解得不是很明白。雖然在平時的開發中,對于頭文件也沒有犯過什么大的錯誤,但是總覺得對頭文件這塊理解得不是很透徹。所以趁著這次嵌入式開發的機會,好好對頭文件這部分的內容進行了分析和總結。下面我們主要從兩個方面對頭文件進行分析,即頭文件是做什么的,頭文件編寫的過程中要注意些什么?

?

??? (1)頭文件的作用

??? 其實很多的編程語言是沒有頭文件的,比如說C#、java語言。為什么呢,因為這些語言數據結構和函數操作是捆綁在一起的。而C語言則不一樣,它是把頭文件和實現文件分開來的。頭文件的內容主要有哪些呢,也就是嵌套頭文件、宏定義、數據類型、函數原型定義、static函數等等。

?

??? (2)頭文件的編寫

?

??? a)頭文件要簡潔

??? 很多源文件在編寫的時候常常喜歡添加很多的頭文件,不管是需要的還是不需要的。可是,我們要知道,頭文件的作用主要是定義數據類型和函數類型的。本質上來說,頭文件很少會創建實質性的代碼,不管是數據段的內容,還是代碼段的內容。簡潔的頭文件不僅有利于快速排除編譯故障,還能提高編譯的速度。有經驗的朋友都知道,源文件的編譯錯誤比較容易解決,而頭文件的編譯錯誤常常十分復雜。所以,我們必須在一切可能的條件下保證頭文件的簡潔。

?

??? b)頭文件注意互斥性

?? 注意頭文件的互斥性,需要我們在開發中養成良好的編程習慣。不管是創建頭文件,首先要做的事情就是添加編譯宏。看上去這是一個十分不起眼的舉動,但是常常可以幫助你減少許多不必要的麻煩。

[cpp] view plaincopy
  1. #ifndef?_DATA_H??
  2. #define?_DATA_H??
  3. ??
  4. #endif??


??? c)全局變量不要在頭文件里面定義,如果是外部引用,必須添加上extern

[cpp] view plaincopy
  1. extern?int?g_Data;????

???

??? d)不要在頭文件里面實現函數,如果要實現,也必須要添加static

[cpp] view plaincopy
  1. static?int?add(int?a,?int?b)??
  2. {??
  3. ????return?a?+?b;??
  4. }??


??? e)頭文件當中如果需要嵌入別的頭文件,那么只是為了引用另外一個頭文件的數據結構

?

??? f)頭文件中引用的數據類型如果沒有說明,那么在被源文件引用的時候,只要保證其他的頭文件存在這個數據類型定義即可

?

??? g)源文件引用頭文件的時候需要注意頭文件的順序,有的時候順序變了,可能編譯就失敗了。原因就是之前后面頭文件中定義的數據類型找不到出處了

?

??? h)某些工程沒有把頭文件和源文件綁定在一起,修改頭文件必須刪除工程重新編譯

?

??? i)頭文件的存在只是為了源文件才存在的,如果沒有必要不要寫頭文件。要寫,影響范圍也要控制在最小的范圍內

?

??? j)如果頭文件定義了數據結構,那么需要嵌入引用頭文件,反之如果只是指針,聲明一下即可,比如說

[cpp] view plaincopy
  1. struct?_Data;??
  2. typedef?struct?_Data?Data;??

?

?? k)如果有可能經常整理自己的頭文件,全部刪除再一個一個添加,這樣就知道哪些是我們需要的,哪些不是

?

??? l)對于某些宏,如果不確定文件本身是在哪里定義的,可以在源文件中再定義一次,這樣編譯器就會精確提示我們原來這個宏是在那里定義的

??? 好了,差不多就這么多了。

?

嵌入式操作系統內核原理和開發(內存分配算法)

內存分配是操作系統必須面對的一個環節,除非這個系統本身不需要內存安排,所有業務可以通過全局數據和堆棧搞定。內存分配其實不困難,但是由內存引申出來的東西就比較復雜了。早前沒有MMU,系統本身的空間和用戶空間沒有優先級之分,所以不同的程序之間的內存都是共享的,相互影響也是不可避免的。所以,一般來說,除了內存分配之外,還需要一些日志信息、檢測信息幫助我們進行調試和分析。當然,這些都不是我們關心的內容,我們關注的就是內存有哪些通用的分配算法。

?

??? (1)固定內存分配

??? 固定內存分配算法是最簡單的算法,也是最好理解的算法。比如說有16M內存,現在我們假設分配的基本內存是4K,那么總共有16M/4K = 4K個單元。所以,如果用戶想申請內存,最多就是4K次。如果用戶想要多一點內存,那么系統把相鄰的內存分給用戶使用即可。

?

??? (2)鏈表內存分配

??? 固定內存分配雖然好,但是還有一個缺點,那就是如果存在很多的浪費機會。試想一下,如果用戶只要幾十個byte,那么也要分配給它4K個字節,浪費的空間超過了99%。所以在此基礎之上,我們提出了鏈表內存算法。鏈表算法中保存有空閑結點,內存釋放的時候,那么內存查到空閑結點,該合并合并,該釋放的釋放;當然如果要申請內存的話,那方法就多了去了,可以最差申請、最優申請、最好申請,這些都是可以的。

?

??? (3)伙伴算法

??? 鏈表算法相比較固定內存算法,可以節省不少內存。但是鏈表算法本身有一個特點,那就是容易形成內存碎片。所以,我們可以結合固定分配和鏈表算法的特點,把內存分配成8、16、32、64、128、256、512大小的幾種鏈表。鏈表內部的大小都是相同的,鏈表之間是倍數的關系。分配內存的時候,我們首先尋找最合適的鏈表,然后分配內存,如果內存空間不夠,可以向高一級的內存鏈表申請,這樣拆解下來的內存可以分配到低一級別的鏈表;釋放內存的時候,我們也要注意內存的合并和組合。

?

??? (4)基于內存池的伙伴算法

??? 伙伴算法固然好,但是如果某一種內存申請特別頻繁,那么在伙伴算法中就需要進行反復的拆分和合并處理。一方面,這會影響了內存的分配效率,另外一方面也比較容易造成內存的分配碎片。所以,我們可以在伙伴算法的基礎之上構建一個內存池,在內存釋放的時候,只是標注當前內存不再使用,但是并沒有真正釋放,等到內存池中所有的內存都不再使用的時候再進行釋放,這在一定的程度上會提高內存的分配效率。特別是系統運行一段時間后,這種效果是特別明顯的。

?

??? (5)工作集算法

??? 工作集的算法本質上說不是一種算法,它只是一種基本思想。我們知道,在系統穩定之后,內存中分配的大小、配置的比例關系都是相對固定的,變化不是特別大。如果我們可以把這些數據給記錄下來,在系統啟動的時候預先分配好這些內存,那么不就可以提升系統的啟動速度了嗎?當然工作集中的參數設定更多的是一種經驗值,它需要我們綜合各種因素進行分析,反復比較才會得出比較好的結果。

?

??? 這五種算法只是給出了基本思想,只有付出于實踐,多加操練才能從中有所收獲。

嵌入式操作系統內核原理和開發(基于鏈表節點的內存分配算法)

鏈接節點的內存分配方法,其實就是一種按需分配的內存分配方法。簡單一點說,就是你需要多少內存,我就給你多少內存。當然,為了把分配的內存都連起來,我們還需要對分配節點進行管理記錄。就比如下面這個數據結構,
[cpp] view plaincopy
  1. typedef?struct?_MNG_NODE??
  2. {??
  3. ????struct?_MNG_NODE*?next;??
  4. ????unsigned?int?size;??
  5. }MNG_NODE;??
??? 其中,next節點記錄了下面一個節點的位置,size表示了當前節點下方的內存大小。在內存初始化的時候,我們默認起始內存塊就是一個大節點,其中前面8個字節就是上面的內容。此時,如果需要進行內存拆分,我們就可以把一個節點拆分成兩個節點,就比如這樣,
[cpp] view plaincopy
  1. pNew?=?(MNG_NODE*)((char*)pOld?+?sizeof(MNG_NODE)?+?pOld->size?-?(sizeof(MNG_NODE)?+?size));??
??? pNew代表了新生成的結點,pOld代表了原來的節點。為了不影響原來的節點,每次分配新節點的時候必須在內存的高端位置進行分配。這樣也保證了原來節點結構和數據的連貫性。當然分配結束之后,只需要對節點的的size重新進行一下賦值就可以了。
[cpp] view plaincopy
  1. pNew->size?=?size;??
  2. pOld->size?-=?sizeof(MNG_NODE)?+?size;??
??? 此時pOld節點會歸還到pFreeList當中,而生成的pNew節點則插入到pAllocList當中。其中,pFreeList表示當前的空閑節點,pAllocList表示已分配節點。相比較而言,內存的釋放就比較簡單,只需要把節點找出來插入到pAllocList中即可。當然,此時如果能做一下前后節點的合并工作就更好了。

??? 不過,上面描述的步驟只是就比較重要知識點講解了一下。在真正設計代碼的過程中還需要考慮到節點的查找、內存大小的判斷、數據初始化、鏈表插入、測試用例編寫等工作,步驟會稍微繁瑣一下。下面我們就給出完整的代碼示例。

[cpp] view plaincopy
  1. /*************************************************?
  2. *??????malloc?&?free?in?link?node?algorithm?
  3. **************************************************/??
  4. ??
  5. #include?<string.h>??
  6. #include?<malloc.h>??
  7. ??
  8. /*************************************************?
  9. *???????????struct?definition?
  10. **************************************************/??
  11. ??
  12. typedef?struct?_MNG_NODE??
  13. {??
  14. ????struct?_MNG_NODE*?next;??
  15. ????unsigned?int?size;??
  16. }MNG_NODE;??
  17. ??
  18. ??
  19. /*************************************************?
  20. *???????????global?variable?declaration?
  21. **************************************************/??
  22. ??
  23. #define?MEM_BUFFER_LENGTH???(0x1?<<?24)??
  24. ??
  25. static?void*?pGlbData;??
  26. static?MNG_NODE*?pFreeList;??
  27. static?MNG_NODE*?pAllocList;??
  28. ??
  29. /*************************************************?
  30. *?function:?add?node?into?headlist?
  31. **************************************************/??
  32. ??
  33. static?void?add_node_into_list_head(MNG_NODE*?pNode,?MNG_NODE**?ppList)??
  34. {??
  35. ????pNode->next?=?*ppList;??
  36. ????*ppList?=?pNode;?????
  37. }??
  38. ??
  39. ??
  40. /*************************************************?
  41. *?function:?find?best?fit?node?
  42. **************************************************/??
  43. ??
  44. static?MNG_NODE*?find_best_fit_node(unsigned?int?size)??
  45. {??
  46. ????MNG_NODE*?pCur?=?pFreeList;??
  47. ????MNG_NODE*?pPre?=?pCur;??
  48. ??
  49. ????while(pCur?&&?pCur->size?<?(size?+?sizeof(MNG_NODE)))??
  50. ????{???
  51. ????????pPre?=?pCur;??
  52. ????????pCur?=?pCur->next;??
  53. ????}?????
  54. ??
  55. ????if(NULL?==?pCur)??
  56. ????????return?NULL;??
  57. ??
  58. ????if(pFreeList?==?pCur)??
  59. ????????pFreeList?=?pFreeList->next;??
  60. ????else??
  61. ????????pPre->next?=?pCur->next;??
  62. ??
  63. ????return?pCur;??
  64. }??
  65. ??
  66. ??
  67. /*************************************************?
  68. *?function:?implement?memory?allocation?
  69. **************************************************/??
  70. ??
  71. static?void*?_mem_malloc(unsigned?int?size)??
  72. {??
  73. ????MNG_NODE*?pOld;??
  74. ????MNG_NODE*?pNew;??
  75. ??
  76. ????pOld?=?find_best_fit_node(size);??
  77. ????if(NULL?==?pOld)??
  78. ????????return?NULL;??
  79. ??
  80. ????pNew?=?(MNG_NODE*)((char*)pOld?+?sizeof(MNG_NODE)?+?pOld->size?-?(sizeof(MNG_NODE)?+?size));??
  81. ????pNew->size?=?size;??
  82. ????pOld->size?-=?sizeof(MNG_NODE)?+?size;??
  83. ??
  84. ????add_node_into_list_head(pOld,?&pFreeList);??
  85. ????add_node_into_list_head(pNew,?&pAllocList);??
  86. ??
  87. ????return?(void*)((char*)pNew?+?sizeof(MNG_NODE));??
  88. }??
  89. ??
  90. ??
  91. /*************************************************?
  92. *?function:?memory?allocation?
  93. **************************************************/??
  94. ??
  95. void*?mem_malloc(unsigned?int?size)??
  96. {??
  97. ????if(0?==?size)??
  98. ????????return?NULL;??
  99. ??
  100. ????if(size?>?(MEM_BUFFER_LENGTH?-?sizeof(MNG_NODE)))??
  101. ????????return?NULL;??
  102. ??
  103. ????return?_mem_malloc(size);??
  104. }??
  105. ??
  106. ??
  107. /*************************************************?
  108. *?function:?find?previous?node??
  109. **************************************************/??
  110. ??
  111. static?MNG_NODE*?find_previous_node(MNG_NODE*?pNode)??
  112. {??
  113. ????MNG_NODE*?pFind?=?pAllocList;??
  114. ????MNG_NODE*?pPre?=?NULL;??
  115. ??????
  116. ????while(pFind?&&?pFind?!=?pNode)??
  117. ????{??
  118. ????????pPre?=?pFind;??
  119. ????????pFind?=?pFind->next;??
  120. ????}??
  121. ??
  122. ????if(NULL?==?pFind)??
  123. ????????return?NULL;??
  124. ??
  125. ????return?pPre;??
  126. }??
  127. ??
  128. ??
  129. /*************************************************?
  130. *?function:?implement?memory?free?
  131. **************************************************/??
  132. ??
  133. static?void?_mem_free(MNG_NODE*?pNode)??
  134. {??
  135. ????MNG_NODE*?pPreNode;??
  136. ??
  137. ????if(pNode?==?pAllocList)??
  138. ????{??
  139. ????????pAllocList?=?pAllocList->next;??
  140. ????????add_node_into_list_head(pNode,?&pFreeList);??
  141. ????????return;??
  142. ????}??????
  143. ??
  144. ????pPreNode?=?find_previous_node(pNode);??
  145. ????if(NULL?==?pPreNode)??
  146. ????????return;??
  147. ??
  148. ????pPreNode->next?=?pNode->next;??
  149. ????add_node_into_list_head(pNode,?&pFreeList);??
  150. ????return;??
  151. }??
  152. ??
  153. ??
  154. /*************************************************?
  155. *?function:?free?memory?function?
  156. **************************************************/??
  157. ??
  158. void?mem_free(void*?pData)??
  159. {??
  160. ????if(NULL?==?pData)??
  161. ????????return;??
  162. ??
  163. ????if(pData?<?pGlbData?||?pData?>=?(void*)((char*)pGlbData?+?MEM_BUFFER_LENGTH))??
  164. ????????return;??
  165. ??
  166. ????_mem_free(pData?-?sizeof(MNG_NODE));??
  167. }??
  168. ??
  169. ??
  170. /*************************************************?
  171. *?function:?get?memory?buffer?
  172. **************************************************/??
  173. ??
  174. void?mem_init()??
  175. {??
  176. ????pGlbData?=?(void*)malloc(MEM_BUFFER_LENGTH);??
  177. ????if(NULL?==?pGlbData)??
  178. ????????return;??
  179. ??
  180. ????memset(pGlbData,?0,?MEM_BUFFER_LENGTH);??
  181. ????pFreeList?=?(MNG_NODE*)pGlbData;??
  182. ????pFreeList->size?=?MEM_BUFFER_LENGTH?-?sizeof(MNG_NODE);??
  183. ????pAllocList?=?NULL;??
  184. }??
  185. ??
  186. ??
  187. /*************************************************?
  188. *?function:?free?memory?buffer?
  189. **************************************************/??
  190. ??
  191. void?mem_exit()??
  192. {??
  193. ????if(NULL?!=?pGlbData)??
  194. ????????free(pGlbData);?????
  195. ??
  196. ????pFreeList?=?NULL;??
  197. ????pAllocList?=?NULL;??
  198. }??
  199. ??
  200. ??
  201. /*************************************************?
  202. *?function:?file?starts?here?
  203. **************************************************/??
  204. ??
  205. int?main(int?argc,?char*?argv[])??
  206. {??
  207. ????mem_init();??
  208. ????mem_exit();??
  209. ????return?1;??
  210. }?

嵌入式操作系統內核原理和開發(最快、最優、最差內存分配算法)

前面我們說到了基于 鏈表的內存分配算法。但是之前我們也說過,其實內存分配一般有三個原則,最快、最優和最差。最快比較好理解,就是尋找到合適的節點就立即分配內存,我們在前面一篇博客采用的就是這個方法。最優呢,就是尋找可以滿足當前內存分配的最小節點,這樣不會有很大的浪費,但是有可能會產生碎片節點。最后一種就是最差分配算法,說是最差效果未必最差。因為在大的內存分配的時候至少不會很快產生內存碎片,對整個系統的穩定來說有可能是好事。所以這三種方法很難說哪一種好,哪一種不好,需要結合具體的應用場景客觀進行分析。不過話說回來,內存碎片是無論如何都避免不了的。

?

??? 首先,為了靈活對這三種分配算法進行配置,我們定義了宏開關,需要哪個就把那個開關放開。暫時默認打開的算法的是最快分配算法。

[cpp] view plaincopy
  1. #define?MAX_SPEED_MALLOC???1??
  2. #define?MIN_SIZE_MALLOC????0??
  3. #define?MAX_SIZE_MALLOC????0??

??? 因為之前已經討論過最快分配算法,所以這里著重討論的最優分配算法和最差分配算法。又由于兩者的差別極小,所以單獨分析其中一種算法也行。就拿最優分配算法來說,為了尋找到最小的節點,我們需要對整個鏈表進行遍歷,這個還是比較消耗時間的。

[cpp] view plaincopy
  1. while(pCur)??
  2. {???
  3. ????if(pCur->size?>?(size?+?sizeof(MNG_NODE)))??
  4. ????{??
  5. ????????if(NULL?==?pFind?||?pFind->size?>?pCur->size)??
  6. ????????{??
  7. ????????????pFind?=?pCur;??
  8. ????????}??
  9. ????}??
  10. ??
  11. ????pPre?=?pCur;??
  12. ????pCur?=?pCur->next;??
  13. }?????

??? 尋找到pFind這個我們需要的節點之后,還需要從pFreeList中刪除該節點。所以,我們需要進一步的判斷和分析,

[cpp] view plaincopy
  1. if(NULL?==?pFind)??
  2. ????return?NULL;??
  3. ??
  4. pPre?=?find_previous_node_in_list(pFind,?pFreeList);??
  5. if(NULL?==?pPre)??
  6. ????pFreeList?=?pFreeList->next;??
  7. else??
  8. ????pPre->next?=?pFind->next;??
  9. ??
  10. return?pFind;??

????? 首先判斷pFind前面有沒有節點,如果沒有表示pFreeList就是pFind,那么pFreeList需要自行向后退縮;當然如果當前的pFind節點是有前節點的,那么只需要把前節點的next指針重新更改一下即可。當然,這里還對原來的查找節點函數作了一下修改,使之更合理更通用。

[cpp] view plaincopy
  1. /*************************************************?
  2. *?function:?find?previous?node??
  3. **************************************************/??
  4. ??
  5. MNG_NODE*?find_previous_node_in_list(MNG_NODE*?pNode,?MNG_NODE*?pList)??
  6. {??
  7. ????MNG_NODE*?pFind?=?pList;??
  8. ????MNG_NODE*?pPre?=?NULL;??
  9. ??????
  10. ????while(pFind?&&?pFind?!=?pNode)??
  11. ????{??
  12. ????????pPre?=?pFind;??
  13. ????????pFind?=?pFind->next;??
  14. ????}??
  15. ??
  16. ????if(NULL?==?pFind)??
  17. ????????return?NULL;??
  18. ??
  19. ????return?pPre;??
  20. }??

??? 上面也只是說了個大概,具體的內容可以參見下面的源代碼。既可以在VC上編譯,也可以在GCC上面編譯,都沒有問題。當然,如果本地os沒有編譯器,可以選擇網上在線編譯,也是個不錯的選擇。

[cpp] view plaincopy
  1. /*************************************************?
  2. *??????malloc?&?free?in?link?node?algorithm?
  3. **************************************************/??
  4. ??
  5. #include?<string.h>??
  6. #include?<malloc.h>??
  7. ??
  8. /*************************************************?
  9. *???????????struct?definition?
  10. **************************************************/??
  11. ??
  12. typedef?struct?_MNG_NODE??
  13. {??
  14. ????struct?_MNG_NODE*?next;??
  15. ????unsigned?int?size;??
  16. }MNG_NODE;??
  17. ??
  18. ??
  19. /*************************************************?
  20. *??????????macro?declaration?
  21. **************************************************/??
  22. ??
  23. #define?MAX_SPEED_MALLOC???1??
  24. #define?MIN_SIZE_MALLOC????0??
  25. #define?MAX_SIZE_MALLOC????0??
  26. ??
  27. #define?MEM_BUFFER_LENGTH???(0x1?<<?24)??
  28. ??
  29. ??
  30. /*************************************************?
  31. *???????????global?variable?declaration?
  32. **************************************************/??
  33. ??
  34. static?void*?pGlbData;??
  35. static?MNG_NODE*?pFreeList;??
  36. static?MNG_NODE*?pAllocList;??
  37. ??
  38. ??
  39. /*************************************************?
  40. *???????????function?declaration?
  41. **************************************************/??
  42. ??
  43. MNG_NODE*?find_previous_node_in_list(MNG_NODE*?pNode,?MNG_NODE*?pList);??
  44. ??
  45. ??
  46. /*************************************************?
  47. *?function:?add?node?into?headlist?
  48. **************************************************/??
  49. ??
  50. static?void?add_node_into_list_head(MNG_NODE*?pNode,?MNG_NODE**?ppList)??
  51. {??
  52. ????pNode->next?=?*ppList;??
  53. ????*ppList?=?pNode;?????
  54. }??
  55. ??
  56. ??
  57. #if?MAX_SPEED_MALLOC??
  58. /*************************************************?
  59. *?function:?find?best?fit?node?in?max_speed?
  60. **************************************************/??
  61. ??
  62. static?MNG_NODE*?find_best_fit_node(unsigned?int?size)??
  63. {??
  64. ????MNG_NODE*?pFind?=?pFreeList;??
  65. ????MNG_NODE*?pPre?=?pFind;??
  66. ??
  67. ????while(pFind?&&?pFind->size?<?(size?+?sizeof(MNG_NODE)))??
  68. ????{???
  69. ????????pPre?=?pFind;??
  70. ????????pFind?=?pFind->next;??
  71. ????}?????
  72. ??
  73. ????if(NULL?==?pFind)??
  74. ????????return?NULL;??
  75. ??
  76. ????if(pFreeList?==?pFind)??
  77. ????????pFreeList?=?pFreeList->next;??
  78. ????else??
  79. ????????pPre->next?=?pFind->next;??
  80. ??
  81. ????return?pFind;??
  82. }??
  83. #endif??
  84. ??
  85. ??
  86. #if?MIN_SIZE_MALLOC??
  87. /*************************************************?
  88. *?function:?find?best?fit?node?in?min?size?
  89. **************************************************/??
  90. ??
  91. MNG_NODE*?find_best_fit_node(unsigned?int?size)??
  92. {??
  93. ????MNG_NODE*?pCur?=?pFreeList;??
  94. ????MNG_NODE*?pPre?=?pCur;??
  95. ????MNG_NODE*?pFind?=?NULL;??
  96. ??
  97. ????while(pCur)??
  98. ????{???
  99. ????????if(pCur->size?>?(size?+?sizeof(MNG_NODE)))??
  100. ????????{??
  101. ????????????if(NULL?==?pFind?||?pFind->size?>?pCur->size)??
  102. ????????????{??
  103. ????????????????pFind?=?pCur;??
  104. ????????????}??
  105. ????????}??
  106. ??
  107. ????????pPre?=?pCur;??
  108. ????????pCur?=?pCur->next;??
  109. ????}?????
  110. ??
  111. ????if(NULL?==?pFind)??
  112. ????????return?NULL;??
  113. ??
  114. ????pPre?=?find_previous_node_in_list(pFind,?pFreeList);??
  115. ????if(NULL?==?pPre)??
  116. ????????pFreeList?=?pFreeList->next;??
  117. ????else??
  118. ????????pPre->next?=?pFind->next;??
  119. ??
  120. ????return?pFind;??
  121. }??
  122. #endif??
  123. ??
  124. ??
  125. #if?MAX_SIZE_MALLOC??
  126. /*************************************************?
  127. *?function:?find?best?fit?node?in?max?size?
  128. **************************************************/??
  129. ??
  130. MNG_NODE*?find_best_fit_node(unsigned?int?size)??
  131. {??
  132. ????MNG_NODE*?pCur?=?pFreeList;??
  133. ????MNG_NODE*?pPre?=?pCur;??
  134. ????MNG_NODE*?pFind?=?NULL;??
  135. ??
  136. ????while(pCur)??
  137. ????{???
  138. ????????if(pCur->size?>?(size?+?sizeof(MNG_NODE)))??
  139. ????????{??
  140. ????????????if(NULL?==?pFind?||?pFind->size?<?pCur->size)??
  141. ????????????{??
  142. ????????????????pFind?=?pCur;??
  143. ????????????}??
  144. ????????}??
  145. ??
  146. ????????pPre?=?pCur;??
  147. ????????pCur?=?pCur->next;??
  148. ????}?????
  149. ??
  150. ????if(NULL?==?pFind)??
  151. ????????return?NULL;??
  152. ??
  153. ????pPre?=?find_previous_node_in_list(pFind,?pFreeList);??
  154. ????if(NULL?==?pPre)??
  155. ????????pFreeList?=?pFreeList->next;??
  156. ????else??
  157. ????????pPre->next?=?pFind->next;??
  158. ??
  159. ????return?pFind;??
  160. }??
  161. #endif??
  162. ??
  163. ??
  164. /*************************************************?
  165. *?function:?implement?memory?allocation?
  166. **************************************************/??
  167. ??
  168. static?void*?_mem_malloc(unsigned?int?size)??
  169. {??
  170. ????MNG_NODE*?pOld;??
  171. ????MNG_NODE*?pNew;??
  172. ??
  173. ????pOld?=?find_best_fit_node(size);??
  174. ????if(NULL?==?pOld)??
  175. ????????return?NULL;??
  176. ??
  177. ????pNew?=?(MNG_NODE*)((char*)pOld?+?sizeof(MNG_NODE)?+?pOld->size?-?(sizeof(MNG_NODE)?+?size));??
  178. ????pNew->size?=?size;??
  179. ????pOld->size?-=?sizeof(MNG_NODE)?+?size;??
  180. ??
  181. ????add_node_into_list_head(pOld,?&pFreeList);??
  182. ????add_node_into_list_head(pNew,?&pAllocList);??
  183. ??
  184. ????return?(void*)((char*)pNew?+?sizeof(MNG_NODE));??
  185. }??
  186. ??
  187. ??
  188. /*************************************************?
  189. *?function:?memory?allocation?
  190. **************************************************/??
  191. ??
  192. void*?mem_malloc(unsigned?int?size)??
  193. {??
  194. ????if(0?==?size)??
  195. ????????return?NULL;??
  196. ??
  197. ????if(size?>?(MEM_BUFFER_LENGTH?-?sizeof(MNG_NODE)))??
  198. ????????return?NULL;??
  199. ??
  200. ????return?_mem_malloc(size);??
  201. }??
  202. ??
  203. ??
  204. /*************************************************?
  205. *?function:?find?previous?node??
  206. **************************************************/??
  207. ??
  208. MNG_NODE*?find_previous_node_in_list(MNG_NODE*?pNode,?MNG_NODE*?pList)??
  209. {??
  210. ????MNG_NODE*?pFind?=?pList;??
  211. ????MNG_NODE*?pPre?=?NULL;??
  212. ??????
  213. ????while(pFind?&&?pFind?!=?pNode)??
  214. ????{??
  215. ????????pPre?=?pFind;??
  216. ????????pFind?=?pFind->next;??
  217. ????}??
  218. ??
  219. ????if(NULL?==?pFind)??
  220. ????????return?NULL;??
  221. ??
  222. ????return?pPre;??
  223. }??
  224. ??
  225. ??
  226. /*************************************************?
  227. *?function:?implement?memory?free?
  228. **************************************************/??
  229. ??
  230. static?void?_mem_free(MNG_NODE*?pNode)??
  231. {??
  232. ????MNG_NODE*?pPreNode;??
  233. ??
  234. ????if(pNode?==?pAllocList)??
  235. ????{??
  236. ????????pAllocList?=?pAllocList->next;??
  237. ????????add_node_into_list_head(pNode,?&pFreeList);??
  238. ????????return;??
  239. ????}??????
  240. ??
  241. ????pPreNode?=?find_previous_node_in_list(pNode,?pAllocList);??
  242. ????if(NULL?==?pPreNode)??
  243. ????????return;??
  244. ??
  245. ????pPreNode->next?=?pNode->next;??
  246. ????add_node_into_list_head(pNode,?&pFreeList);??
  247. ????return;??
  248. }??
  249. ??
  250. ??
  251. /*************************************************?
  252. *?function:?free?memory?function?
  253. **************************************************/??
  254. ??
  255. void?mem_free(void*?pData)??
  256. {??
  257. ????if(NULL?==?pData)??
  258. ????????return;??
  259. ??
  260. ????if(pData?<?pGlbData?||?pData?>=?(void*)((char*)pGlbData?+?MEM_BUFFER_LENGTH))??
  261. ????????return;??
  262. ??
  263. ????_mem_free((MNG_NODE*)((char*)pData?-?sizeof(MNG_NODE)));??
  264. }??
  265. ??
  266. ??
  267. /*************************************************?
  268. *?function:?get?memory?buffer?
  269. **************************************************/??
  270. ??
  271. void?mem_init()??
  272. {??
  273. ????pGlbData?=?(void*)malloc(MEM_BUFFER_LENGTH);??
  274. ????if(NULL?==?pGlbData)??
  275. ????????return;??
  276. ??
  277. ????memset(pGlbData,?0,?MEM_BUFFER_LENGTH);??
  278. ????pFreeList?=?(MNG_NODE*)pGlbData;??
  279. ????pFreeList->size?=?MEM_BUFFER_LENGTH?-?sizeof(MNG_NODE);??
  280. ????pAllocList?=?NULL;??
  281. }??
  282. ??
  283. ??
  284. /*************************************************?
  285. *?function:?free?memory?buffer?
  286. **************************************************/??
  287. ??
  288. void?mem_exit()??
  289. {??
  290. ????if(NULL?!=?pGlbData)??
  291. ????????free(pGlbData);?????
  292. ??
  293. ????pFreeList?=?NULL;??
  294. ????pAllocList?=?NULL;??
  295. }??
  296. ??
  297. ??
  298. /*************************************************?
  299. *?function:?file?starts?here?
  300. **************************************************/??
  301. ??
  302. int?main(int?argc,?char*?argv[])??
  303. {??
  304. ????mem_init();??
  305. ????mem_exit();??
  306. ????return?1;??
  307. }?

嵌入式操作系統內核原理和開發(信號量)

之前因為工作的原因,操作系統這塊一直沒有繼續寫下去。一方面是自己沒有這方面的經歷,另外一方面就是操作系統比較復雜和瑣碎,調試起來比較麻煩。目前在實際項目中,使用的實時操作系統很多,很多國內的朋友也寫過操作系統,有些項目現在還在維護和修改中,這是十分難得的。就我知道和熟悉的就有三個系統,比如

?

???? (1)RT-THREAD

???? (2)RAW-OS

???? (3)ClearRTOS

?

??? 這里有比較介紹一下,這三個系統是國內的三位朋友開發的。其中rt-thread時間比較久一點,模塊也比較全,bsp、cpu、fs、lwip、gui等輔助的代碼也比較多,有興趣的朋友可以到網站上面下載代碼看一看。raw-os是我今年才發現的一個實時系統,從網站的注冊時間和軟件版本號上來看,系統開發的時間不是很長,不過整個系統代碼的結構非常清晰,是我重點推薦閱讀的代碼。如果朋友們自己download下來,好好看一下其中的代碼,肯定會有不少的收獲。最后一個代碼是作者李云在編寫《專業嵌入式軟件開發》這本書的時候,為了說明os的基本原理而開發的軟件,前后設計了線程、互斥、內存、定時器、驅動框架等內容,值得一讀。

?

??? 當然有了這么多優秀的代碼,我覺得現在自己的工作就不是重新造一個車輪了,而是和大家分享這些優秀的代碼是如何設計的。理解代碼本身不是目的,關鍵是理解代碼背后的基本思路。就我個人看過來,rt-thread和raw-os都可以用來學習,不過raw-os更好一些,主要是因為作者將raw-os移植到的vc上面,學起來也十分方便,要是個人在使用過程中有什么疑問,可以通過郵件和作者及時交流。不過由于raw-os的版本在一直在update之中,所以部分代碼在前后稍微有點差別,不過這些都不是重點,暫時不了解的內容可以通過后面的了解和學習逐步掌握,不會成為太大的障礙。

?

??? 就像今天的題目一樣,我們重點介紹一下信號量的設計原理。首先看一下信號量的數據結構是怎么樣的,

[cpp] view plaincopy
  1. typedef?struct?RAW_SEMAPHORE??
  2. {???
  3. ????RAW_COMMON_BLOCK_OBJECT???????common_block_obj;??
  4. ????RAW_U32???????????????????????count;??
  5. ??????
  6. }?RAW_SEMAPHORE;??


??? 這些代碼都是從raw-os上面摘抄下來的,這個版本是0.94版本,和最新的0.96c版本有點差別。首先分析一下信號量的基本結構,其實非常簡單,就兩個變量,其中comm_block_obj是一個通用類型,記錄了當前結構的名稱、類型和阻塞隊列,而count就是計數,判斷是否還有釋放的資源。

?

??? 說到了信號量的操作,無非就是信號量的創建、獲取、釋放、刪除操作,當然這里作者考慮的比較詳細,在信號量釋放的時候還分成了?WAKE_ONE_SEM和WAKE_ALL_SEM兩種類型。意思很簡單,就是當信號量來臨的時候是喚醒一個等待線程呢,還是喚醒所有的等待線程呢,就是這么回事。下面,我們就按照順序介紹這幾個函數,首先是創建函數,

[cpp] view plaincopy
  1. RAW_U16?raw_semaphore_create(RAW_SEMAPHORE?*semaphore_ptr,?RAW_U8?*name_ptr,?RAW_U32?initial_count)??
  2. {??
  3. ????#if?(RAW_SEMA_FUNCTION_CHECK?>?0)??
  4. ??????
  5. ????if?(semaphore_ptr?==?0)?{??
  6. ??????????
  7. ????????return?RAW_NULL_OBJECT;??
  8. ????}??
  9. ??
  10. ????if?(initial_count?==?0xffffffff)?{??
  11. ??
  12. ????????return?RAW_SEMOPHORE_OVERFLOW;??
  13. ??
  14. ????}??
  15. ??????
  16. ????#endif??
  17. ??
  18. ????/*Init?the?list*/??
  19. ????list_init(&semaphore_ptr->common_block_obj.block_list);??
  20. ??????
  21. ????/*Init?resource*/??
  22. ????semaphore_ptr->count?????=?initial_count;???????????????????????????????????
  23. ??????
  24. ????semaphore_ptr->common_block_obj.name?=?name_ptr;????
  25. ??????
  26. ????semaphore_ptr->common_block_obj.block_way?=?0;??
  27. ??????
  28. ????return?RAW_SUCCESS;??
  29. ??
  30. }??

??? 看著初始化函數,我們發現信號量的初始化其實也非常簡單,基本工作主要有:

??? (1)判斷參數合法性;

????(2)初始化阻塞隊列、名稱等;

??? (3)初始化信號量的計數。

?

??? 說完了這些,我們看看信號量的獲取是怎么完成的,代碼可能長度稍微長一些,不過也不用太緊張,

[cpp] view plaincopy
  1. RAW_U16?raw_semaphore_get(RAW_SEMAPHORE?*semaphore_ptr,??RAW_U32?wait_option)??
  2. {??
  3. ??
  4. ????RAW_U16?error_status;??
  5. ??
  6. ????RAW_SR_ALLOC();??
  7. ??
  8. ????#if?(RAW_SEMA_FUNCTION_CHECK?>?0)??
  9. ??
  10. ????if?(semaphore_ptr?==?0)?{??
  11. ??????????
  12. ????????return?RAW_NULL_OBJECT;??
  13. ????}??
  14. ??????
  15. ????if?(raw_int_nesting)?{??
  16. ??
  17. ????????return?RAW_NOT_CALLED_BY_ISR;??
  18. ????}??
  19. ??
  20. ????#endif??
  21. ??????
  22. ??????
  23. ????RAW_CRITICAL_ENTER();??
  24. ????if?(semaphore_ptr->count)?{????????????????????????
  25. ????????semaphore_ptr->count--;?????????????????????????????????????????
  26. ??
  27. ????????RAW_CRITICAL_EXIT();??
  28. ??????????
  29. ????????return?RAW_SUCCESS;??
  30. ????}??
  31. ??????
  32. ????/*Cann't?get?semphore,?and?return?immediately?if?wait_option?is??RAW_NO_WAIT*/??
  33. ????if?(wait_option?==?RAW_NO_WAIT)?{???
  34. ??
  35. ????????RAW_CRITICAL_EXIT();??
  36. ????????return?RAW_NO_PEND_WAIT;??
  37. ????}????????
  38. ??????
  39. ????if?(raw_sched_lock)?{?????
  40. ????????RAW_CRITICAL_EXIT();??????
  41. ????????return?RAW_SCHED_DISABLE;??
  42. ????}??
  43. ??
  44. ????raw_pend_object(&semaphore_ptr->common_block_obj,?raw_task_active,?wait_option);??
  45. ????RAW_CRITICAL_EXIT();??
  46. ??
  47. ????raw_sched();???
  48. ??????
  49. ????error_status?=?block_state_post_process(raw_task_active,?0);??
  50. ????return?error_status;??
  51. ??
  52. }??

??? 信號量的獲取情況比較復雜一些,這在長度上也體現出來了。不過沒關系,我們一步一步看函數做了什么,

??? (1)判斷參數合法性;

??? (2)判斷當前函數是否處于中斷處理的流程中,如果是選擇返回;

??? (3)判斷當前count是否為0,如果不為 0,則減1返回;

??? (4)如果當前count是0,且線程不愿意等待,那么選擇返回;

??? (5)如果當前禁止調度,那么依然選擇返回;

???? (6)當前線程將自己掛起,從ready隊列中刪除,把自己pend到信號量的阻塞隊列中;

???? (7)阻塞的線程再次獲得了運行的機會,我們從task數據結構獲得返回結果,此時也不一定是因為獲得了資源的緣故哦。

?

??? 上面的get函數看上去比較復雜,但是所有的同步函數基本上都是這樣設計的,看多了反而有一種八股文的感覺。剛開始看的同學可能覺得不是很習慣。不要緊,每天多看兩眼,時間長了就ok了。好了,接著我們繼續去看看信號量的釋放函數是怎么處理的,大家做好心理準備哦,

[cpp] view plaincopy
  1. static?RAW_U16?internal_semaphore_put(RAW_SEMAPHORE?*semaphore_ptr,?RAW_U8?opt_wake_all)??
  2. {??
  3. ????LIST?*block_list_head;??
  4. ??????
  5. ????RAW_SR_ALLOC();??
  6. ??
  7. ????#if?(RAW_SEMA_FUNCTION_CHECK?>?0)??
  8. ??????
  9. ????if?(semaphore_ptr?==?0)?{??
  10. ??????????
  11. ????????return?RAW_NULL_OBJECT;??
  12. ????}??
  13. ??????
  14. ????#endif??
  15. ??
  16. ????block_list_head?=?&semaphore_ptr->common_block_obj.block_list;??
  17. ??????
  18. ????RAW_CRITICAL_ENTER();??
  19. ????/*if?no?block?task?on?this?list?just?return*/??
  20. ????if?(is_list_empty(block_list_head))?{??????????
  21. ??????????
  22. ????????if?(semaphore_ptr->count?==?0xffffffff)?{??
  23. ??
  24. ????????????RAW_CRITICAL_EXIT();??
  25. ????????????return?RAW_SEMOPHORE_OVERFLOW;??
  26. ??
  27. ????????}??
  28. ????????/*increase?resource*/??
  29. ????????semaphore_ptr->count++;????????????????????????????????????????
  30. ??????????
  31. ????????RAW_CRITICAL_EXIT();??
  32. ????????return?RAW_SUCCESS;??
  33. ????}??
  34. ??
  35. ????/*wake?all?the?task?blocked?on?this?semphore*/??
  36. ????if?(opt_wake_all)?{??
  37. ??
  38. ????????while?(!is_list_empty(block_list_head))?{??
  39. ????????????raw_wake_object(list_entry(block_list_head->next,?RAW_TASK_OBJ,?task_list));??
  40. ????????}??
  41. ??
  42. ????}??
  43. ??
  44. ????else?{??
  45. ??????????
  46. ????????/*Wake?up?the?highest?priority?task?block?on?the?semaphore*/??
  47. ????????raw_wake_object(list_entry(block_list_head->next,?RAW_TASK_OBJ,?task_list));??
  48. ????}??
  49. ??????
  50. ????RAW_CRITICAL_EXIT();??
  51. ??
  52. ????raw_sched();??????
  53. ??
  54. ????return?RAW_SUCCESS;??
  55. }??

??? 看上去,信號量的釋放函數也比較長,不過只要有耐心,都是可以看明白的,我們就來具體分析一下,

??? (1)判斷參數的合法性;

????(2)判斷當前是否有等待隊列,如果沒有,則count自增,函數返回,當然如果count達到了0xffffffff也要返回,不過概率極低;

??? (3)?當前存在等待隊列,根據opt_wake_all的要求是喚醒一個線程還是喚醒所有的線程;

??? (4)調用系統調度函數,讓高優先級任務及時得到運行的機會;

??? (5)當前線程再次得到運行的機會,函數返回。

?

??? 有了上面的講解,我們發現os的代碼其實也沒有那么恐怖。所以,請大家一鼓作氣,看看信號量是怎么刪除的吧,

[cpp] view plaincopy
  1. RAW_U16?raw_semaphore_delete(RAW_SEMAPHORE?*semaphore_ptr)??
  2. {??
  3. ????LIST?*block_list_head;??
  4. ??????
  5. ????RAW_SR_ALLOC();??
  6. ??
  7. ????#if?(RAW_SEMA_FUNCTION_CHECK?>?0)??
  8. ??????
  9. ????if?(semaphore_ptr?==?0)?{??
  10. ??????????
  11. ????????return?RAW_NULL_OBJECT;??
  12. ????}??
  13. ??????
  14. ????#endif??
  15. ??
  16. ????block_list_head?=?&semaphore_ptr->common_block_obj.block_list;??
  17. ??????
  18. ????RAW_CRITICAL_ENTER();??
  19. ??
  20. ????/*All?task?blocked?on?this?queue?is?waken?up*/??
  21. ????while?(!is_list_empty(block_list_head))?{??
  22. ????????delete_pend_obj(list_entry(block_list_head->next,?RAW_TASK_OBJ,?task_list));???
  23. ????}???????????????????????????????
  24. ??
  25. ????RAW_CRITICAL_EXIT();??
  26. ????raw_sched();???
  27. ????return?RAW_SUCCESS;??
  28. }??

??? 信號量刪除的工作其實很少,也很簡單,同樣我們也來梳理一下,

??? (1)判斷參數合法性;

??? (2)喚醒阻塞隊列中的每一個線程;

??? (3)調用系統調度函數,因為高優先級的任務很有可能剛剛從阻塞隊列中釋放出來;

??? (4)當前線程再次運行,函數返回。

?

??? 通過上面幾個函數的講解,我們發現關于os互斥部分的代碼其實也不復雜。只要對系統本身和中斷有一些了解,其實代碼都是可以看懂的。當然,上面的代碼我們還是講的比較粗糙,所以有些細節還是要補充一下,

?

??? (1)全局變量操作的函數必須在關中斷的情況下進行操作;

??? (2)實時系統的搶占是每時每刻都在進行的,比如中斷返回時、信號量釋放時、調用延時函數、調用調度函數的時候,所以大家心中要有搶占的概念;

??? (3)互斥函數中大量使用了鏈表的結構,建議大家好好掌握鏈表的相關算法;

??? (4)關于os的代碼一定要多看、多思考、多練習才會有進步和提高,紙上得來終覺淺、絕知此事要躬行。

嵌入式操作系統內核原理和開發(互斥量)

今天下午打開郵箱,打開rawos作者給我發的郵件,甚是驚喜。感謝他對我的支持,因為自己閱讀過很多os的代碼,包括ucos、rtthread、vxWorks、linux等等,所以閱讀rawos對于我來說不算特別辛苦的事情。除了某些細節之外,我對整個系統的設計還算得上是比較了解的,所以也打算把這個代碼介紹給大家。能在現實的硬件中使用當然最好,如果沒有這樣的機會,也可以提高個人的認識水平,或者介紹給內部的團隊成員,大家一起分析和學習也不失為一個很好的方法。
?
? ? ?閑話不多說,話題還是轉到我們今天的主題上面,即互斥量。學過操作系統課程的朋友對這個詞匯肯定不會很陌生。和信號量相比,互斥保護的資源一般是唯一的。也就是說,資源就一份,你占有了,我就沒有辦法占有;當然如果你釋放了,此時我就有機會占有了。
?
? ? ?一切的一切看上去沒有什么問題。但是,我們都知道在實時嵌入式系統當中,線程之間的調度是嚴格按照優先級來進行調度。比方說,優先級為10的任務必須比優先級為11的任務優先得到調度。那么,有同學會問了,那優先級為11的任務什么時候才能得到調度呢,其實這個要求還是蠻苛刻的。要想優先級為11的任務得到調度,此時必須沒有優先級10的任務、或者任務pend到資源上了、或者自身delay、或者被人suspend了。否則,優先級為10的任務會這么一直運行下去。那,這和我們的互斥量有什么關系呢?請聽我一一講來。
?
? ? ?我們假設現在有兩個任務都準備運行,分別人任務A、B,優先級依次是10、11。某一段時間后,優先級為10和優先級為11的任務都在嘗試獲取某個資源。本來按照優先級的先后順序,優先級為10的任務應該率先獲取資源,這都沒問題。但是,假設在嘗試獲取資源前,優先級為10的任務開了個小差,sleep一會,那么這個時候優先級為11的任務就可以開始運行了。等到優先級為10的任務蘇醒過來,想重新獲取資源的時候,驚訝地發現資源早就被別人給占了。因為資源目前只有一份,所以它只好把自己pend到等待隊列里面,慢慢等待好心人能快點把資源釋放出來。一切的一切看上去沒有什么問題,但是這卻和實時系統設計的初衷是相違背的。前面我們規定高優先級的任務必須優先得到運行的機會,而目前這種情況和我們的設計原則是背道而馳的。
?
? ? ?當然這個問題很早就被大家發現了,大家也在嘗試不同的方法來解決。目前使用的比較多的就是兩種方法,一種是給互斥量設定一個優先級,另外一種就是對優先級進行繼承處理。看上去是兩種方法,其實目的只有一個,就是讓那些占有互斥量的thread提高優先級,趕快運行結束,把資源還給后面真正需要的人。看上去一切解決得都很完美,但是大家有沒有考慮過這樣一個問題,如果線程連續占有多個互斥量,優先級又該怎么處理?如果pend的任務被修改了優先級該怎么處理?如果這兩種方法一起被使用,那又該怎么處理?我想,這就是作者在后期對互斥量代碼進行重構的原因吧。當然了,上面討論的內容已經是比較深的了,大家可以看看早期互斥量是怎么設計的,慢慢來,這樣才會對作者的設計意圖更加了解一些。
?
? ? ?老規矩,我們首先看看互斥量是怎么設計的,
[cpp] view plaincopy
  1. typedef?struct?RAW_MUTEX??
  2. ??{???
  3. ????RAW_COMMON_BLOCK_OBJECT???????common_block_obj;??
  4. ????RAW_U8?????????????????????count;??
  5. ??????
  6. ????/*ponit?to?occupy?task*/??
  7. ????RAW_TASK_OBJ???*occupy;??
  8. ????/*occupy?task?original?priority*/??
  9. ????RAW_U8?occupy_original_priority;??
  10. ??}?RAW_MUTEX;??

?? ? 看上去互斥量的東西多一點,其實也還可以,只要大家明白了互斥量處理邏輯再回頭來看看這些東西的時候,認識就會更加深刻。我們看看,數據結構里面都有什么,
? ? ?(1)通用互斥結構,這在前面信號量的時候已經介紹過一遍;
? ? ?(2)計數,判斷資源是否還在;
? ? ?(3)當前所屬的任務;
? ? ?(4)該任務原來的優先級。
?
? ? ?說好了基本結構,我們看看互斥量的構造、申請、釋放、刪除函數是怎么設計的,首先當然還是初始化函數,
[cpp] view plaincopy
  1. RAW_U16?raw_mutex_create(RAW_MUTEX?*mutex_ptr,?RAW_U8?*name_ptr)??
  2. ??{??
  3. ????#if?(RAW_MUTEX_FUNCTION_CHECK?>?0)??
  4. ????
  5. ????if?(mutex_ptr?==?0)??
  6. ????????return?RAW_NULL_OBJECT;??
  7. ??????
  8. ????#endif??
  9. ????
  10. ????/*Init?the?list*/??
  11. ????list_init(&mutex_ptr->common_block_obj.block_list);??
  12. ????mutex_ptr->common_block_obj.block_way?=?0;??
  13. ????mutex_ptr->common_block_obj.name??=??name_ptr;??
  14. ????
  15. ????/*No?one?occupy?mutex?yet*/??
  16. ????mutex_ptr->occupy?????=?0;??
  17. ????
  18. ????/*resource?is?available?at?init?state*/???
  19. ????mutex_ptr->count???=?1;??????????
  20. ????mutex_ptr->occupy_original_priority?=??0;??
  21. ??????
  22. ????
  23. ????return?RAW_SUCCESS;??
  24. ??}??
  25. ???
? ? 初始化的函數還是比較簡單的,主要做了下面的流程,
? ? ?(1)初始化互斥結構的公共屬性,比如名字、阻塞方式等等;
? ? ?(2)初始化當前資源數量;
? ? ?(3)初始化占有資源的線程指針,還有就是線程的優先級。
?
? ? ?創建了互斥量之后,我們就要看看互斥量是怎么申請的?代碼有點長,同學們可以心理調整一下了,
[cpp] view plaincopy
  1. RAW_U16?raw_mutex_get(RAW_MUTEX?*mutex_ptr,?RAW_U32?wait_option)??
  2. ??{??
  3. ????RAW_U16?error_status;??
  4. ????RAW_SR_ALLOC();??
  5. ????
  6. ????#if?(RAW_MUTEX_FUNCTION_CHECK?>?0)??
  7. ????
  8. ????
  9. ????if?(mutex_ptr?==?0)?{??
  10. ????????return?RAW_NULL_OBJECT;??
  11. ????}??
  12. ????
  13. ????if?(raw_int_nesting)?{??
  14. ??????????
  15. ????????return?RAW_NOT_CALLED_BY_ISR;??
  16. ??????????
  17. ????}??
  18. ??????
  19. ????#endif??
  20. ??????
  21. ????RAW_CRITICAL_ENTER();??
  22. ??????
  23. ?????/*?mutex?is?available?*/??
  24. ????if?(mutex_ptr->count)?{?????
  25. ????????mutex_ptr->occupy???????=??raw_task_active;???????????????????????????????????
  26. ????????mutex_ptr->occupy_original_priority?=??raw_task_active->priority;??
  27. ????????mutex_ptr->count???=?0;??
  28. ????
  29. ????????RAW_CRITICAL_EXIT();??
  30. ????
  31. ????????return?RAW_SUCCESS;??
  32. ????}??
  33. ????
  34. ????
  35. ????/*if?the?same?task?get?the?same?mutex?again,?it?causes?deadlock*/???
  36. ????if?(raw_task_active?==?mutex_ptr->occupy)?{???????
  37. ????
  38. ????????#if?(CONFIG_RAW_ASSERT?>?0)??
  39. ????????RAW_ASSERT(0);??
  40. ????????#endif??
  41. ??????????
  42. ????????????RAW_CRITICAL_EXIT();?????
  43. ????????return?RAW_MUTEX_DEADLOCK;??
  44. ?????}??
  45. ????
  46. ????/*Cann't?get?mutex,?and?return?immediately?if?wait_option?is??RAW_NO_WAIT*/??
  47. ????if?(wait_option?==?RAW_NO_WAIT)?{???
  48. ????
  49. ????????RAW_CRITICAL_EXIT();??
  50. ????
  51. ????????return?RAW_NO_PEND_WAIT;??
  52. ????
  53. ????}??
  54. ????
  55. ????/*system?is?locked?so?task?can?not?be?blocked?just?return?immediately*/??
  56. ????if?(raw_sched_lock)?{?????
  57. ????????RAW_CRITICAL_EXIT();??????
  58. ????????return?RAW_SCHED_DISABLE;??
  59. ????}??
  60. ????
  61. ??????/*if?current?task?is?a?higher?priority?task?and?block?on??the?mutex?
  62. ??????*priority?inverse?condition?happened,?priority?inherit?method?is?used?here*/??
  63. ??????
  64. ????if?(raw_task_active->priority?<?mutex_ptr->occupy->priority)?{????
  65. ????????switch?(mutex_ptr->occupy->task_state)?{??
  66. ??????????????
  67. ????????????case?RAW_RDY:??
  68. ????????????????/*remove?from?the?ready?list*/??
  69. ????????????????remove_ready_list(&raw_ready_queue,?mutex_ptr->occupy);??
  70. ????????????????/*raise?the?occupy?task?priority*/??
  71. ????????????????mutex_ptr->occupy->priority?=?raw_task_active->priority;?????
  72. ????????????????/*readd?to?the?ready?list?head*/??
  73. ????????????????add_ready_list_head(&raw_ready_queue,?mutex_ptr->occupy);??
  74. ????????????????break;??
  75. ??????????????
  76. ????
  77. ????????????case?RAW_DLY:??
  78. ????????????case?RAW_DLY_SUSPENDED:??
  79. ????????????case?RAW_SUSPENDED:??
  80. ????????????????/*occupy?task?is?not?on?any?list,?so?just?change?the?priority*/???
  81. ????????????????mutex_ptr->occupy->priority?=?raw_task_active->priority;?????
  82. ????????????????break;??
  83. ????
  84. ????????????case?RAW_PEND:????????????????????????/*?Change?the?position?of?the?task?in?the?wait?list???????*/??
  85. ????????????case?RAW_PEND_TIMEOUT:??
  86. ????????????case?RAW_PEND_SUSPENDED:??
  87. ????????????case?RAW_PEND_TIMEOUT_SUSPENDED:??????
  88. ????????????????/*occupy?task?is?on?the?block?list?so?change?the?priority?on?the?block?list*/??
  89. ????????????????mutex_ptr->occupy->priority?=?raw_task_active->priority;?????
  90. ????????????????change_pend_list_priority(mutex_ptr->occupy);???
  91. ????????????????break;??
  92. ????
  93. ????????????default:??
  94. ????????????????RAW_CRITICAL_EXIT();??????
  95. ????????????????return?RAW_INVALID_STATE;??
  96. ????????}??
  97. ??????????
  98. ????}??
  99. ????
  100. ????/*Any?way?block?the?current?task*/??
  101. ????raw_pend_object(&mutex_ptr->common_block_obj,?raw_task_active,?wait_option);??
  102. ????
  103. ????RAW_CRITICAL_EXIT();??????
  104. ????
  105. ????/*find?the?next?highest?priority?task?ready?to?run*/??
  106. ????raw_sched();???????????????????????????????????????????????
  107. ????
  108. ????/*So?the?task?is?waked?up,?need?know?which?reason?cause?wake?up.*/??
  109. ????error_status?=?block_state_post_process(raw_task_active,?0);??
  110. ??????
  111. ????return?error_status;??
  112. ??}??
  113. ???
? ? 這段代碼其實開頭都還好,關鍵是末尾要結束的時候有一段代碼比較費解。我想,這就是我前面說過的優先級反轉問題。為了解決這一問題,在rawos版本中采取了優先級繼承的方法。我們還是詳細看一下邏輯本身是怎么樣的,
? ? ?(1)判斷參數合法性;
? ? ?(2)判斷資源是否可取,如果可取,則在記錄當前線程和優先級后返回;
? ? ?(3)如果資源被自己重復申請,返回;
? ? ?(4)如果線程不愿等待,返回;
? ? ?(5)如果此時禁止調度,返回;
? ? ?(6)如果此時優先級大于互斥量占有者的優先級,分情況處理
? ? ? ? ? ? ?a)占有者處于ready的狀態,那么修改它的優先級,重新加入調度隊列;
? ? ? ? ? ? ?b)占有者處于sleep的狀態,直接修改優先級即可;
? ? ? ? ? ? ?c)占有者也被pend到別的資源上面了,那么修改那個資源的pend列表,可能設計到調度順序問題。
? ? ?(7)線程把自己pend到互斥量等待隊列上面;
? ? ?(8)線程調用系統調度函數,切換到其他線程運行;
? ? ?(9)線程再次得到運行的機會,從task獲取結果后返回。
?
? ? ?基本上上面的介紹算得上是很詳細了,那么互斥量的釋放基本上是一個逆操作的過程,朋友也可以思考一下應該怎么解決才好,
[cpp] view plaincopy
  1. RAW_U16?raw_mutex_put(RAW_MUTEX?*mutex_ptr)??
  2. ??{??
  3. ????
  4. ????LIST?*block_list_head;??
  5. ??????
  6. ????RAW_SR_ALLOC();??
  7. ????
  8. ????#if?(RAW_MUTEX_FUNCTION_CHECK?>?0)??
  9. ????
  10. ????if?(mutex_ptr?==?0)?{??
  11. ????????return?RAW_NULL_OBJECT;??
  12. ????}??
  13. ??????
  14. ????#endif??
  15. ????
  16. ????block_list_head?=?&mutex_ptr->common_block_obj.block_list;??
  17. ??????
  18. ????RAW_CRITICAL_ENTER();??
  19. ????
  20. ????/*Must?release?the?mutex?by?self*/??
  21. ????if?(raw_task_active?!=?mutex_ptr->occupy)?{?????????????
  22. ????????RAW_CRITICAL_EXIT();??
  23. ????????return?RAW_MUTEX_NOT_RELEASE_BY_OCCYPY;??
  24. ????}??
  25. ????
  26. ????/*if?no?block?task?on?this?list?just?return*/??
  27. ????if?(is_list_empty(block_list_head))?{??????????
  28. ????????mutex_ptr->count???=?1;??????????????????????????????????????
  29. ????????RAW_CRITICAL_EXIT();??
  30. ????????return?RAW_SUCCESS;??
  31. ????}??
  32. ????
  33. ?????/*if?priority?was?changed,?just?change?it?back?to?original?priority*/??
  34. ???????
  35. ????if?(raw_task_active->priority?!=?mutex_ptr->occupy_original_priority)?{??
  36. ??????????
  37. ????????remove_ready_list(&raw_ready_queue,?raw_task_active);??
  38. ????????raw_task_active->priority?=?mutex_ptr->occupy_original_priority;?????
  39. ????????add_ready_list_end(&raw_ready_queue,?raw_task_active);??
  40. ????
  41. ????}??
  42. ????
  43. ????/*?there?must?have?task?blocked?on?this?mutex?object*/????????????????????????????????????????????????????????????????????????????????????????????????????????????????
  44. ????mutex_ptr->occupy???????=???list_entry(block_list_head->next,?RAW_TASK_OBJ,?task_list);???
  45. ????/*the?first?blocked?task?became?the?occupy?task*/??
  46. ????mutex_ptr->occupy_original_priority?=?mutex_ptr->occupy->priority;??
  47. ????/*mutex?resource?is?occupied*/??
  48. ????mutex_ptr->count???=?0;????
  49. ????
  50. ????/*Wake?up?the?occupy?task,?which?is?the?highst?priority?task?on?the?list*/?????????????????????????????????????????????????????????????????????????????????????????????????????????
  51. ????raw_wake_object(mutex_ptr->occupy);??
  52. ??????
  53. ????RAW_CRITICAL_EXIT();??
  54. ????
  55. ????
  56. ????raw_sched();????????????????????????????????????????
  57. ????
  58. ????return?RAW_SUCCESS;??
  59. ??????
  60. ??}??
  61. ???
? ? 和之前的信號量釋放相比,互斥量的釋放要復雜一切,關鍵就在于修改優先級的問題。我們來梳理一下,
? ? ?(1)判斷參數合法性;
? ? ?(2)判斷線程是否為互斥量的占有線程,不是則返回;
? ? ?(3)判斷等待隊列是否為空,為空的話則返回;
? ? ?(4)判斷占有任務的優先級有沒有發生變化,如果有則需要重新修改優先級,重新加入調度隊列中;
? ? ?(5)選擇下一個可以調度的線程;
? ? ?(6)函數返回。
?
? ? ?說了這么些,就剩下最后一個刪除互斥量了,大家再接再厲,一起去學習。
[cpp] view plaincopy
  1. RAW_U16??raw_mutex_delete(RAW_MUTEX?*mutex_ptr)??
  2. ??{??
  3. ????LIST?*block_list_head;??
  4. ??????
  5. ????RAW_TASK_OBJ??*mutex_occupy;??
  6. ??????
  7. ????RAW_SR_ALLOC();??
  8. ????
  9. ????#if?(RAW_MUTEX_FUNCTION_CHECK?>?0)??
  10. ????
  11. ????if?(mutex_ptr?==?0)?{??
  12. ????????return?RAW_NULL_OBJECT;??
  13. ????}??
  14. ??????
  15. ????#endif??
  16. ????
  17. ????block_list_head?=?&mutex_ptr->common_block_obj.block_list;??
  18. ??????
  19. ????RAW_CRITICAL_ENTER();??
  20. ????
  21. ????mutex_occupy?=?mutex_ptr->occupy;???????
  22. ????/*if?mutex?is?occupied?and?occupy?priority?is?not?the?original?priority*/??
  23. ????if?((mutex_occupy)?&&??(mutex_occupy->priority?!=??mutex_ptr->occupy_original_priority))?{??
  24. ????????switch?(mutex_occupy->task_state)?{????????????????????????
  25. ????????????case?RAW_RDY:?????
  26. ????????????????/*remove?from?the?ready?list*/??
  27. ????????????????remove_ready_list(&raw_ready_queue,?mutex_ptr->occupy);??
  28. ????????????????/*raise?the?occupy?task?priority*/??
  29. ????????????????mutex_occupy->priority?=??mutex_ptr->occupy_original_priority;??
  30. ????????????????/*readd?to?the?ready?list?head*/??
  31. ????????????????add_ready_list_end(&raw_ready_queue,?mutex_ptr->occupy);??
  32. ????????????????break;??
  33. ????
  34. ????????????case?RAW_DLY:??
  35. ????????????case?RAW_SUSPENDED:??
  36. ????????????case?RAW_DLY_SUSPENDED:??
  37. ????????????????/*occupy?task?is?not?on?any?list,?so?just?change?the?priority*/???
  38. ????????????????mutex_occupy->priority?=?mutex_ptr->occupy_original_priority;????
  39. ????????????????break;??
  40. ????
  41. ????????????case?RAW_PEND:??
  42. ????????????case?RAW_PEND_TIMEOUT:??
  43. ????????????case?RAW_PEND_SUSPENDED:??
  44. ????????????case?RAW_PEND_TIMEOUT_SUSPENDED:??
  45. ????????????????/*occupy?task?is?on?the?block?list?so?change?the?priority?on?the?block?list*/??
  46. ????????????????mutex_occupy->priority?=?mutex_ptr->occupy_original_priority;????
  47. ????????????????change_pend_list_priority(mutex_occupy);???
  48. ????
  49. ????????????????break;??
  50. ????
  51. ????????????default:??
  52. ????????????????RAW_CRITICAL_EXIT();??
  53. ????????????????return?RAW_STATE_UNKNOWN;??
  54. ????????}??
  55. ????}??
  56. ????
  57. ??????
  58. ????/*All?task?blocked?on?this?queue?is?waken?up*/??
  59. ????while?(!is_list_empty(block_list_head))?{??
  60. ????????delete_pend_obj(list_entry(block_list_head->next,?RAW_TASK_OBJ,?task_list));???
  61. ????}????????????????
  62. ????
  63. ????RAW_CRITICAL_EXIT();??
  64. ????
  65. ????raw_sched();???
  66. ??????
  67. ????return?RAW_SUCCESS;??
  68. ??}??
  69. ???

? ? 互斥量的操作在實際情形下未必是存在的,所以作者在設計的時候添加了一個編譯宏。不過刪除所做的工作也不難理解,一個是處理好當前占有者的關系,一個是處理好等待隊列的關系。我們來細看一下流程,
? ? (1)判斷當前參數合法性;
? ? (2)判斷占有者的情況,修改任務優先級,這里的情形和上面申請互斥量的處理方式是一樣的,不再贅述;
? ? (3)喚醒所有的等待線程,如果線程已經suspend掉了,那么繼續suspend;
? ? (4)調度到其他線程,防止有優先級高的任務已經被釋放出來了;
? ? (5)函數返回,結束。
?

嵌入式操作系統內核原理和開發(事件)

在很多操作系統的書上,其實互斥和同步是放在一起進行介紹的。互斥,比較簡單,就是對某一份資源或者幾份資源進行搶占獲取。而同步是什么意思呢,就是某一個線程等待另外一個線程的通知,只有收到了通知,它才會去干某些事情。


? ? ?通常情況下,如果是搶占的話,那么兩個人使用的必須是同一個鎖,而同步的話,則需要好幾個鎖,因為一般情況下大家等待的東西都是不一樣的,所以好幾個鎖是不可避免的。那么,有沒有什么辦法,可以用一個鎖實現幾個事情的并發和同步呢?這就是我們今天所要說的事件。可以從一個例子說明一下。


? ? ?比方說,我們現在打算進行八寶飯的烹飪。那么,在此之前需要進行各個輔料的準備工作,等到這些輔料都準備好了,就可以開始煮八寶飯了。因為輔料之間是相互獨立的,所以完全可以分開獨立完成,而在所有輔料都沒有完成之前,我們只能等待。等到材料全部準備好,我們就可以開始烹飪的工作了。當然,在烹飪的時候,我們又可以準備進行下一輪工作了,也就是說進行下一次八寶飯的輔料準備。在這個地方,輔料的準備是由各個子線程完成的,而煮飯這個工作是主線程完成的,主線程和子線程之間就是通過事件進行溝通的。主線程需要知道當前各個材料準備好了沒,而子線程需要知道八寶飯燒好了沒,是不是該進行下一輪輔料的準備了。這個中間就存在一個同步的問題了。


? ? ?如果大家對之前的信號量還有印象的話,當初我們是用count來表示資源的個數。而今天,我們用flags來表示事件狀態,而其中的bit則表示了一個一個具體的事件。只不過有的線程在等待多個事件,而有的線程在等待一個事件,有的線程在獲取事件后bit位立即清除,有的線程在獲取事件后繼續留存。


? ? 所以下面,我們就看看raw-os上面的事件是怎么設計的。當然,我們首先看到的還是關于事件的基本數據結構,

[cpp] view plaincopy
  1. typedef?struct?RAW_EVENT??
  2. ?{???
  3. ????RAW_COMMON_BLOCK_OBJECT???????common_block_obj;??
  4. ????RAW_U32??flags;??
  5. ??????
  6. ?}?RAW_EVENT;??
  7. ???
  8. ???
? ? 這和我們之前介紹的沒什么不一樣,就是通用結構加上flag標志。關于事件的基本處理函數也不復雜,主要就是創建、申請、設置和刪除四個基本操作。我們來看看每一步分別是怎么實現的,首先介紹的還是事件的創建過程,
[cpp] view plaincopy
  1. RAW_U16?raw_event_create(RAW_EVENT?*event_ptr,?RAW_U8?*name_ptr,?RAW_U32?flags_init)??
  2. ?{??
  3. ????#if?(RAW_EVENT_FUNCTION_CHECK?>?0)??
  4. ??????
  5. ????if?(event_ptr?==?0)?{??
  6. ??????????
  7. ????????return?RAW_NULL_OBJECT;??
  8. ????}??
  9. ??????
  10. ????#endif??
  11. ???
  12. ????/*Init?the?list*/??
  13. ????list_init(&event_ptr->common_block_obj.block_list);??
  14. ????event_ptr->common_block_obj.block_way?=?0;??
  15. ????event_ptr->common_block_obj.name?=?name_ptr;????
  16. ????event_ptr->flags?=?flags_init?;??
  17. ??????
  18. ????return?RAW_SUCCESS;??
  19. ?}??
  20. ???

? ? 看了代碼,相信要說的部分不是很多,關鍵就是flags的賦值部分,其他的都和信號量差不太多。這里的flags代表了某一個起始狀態,也就是說當前可以干什么事情、滿足哪些條件等等。下面,我們繼續看事件的獲取函數,稍微復雜一些,

[cpp] view plaincopy
  1. RAW_U16?raw_event_get(RAW_EVENT?*event_ptr,?RAW_U32??requested_flags,?RAW_U8?get_option,?RAW_U32?wait_option)??
  2. ?{??
  3. ????????RAW_U16?error_status;??
  4. ??????
  5. ?????RAW_U8?status;??
  6. ????RAW_SR_ALLOC();??
  7. ???
  8. ????#if?(RAW_EVENT_FUNCTION_CHECK?>?0)??
  9. ???
  10. ????if?(raw_int_nesting)?{??
  11. ???
  12. ????????return?RAW_NOT_CALLED_BY_ISR;??
  13. ??????????
  14. ????}??
  15. ???
  16. ????if?((get_option??!=?RAW_AND)?&&?(get_option??!=?RAW_OR)?&&?(get_option??!=?RAW_AND_CLEAR)?&&?(get_option??!=?RAW_OR_CLEAR))?{??
  17. ???
  18. ????????return?RAW_NO_THIS_OPTION;??
  19. ????}??
  20. ??????
  21. ????#endif??
  22. ??????
  23. ?????RAW_CRITICAL_ENTER();??
  24. ???
  25. ????/*if?option?is?and?flag*/??
  26. ?????if?(get_option?&?RAW_FLAGS_AND_MASK)?{??
  27. ??????
  28. ?????????if?((event_ptr->flags?&?requested_flags)?==?requested_flags)?{??
  29. ???????
  30. ?????????????status?=?RAW_TRUE;??
  31. ?????????}??
  32. ??????????????????
  33. ?????????else?{??
  34. ?????????????status?=??RAW_FALSE;??
  35. ?????????}??
  36. ??????????????????
  37. ?????}??
  38. ????/*if?option?is?or?flag*/??
  39. ?????else?{??
  40. ???????
  41. ?????????if?(event_ptr->flags?&?requested_flags)?{??
  42. ???
  43. ??????????????
  44. ?????????????status?=??RAW_TRUE;??
  45. ?????????}??
  46. ??????????????????
  47. ?????????else?{??
  48. ????
  49. ?????????????status?=??RAW_FALSE;??
  50. ?????????}??
  51. ??????????????????
  52. ?????}??
  53. ???
  54. ???
  55. ??????????
  56. ?????if?(status)?{??
  57. ???
  58. ????????/*does?it?need?to?clear?the?flags*/??
  59. ????????if?(get_option?&?RAW_FLAGS_CLEAR_MASK)?{??
  60. ????????????event_ptr->flags??&=??~requested_flags;??
  61. ????????}??
  62. ??????????
  63. ????????RAW_CRITICAL_EXIT();??????
  64. ????????return?RAW_SUCCESS;??
  65. ??????????????????
  66. ?????}??
  67. ??????????
  68. ????/*Cann't?get?event,?and?return?immediately?if?wait_option?is??RAW_NO_WAIT*/??
  69. ????if?(wait_option?==?RAW_NO_WAIT)?{???
  70. ????????RAW_CRITICAL_EXIT();??
  71. ????????return?RAW_NO_PEND_WAIT;??
  72. ????}?????
  73. ???
  74. ????/*system?is?locked?so?task?can?not?be?blocked?just?return?immediately*/??
  75. ????if?(raw_sched_lock)?{?????
  76. ????????RAW_CRITICAL_EXIT();??????
  77. ????????return?RAW_SCHED_DISABLE;??
  78. ????}??
  79. ????
  80. ????/*Remember?the?passed?information*/??
  81. ????raw_task_active->raw_suspend_option?=??get_option;??
  82. ????raw_task_active->raw_suspend_flags?=?requested_flags;??
  83. ??????
  84. ????raw_pend_object(&event_ptr->common_block_obj,?raw_task_active,?wait_option);??
  85. ????RAW_CRITICAL_EXIT();??
  86. ???
  87. ????raw_sched();???
  88. ??????
  89. ????RAW_CRITICAL_ENTER();??
  90. ??????
  91. ????/*does?it?need?to?clear?the?flags*/??
  92. ????if?(get_option?&?RAW_FLAGS_CLEAR_MASK)?{??
  93. ????????event_ptr->flags??&=??~requested_flags;??
  94. ????}??
  95. ??????
  96. ????RAW_CRITICAL_EXIT();??
  97. ??????
  98. ????/*So?the?task?is?waked?up,?need?know?which?reason?cause?wake?up.*/??
  99. ????error_status?=?block_state_post_process(raw_task_active,?0);??
  100. ????return?error_status;??
  101. ???
  102. ??????
  103. ?}??
  104. ???
? ? 注意,這里事件和其他get函數的最大差別就是,函數多了一個get_option,它表示當前是同時申請多個事件還是多個事件中的一個事件,申請后是否需要進行clear置位等等,我們不妨看看具體細節,

? ? (1)判斷函數是否在中斷中;

? ? (2)判斷get_option是否合法;

? ? (3)判斷是否存在可以獲取的事件,and或者是or;

? ? (4)如果事件可以獲取,那么再判斷是否需要置位操作,函數返回;

? ? (5)判斷是否愿意等待,否則返回;

? ? (6)判斷是否禁止調度,是則返回;

? ? (7)將自己pend到等待隊列中;

? ? (8)調用公共調度函數轉到其他線程繼續運行;

? ? (9)當前線程重新得到運行的機會,根據選項清除標志位,函數返回。


? ? 看完了事件的申請,下面就可以看看事件的設置函數了,

[cpp] view plaincopy
  1. RAW_U16?raw_event_set(RAW_EVENT?*event_ptr,?RAW_U32??flags_to_set,?RAW_U8?set_option)??
  2. ?{??
  3. ????LIST?????????????????????????????????????????*iter;??
  4. ????LIST?????????????????????????????????????????*event_head_ptr;??
  5. ????LIST?????????????????????????????????????????*iter_temp;??
  6. ????struct?RAW_TASK_OBJ??????????*task_ptr;??
  7. ??????
  8. ????RAW_U8?status;??
  9. ????RAW_U8?need_sche?=?0;??
  10. ??????
  11. ????RAW_SR_ALLOC();??
  12. ???
  13. ????#if?(RAW_EVENT_FUNCTION_CHECK?>?0)??
  14. ???
  15. ????if?(event_ptr?==?0)?{??
  16. ????????return?RAW_NULL_OBJECT;??
  17. ????}??
  18. ??????
  19. ????if?((set_option??!=?RAW_AND)?&&?(set_option??!=?RAW_OR))?{??
  20. ????????return?RAW_NO_THIS_OPTION;??
  21. ????}??
  22. ??????
  23. ????#endif??
  24. ???
  25. ????event_head_ptr?=??&event_ptr->common_block_obj.block_list;??
  26. ??????
  27. ????status?=?RAW_FALSE;??
  28. ??????
  29. ????RAW_CRITICAL_ENTER();??
  30. ???
  31. ?????/*if?the?set_option?is?AND_MASK,?it?just?clear?the?flags?and?will?return?immediately!*/??
  32. ?????if?(set_option?&?RAW_FLAGS_AND_MASK)??{??
  33. ??????
  34. ????????event_ptr->flags??&=??flags_to_set;??
  35. ???
  36. ????????RAW_CRITICAL_EXIT();??
  37. ????????return??RAW_SUCCESS;??
  38. ?????}??
  39. ????/*if?it?is?or?mask?then?set?the?flag?and?continue.........*/??
  40. ?????else??{??
  41. ??????????????
  42. ????????event_ptr->flags?|=?flags_to_set;??????
  43. ?????}??
  44. ???
  45. ????iter?=?event_head_ptr->next;??
  46. ???
  47. ????/*if?list?is?not?empty*/??
  48. ????while?(iter?!=event_head_ptr)?{??
  49. ???
  50. ????????task_ptr?=??list_entry(iter,?RAW_TASK_OBJ,?task_list);??
  51. ????????iter_temp?=??iter->next;??
  52. ??????????
  53. ????????if?(task_ptr->raw_suspend_option?&?RAW_FLAGS_AND_MASK)??{??
  54. ???
  55. ????????????if?((event_ptr->flags??&?task_ptr?->raw_suspend_flags)?==?task_ptr?->raw_suspend_flags)??
  56. ????????????????status?=??RAW_TRUE;??
  57. ????????????else??
  58. ???
  59. ????????????????status?=???RAW_FALSE;??
  60. ????????}??
  61. ???
  62. ??????????
  63. ????????else?{??
  64. ???
  65. ????????????if?(event_ptr->flags??&??task_ptr?->raw_suspend_flags)??
  66. ??????????????????
  67. ????????????????status?=??RAW_TRUE;??
  68. ????????????else??
  69. ??????????????????
  70. ????????????????status?=??RAW_FALSE;??
  71. ????????}??
  72. ???
  73. ??????????
  74. ????????if??(status)??{??
  75. ??????????????
  76. ????????????/*Ok?the?task?condition?is?met,?just?wake?this?task*/??
  77. ????????????raw_wake_object(task_ptr);??
  78. ??????
  79. ????????????/*if??task?is?waken?up*/??
  80. ????????????need_sche?=?1;??
  81. ????????}??
  82. ???
  83. ????????iter?=?iter_temp;??
  84. ???
  85. ????}??
  86. ???
  87. ????RAW_CRITICAL_EXIT();??
  88. ???
  89. ????if?(need_sche)?{??
  90. ??????????
  91. ????????raw_sched();??
  92. ????}??
  93. ??????
  94. ????return?RAW_SUCCESS;??
  95. ??????????
  96. ???????
  97. ?}??
  98. ???
? ? 從函數上也看得出來,這里有一個set_option的選項,主要是為了供調用者選擇是進行and設置還是or設置,細節如下所示,

? ? (1)判斷參數合法性;

? ? (2)判斷set_option合法性;

? ? (3)如果選項為and,在設置完flags之后函數返回;

? ? (4)設置flags標志位,開始遍歷每一個等待線程;

? ? (5)如果存在合適的線程,不管是等待多個事件還是一個事件,都將它們喚醒,設置重新調度標志;

? ? (6)如果重新調度標志為1,調用系統調度函數切換到其他線程運行;

? ? (7)當前線程再次獲取到運行的機會,函數返回。


? ? 轉眼之間,我們就到了事件的刪除過程了。其實事件的刪除非常簡單,它就是把所有的等待線程喚醒,就這么簡單,不知道我說清楚了沒?當然了,這中間可能會有高優先級的線程被加入到ready隊列里面,所以重新schedule一下也是很有必要的。

[cpp] view plaincopy
  1. RAW_U16?raw_event_delete(RAW_EVENT?*event_ptr)??
  2. ?{??
  3. ????LIST?*block_list_head;??
  4. ??????
  5. ????RAW_SR_ALLOC();??
  6. ???????
  7. ????#if?(RAW_EVENT_FUNCTION_CHECK?>?0)??
  8. ???
  9. ????if?(event_ptr?==?0)?{??
  10. ????????return?RAW_NULL_OBJECT;??
  11. ????}?????
  12. ??????
  13. ????#endif??
  14. ??????
  15. ????block_list_head?=?&event_ptr->common_block_obj.block_list;??
  16. ??????
  17. ????RAW_CRITICAL_ENTER();??
  18. ???
  19. ????/*All?task?blocked?on?this?queue?is?waken?up?until?list?is?empty*/??
  20. ????while?(!is_list_empty(block_list_head))?{??
  21. ????????delete_pend_obj(list_entry(block_list_head->next,?RAW_TASK_OBJ,?task_list));???
  22. ????}??????
  23. ???
  24. ????event_ptr->flags?=?0;??
  25. ??????
  26. ????RAW_CRITICAL_EXIT();??
  27. ??????
  28. ?????raw_sched();????
  29. ???
  30. ????return?RAW_SUCCESS;??
  31. ?}??
  32. ??

嵌入式操作系統內核原理和開發(消息隊列)

?消息隊列是線程交互的一種方法,任務可以通過消息隊列來實現數據的溝通和交換。在嵌入式系統上,這可以說這是用的最多的一種方法。通過消息隊列,無論是發送者,還是接受者都可以循環地處理各種消息。而我們知道,存儲消息最好的方式就是循環隊列,如果消息已滿,那么發送者可以把自己pend到等待隊列上;而如果此時沒有消息,那么接受者也可以把自己pend到等待隊列上。當然實現消息隊列的方法很多,甚至用戶可以自己利用互斥量和信號量來實現,而嵌入式系統常常會默認提供這樣的功能函數,我想主要的目的還是為了方便用戶,讓他們可以更多地從業務的角度來看問題,而不是把重點關注在這些底層的細節上面。
??
? ? ? 首先,我們還是看看rawos上面關于消息隊列的數據結構是怎么定義的,
[cpp] view plaincopy
  1. typedef?struct?RAW_MSG_Q?{??????
  2. ??????
  3. ????RAW_VOID?????????**queue_start;????????????/*?Pointer?to?start?of?queue?data??????????????????????????????*/??
  4. ????RAW_VOID?????????**queue_end;??????????????/*?Pointer?to?end???of?queue?data??????????????????????????????*/??
  5. ????RAW_VOID?????????**write;???????????????/*?Pointer?to?where?next?message?will?be?inserted??in???the?Q??*/??
  6. ????RAW_VOID?????????**read;??????????????/*?Pointer?to?where?next?message?will?be?extracted?from?the?Q??*/??
  7. ????RAW_U32???????size;?????????????/*?Size?of?queue?(maximum?number?of?entries)???????????????????*/??
  8. ????RAW_U32???????current_numbers;??????????/*?Current?number?of?entries?in?the?queue??????????????????????*/??
  9. ????RAW_U16????????blocked_send_task_numbers;?/*number?of?blocked?send?task?numbers????????????????????*/??
  10. ????RAW_U16????????blocked_receive_task_numbers;?/*number?of?blocked?send?task?numbers????????????????????*/??
  11. ??????????
  12. ??}?RAW_MSG_Q;??
  13. ????
  14. ??typedef?struct?RAW_QUEUE??
  15. ??{???
  16. ????RAW_COMMON_BLOCK_OBJECT???????common_block_obj;??
  17. ????RAW_MSG_Q?????????????msg_q;??
  18. ??????
  19. ??}?RAW_QUEUE;??


? ? 上面的代碼中有兩段數據結構,第一段主要表示循環隊列的內容,其中包括了隊列首地址、隊列末尾地址、當前隊列讀取地址、當前隊列插入地址、隊列大小、消息個數、阻塞的發送線程數據、阻塞的接受線程數目。而第二段數據結構就比較簡單,它把通用等待結構和循環隊列合在了一起,共同構成了消息隊列的數據結構。
?
? ? ?根據我們以前的經驗,互斥同步數據結構的操作都會分成幾個部分,當然消息隊列也不例外,也會分成初始化、發送消息、接受消息、清除消息、刪除消息隊列等幾種操作函數。當然,消息隊列還是增加了一個新的選項,那就是插入消息的時候可以插入在隊列的前方,還是插入在隊列的尾部,這在某種程度上決定了消息的優先級。說到這,我們還是看看消息隊列是怎么初始化的,
[cpp] view plaincopy
  1. RAW_U16?raw_queue_create(RAW_QUEUE??*p_q,?RAW_U8????*p_name,?RAW_VOID?**msg_start,?RAW_U32??number)??
  2. ??{??
  3. ????
  4. ????#if?(RAW_QUEUE_FUNCTION_CHECK?>?0)??
  5. ????
  6. ????if?(p_q?==?0)?{??
  7. ??????????
  8. ????????return?RAW_NULL_OBJECT;??
  9. ????}??
  10. ??????
  11. ????if?(?msg_start?==?0)?{??
  12. ??????????
  13. ????????return?RAW_NULL_POINTER;??
  14. ????}??
  15. ??????
  16. ????if?(number?==?0)?{??
  17. ??????????
  18. ????????return?RAW_ZERO_NUMBER;??
  19. ????}??
  20. ??????
  21. ????#endif??
  22. ????
  23. ????list_init(&p_q->common_block_obj.block_list);??
  24. ???????????????????????????????????
  25. ????p_q->common_block_obj.name?=?p_name;?????
  26. ????p_q->common_block_obj.block_way?=?0;??
  27. ????p_q->msg_q.queue_start??=???????msg_start;???????????????/*??????Initialize?the?queue?????????????????*/??
  28. ????p_q->msg_q.queue_end?????????????=?&msg_start[number];??
  29. ????p_q->msg_q.write??????????????=?msg_start;??
  30. ????p_q->msg_q.read?????????????=?msg_start;??
  31. ????p_q->msg_q.size????????????=??number;??
  32. ????p_q->msg_q.current_numbers?????????=?0;??
  33. ????p_q->msg_q.blocked_send_task_numbers?=?0;??
  34. ????p_q->msg_q.blocked_receive_task_numbers?=?0;??
  35. ????return?RAW_SUCCESS;??
  36. ??}??
  37. ???
? ? ?雖然相比較之前的互斥函數,消息隊列的初始化內容好像多一些。但是大家如果對循環隊列的知識比較了解的話,其實也不是很復雜的。我們看到,函數除了對通用阻塞結構進行初始化之外,就是對這些循環隊列進行初始化。接著,我們就可以看看消息發送函數是怎么樣的,
[cpp] view plaincopy
  1. static?RAW_U16?internal_msg_post(RAW_QUEUE?*p_q,?RAW_VOID?*p_void,??RAW_U8?opt_send_method,?RAW_U8?opt_wake_all,?RAW_U32?wait_option)???????????????
  2. ??{??
  3. ????RAW_U16?error_status;??
  4. ????LIST?*block_list_head;??
  5. ????RAW_U8?block_way;??
  6. ??????
  7. ????RAW_SR_ALLOC();??
  8. ????
  9. ????#if?(RAW_QUEUE_FUNCTION_CHECK?>?0)??
  10. ????
  11. ????if?(raw_int_nesting)?{??
  12. ????
  13. ????????if?(wait_option?!=?RAW_NO_WAIT)?{??
  14. ??????????????
  15. ????????????return?RAW_NOT_CALLED_BY_ISR;??
  16. ????????}??
  17. ????}??
  18. ????
  19. ????if?(p_q?==?0)?{??
  20. ??????????
  21. ????????return?RAW_NULL_OBJECT;??
  22. ????}??
  23. ??????
  24. ????if?(p_void?==?0)?{??
  25. ??????????
  26. ????????return?RAW_NULL_POINTER;??
  27. ????}??
  28. ??????
  29. ????#endif??
  30. ????
  31. ????block_list_head?=?&p_q->common_block_obj.block_list;??
  32. ??????
  33. ????RAW_CRITICAL_ENTER();??
  34. ????
  35. ????/*queue?is?full?condition,?there?should?be?no?received?task?blocked?on?queue?object!*/??
  36. ????if?(p_q->msg_q.current_numbers?>=?p_q->msg_q.size)?{????
  37. ????
  38. ????????if?(wait_option??==?RAW_NO_WAIT)?{??
  39. ????????????RAW_CRITICAL_EXIT();??
  40. ????????????return?RAW_MSG_MAX;??
  41. ????????}??
  42. ????
  43. ????????else?{??
  44. ??????????????
  45. ????????????/*system?is?locked?so?task?can?not?be?blocked?just?return?immediately*/??
  46. ????????????if?(raw_sched_lock)?{?????
  47. ????????????????RAW_CRITICAL_EXIT();??????
  48. ????????????????return?RAW_SCHED_DISABLE;??????
  49. ????????????}??
  50. ????????????/*queue?is?full?and??SEND_TO_FRONT??method?is?not?allowd*/??
  51. ????????????if?(opt_send_method?==?SEND_TO_FRONT)?{??
  52. ????
  53. ????????????????RAW_CRITICAL_EXIT();??????
  54. ????????????????return?RAW_QUEUE_FULL_OPT_ERROR;????
  55. ????????????}??
  56. ????
  57. ????????????p_q->msg_q.blocked_send_task_numbers++;??
  58. ????????????raw_task_active->msg?=?p_void;??
  59. ????????????block_way?=?p_q->common_block_obj.block_way;??
  60. ????????????p_q->common_block_obj.block_way?=?RAW_BLOCKED_WAY_FIFO;??
  61. ????????????/*there?should?be?no?blocked?received?task?beacuse?msg?exits*/??
  62. ????????????raw_pend_object(&p_q->common_block_obj,?raw_task_active,?wait_option);??
  63. ????????????p_q->common_block_obj.block_way?=?block_way;??
  64. ??????????????
  65. ????????????RAW_CRITICAL_EXIT();??
  66. ????
  67. ????????????raw_sched();???
  68. ??????????????
  69. ????????????error_status?=?block_state_post_process(raw_task_active,?0);??
  70. ????
  71. ????????????return?error_status;??????????
  72. ??????????????
  73. ????????}??
  74. ????
  75. ????}??
  76. ????
  77. ????/*Queue?is?not?full?here,?there?should?be?no?blocked?send?task*/??????
  78. ????/*If?there?is?no?blocked?receive?task*/??
  79. ????if?(is_list_empty(block_list_head))?{??????????
  80. ????
  81. ????????p_q->msg_q.current_numbers++;?????????????????????????????????/*?Update?the?nbr?of?entries?in?the?queue????????*/??
  82. ??????????
  83. ????????if?(opt_send_method?==?SEND_TO_END)??{??
  84. ????
  85. ????????????*p_q->msg_q.write++?=?p_void;????????????????????????????????
  86. ????
  87. ????????????if?(p_q->msg_q.write?==?p_q->msg_q.queue_end)?{?????
  88. ??????????????????
  89. ????????????????p_q->msg_q.write?=?p_q->msg_q.queue_start;??
  90. ??????????????????
  91. ????????????}?????
  92. ????
  93. ????????}??
  94. ????
  95. ????????else?{??
  96. ????
  97. ????????????if?(p_q->msg_q.read?==?p_q->msg_q.queue_start)?{????????????????
  98. ????????????????p_q->msg_q.read?=?p_q->msg_q.queue_end;??
  99. ????????????}??
  100. ??????????????
  101. ????????????p_q->msg_q.read--;??
  102. ????????????*p_q->msg_q.read?=?p_void;???????????????????????????????/*?Insert?message?into?queue?????????????????????*/??
  103. ??????????????
  104. ????????}??
  105. ??????????
  106. ????????RAW_CRITICAL_EXIT();??
  107. ??????????
  108. ????????return?RAW_SUCCESS;??
  109. ????}??
  110. ????
  111. ????/*wake?all?the?task?blocked?on?this?queue*/??
  112. ????if?(opt_wake_all)?{??
  113. ????
  114. ????????while?(!is_list_empty(block_list_head))?{??
  115. ????????????wake_send_msg(list_entry(block_list_head->next,?RAW_TASK_OBJ,?task_list),??p_void);????
  116. ????????}??
  117. ??????????
  118. ????????p_q->msg_q.blocked_receive_task_numbers?=?0;??
  119. ????}??
  120. ??????
  121. ????/*wake?hignhest?priority?task?blocked?on?this?queue?and?send?msg?to?it*/??
  122. ????else?{??
  123. ??????????
  124. ????????wake_send_msg(list_entry(block_list_head->next,?RAW_TASK_OBJ,?task_list),??p_void);????
  125. ????????p_q->msg_q.blocked_receive_task_numbers--;??
  126. ????}??
  127. ??????
  128. ????RAW_CRITICAL_EXIT();??
  129. ????
  130. ????raw_sched();??????
  131. ????return?RAW_SUCCESS;??
  132. ??}??
  133. ???
? ? 這里消息發送函數稍顯冗長,這主要是因為消息發送的情況比較復雜,方方面面考慮的情況比較多。但是整個函數處理的邏輯還是比較清晰的,只要有耐心,慢慢讀下去還是沒有什么問題。這里不妨和大家一起看一下消息發送函數是怎么實現的,
? ? ?(1)檢驗參數合法性,注意在中斷下調用這個函數時,必須是RAW_NO_WAIT的選項,中斷畢竟是不好調度的;
? ? ?(2) 處理消息已滿的情況,
? ? ? ? ? ? ?a)如果線程不想等待,函數返回;
? ? ? ? ? ? ?b)如果禁止調度,函數返回;
? ? ? ? ? ? ?c)消息存儲到線程的msg里面,線程把自己pend到等待隊列中;
? ? ? ? ? ? ?d)調用系統調度函數,等待再次被調度的機會,函數返回。
? ? ?(3)當前消息未滿,但是當前沒有等待隊列,那么根據要求把消息壓入循環隊列,函數返回;
? ? ?(4)當前消息未滿,且存在等待隊列,說明此時已經沒有消息可讀,
? ? ? ? ? ? ?a)如果需要喚醒所有的等待線程,那么喚醒所有的線程,等待線程總數置為0;
? ? ? ? ? ? ?b)如果只是喚起某一個線程,那么喚醒第一個等待線程,等待線程總數自減;
? ? ?(5)調用系統調度函數,防止有高優先級的線程加入調度隊列;
? ? ?(6)線程再次得到運行的機會,函數返回。
?
? ? ?看到上面的代碼,我們發現只要梳理好了代碼的邏輯,其實消息發送函數也是比較好理解的。當然,有消息的發送,就必然會存在消息的接受了。此時肯定也會出現沒有消息、有消息兩種情況了。
[cpp] view plaincopy
  1. RAW_U16?raw_queue_receive?(RAW_QUEUE?*p_q,?RAW_U32?wait_option,?RAW_VOID??**msg)??
  2. ??{??
  3. ????
  4. ????RAW_VOID?*pmsg;??
  5. ????RAW_U16?result;??
  6. ????LIST?*block_list_head;??
  7. ????RAW_TASK_OBJ?*blocked_send_task;??
  8. ??????
  9. ????RAW_SR_ALLOC();??
  10. ????
  11. ????#if?(RAW_QUEUE_FUNCTION_CHECK?>?0)??
  12. ????
  13. ????if?(raw_int_nesting)?{??
  14. ??????????
  15. ????????return?RAW_NOT_CALLED_BY_ISR;??
  16. ??????????
  17. ????}??
  18. ????
  19. ????if?(p_q?==?0)?{??
  20. ??????????
  21. ????????return?RAW_NULL_OBJECT;??
  22. ????}??
  23. ??????
  24. ????if?(msg?==?0)?{??
  25. ??????????
  26. ????????return?RAW_NULL_POINTER;??
  27. ????}??
  28. ??????
  29. ????#endif??
  30. ????
  31. ????block_list_head?=?&p_q->common_block_obj.block_list;??
  32. ??????
  33. ????RAW_CRITICAL_ENTER();??
  34. ????
  35. ??????
  36. ????????/*if?queue?has?msgs,?just?receive?it*/??
  37. ????if?(p_q->msg_q.current_numbers)?{???
  38. ??????????
  39. ????????pmsg?=?*p_q->msg_q.read++;??????????????????????
  40. ??????????
  41. ????????if?(p_q->msg_q.read?==?p_q->msg_q.queue_end)?{???????????
  42. ????????????p_q->msg_q.read?=?p_q->msg_q.queue_start;??
  43. ????????}??
  44. ????
  45. ????????*msg?=?pmsg;??
  46. ????
  47. ????????/*if?there?are??blocked_send_tasks,?just?reload?the?task?msg?to?end*/??
  48. ????????if?(p_q->msg_q.blocked_send_task_numbers)?{??
  49. ????
  50. ????????????blocked_send_task?=?list_entry(block_list_head->next,?RAW_TASK_OBJ,?task_list);??
  51. ??????????????
  52. ????????????p_q->msg_q.blocked_send_task_numbers--;??
  53. ??????????????
  54. ????????????*p_q->msg_q.write++?=?blocked_send_task->msg;????????????????????????????????
  55. ????
  56. ????????????if?(p_q->msg_q.write?==?p_q->msg_q.queue_end)?{?????
  57. ??????????????????
  58. ????????????????p_q->msg_q.write?=?p_q->msg_q.queue_start;??
  59. ??????????????????
  60. ????????????}?????
  61. ??????????????
  62. ????????????raw_wake_object(blocked_send_task);??
  63. ????????????RAW_CRITICAL_EXIT();??
  64. ??????????????
  65. ????????????raw_sched();????
  66. ????????????return?RAW_SUCCESS;??
  67. ??????????
  68. ????????}??
  69. ????
  70. ????????p_q->msg_q.current_numbers--;????
  71. ??????????
  72. ????????RAW_CRITICAL_EXIT();??
  73. ??????????
  74. ????????return?RAW_SUCCESS;???????????????????????????
  75. ????}??
  76. ????
  77. ????
  78. ????
  79. ????if?(wait_option?==?RAW_NO_WAIT)?{????/*?Caller?wants?to?block?if?not?available?????????????????*/??
  80. ????????*msg?=?(RAW_VOID?*)0;??
  81. ????????RAW_CRITICAL_EXIT();??
  82. ????????return?RAW_NO_PEND_WAIT;??
  83. ????}???
  84. ????
  85. ????if?(raw_sched_lock)?{?????
  86. ????????RAW_CRITICAL_EXIT();??????
  87. ????????return?RAW_SCHED_DISABLE;??????
  88. ????}??
  89. ????
  90. ????raw_pend_object(&p_q->common_block_obj,?raw_task_active,?wait_option);??
  91. ????p_q->msg_q.blocked_receive_task_numbers++;??
  92. ??????
  93. ????RAW_CRITICAL_EXIT();??
  94. ??????
  95. ????raw_sched();???????????????????????????????????????????????
  96. ????
  97. ????RAW_CRITICAL_ENTER();??
  98. ??????
  99. ????*msg??????=?(RAW_VOID??????*)0;??
  100. ????result?=?block_state_post_process(raw_task_active,?msg);??
  101. ??????
  102. ????RAW_CRITICAL_EXIT();????
  103. ????
  104. ????return?result;??
  105. ??????
  106. ??}??
  107. ???
? ? 和發送消息函數相比,接受消息的操作還是要少一些,不要緊,大家一起來看一下實現邏輯,
? ? ?(1)判斷參數合法性;
? ? ?(2)如果當前存在消息,
? ? ? ? ? ? ?a)讀取循環隊列中的消息;
? ? ? ? ? ? ?b)判斷當前是否存在等待線程,因為之前有可能存在沒有壓入隊列的消息,那么此時壓入消息,喚醒該線程即可,調用系統調度函數返回;
? ? ? ? ? ? ?c)沒有等待線程,消息總數自減,函數返回。
? ? ?(3)當前沒有消息,
? ? ? ? ? ? ?a)線程不愿等待,函數返回;
? ? ? ? ? ? ?b)系統禁止調度,函數返回;
? ? ? ? ? ? ?c)線程將自己pend到等待隊列中;
? ? ? ? ? ? ?d)調用系統調度函數,切換到其他線程繼續運行;
? ? ? ? ? ? ?e)線程再次獲得運行的機會,從thread結構中獲取返回結果,函數返回。
?
? ? ?和發送消息、接受消息比較起來,清除消息和刪除消息的處理就比較簡單了。為了說明問題,我們不妨放在一起討論一下,
[cpp] view plaincopy
  1. RAW_U16?raw_queue_flush(RAW_QUEUE??*p_q)??
  2. ??{??
  3. ????LIST?*block_list_head;??
  4. ????
  5. ????RAW_SR_ALLOC();??
  6. ??????
  7. ????RAW_TASK_OBJ?*block_task;??
  8. ??????
  9. ????#if?(RAW_QUEUE_FUNCTION_CHECK?>?0)??
  10. ????
  11. ????if?(p_q?==?0)?{??
  12. ??????????
  13. ????????return?RAW_NULL_OBJECT;??
  14. ????}??
  15. ??????
  16. ????#endif??
  17. ??????
  18. ????block_list_head?=?&p_q->common_block_obj.block_list;??
  19. ??????
  20. ????RAW_CRITICAL_ENTER();??
  21. ????
  22. ????/*if?queue?is?full?and?task?is?blocked?on?this?queue,?then?wake?all?the?task*/??
  23. ????if?(p_q->msg_q.current_numbers?>=?p_q->msg_q.size)?{??
  24. ????????while?(!is_list_empty(block_list_head))?{??
  25. ????????????block_task?=?list_entry(block_list_head->next,?RAW_TASK_OBJ,?task_list);??
  26. ????????????raw_wake_object(block_task);??
  27. ????????????block_task->block_status?=?RAW_B_ABORT;??
  28. ????
  29. ????????}??
  30. ????
  31. ????????p_q->msg_q.blocked_send_task_numbers?=?0;??
  32. ????}??
  33. ??????
  34. ????RAW_CRITICAL_EXIT();???
  35. ??????
  36. ????raw_sched();??
  37. ??????
  38. ????return?RAW_SUCCESS;??
  39. ??}??
  40. ??#endif??
  41. ????
  42. ????
  43. ??#if?(CONFIG_RAW_QUEUE_DELETE?>?0)??
  44. ??RAW_U16?raw_queue_delete(RAW_QUEUE?*p_q)??
  45. ??{??
  46. ????LIST??*block_list_head;??
  47. ??????
  48. ????RAW_SR_ALLOC();??
  49. ????
  50. ????#if?(RAW_QUEUE_FUNCTION_CHECK?>?0)??
  51. ????
  52. ????if?(p_q?==?0)?{??
  53. ??????????
  54. ????????return?RAW_NULL_OBJECT;??
  55. ????}??
  56. ??????
  57. ????#endif??
  58. ????
  59. ????block_list_head?=?&p_q->common_block_obj.block_list;??
  60. ??????
  61. ????RAW_CRITICAL_ENTER();??
  62. ????
  63. ????/*All?task?blocked?on?this?queue?is?waken?up*/??
  64. ????while?(!is_list_empty(block_list_head))??{??
  65. ????????delete_pend_obj(list_entry(block_list_head->next,?RAW_TASK_OBJ,?task_list));???
  66. ????}???????????????????????????????
  67. ??????
  68. ????RAW_CRITICAL_EXIT();??
  69. ????
  70. ????raw_sched();???
  71. ??????
  72. ????return?RAW_SUCCESS;??
  73. ??????
  74. ??}??
  75. ??#endif??
  76. ???
? ? ?從代碼據結構上也看得出來,兩個函數的處理邏輯十分相像,所以可以放在一起研究一下,
? ? ?(1)判斷參數合法性;
? ? ?(2)喚醒等待線程,這里消息清除函數喚醒的是發送線程,而消息刪除函數喚醒的所有線程;
? ? ?(3)調用系統調度函數,切換到其他線程繼續運行;
? ? ?(4)當前線程再次獲得運行的機會,函數返回,一切ok搞定。
?

嵌入式操作系統內核原理和開發(實時調度)

和很多通用的操作系統相比, 實時操作系統有自己的一個特點,那就是實時調度。通用操作系統的線程優先級一般是可以變化的,而實時系統的線程優先級卻是不變的。之所以這么設計,是為了保證高優先級的任務在第一時間獲得調度,這樣才能保證調度的實時性。因為實時系統是嚴格按照優先級搞定調度的,所以不管什么時候,我們只要尋找到最高優先級的任務即可。


? ? rawos系統可以支持256個優先級,對任務的創建個數也沒有限制,所以就會出現多個任務共享一個優先級的情況。因此系統本身對同優先級的任務分配了定額的時間片,一旦該任務時間片用完,就會被放到優先級的末尾,直到獲得下一次的調度機會,下面的代碼就說明了這一情況,它是在時鐘中斷的時候被調度的,

[cpp] view plaincopy
  1. void?caculate_time_slice()??
  2. ?{??
  3. ????RAW_TASK_OBJ???*task_ptr;??
  4. ????LIST?*head;??
  5. ??????
  6. ????RAW_SR_ALLOC();??
  7. ???
  8. ????task_ptr?=?raw_task_active;??
  9. ????head?=?&raw_ready_queue.task_ready_list[task_ptr->priority];??
  10. ???????
  11. ????RAW_CRITICAL_ENTER();??
  12. ?????????????????????????????
  13. ????if?(is_list_empty(head))?{??
  14. ???
  15. ????????RAW_CRITICAL_EXIT();??
  16. ????????return;??
  17. ????}??
  18. ???
  19. ????/*there?is?only?one?task?on?this?ready?list,?so?do?not?need?to?caculate?time?slice*/??
  20. ????if?(head->next->next?==?head)??{??
  21. ??????????
  22. ????????RAW_CRITICAL_EXIT();??
  23. ????????return;??
  24. ??????????
  25. ????}??
  26. ???
  27. ????if?(task_ptr->time_slice)?{??
  28. ????????task_ptr->time_slice--;??
  29. ????}??
  30. ???
  31. ????/*if?current?active?task?has?time_slice,?just?return*/??
  32. ????if?(task_ptr->time_slice)?{?????????????????
  33. ????????RAW_CRITICAL_EXIT();??
  34. ????????return;??
  35. ????}??
  36. ???
  37. ????/*Move?current?active?task?to?the?end?of?ready?list?for?the?same?priority*/??
  38. ????move_to_ready_list_end(&raw_ready_queue,?task_ptr);??
  39. ???
  40. ????/*restore?the?task?time?slice*/???
  41. ????task_ptr->time_slice?=?task_ptr->time_total;????
  42. ??????
  43. ????RAW_CRITICAL_EXIT();??
  44. ?}??
  45. ???
? ? 上面說的是一個優先級下面有多個任務的情況,如果優先級本身只有一個任務,那么就很抱歉了,下面還得繼續運行這個任務。另外,我們在windows上面編程的時候喜歡暫時釋放線程的運行權利,調用sleep(0)即可,那么這在rawos上是怎么實現的呢,
[cpp] view plaincopy
  1. RAW_U16??raw_sleep(RAW_U32??dly)???
  2. ?{??
  3. ????RAW_U16?error_status;??
  4. ??????
  5. ????RAW_SR_ALLOC();??
  6. ???
  7. ????#if?(RAW_TASK_FUNCTION_CHECK?>?0)??
  8. ??????
  9. ????if?(raw_int_nesting)?{??
  10. ??????????
  11. ????????return?RAW_NOT_CALLED_BY_ISR;??
  12. ????}??
  13. ????#endif????????
  14. ??????????
  15. ????RAW_CRITICAL_ENTER();??
  16. ???
  17. ????if??(dly)?{??
  18. ???
  19. ????????/*system?is?locked?so?task?can?not?sleep?just?return?immediately*/??
  20. ????????if?(raw_sched_lock)?{?????
  21. ????????????RAW_CRITICAL_EXIT();??????
  22. ????????????return?RAW_SCHED_DISABLE;??
  23. ????????}??
  24. ???
  25. ????????raw_task_active->task_state?=?RAW_DLY;??
  26. ???
  27. ????????tick_list_insert(raw_task_active,?dly);??
  28. ?????????????????????
  29. ????????remove_ready_list(&raw_ready_queue,?raw_task_active);??
  30. ????}??
  31. ??????
  32. ????else?{????
  33. ????????/*make?current?task?to?the?end?of?ready?list*/??
  34. ?????????move_to_ready_list_end(&raw_ready_queue,?raw_task_active);??
  35. ????}??
  36. ???
  37. ????RAW_CRITICAL_EXIT();??
  38. ???
  39. ????raw_sched();?????
  40. ???
  41. ????if?(dly)?{??
  42. ????????/*task?is?timeout?after?sleep*/??
  43. ????????error_status?=?block_state_post_process(raw_task_active,?0);??
  44. ????}??
  45. ???
  46. ????else?{??
  47. ??????????
  48. ????????error_status?=?RAW_SUCCESS;??
  49. ???
  50. ????}??
  51. ??????
  52. ????return?error_status;??
  53. ?}??
  54. ???
? ? 通過的上面的代碼,我們可以看到其實系統啥也沒干,只是把任務方法放到優先級的鏈表末尾了。因為我們的系統需要實時調度,所以即使把使用權出讓出來,也不可能讓低優先的任務運行,只能讓同優先級的其他任務運行了。當然,同優先級沒有其他任務的時候,只好它自己繼續玩了。說了這么多,我們看看系統是怎么調度的,
[cpp] view plaincopy
  1. void?raw_sched()??
  2. ?{??
  3. ????RAW_SR_ALLOC();??
  4. ???
  5. ????/*if?it?is?in?interrupt?or?system?is?locked,?just?return*/???
  6. ????if?(raw_int_nesting?||?raw_sched_lock)?{????????????????
  7. ????????return;???????????????????????????????????????????????
  8. ????}??
  9. ??????
  10. ????RAW_CRITICAL_ENTER();??
  11. ???????????????????
  12. ??????
  13. ????get_ready_task(&raw_ready_queue);??
  14. ???
  15. ????/*if?highest?task?is?currently?task,?then?no?need?to?do?switch?and?just?return*/??
  16. ????if?(high_ready_obj?==?raw_task_active)?{???????????????????
  17. ????????RAW_CRITICAL_EXIT();???????????????????????????????????????
  18. ????????return;??
  19. ????}??
  20. ?????
  21. ????CONTEXT_SWITCH();?????????????????????????????????????????????
  22. ????RAW_CRITICAL_EXIT();??
  23. ???
  24. ?}??
  25. ???
? ? 這個函數看上去很長,其實最重要的部分就是get_ready_task這個函數,它的目的就是尋找到當前最高優先級下面的任務,大家看看代碼就明白了,
[cpp] view plaincopy
  1. void?get_ready_task(RAW_RUN_QUEUE?*rq)??
  2. {??
  3. ????LIST?*node?;??
  4. ????RAW_S32?highest_pri?=??rq->highest_priority;??
  5. ????/*Highest?task?must?be?the?first?element?on?the?list*/??
  6. ????node?=?rq->task_ready_list[highest_pri].next;??
  7. ??
  8. ????high_ready_obj?=?list_entry(node,?RAW_TASK_OBJ,?task_list);??
  9. ??????
  10. }??
? ? 所以,實時系統的核心就是尋找到那個最高優先級就可以了。在實時系統上面,我們一般用bitmap表示優先級,如果對應的優先級存在,那么該位置1,反之置0。那么什么情況下,會發生優先級的改變呢?其實就兩種情況,一種是需要把任務加入調度隊列的時候,還有一種就是把任務清除出調度隊列的時候。
[cpp] view plaincopy
  1. void?add_ready_list(RAW_RUN_QUEUE?*rq,?RAW_TASK_OBJ?*task_ptr)??
  2. ?{??
  3. ????/*if?task?priority?is?equal?current?task?priority?then?add?to?the?end*/??
  4. ????if?(task_ptr->priority?==?raw_task_active->priority)?{??
  5. ????????add_ready_list_end(rq,?task_ptr);??
  6. ????}??
  7. ????/*if?not?add?to?the?list?front*/??
  8. ????else?{??
  9. ????????add_ready_list_head(rq,?task_ptr);??
  10. ????}??
  11. ??????
  12. ?}??
  13. ???
  14. ???
  15. ?void?remove_ready_list(RAW_RUN_QUEUE?*rq,?RAW_TASK_OBJ?*task_ptr)??
  16. ?{??
  17. ???
  18. ????RAW_S32?????i;??
  19. ????RAW_S32??priority?=?task_ptr->priority;??
  20. ???
  21. ????list_delete(&task_ptr->task_list);??
  22. ???
  23. ????/*if?the?ready?list?is?not?empty,?we?do?not?need?to?update?the?highest?priority*/??
  24. ????if?(!is_list_empty(&rq->task_ready_list[priority])?)?{??
  25. ????????return;??
  26. ????}??
  27. ???
  28. ????bit_clear(rq->task_bit_map,?priority);??
  29. ???
  30. ????/*If?task?priority?not?equal?to?the?highest?priority,?then?we?do?not?need?to?update?the?highest?priority*/??
  31. ????if?(priority?!=?rq->highest_priority)?{??
  32. ????????return;??
  33. ????}??
  34. ??????
  35. ????i?=??bit_search_first_one(rq->task_bit_map,?priority,??CONFIG_RAW_PRIO_MAX?-?priority);??
  36. ????/*Update?the?next?highest?priority?task*/??
  37. ????if?(i?>=?0)?{??
  38. ????????rq->highest_priority?=?priority?+?i;??
  39. ????}???
  40. ???
  41. ????else?{??
  42. ??????????
  43. ????????#if?(CONFIG_RAW_ASSERT?>?0)??
  44. ????????RAW_ASSERT(0);??
  45. ????????#endif??
  46. ???
  47. ????}??
  48. ??????
  49. ?}??
  50. ???
? ? 加入調度隊列的情況考慮的比較少,我們只需要判斷當前的最高優先級和加入任務優先級之間的關系即可。如果新加入的任務優先級高,那么優先級發生了改變;反之什么也不需要做。反之刪除任務則比較復雜一些,我們需要判斷移除的任務是不是最高優先級,不是還好處理,如果清除出去的任務正好是最高優先級,我們就需要從bitmap中尋找下一個最高優先級了,這個函數就是bit_search_first_one。函數第一個參數是bitmap數組,第二個參數是當前最高優先級,第三個參數是剩下的優先級總數,返回值為次優先級距離當前最高優先級的偏移值。
[cpp] view plaincopy
  1. /*For?32?bit?cpu*/??
  2. ?RAW_S32?bit_search_first_one(RAW_U32?*base,?RAW_S32?offset,??RAW_S32?width)??
  3. ?{??
  4. ?????register?RAW_U32?*cp,?v;??
  5. ?????register?RAW_S32?position;??
  6. ???
  7. ?????cp?=?base;??
  8. ?????cp?+=?offset?>>?5;??
  9. ??????????
  10. ?????if?(offset?&?31)?{??
  11. ?#if?(CONFIG_RAW_LITTLE_ENDIAN?>?0)??
  12. ?????????v?=?*cp?&?~(((RAW_U32)1?<<?(offset?&?31))?-?1);??
  13. ?#else??
  14. ??????????v?=?*cp?&?(((RAW_U32)1?<<?(32?-?(offset?&?31)))?-?1);??
  15. ?#endif??
  16. ?????}???
  17. ??????????
  18. ????else?{??
  19. ?????????v?=?*cp;??
  20. ?????}??
  21. ???
  22. ?????position?=?0;??
  23. ?????while?(position?<?width)?{??
  24. ?????????if?(v)?{????????????/*?includes?1?-->?search?bit?of?1?*/??
  25. ?????????????if?(!position)?position?-=?(offset?&?31);??
  26. ??????????????????????????
  27. ?#if??(CONFIG_RAW_LITTLE_ENDIAN?>?0)??
  28. ???
  29. ?????????????if?(!(v?&?0xffff))?{??
  30. ?????????????????v?>>=?16;??
  31. ?????????????????position?+=?16;??
  32. ?????????????}??
  33. ?????????????if?(!(v?&?0xff))?{??
  34. ?????????????????v?>>=?8;??
  35. ?????????????????position?+=?8;??
  36. ?????????????}??
  37. ?????????????if?(!(v?&?0xf))?{??
  38. ?????????????????v?>>=?4;??
  39. ?????????????????position?+=?4;??
  40. ?????????????}??
  41. ?????????????if?(!(v?&?0x3))?{??
  42. ?????????????????v?>>=?2;??
  43. ?????????????????position?+=?2;??
  44. ?????????????}??
  45. ?????????????if?(!(v?&?0x1))?{??
  46. ?????????????????++position;??
  47. ?????????????}??
  48. ???????????????
  49. ?#else??
  50. ??????????????
  51. ????????????if?(!(v?&?0xffff0000))?{??
  52. ????????????????v?<<=?16;??
  53. ????????????????position?+=?16;??
  54. ????????????}??
  55. ??????????????
  56. ????????????if?(!(v?&?0xff000000))?{??
  57. ????????????????v?<<=?8;??
  58. ????????????????position?+=?8;??
  59. ????????????}??
  60. ??????????????
  61. ????????????if?(!(v?&?0xf0000000))?{??
  62. ????????????????v?<<=?4;??
  63. ????????????????position?+=?4;??
  64. ????????????}??
  65. ??????????????
  66. ????????????if?(!(v?&?0xc0000000))?{??
  67. ????????????????v?<<=?2;??
  68. ????????????????position?+=?2;??
  69. ????????????}??
  70. ??????????????
  71. ????????????if?(!(v?&?0x80000000))?{??
  72. ????????????????++position;??
  73. ????????????}??
  74. ??????????????????????????
  75. ?#endif??
  76. ?????????????if?(position?<?width)?{??
  77. ??????????????????????????????
  78. ?????????????????return?position;??
  79. ??????????????????????????????????
  80. ?????????????}?else?{??
  81. ??????????????????????
  82. ?????????????????return?-1;??
  83. ??????????????????????????????????
  84. ?????????????}??
  85. ?????????}?else?{??????????????/*?all?bits?are?0?-->?1?Word?skip?*/??
  86. ?????????????if?(position)?{??
  87. ?????????????????position?+=?32;??
  88. ?????????????}?else?{??
  89. ?????????????????position?=?32?-?(offset?&?31);??
  90. ?????????????}??
  91. ?????????????v?=?*++cp;??
  92. ?????????}??
  93. ?????}??
  94. ???
  95. ?????return?-1;??
  96. ?}??
  97. ???

? ? 這個函數其實有兩個,其中一個是32位cpu,另一個是為8位cpu準備的。當然,我們這里看到的是前一種情形,這里作者還考慮了大小端的情況,小端就是x86之類的cpu,大端就是powerpc之類的cpu,主要是指字節序不同而已。按照作者的說法,這是目前最快的查找方法,能比ucos查找表的方法快多少,我不太清楚,估計只能按照系統設備性能的平均值來估算了,別的還真不好說。要是本身用戶側的代碼寫的比較差,那么爭取的這點調度性能其實意義也不大。

嵌入式操作系統內核原理和開發(延時操作)

?延時操作是操作系統中經常遇到的一種情形。延時的原因很多,有的時候是為了等待外設芯片處理結束,有的時候是為了暫時釋放cpu的使用權,有的就是為了希望在一段時間獲取資源,如果沒法在單位時間內獲取,放棄等待。但是不管怎么說,延時都是操作系統必不可少的一個工作。下面我們就看看延時是怎么實現的,
[cpp] view plaincopy
  1. static?void?tick_list_priority_insert(LIST?*head,?RAW_TASK_OBJ?*task_ptr)??
  2. ??{??
  3. ????RAW_U32?val;??
  4. ??????
  5. ????LIST?*q,*start,?*end;??
  6. ????RAW_TASK_OBJ??*task_iter_temp;??
  7. ????
  8. ????start?=?end?=?head;??
  9. ????val?=?task_ptr->tick_remain;??
  10. ??????
  11. ????
  12. ????for?(q?=?start->next;?q?!=?end;?q?=?q->next)?{??
  13. ??????????
  14. ????????task_iter_temp?=?list_entry(q,?RAW_TASK_OBJ,?tick_list);??
  15. ??????????
  16. ????????/*sorted?by?remain?time*/??
  17. ??????????
  18. ????????if?((task_iter_temp->tick_match?-?raw_tick_count)?>?val)?{??
  19. ????????????break;??
  20. ????????}??
  21. ????}??
  22. ????
  23. ????list_insert(q,?&task_ptr->tick_list);??
  24. ????
  25. ????
  26. ??}??
  27. ????
  28. ????
  29. ??void??tick_list_insert(RAW_TASK_OBJ???*task_ptr,?RAW_U32??time)??
  30. ????????????????????????????
  31. ??{??
  32. ????LIST?????*tick_head_ptr;??
  33. ????
  34. ????RAW_U16???spoke;??
  35. ????
  36. ????if?(time)?{??
  37. ?????????????????????????????????????
  38. ????????task_ptr->tick_match?=?raw_tick_count?+?time;??
  39. ????????task_ptr->tick_remain???=?time;??
  40. ????
  41. ????????spoke???=?(RAW_U16)(task_ptr->tick_match??&??(TICK_HEAD_ARRAY?-?1)?);??
  42. ????????tick_head_ptr?=?&tick_head[spoke];??
  43. ????
  44. ????????tick_list_priority_insert(tick_head_ptr,?task_ptr);??
  45. ????
  46. ????????task_ptr->tick_head?=?tick_head_ptr;?????
  47. ????
  48. ????}??????????????????
  49. ??????
  50. ??}??
  51. ???

? ? 延時的代碼其實不是很多,所以我在這里把最重要的兩個函數給粘貼到這里了。因為每個線程都有可能延時,那么怎么處理這些線程之間的關系就是我們需要做的事情了。我們看到了,我們直接用tick_match表示線程需要等待的那個時間點就可以了。當然,tick是不斷增加的,我們可以把尾數相同的線程按照高低順序排列在一起,這樣在對應的tick到來的時候,就直接按照尾數查找就可以了,tick_list_priority_insert就是干了這么一件事情。
?
? ? ?那么,tick什么時候到期呢?到期又該怎么處理呢,我們接著往下看,

[cpp] view plaincopy
  1. void??tick_list_update(void)??
  2. ??{??
  3. ??????
  4. ????LIST?????*tick_head_ptr;??
  5. ????RAW_TASK_OBJ????????????*p_tcb;??
  6. ????LIST????????????????????????????*iter;??
  7. ????LIST????????????????????????????*iter_temp;??
  8. ????
  9. ????RAW_U16???spoke;??
  10. ????
  11. ????RAW_SR_ALLOC();??
  12. ????
  13. ????RAW_CRITICAL_ENTER();??
  14. ??????
  15. ????raw_tick_count++;???????????????????????????????????????????????????????
  16. ????spoke????=?(RAW_U16)(raw_tick_count?&??(TICK_HEAD_ARRAY?-?1)?);??
  17. ????tick_head_ptr??=?&tick_head[spoke];??
  18. ????iter????=?tick_head_ptr->next;??
  19. ??????
  20. ????while?(RAW_TRUE)?{??
  21. ????
  22. ????????/*search?all?the?time?list?if?possible*/??
  23. ????????if?(iter?!=?tick_head_ptr)?{??
  24. ????
  25. ????????????iter_temp?=??iter->next;??
  26. ????????????p_tcb?=??list_entry(iter,?RAW_TASK_OBJ,?tick_list);??
  27. ????
  28. ????????????/*Since?time?list?is?sorted?by?remain?time,?so?just?campare??the?absolute?time*/??
  29. ????????????if?(raw_tick_count?==?p_tcb->tick_match)?{??
  30. ??????????????
  31. ????????????????switch?(p_tcb->task_state)?{??
  32. ????????????????????case?RAW_DLY:??
  33. ??????????????????????????
  34. ????????????????????????p_tcb->block_status?=?RAW_B_OK;???
  35. ????????????????????????p_tcb->task_state?=?RAW_RDY;????
  36. ????????????????????????tick_list_remove(p_tcb);??
  37. ????????????????????????add_ready_list(&raw_ready_queue,?p_tcb);??
  38. ????????????????????????break;???
  39. ????
  40. ????????????????????case?RAW_PEND_TIMEOUT:??
  41. ??????????????????????????
  42. ????????????????????????p_tcb->block_status?=?RAW_B_TIMEOUT;???
  43. ????????????????????????p_tcb->task_state?=?RAW_RDY;???
  44. ????????????????????????p_tcb->block_obj?=?0;??
  45. ????????????????????????tick_list_remove(p_tcb);??
  46. ????????????????????????/*remove?task?on?the?block?list?because?task?is?timeout*/??
  47. ????????????????????????list_delete(&p_tcb->task_list);???
  48. ????????????????????????add_ready_list(&raw_ready_queue,?p_tcb);??
  49. ????????????????????????break;??
  50. ??????????????????????????
  51. ????
  52. ????????????????????case?RAW_PEND_TIMEOUT_SUSPENDED:??
  53. ???????????????????????????????
  54. ????????????????????????p_tcb->block_status?=?RAW_B_TIMEOUT;???
  55. ????????????????????????p_tcb->task_state?=?RAW_SUSPENDED;????
  56. ????????????????????????p_tcb->block_obj?=?0;??
  57. ????????????????????????tick_list_remove(p_tcb);??
  58. ????????????????????????/*remove?task?on?the?block?list?because?task?is?timeout*/??
  59. ????????????????????????list_delete(&p_tcb->task_list);???
  60. ????????????????????????break;??
  61. ???????????????????????
  62. ????
  63. ????
  64. ????????????????????case?RAW_DLY_SUSPENDED:??
  65. ????????????????????????????????????????????????
  66. ????????????????????????p_tcb->task_state??=??RAW_SUSPENDED;??
  67. ????????????????????????p_tcb->block_status?=?RAW_B_OK;???
  68. ????????????????????????tick_list_remove(p_tcb);?????????????????????
  69. ????????????????????????break;??
  70. ????
  71. ????????????????????default:??
  72. ??????????????????????????
  73. ????????????????????????#if?(CONFIG_RAW_ASSERT?>?0)??
  74. ????????????????????????RAW_ASSERT(0);??
  75. ????????????????????????#endif??
  76. ??????????????????????????
  77. ????????????????????????break;??
  78. ????????????????}??
  79. ????
  80. ????????????????iter??=?iter_temp;??
  81. ????????????}??
  82. ????
  83. ????????/*if?current?task?time?out?absolute?time?is?not?equal?current?system?time,?just?break?because?timer?list?is?sorted*/??
  84. ????????????else?{??
  85. ??????????????
  86. ????????????????break;??
  87. ????
  88. ????????????}??
  89. ????
  90. ????????}??
  91. ???
  92. ??????????
  93. ????????/*finish?all?the?time?list?search?*/???
  94. ??????????
  95. ????????else?{??
  96. ??????????????
  97. ????????????break;??
  98. ????????}??
  99. ??????????
  100. ????}??
  101. ????
  102. ????RAW_CRITICAL_EXIT();??
  103. ??}??
  104. ???

? ? ?這個函數是在時鐘中斷的時候被調用的,根據函數的先后順序,看看函數實現了哪些功能,
? ? ?(1)自增raw_tick_count;
? ? ?(2)根據尾數獲取tick隊列的頭指針;
? ? ?(3)開始循環迭代處理延時線程;
? ? ? ? ? ? ?a)如果沒有沒有延時線程,循環跳出;
? ? ? ? ? ? ?b)如果線程的終點tick和當前tick不匹配,跳出循環,因為tick都是排序好的,所以后面的tick肯定不滿足要求;
? ? ? ? ? ? ?c)如果當前tick滿足要求,根據線程狀態進行處理,主要分為延時、阻塞超時、延時掛起、阻塞超時掛起四種狀態;
? ? ? ? ? ? ?d)獲取下一個延時線程,觀察是否滿足要求,如果是繼續回到c,否則退出循環。
? ? ?(4)函數返回,繼續時鐘中斷的剩余操作。
?
? ? ?最后,我們補充一下關于有限時間等待的知識。就像以前關于互斥操作的內容一樣,其實某些情況下,我們是有時間限制的。一段時間沒有獲取資源,我們就不希望等待了,所以這里的延時操作還包括這部分的內容,我們看看阻塞函數的相關代碼就明白了。

[cpp] view plaincopy
  1. RAW_U16?raw_pend_object(RAW_COMMON_BLOCK_OBJECT??*block_common_obj,?RAW_TASK_OBJ?*task_ptr,?RAW_U32?timeout)??
  2. ??{??
  3. ????
  4. ????#if?(CONFIG_RAW_ASSERT?>?0)??
  5. ??????
  6. ????if?(timeout?==?0)?{??
  7. ????????RAW_ASSERT(0);??
  8. ????}??
  9. ??????
  10. ????#endif??
  11. ??????
  12. ????task_ptr->block_obj?=?block_common_obj;??
  13. ????
  14. ??????
  15. ????if?(timeout?==?RAW_WAIT_FOREVER)?{??
  16. ??????????
  17. ????
  18. ????????task_ptr->task_state?=?RAW_PEND;??
  19. ????
  20. ????}??
  21. ????/*task?is?blocked?with?timeout*/??
  22. ????else?{??
  23. ????????
  24. ????????tick_list_insert(task_ptr,timeout);??
  25. ????
  26. ????????task_ptr->task_state?=?RAW_PEND_TIMEOUT;??
  27. ????
  28. ????}??
  29. ??????
  30. ????/*Remove?from?the?ready?list*/??
  31. ????remove_ready_list(&raw_ready_queue,?task_ptr);??
  32. ??????
  33. ????if?(block_common_obj->block_way?==?RAW_BLOCKED_WAY_FIFO)?{??
  34. ??????????
  35. ????????list_insert(&block_common_obj->block_list,?&task_ptr->task_list);??
  36. ????
  37. ????}??
  38. ????
  39. ????else?{??
  40. ??????????
  41. ????????/*add?to?the?priority?sorted?block?list*/??
  42. ????????add_to_priority_list(&block_common_obj->block_list,?task_ptr);??
  43. ??????????
  44. ????}??
  45. ??????
  46. ????return?RAW_SUCCESS;??
  47. ??}??
  48. ???
? ? 大家留意一下這里timeout參數的處理過程,關注一下對應的tick_list_insert函數,這樣就可以明白我的意思了。

嵌入式操作系統內核原理和開發(實時系統中的定時器)

關于定時器的內容,其實我們之前也討論過,也書寫過相應的代碼,但是表達得比較晦澀,效率也比較低。所以我們這里重新再講一下定時器的相關代碼,看看嵌入式系統中的定時器是怎么實現的。在我們之前討論線程延時的時候就使用hash的方法,將不同的線程歸類到不同的延時隊列當中,并且按照時間長短先后排列,這樣在最短的時間內就可以尋找到最合適的線程了。本質上,線程延時和定時器的基本原理是一樣的。唯一的區別就是,線程延時響應的優先級要高一些,而定時器一般由獨立線程完成,rawos也是這么做的。
[cpp] view plaincopy
  1. void?timer_task(void?*pa)???
  2. ?{??
  3. ????RAW_U16?????????????????????????????position;??
  4. ????LIST????????????????????????????????*timer_head_ptr;??
  5. ????LIST????????????????????????????????*iter;??
  6. ????LIST????????????????????????????????*iter_temp;??
  7. ????RAW_TIMER???????????????????*timer_ptr;??
  8. ???
  9. ????timer_sem.count?=?0;??
  10. ??????
  11. ????while?(1)?{??
  12. ??????????
  13. ????????/*timer?task?will?be?blocked?after?call?this?function*/??
  14. ????????raw_semaphore_get(&timer_sem,??RAW_WAIT_FOREVER);??
  15. ??????????
  16. ???????/*Disable?the?system?schedule?we?do?not?need?disable?interrupt?since?nothing?to?do?with?interrupt*/??
  17. ????????raw_disable_sche();??
  18. ???
  19. ????????/*calculate?which??timer_head*/??
  20. ????????raw_timer_count++;????????????????????????????????????????????
  21. ????????position?=?(RAW_U16)(raw_timer_count?&?(TIMER_HEAD_NUMBERS?-?1)?);??
  22. ????????timer_head_ptr??=?&timer_head[position];??
  23. ???
  24. ????????iter????=timer_head_ptr->next;??
  25. ??????????
  26. ????????while?(RAW_TRUE)?{??
  27. ????????????/*if?timer?exits*/????
  28. ????????????????if?(iter?!=timer_head_ptr)?{??
  29. ????????????????????/*Must?use?iter_temp?because?iter?may?be?remove?later.*/??
  30. ????????????????????iter_temp?=?iter->next;??
  31. ????????????????????timer_ptr?=??list_entry(iter,?RAW_TIMER,?timer_list);??
  32. ????????????????????????
  33. ????????????????????/*if?timeout*/??
  34. ?????????????????if?(raw_timer_count?==?timer_ptr->match)?{????
  35. ???
  36. ????????????????????????/*remove?form?timer?list*/??
  37. ????????????????????????timer_list_remove(timer_ptr);??
  38. ????????????????????????/*if?timer?is?reschedulable*/?????????????
  39. ????????????????????if?(timer_ptr->reschedule_ticks)?{??
  40. ????????????????????????????/*Sort?by?remain?time*/??
  41. ????????????????????????????timer_ptr->remain?=?timer_ptr->reschedule_ticks;??
  42. ??????????????????????????????
  43. ????????????????????????????timer_ptr->match??=?raw_timer_count?+?timer_ptr->remain;??
  44. ????????????????????????????position???=?(RAW_U16)(timer_ptr->match?&?(TIMER_HEAD_NUMBERS?-?1));??
  45. ????????????????????????????timer_ptr->to_head?=?&timer_head[position];??
  46. ????????????????????????????timer_list_priority_insert(&timer_head[position],?timer_ptr);??
  47. ????????????????????????????????????
  48. ?????????????????????}???
  49. ???
  50. ????????????????????????/*Any?way?both?condition?need?to?call?registered?timer?function*/??
  51. ???????????????????
  52. ????????????????????if?(timer_ptr->raw_timeout_function)?{??
  53. ????????????????????????timer_ptr->raw_timeout_function(timer_ptr->raw_timeout_param);??
  54. ???????????????????????????????????
  55. ????????????????????}??
  56. ??????????????????
  57. ????????????????????????iter??=?iter_temp;???
  58. ?????????????????}???
  59. ???
  60. ????????????????else?{???
  61. ??????????????????????
  62. ????????????????????????break;??
  63. ??????????????????????
  64. ?????????????}??
  65. ???
  66. ???????????}??
  67. ????????????/*exit?because?timer?is?not?exit*/????????
  68. ????????????else?{??
  69. ???????????????
  70. ???????????????break;??
  71. ????????????}??
  72. ??????????????
  73. ?????????}??
  74. ??????????
  75. ?????????raw_enable_sche();??
  76. ????}??
  77. ?}??
  78. ???
? ? 由于基本原理和之前的線程延時是一樣的,所以這里就不重復了。定時器的基本操作其實也不多,主要包括定時器創建、啟動定時器、修改定時器、關閉定時器、刪除定時器共五個基本函數,大家可以和我一起慢慢看過來。
[cpp] view plaincopy
  1. RAW_U16?raw_timer_create(RAW_TIMER?*timer_ptr,?RAW_U8??*name_ptr,???
  2. ?????????????RAW_VOID??(*expiration_function)(RAW_U32),?RAW_U32?expiration_input,??
  3. ???????????RAW_U32?initial_ticks,?RAW_U32?reschedule_ticks,?RAW_U8?auto_activate)??
  4. ???
  5. ?{??
  6. ???
  7. ????#if?(RAW_TIMER_FUNCTION_CHECK?>?0)??
  8. ??????
  9. ????if?(timer_ptr?==?0)?{??
  10. ????????return?RAW_NULL_OBJECT;??
  11. ????}??
  12. ??????
  13. ????if?(expiration_function?==?0)?{??
  14. ????????return?RAW_NULL_POINTER;??
  15. ????}??
  16. ??????
  17. ????#endif??
  18. ??????
  19. ????timer_ptr->name?=?name_ptr;??
  20. ????timer_ptr->raw_timeout_function?=?expiration_function;??
  21. ????timer_ptr->raw_timeout_param?=?expiration_input;??
  22. ????timer_ptr->init_count?=?initial_ticks;??
  23. ????timer_ptr->reschedule_ticks?=?reschedule_ticks;??
  24. ????timer_ptr->remain?=?0;??
  25. ????timer_ptr->match?=?0;??
  26. ????timer_ptr->timer_state?=?TIMER_DEACTIVE;??
  27. ????timer_ptr->to_head?=?0;??
  28. ??????
  29. ????list_init(&timer_ptr->timer_list);??
  30. ??????
  31. ????if?(auto_activate)?{??
  32. ??????????
  33. ?????????raw_timer_activate(timer_ptr);??
  34. ????}??
  35. ??????
  36. ????return?RAW_SUCCESS;??
  37. ?}??
  38. ???
? ? 創建定時器的操作很簡單,主要是把輸入的參數存入到RAW_TIMER結構里面。相關的參數包括名稱、函數指針、函數參數、初始tick、循環tick、標志參數。當然,由于定時器分為單次定時和循環定時兩種,所以這里會有兩個參數,如果不是循環定時器,循環tick參數可以不用配置。
[cpp] view plaincopy
  1. RAW_U16??raw_timer_activate(RAW_TIMER?*timer_ptr)??
  2. ?{??
  3. ????RAW_U16?position;??
  4. ????RAW_SR_ALLOC();??
  5. ???
  6. ????#if?(RAW_TIMER_FUNCTION_CHECK?>?0)??
  7. ??????
  8. ????if?(timer_ptr?==?0)?{??
  9. ????????return?RAW_NULL_OBJECT;??
  10. ????}??
  11. ??????
  12. ????#endif??
  13. ???
  14. ????/*Timer?state?TIMER_ACTIVE?TIMER_DELETED?is?not?allowed?to?delete*/??
  15. ????if?(timer_ptr->timer_state?==?TIMER_ACTIVE)??
  16. ????????return?RAW_TIMER_STATE_INVALID;???
  17. ???
  18. ????if?(timer_ptr->timer_state?==?TIMER_DELETED)??
  19. ????????return?RAW_TIMER_STATE_INVALID;??
  20. ??????
  21. ????RAW_CRITICAL_ENTER();??
  22. ????timer_ptr->match??=?raw_timer_count?+?timer_ptr->init_count;??
  23. ????position???=?(RAW_U16)(timer_ptr->match?&?(TIMER_HEAD_NUMBERS?-?1)?);??
  24. ????/*Sort?by?remain?time*/??
  25. ????timer_ptr->remain?=?timer_ptr->init_count;??
  26. ????/*Used?by?timer?delete*/??
  27. ????timer_ptr->to_head?=?&timer_head[position];??
  28. ??????
  29. ????timer_list_priority_insert(&timer_head[position],?timer_ptr);??
  30. ????timer_ptr->timer_state?=?TIMER_ACTIVE;??
  31. ???
  32. ????RAW_CRITICAL_EXIT();??
  33. ???
  34. ???
  35. ????return?RAW_SUCCESS;??
  36. ?}??
  37. ???
? ? 啟動定時器,就是把tick加入到定時器隊列當中,看看timer_list_priority_insert這個函數你就明白了。因為整個函數的處理過程涉及到? 全局變量timer_head,所以需要在前后面添加中斷的處理動作。
[cpp] view plaincopy
  1. RAW_U16?raw_timer_change(RAW_TIMER?*timer_ptr,?RAW_U32?initial_ticks,?RAW_U32??reschedule_ticks)??
  2. ?{??
  3. ????RAW_SR_ALLOC();??
  4. ??????
  5. ????#if?(RAW_TIMER_FUNCTION_CHECK?>?0)??
  6. ??????
  7. ????if?(timer_ptr?==?0)?{??
  8. ????????return?RAW_NULL_OBJECT;??
  9. ????}??
  10. ??????
  11. ????#endif??
  12. ??????
  13. ????/*Only?timer?state?TIMER_DEACTIVE?is?not?allowed?here*/???
  14. ????if?(timer_ptr->timer_state?!=?TIMER_DEACTIVE)?{??
  15. ????????return?RAW_TIMER_STATE_INVALID;??
  16. ????}??
  17. ??????
  18. ????if?(timer_ptr->timer_state?==?TIMER_DELETED)?{??
  19. ????????return?RAW_TIMER_STATE_INVALID;??
  20. ????}??
  21. ??????
  22. ????RAW_CRITICAL_ENTER();??
  23. ????timer_ptr->init_count?=?initial_ticks;??
  24. ????timer_ptr->reschedule_ticks?=?reschedule_ticks;??
  25. ????RAW_CRITICAL_EXIT();??
  26. ????return?RAW_SUCCESS;??
  27. ?}??
? ? 定時器修改函數就是把初始tick和循環tick修改一下,當然如果此時定時器還沒有運行,初始tick還有用。但是一旦定時器起作用了,起作用的就只能是循環tick了,這一點大家要好好注意。
[cpp] view plaincopy
  1. RAW_U16?raw_timer_deactivate(RAW_TIMER?*timer_ptr)??
  2. ?{??
  3. ????RAW_SR_ALLOC();??
  4. ???
  5. ????#if?(RAW_TIMER_FUNCTION_CHECK?>?0)??
  6. ??????
  7. ????if?(timer_ptr?==?0)?{??
  8. ????????return?RAW_NULL_OBJECT;??
  9. ????}??
  10. ??????
  11. ????#endif??
  12. ??????
  13. ????/*Timer?state?TIMER_DEACTIVE??TIMER_DELETED?is?not?allowed?to?delete*/??
  14. ????if?(timer_ptr->timer_state?==?TIMER_DEACTIVE)?{??
  15. ????????return?RAW_TIMER_STATE_INVALID;??
  16. ????}??
  17. ??????
  18. ????if?(timer_ptr->timer_state?==?TIMER_DELETED)?{??
  19. ????????return?RAW_TIMER_STATE_INVALID;??
  20. ???
  21. ????}??
  22. ??????
  23. ????RAW_CRITICAL_ENTER();??
  24. ????timer_list_remove(timer_ptr);??
  25. ????timer_ptr->timer_state?=?TIMER_DEACTIVE;??
  26. ????RAW_CRITICAL_EXIT();??
  27. ??????
  28. ????return?RAW_SUCCESS;??
  29. ???
  30. ?}??
  31. ???

? ? 停止定時器,顧名思義就是將定時器從隊列中刪除,同時設置狀態為TIMER_DEACTIVE。當然在進行操作之前需要判斷一下當前定時器的屬性,如果定時器本身已經刪除或者停止,那什么也不用做了。

[cpp] view plaincopy
  1. RAW_U16?raw_timer_delete(RAW_TIMER?*timer_ptr)??
  2. ?{??
  3. ????RAW_SR_ALLOC();??
  4. ??????
  5. ????#if?(RAW_TIMER_FUNCTION_CHECK?>?0)??
  6. ??????
  7. ????if?(timer_ptr?==?0)?{??
  8. ????????return?RAW_NULL_OBJECT;??
  9. ????}??
  10. ??????
  11. ????#endif??
  12. ??????
  13. ????if?(timer_ptr->timer_state?==?TIMER_DELETED)?{??
  14. ??????????
  15. ????????return?RAW_TIMER_STATE_INVALID;??
  16. ??????????
  17. ????}??
  18. ??????
  19. ????RAW_CRITICAL_ENTER();??
  20. ??????
  21. ????timer_list_remove(timer_ptr);??
  22. ????timer_ptr->timer_state?=?TIMER_DELETED;??
  23. ??????
  24. ????RAW_CRITICAL_EXIT();??
  25. ??????
  26. ????return?RAW_SUCCESS;??
  27. ???
  28. ?}??
  29. ???
? ? 定時器的刪除函數和定時器的停止函數是一樣的,主要是判斷邏輯不一樣的。刪除定時器,只要定時器的狀態不是已經刪除就可以了,其他的操作都是一樣的。

嵌入式操作系統內核原理和開發(線程狀態)

從第一篇的os博客以來,談了很多內容,有中斷、切換、調度、內存、互斥和延時等等,但是線程的狀態卻沒有涉及到,今天我們要好好說一說。說到線程的狀態,按照一般的說法,主要包括就緒、延時、阻塞、阻塞超時四個狀態。如果線程沒有死亡的話,那么這幾個狀態也夠用了,但是我們后來發現可能需要對某些線程進行掛起處理,這可能是出現了故障或者是為了調試使用。因此,除了上面的四個狀態,我們還要補充對應的四個掛起狀態,分別是掛起、延時掛起、阻塞掛起、阻塞延時掛起。


? ? ?說到了線程狀態,下面我們就看看常見的線程處理函數有哪些,無外乎線程創建、線程延時、線程掛起、線程恢復和線程刪除等等。?

[cpp] view plaincopy
  1. RAW_U16?raw_task_create(RAW_TASK_OBJ??*task_obj,?RAW_U8??*task_name,??RAW_VOID???*task_arg,???
  2. ???????????????????????????????RAW_U8??task_prio,??RAW_U16??time_slice,??PORT_STACK??*task_stack_base,???
  3. ???????????????????????????????RAW_U32?stack_size,?RAW_TASK_ENTRY?task_entry,?RAW_U8?auto_start)??
  4. ??????????????????????????????????????????
  5. ??{??
  6. ????#if?(RAW_TASK_STACK_CHECK?>?0)??
  7. ????PORT_STACK??*p_stack;??
  8. ????RAW_U32?i;??
  9. ????#endif??
  10. ??????
  11. ????RAW_SR_ALLOC();??
  12. ??????
  13. ????#if?(RAW_TASK_FUNCTION_CHECK?>?0)??
  14. ????
  15. ????if?(task_obj?==?0)?{??
  16. ????????return?RAW_NULL_OBJECT;??
  17. ????}??
  18. ??????
  19. ????if?(task_prio?>=?CONFIG_RAW_PRIO_MAX)?{??
  20. ????????return?RAW_BYOND_MAX_PRIORITY;??
  21. ????}??
  22. ??????
  23. ????if?(task_stack_base?==?0)?{??
  24. ????????return?RAW_NULL_POINTER;??
  25. ????}??
  26. ????
  27. ????if?(task_entry?==?0)?{??
  28. ????????return?RAW_NULL_POINTER;??
  29. ????}??
  30. ??????
  31. ????#endif??
  32. ????
  33. ????RAW_CRITICAL_ENTER();??
  34. ??????
  35. ?????if?(task_prio?==?IDLE_PRIORITY)?{??
  36. ??????????
  37. ????????if?(idle_task_exit)?{??
  38. ??????????????
  39. ????????????RAW_CRITICAL_EXIT();??
  40. ????????????return?RAW_IDLE_EXIT;??
  41. ??????????????????
  42. ????????}??
  43. ??????????
  44. ????????idle_task_exit?=?1;??
  45. ????}??
  46. ????
  47. ????
  48. ????RAW_CRITICAL_EXIT();??
  49. ??????
  50. ????raw_memset(task_obj,?0,?sizeof(RAW_TASK_OBJ));??
  51. ????
  52. ????#if?(CONFIG_ROUND_ROBIN?>?0)??
  53. ??????
  54. ????if?(time_slice)?{??
  55. ????????task_obj->time_total????????=?time_slice;??
  56. ??????????
  57. ????}??
  58. ??????
  59. ????else??{??
  60. ??????????
  61. ????????task_obj->time_total????????=?TIME_SLICE_DEFAULT;??
  62. ????}??
  63. ????
  64. ????task_obj->time_slice?=?task_obj->time_total;??
  65. ????
  66. ????#endif??
  67. ??????
  68. ????if?(auto_start)??
  69. ????????task_obj->task_state?=?RAW_RDY;??
  70. ????else??
  71. ????????task_obj->task_state?=?RAW_SUSPENDED;??
  72. ????
  73. ????
  74. ????#if?(RAW_TASK_STACK_CHECK?>?0)??
  75. ??????
  76. ????task_obj->task_stack_base?=?task_stack_base;??
  77. ????p_stack?=?task_stack_base;??
  78. ??????
  79. ??????for?(i?=?0;?i?<?stack_size;?i++)?{?????????????????????????????
  80. ????????*p_stack++?=0;??????????????????????????????????????????????
  81. ??????????????????
  82. ????}??
  83. ??????????
  84. ????#endif??
  85. ????
  86. ????task_obj->task_stack??=?port_stack_init(task_stack_base,?stack_size,?task_arg,?task_entry);??
  87. ????task_obj->task_name???=?task_name;???
  88. ????task_obj->priority????=?task_prio;??
  89. ????
  90. ????task_create_hook(task_obj);??
  91. ????
  92. ????
  93. ????RAW_CRITICAL_ENTER();??
  94. ??????
  95. ????#if?(RAW_TASK_STACK_CHECK?>?0)??
  96. ????task_obj->stack_size?=?stack_size;??
  97. ????list_insert(&task_head,?&task_obj->stack_check_list);??
  98. ????#endif??
  99. ????
  100. ????if?(auto_start)?{??
  101. ????????add_ready_list_end(&raw_ready_queue,?task_obj);??
  102. ????}??
  103. ??????
  104. ????if?(raw_os_active?!=??RAW_OS_RUNNING)?{?????????????????/*?Return?if?multitasking?has?not?started?????????????????*/??
  105. ????????RAW_CRITICAL_EXIT();??
  106. ????????return?RAW_OS_STOPPED;??
  107. ????}??
  108. ????
  109. ????RAW_CRITICAL_EXIT();??
  110. ??????
  111. ????if?(auto_start)?{??
  112. ????????raw_sched();??
  113. ????}??
  114. ??????
  115. ????return?RAW_SUCCESS;??
  116. ??????????????
  117. ??}??
  118. ???
? ? 創建線程的函數是比較復雜的,內容長一些,參數也多一些。首先看看有哪些參數,雖然很多,但是慢慢梳理一下也不難理解,有名稱、參數、優先級、時間片、堆棧起始指針、堆棧大小、入口函數和標志。整個函數基本上都是賦值的過程,最重要的其實就兩個部分,一個是port_stack_init,另一個就是add_ready_list_end。前者可以對堆棧進行默認處理,比如壓入一些寄存器、壓入函數參數、函數指針等等,后者就是把線程加入到就緒隊列。
[cpp] view plaincopy
  1. RAW_U16??raw_sleep(RAW_U32??dly)???
  2. ??{??
  3. ????RAW_U16?error_status;??
  4. ??????
  5. ????RAW_SR_ALLOC();??
  6. ????
  7. ????#if?(RAW_TASK_FUNCTION_CHECK?>?0)??
  8. ??????
  9. ????if?(raw_int_nesting)?{??
  10. ??????????
  11. ????????return?RAW_NOT_CALLED_BY_ISR;??
  12. ????}??
  13. ????#endif????????
  14. ??????????
  15. ????RAW_CRITICAL_ENTER();??
  16. ????
  17. ????if??(dly)?{??
  18. ????
  19. ????????/*system?is?locked?so?task?can?not?sleep?just?return?immediately*/??
  20. ????????if?(raw_sched_lock)?{?????
  21. ????????????RAW_CRITICAL_EXIT();??????
  22. ????????????return?RAW_SCHED_DISABLE;??
  23. ????????}??
  24. ????
  25. ????????raw_task_active->task_state?=?RAW_DLY;??
  26. ????
  27. ????????tick_list_insert(raw_task_active,?dly);??
  28. ?????????????????????
  29. ????????remove_ready_list(&raw_ready_queue,?raw_task_active);??
  30. ????}??
  31. ??????
  32. ????else?{????
  33. ????????/*make?current?task?to?the?end?of?ready?list*/??
  34. ?????????move_to_ready_list_end(&raw_ready_queue,?raw_task_active);??
  35. ????}??
  36. ????
  37. ????RAW_CRITICAL_EXIT();??
  38. ????
  39. ????raw_sched();?????
  40. ????
  41. ????if?(dly)?{??
  42. ????????/*task?is?timeout?after?sleep*/??
  43. ????????error_status?=?block_state_post_process(raw_task_active,?0);??
  44. ????}??
  45. ????
  46. ????else?{??
  47. ??????????
  48. ????????error_status?=?RAW_SUCCESS;??
  49. ????
  50. ????}??
  51. ??????
  52. ????return?error_status;??
  53. ??}??
  54. ???
? ? 我們之前也介紹過系統的延時功能。延時,就是把線程暫時從就緒隊列清除出來,添加到延時隊列中。當然如果參數為0,那表示作者只是希望暫時釋放cpu的使用權,如果此時沒有同等優先級的任務,那么下一個運行的線程還是它自己。?
[cpp] view plaincopy
  1. RAW_U16??raw_task_suspend(RAW_TASK_OBJ?*task_ptr)??
  2. ??{??
  3. ????RAW_SR_ALLOC();??
  4. ????
  5. ????#if?(RAW_TASK_FUNCTION_CHECK?>?0)??
  6. ??????
  7. ????if?(task_ptr?==?0)?{??
  8. ????????return?RAW_NULL_OBJECT;??
  9. ????}??
  10. ??????
  11. ????#endif????????
  12. ????
  13. ????if?(task_ptr->priority?==?IDLE_PRIORITY)?{??
  14. ????????return?RAW_SUSPEND_TASK_NOT_ALLOWED;??
  15. ????}??
  16. ??????
  17. ????RAW_CRITICAL_ENTER();??
  18. ????
  19. ????if?(task_ptr?==?raw_task_active)?{??
  20. ??????????
  21. ????????if?(raw_sched_lock)?{????
  22. ????????????RAW_CRITICAL_EXIT();??
  23. ????????????return?RAW_SCHED_LOCKED;??
  24. ????????}??
  25. ????}??
  26. ????
  27. ????switch?(task_ptr->task_state)?{??
  28. ????????case?RAW_RDY:??
  29. ????????????task_ptr->task_state??=??RAW_SUSPENDED;??
  30. ????????????remove_ready_list(&raw_ready_queue,?task_ptr);??
  31. ????????????break;??
  32. ????
  33. ????????case?RAW_DLY:??
  34. ????????????task_ptr->task_state??=?RAW_DLY_SUSPENDED;??
  35. ????????????break;??
  36. ????
  37. ????????case?RAW_PEND:??
  38. ??????????????
  39. ????????????task_ptr->task_state??=?RAW_PEND_SUSPENDED;??
  40. ????????????break;??
  41. ??????????????
  42. ????
  43. ????????case?RAW_PEND_TIMEOUT:??
  44. ????????????task_ptr->task_state??=?RAW_PEND_TIMEOUT_SUSPENDED;??
  45. ????????????break;??
  46. ??????????????
  47. ????
  48. ????????case?RAW_DLY_SUSPENDED:??
  49. ????????case?RAW_PEND_SUSPENDED:??
  50. ????????case?RAW_PEND_TIMEOUT_SUSPENDED:??
  51. ????????????RAW_CRITICAL_EXIT();??
  52. ????????????return?RAW_SUSPENDED_AGAIN;??
  53. ??????????
  54. ????
  55. ????????default:??
  56. ??????????????
  57. ????????????#if?(CONFIG_RAW_ASSERT?>?0)??
  58. ????????????RAW_ASSERT(0);??
  59. ????????????#endif??
  60. ??????????????
  61. ????????????RAW_CRITICAL_EXIT();??
  62. ????????????return??RAW_STATE_UNKNOWN;??
  63. ????}??
  64. ????
  65. ????RAW_CRITICAL_EXIT();??
  66. ??????
  67. ????raw_sched();???????
  68. ????
  69. ????return?RAW_SUCCESS;??
  70. ??}??
  71. ???
? ? 掛起任務的動作其實是比較殘暴的,因為此時你不知道線程處于什么狀態。當然任務如果已經被掛起了,那什么也不用做了,否則就需要把任務修改為對應的掛起狀態就可以了。當然如果任務是就緒態的,還得把任務清除處理來。在函數結束的時候,我們需要重新進行調度,因為很有可能當前最高優先級的線程已經發生了改變。
[cpp] view plaincopy
  1. RAW_U16??raw_task_resume(RAW_TASK_OBJ?*task_ptr)??
  2. ??{??
  3. ????RAW_SR_ALLOC();??
  4. ????
  5. ????#if?(RAW_TASK_FUNCTION_CHECK?>?0)??
  6. ??????
  7. ????if?(task_ptr?==?0)?{??
  8. ????????return?RAW_NULL_OBJECT;??
  9. ????}??
  10. ??????
  11. ????#endif????????
  12. ??????
  13. ????RAW_CRITICAL_ENTER();??
  14. ??????
  15. ????switch?(task_ptr->task_state)?{??
  16. ????????case?RAW_RDY:??
  17. ????????case?RAW_DLY:??
  18. ????????case?RAW_PEND:??
  19. ????????case?RAW_PEND_TIMEOUT:??
  20. ??????????????
  21. ????????????RAW_CRITICAL_EXIT();??
  22. ????????????return?HAS_NOT_SUSPENDED;??
  23. ??????????????
  24. ????
  25. ????????case?RAW_SUSPENDED:??
  26. ????????????task_ptr->task_state?=?RAW_RDY;??
  27. ????????????add_ready_list(&raw_ready_queue,?task_ptr);??
  28. ????????????break;??
  29. ????
  30. ????????case?RAW_DLY_SUSPENDED:??
  31. ??????
  32. ????????????task_ptr->task_state?=?RAW_DLY;??
  33. ????????????break;??
  34. ????
  35. ????????case?RAW_PEND_SUSPENDED:??
  36. ??????????
  37. ????????????task_ptr->task_state?=?RAW_PEND;??
  38. ????????????break;??
  39. ????
  40. ????????case?RAW_PEND_TIMEOUT_SUSPENDED:??
  41. ??????????
  42. ????????????task_ptr->task_state?=?RAW_PEND_TIMEOUT;??
  43. ????????????break;??
  44. ??????????????
  45. ????????default:??
  46. ???
  47. ????????????#if?(CONFIG_RAW_ASSERT?>?0)??
  48. ????????????RAW_ASSERT(0);??
  49. ????????????#endif??
  50. ??????????????
  51. ????????????RAW_CRITICAL_EXIT();??
  52. ??????????????
  53. ????????????return?RAW_STATE_UNKNOWN;??
  54. ????
  55. ????}??
  56. ????
  57. ????RAW_CRITICAL_EXIT();??
  58. ??????
  59. ????raw_sched();?????
  60. ????
  61. ????return?RAW_SUCCESS;??
  62. ??}??
  63. ???
? ? 恢復函數其實就是掛起函數的逆向操作。如果任務沒有被掛起,那么什么也不用做。否則就需要把任務的狀態修改為對應的非掛起狀態,當然該就緒的線程還得加入到就緒隊列當中去。同時在函數結束之前不忘調度一下,說不定剛剛釋放的這個線程就是優先級最高的那個線程。
[cpp] view plaincopy
  1. RAW_U16?raw_task_delete(RAW_TASK_OBJ?*task_ptr)??
  2. ?{??
  3. ????RAW_SR_ALLOC();??
  4. ???
  5. ????#if?(RAW_TASK_FUNCTION_CHECK?>?0)??
  6. ???
  7. ????if?(task_ptr?==?0)?{??
  8. ??????????
  9. ????????return?RAW_NULL_OBJECT;??
  10. ????}??
  11. ???
  12. ????if?(raw_int_nesting)?{??
  13. ??????????
  14. ????????return?RAW_NOT_CALLED_BY_ISR;??
  15. ??????????
  16. ????}??
  17. ??????
  18. ????#endif??
  19. ???
  20. ????if?(task_ptr->priority?==?IDLE_PRIORITY)?{??
  21. ??????????
  22. ????????return?RAW_DELETE_TASK_NOT_ALLOWED;??
  23. ????}??
  24. ???
  25. ????RAW_CRITICAL_ENTER();??
  26. ???
  27. ????if?(task_ptr?==?raw_task_active)?{??
  28. ??????????
  29. ????????if?(raw_sched_lock)?{????
  30. ????????????RAW_CRITICAL_EXIT();??
  31. ????????????return?RAW_SCHED_LOCKED;??
  32. ????????}??
  33. ????}??
  34. ??????
  35. ????switch?(task_ptr->task_state)?{??
  36. ????????case?RAW_RDY:??
  37. ????????????remove_ready_list(&raw_ready_queue,?task_ptr);??
  38. ????????????break;??
  39. ???
  40. ????????case?RAW_SUSPENDED:??
  41. ????????????break;??
  42. ???
  43. ????????case?RAW_DLY:?????????????????????????????/*?Task?is?only?delayed,?not?on?any?wait?list?????????????*/??
  44. ????????case?RAW_DLY_SUSPENDED:??
  45. ????????????tick_list_remove(task_ptr);??
  46. ????????????break;??
  47. ???
  48. ????????case?RAW_PEND:??
  49. ????????case?RAW_PEND_SUSPENDED:??
  50. ????????case?RAW_PEND_TIMEOUT:??
  51. ????????case?RAW_PEND_TIMEOUT_SUSPENDED:??
  52. ????????????tick_list_remove(task_ptr);??
  53. ????????????list_delete(&task_ptr->task_list);??
  54. ????????????break;??
  55. ???
  56. ????????default:??
  57. ??????????????
  58. ????????????#if?(CONFIG_RAW_ASSERT?>?0)??
  59. ????????????RAW_ASSERT(0);??
  60. ????????????#endif??
  61. ??????????????
  62. ????????????RAW_CRITICAL_EXIT();??
  63. ????????????return??RAW_STATE_UNKNOWN;??
  64. ????}????
  65. ???
  66. ????task_ptr->task_state?=?RAW_DELETED;?????
  67. ??????
  68. ????#if?(RAW_TASK_STACK_CHECK?>?0)??
  69. ????/*make?after_delete_list?to?right?position*/??
  70. ????after_delete_list?=?task_ptr->stack_check_list.next;??
  71. ??????
  72. ????if?(after_delete_list?==?&task_head)?{??
  73. ????????????????after_delete_list?=?task_head.next;??
  74. ????}??
  75. ??????
  76. ????list_delete(&task_ptr->stack_check_list);??
  77. ???????
  78. ????#endif??
  79. ??????
  80. ????RAW_CRITICAL_EXIT();??
  81. ???
  82. ????raw_sched();???
  83. ??????
  84. ????return?RAW_SUCCESS;??
  85. ?}??
  86. ???
? ? 刪除函數的動作其實是比較殘忍的,因為此時你不清楚線程已經執行到哪一步了,擁有了那些資源,正在處理哪些資源,所以沒事不要用這個函數。這里做的只是把任務從就緒隊列、等待隊列和阻塞隊列清除出來,但是真正善后的工作要比這多得多,如果有興趣,你看看linux的exit函數就明白了。

嵌入式操作系統內核原理和開發(等值block內存池設計)

?內存池設計是嵌入式系統的一個重要環節,之前我們也討論過相關的內容。但是,看了rawos的代碼之后,我覺得rawos的內存池設計更有特點。整個內存池的設計非常健壯,不但考慮了字節對齊的問題,而且還引入了等待調度機制,這是我所沒有想到的。所以,在此我很愿意和大家分享這份優秀的代碼。閑話不多說,我們看看rawos的mempool數據結構是什么樣的,
[cpp] view plaincopy
  1. typedef?struct?MEM_POOL??
  2. ??{??
  3. ????RAW_COMMON_BLOCK_OBJECT???????common_block_obj;??
  4. ??????
  5. ????/*?Define?the?number?of?available?memory?blocks?in?the?pool.??*/??
  6. ????RAW_U32??????raw_block_pool_available;??
  7. ????
  8. ????/*?Define?the?head?pointer?of?the?available?block?pool.??*/??
  9. ????RAW_U8??????*raw_block_pool_available_list;??
  10. ????
  11. ??}?MEM_POOL;??
  12. ????
? ? 內存池的結構非常簡單,主要包括了通用阻塞結構、block數值,block起始指針。內存池下面可以包括若干個block,每個block的大小都是相等的,同時block之間是通過鏈表串聯在一起的,這個我們看了后面的代碼就明白了。mempool的處理函數不多,就三個,初始化、申請、釋放函數。
[cpp] view plaincopy
  1. RAW_U16??raw_block_pool_create(MEM_POOL?*pool_ptr,?RAW_U8??*name_ptr,?RAW_U32??block_size,?RAW_VOID??*pool_start,?RAW_U32??pool_size)??
  2. ??{??
  3. ????
  4. ????//MEM_POOL???*tail_ptr;??????????????????/*?Working?block?pool?pointer??*/??
  5. ????RAW_U32???????blocks;?????????????????????/*?Number?of?blocks?in?pool????*/??
  6. ????RAW_U8????????*block_ptr;??????????????????/*?Working?block?pointer???????*/??
  7. ????RAW_U8????????*next_block_ptr;?????????????/*?Next?block?pointer??????????*/??
  8. ????RAW_U8????????*end_of_pool;????????????????/*?End?of?pool?area????????????*/??
  9. ????RAW_U8??????????block_align_mask;??
  10. ??????
  11. ????#if?(RAW_BLOCK_FUNCTION_CHECK?>?0)??
  12. ???????/*?Check?for?invalid?pool?size.??*/??
  13. ??????
  14. ?????if?(pool_size?<?(block_size?+??block_size)?)?{??
  15. ??????????
  16. ????????return?RAW_BLOCK_SIZE_ERROR;??
  17. ????}??
  18. ????
  19. ????if?(pool_ptr?==?0)?{??
  20. ??????????
  21. ????????return?RAW_NULL_OBJECT;??
  22. ????}??
  23. ??????
  24. ????if?(pool_start?==?0)?{??
  25. ??????????
  26. ????????return?RAW_NULL_POINTER;??
  27. ????}??
  28. ??????
  29. ????#endif??
  30. ????
  31. ?????block_align_mask?=?sizeof(void?*)?-?1u;??
  32. ????
  33. ????if?(((RAW_U32)pool_start?&?block_align_mask)){???????????????????????????????
  34. ????
  35. ????????return?RAW_INVALID_ALIGN;??
  36. ????
  37. ????}??
  38. ???????
  39. ????if?((pool_size?&?block_align_mask))?{?????
  40. ??????????
  41. ????????return?RAW_INVALID_ALIGN;??
  42. ????}??
  43. ????
  44. ????if?((block_size?&?block_align_mask))?{?????
  45. ??????????
  46. ????????return?RAW_INVALID_ALIGN;??
  47. ????}??
  48. ??????
  49. ????/*Init?the?list*/??
  50. ????list_init(&pool_ptr->common_block_obj.block_list);??
  51. ????
  52. ????/*?Setup?the?basic?block?pool?fields.??*/??
  53. ????pool_ptr?->common_block_obj.name?=??name_ptr;??
  54. ????pool_ptr?->common_block_obj.block_way?=?0;??
  55. ??????
  56. ????/*?Calculate?the?end?of?the?pool's?memory?area.??*/??
  57. ????end_of_pool?=??(RAW_U8??*)?pool_start?+?pool_size;??
  58. ????
  59. ????/*?Walk?through?the?pool?area,?setting?up?the?available?block?list.??*/??
  60. ????blocks?=????????????0;??
  61. ????block_ptr?=?????????(RAW_U8??*)?pool_start;??
  62. ????next_block_ptr?=????block_ptr?+?block_size;??
  63. ??????
  64. ????while?(next_block_ptr?<=?end_of_pool)?{??
  65. ????
  66. ????????????blocks++;??
  67. ??????????????
  68. ????????????if?(next_block_ptr?==?end_of_pool)?{??
  69. ??????????????????
  70. ????????????????break;??
  71. ????
  72. ????????????}??
  73. ????
  74. ????????????/*?Setup?the?link?to?the?next?block.??*/??
  75. ????????????*((RAW_U8??*?*)?block_ptr)?=??next_block_ptr;??
  76. ????
  77. ????????????/*?Advance?to?the?next?block.??*/??
  78. ????????????block_ptr?=???next_block_ptr;??
  79. ????
  80. ????????????/*?Update?the?next?block?pointer.??*/??
  81. ????????????next_block_ptr?=??block_ptr?+?block_size;??
  82. ????}??
  83. ????
  84. ????/*?Set?the?last?block's?forward?pointer?to?NULL.??*/??
  85. ????*((RAW_U8??*?*)?block_ptr)?=??0;??
  86. ????
  87. ????/*?Save?the?remaining?information?in?the?pool?control?block.??*/??
  88. ????pool_ptr?->raw_block_pool_available?=??blocks;??
  89. ????
  90. ????
  91. ????pool_ptr?->raw_block_pool_available_list?=??(RAW_U8??*)?pool_start;??
  92. ????
  93. ????
  94. ????return?RAW_SUCCESS;??
  95. ??}??
  96. ???
? ? 上面就是內存池的創建函數,入參共有五個參數,分別是mempool結構、名稱、block大小、pool起始地址、pool大小。函數基本內容如下所示,
? ? ?(1)判斷內存池、指針參數合法性;
? ? ?(2)檢驗指針是否n字節對齊,n取決于地址的大小;
? ? ?(3)構建block鏈表,前后相連,最后一個block指向NULL指針;
? ? ?(4)將pool首地址賦值給raw_block_pool_available_list,函數返回。
[cpp] view plaincopy
  1. RAW_U16?raw_block_allocate(MEM_POOL?*pool_ptr,?RAW_VOID?**block_ptr,?RAW_U32?wait_option)??
  2. ??{??
  3. ??????
  4. ????RAW_U16?????????????status;???????????????????????????????
  5. ????
  6. ????RAW_U8??????*work_ptr;????????????????????????
  7. ????
  8. ????RAW_SR_ALLOC();??
  9. ????
  10. ????#if?(RAW_BLOCK_FUNCTION_CHECK?>?0)??
  11. ???????
  12. ????if?(pool_ptr?==?0)?{??
  13. ????????return?RAW_NULL_OBJECT;??
  14. ????}??
  15. ??????
  16. ????if?(block_ptr?==?0)?{??
  17. ??????????
  18. ????????return?RAW_NULL_POINTER;??
  19. ????}??
  20. ????
  21. ????if?(raw_int_nesting)?{??
  22. ????
  23. ????????if?(wait_option?!=?RAW_NO_WAIT)?{??
  24. ??????????????
  25. ????????????return?RAW_NOT_CALLED_BY_ISR;??
  26. ????????}??
  27. ??????????
  28. ????}??
  29. ??????
  30. ????#endif??
  31. ????
  32. ????RAW_CRITICAL_ENTER();??
  33. ????
  34. ????/*?Determine?if?there?is?an?available?block.??*/??
  35. ????if?(pool_ptr?->raw_block_pool_available)?{??
  36. ????
  37. ????????/*?Yes,?a?block?is?available.??Decrement?the?available?count.??*/??
  38. ????????pool_ptr?->raw_block_pool_available--;??
  39. ????
  40. ????????/*?Pickup?the?current?block?pointer.??*/??
  41. ????????work_ptr?=??pool_ptr?->raw_block_pool_available_list;??
  42. ????
  43. ????????/*?Return?the?first?available?block?to?the?caller.??*/??
  44. ????????*((RAW_U8?**)block_ptr)?=??work_ptr;??
  45. ????
  46. ????????/*?Modify?the?available?list?to?point?at?the?next?block?in?the?pool.?*/??
  47. ????????pool_ptr?->raw_block_pool_available_list?=?*((RAW_U8?**)work_ptr);??
  48. ????
  49. ????????/*?Set?status?to?success.??*/??
  50. ????????status?=??RAW_SUCCESS;??
  51. ????}??
  52. ????
  53. ????/*if?no?block?memory?is?available?then?do?it?depend?wait_option*/??
  54. ????else?{????
  55. ??????????
  56. ????????if?(wait_option?==?RAW_NO_WAIT)?{???
  57. ????????????*((RAW_U8?**)block_ptr)?????=?0;??
  58. ????????????RAW_CRITICAL_EXIT();??
  59. ????????????return?RAW_NO_PEND_WAIT;??
  60. ????????}????
  61. ????
  62. ????????/*system?is?locked?so?task?can?not?be?blocked?just?return?immediately*/??
  63. ????????if?(raw_sched_lock)?{????
  64. ????????????*((RAW_U8?**)block_ptr)?????=?0;??
  65. ????????????RAW_CRITICAL_EXIT();??????
  66. ????????????return?RAW_SCHED_DISABLE;??????
  67. ????????}??
  68. ??????
  69. ????????raw_pend_object(&pool_ptr->common_block_obj,?raw_task_active,?wait_option);??
  70. ????
  71. ????????RAW_CRITICAL_EXIT();??
  72. ????
  73. ????????raw_sched();???????????????????????????????????????????????
  74. ????
  75. ????????RAW_CRITICAL_ENTER();??
  76. ????
  77. ????????*((RAW_U8?**)block_ptr)?????=?0;??
  78. ????????status?=?block_state_post_process(raw_task_active,?block_ptr);??
  79. ??????????
  80. ????????RAW_CRITICAL_EXIT();????
  81. ????
  82. ????}??
  83. ????
  84. ????
  85. ????return?status;??
  86. ????
  87. ??}??
  88. ???
? ? 和其他的內存池申請函數不一樣,這里有一個wait_option選項。也就是說,如果當前沒有合適的block,那么你可以選擇等待處理。一旦別的線程釋放內存,你就可以得到調度繼續運行了。當然你也可以不等待,一旦尋找不到合適的block,立即返回為NULL。
[cpp] view plaincopy
  1. RAW_U16?raw_block_release(MEM_POOL?*pool_ptr,?RAW_VOID?*block_ptr)??
  2. ??{??
  3. ????LIST?*block_list_head;??
  4. ??????
  5. ????RAW_U8????????*work_ptr;???????????/*?Working?block?pointer???*/??
  6. ????RAW_U8??????????need_schedule?=?0;??
  7. ??????
  8. ????RAW_SR_ALLOC();??
  9. ????
  10. ????#if?(RAW_BLOCK_FUNCTION_CHECK?>?0)??
  11. ???????
  12. ????if?(block_ptr?==?0)?{??
  13. ????????return?RAW_NULL_OBJECT;??
  14. ????}??
  15. ??????
  16. ????if?(pool_ptr?==?0)?{??
  17. ??????????
  18. ????????return?RAW_NULL_OBJECT;??
  19. ????}??
  20. ??????
  21. ????#endif??
  22. ????
  23. ????block_list_head?=?&pool_ptr->common_block_obj.block_list;??
  24. ??????
  25. ????RAW_CRITICAL_ENTER();??
  26. ??????
  27. ????work_ptr?=??((RAW_U8?*)?block_ptr);??
  28. ??????
  29. ????if?(is_list_empty(block_list_head))?{??????????
  30. ????
  31. ????????/*?Put?the?block?back?in?the?available?list.??*/??
  32. ????????*((RAW_U8??**)?work_ptr)?=??pool_ptr?->raw_block_pool_available_list;??
  33. ????
  34. ????????/*?Adjust?the?head?pointer.??*/??
  35. ????????pool_ptr?->raw_block_pool_available_list?=??work_ptr;??????????
  36. ????
  37. ????????/*?Increment?the?count?of?available?blocks.??*/??
  38. ????????pool_ptr?->raw_block_pool_available++;??
  39. ????}??
  40. ????
  41. ????else?{??
  42. ??????????
  43. ????????need_schedule?=?1;??
  44. ????????wake_send_msg(list_entry(block_list_head->next,?RAW_TASK_OBJ,?task_list),??block_ptr);?????
  45. ????
  46. ????}??
  47. ?????
  48. ????RAW_CRITICAL_EXIT();??
  49. ????
  50. ????if?(need_schedule)?{??
  51. ????????raw_sched();??
  52. ????}??
  53. ??????
  54. ????/*?Return?completion?status.??*/??
  55. ????return?RAW_SUCCESS;??
  56. ??}??
  57. ???
? ? 和其他的內存free函數不一樣,這里的free函數多了一個wake_send_msg的功能。這也就是說,當然如果存在阻塞等待資源的線程,那么把資源送給該線程,同時把該線程喚醒,還要把need_schedule設置為1才可以。當然如果沒有等待的線程,那么直接把內存插入到鏈表前面中即可,就是這么簡單。

嵌入式操作系統內核原理和開發(改進的鏈表內存分配算法)

之前我自己也寫過基于鏈表的內存分配算法,但是看了rawos的內存分配算法,還是感覺rawos寫的要更好些。大家都知道,鏈表內存分配就是從鏈表中快速找到最合適的內存塊分配給用戶線程。因為這種分配是隨機的,所以一般來說內存碎片是不可避免的。但是,我們在編寫代碼的時候還是應該注意內存合并的問題。這里我們為什么要介紹rawos的內存分配呢,還包括有這么幾個原因,


? ? (1)整個內存鏈表采用循環鏈表的方法,使用起來簡單可靠;

? ? (2)內存分配的時候所有節點都是連在一起的,通過標志數據判斷當前數據是否已經分配;

? ? (3)如果當前沒有合適的內存,可以選擇等待;

? ? (4)代碼充分考慮了合并和切分的問題;

? ? (5)在遍歷內存的時候靈活處理了中斷問題,可以及時響應中斷,這個比我考慮得要周到很多;

? ? (6)代碼注釋豐富,只要多讀幾遍,是可以慢慢理解代碼內容的。


? ? 整個代碼的結構清晰,共四個函數,分別是創建函數、內存查找函數、內存分配函數、內存釋放函數。其中在內存分配和釋放的時候,都會調用到內存查找函數。

[cpp] view plaincopy
  1. RAW_U16??raw_byte_pool_create(RAW_BYTE_POOL_STRUCT?*pool_ptr,?RAW_U8?*name_ptr,?RAW_VOID?*pool_start,?RAW_U32?pool_size)??
  2. ?{??
  3. ???
  4. ????RAW_U8??*block_ptr;??????????????????/*?Working?block?pointer???????*/??
  5. ????RAW_U8???byte_align_mask;??
  6. ??????
  7. ????#if?(RAW_BYTE_FUNCTION_CHECK?>?0)??
  8. ???
  9. ?????if?(pool_ptr?==?0)?{??
  10. ??????????????
  11. ?????????return?RAW_NULL_POINTER;??
  12. ?????}??
  13. ???
  14. ?????if?(pool_start?==?0)?{??
  15. ??????????????
  16. ?????????return?RAW_NULL_POINTER;??
  17. ?????}??
  18. ???
  19. ?????/*?Check?for?invalid?pool?size.??*/??
  20. ?????if?(pool_size?<?RAW_BYTE_POOL_MIN)?{??
  21. ??????????????
  22. ?????????return?RAW_BYTE_SIZE_ERROR;??
  23. ??????????????????
  24. ?????}??
  25. ???
  26. ????#endif??
  27. ???
  28. ?????byte_align_mask?=?sizeof(void?*)?-?1u;??
  29. ???
  30. ????/*pool_start?needs?4?bytes?aligned*/??
  31. ????if?(((RAW_U32)pool_start?&?byte_align_mask)){???????????????????????????????
  32. ???
  33. ????????return?RAW_INVALID_ALIGN;??
  34. ???
  35. ????}??
  36. ???
  37. ????/*pool_size?needs?4?bytes?aligned*/??
  38. ????if?((pool_size?&?byte_align_mask))?{?????
  39. ??????????
  40. ????????return?RAW_INVALID_ALIGN;??
  41. ????}??
  42. ???
  43. ?/*Init?the?list*/??
  44. ????list_init(&pool_ptr->common_block_obj.block_list);??
  45. ??????????
  46. ????/*?Setup?the?basic?byte?pool?fields.??*/??
  47. ????pool_ptr?->common_block_obj.name?=??name_ptr;??
  48. ????pool_ptr?->common_block_obj.block_way?=?0;??
  49. ????pool_ptr?->raw_byte_pool_search?=??(RAW_U8??*)?pool_start;??
  50. ????pool_ptr?->raw_byte_pool_owner?=?0;??
  51. ???
  52. ????/*?Initially,?the?pool?will?have?two?blocks.??One?large?block?at?the??
  53. ???????beginning?that?is?available?and?a?small?allocated?block?at?the?end?
  54. ???????of?the?pool?that?is?there?just?for?the?algorithm.??Be?sure?to?count?
  55. ???????the?available?block's?header?in?the?available?bytes?count.??*/??
  56. ????pool_ptr?->raw_byte_pool_available?=???pool_size?-?sizeof(void?*)?-?sizeof(RAW_U32);??
  57. ????pool_ptr?->raw_byte_pool_fragments?=????2;??
  58. ???
  59. ????/*?Calculate?the?end?of?the?pool's?memory?area.??*/??
  60. ????block_ptr?=??((RAW_U8??*)?pool_start)?+?(RAW_U32)?pool_size;??
  61. ???
  62. ????/*?Backup?the?end?of?the?pool?pointer?and?build?the?pre-allocated?block.??*/??
  63. ????block_ptr?=??block_ptr?-?sizeof(RAW_U32);??
  64. ????*((RAW_U32?*)?block_ptr)?=??RAW_BYTE_BLOCK_ALLOC;??
  65. ????block_ptr?=??block_ptr?-?sizeof(RAW_U8??*);??
  66. ????*((RAW_U8??*?*)?block_ptr)?=??pool_start;??
  67. ???
  68. ????/*?Now?setup?the?large?available?block?in?the?pool.??*/??
  69. ????*((RAW_U8??*?*)?pool_start)?=??block_ptr;??
  70. ????block_ptr?=??(RAW_U8??*)?pool_start;??
  71. ????block_ptr?=??block_ptr?+?sizeof(RAW_U8??*);??
  72. ????*((RAW_U32?*)?block_ptr)?=??RAW_BYTE_BLOCK_FREE;??
  73. ???
  74. ????
  75. ?????return?RAW_SUCCESS;??
  76. ?}??
  77. ???
? ? 內存池的創建還是比較簡單的,基本的步驟如下所示,

? ? (1)驗證參數的合法性,比如是否是NULL指針、是否對齊;

? ? (2)初始化內存池的基本參數,比如說名稱、默認阻塞結構、入口地址、剩余大小等等;

? ? (3)構建兩個block節點,前面是free節點,后面是alloc節點,兩個節點構成循環節點。每個節點有三個部分組成,分別是下一跳地址、標志、buffer。


[cpp] view plaincopy
  1. static?void?*raw_byte_pool_search(RAW_BYTE_POOL_STRUCT?*pool_ptr,?RAW_U32?memory_size)??
  2. ?{??
  3. ???
  4. ????RAW_U8??*??current_ptr;????????????????/*?Current?block?pointer??????*/??
  5. ????RAW_U8??*??next_ptr;???????????????????/*?Next?block?pointer?????????*/??
  6. ????RAW_U32?????available_bytes;????????????/*?Calculate?bytes?available??*/??
  7. ????RAW_U32?????examine_blocks;?????????????/*?Blocks?to?be?examined??????*/??
  8. ???
  9. ????RAW_SR_ALLOC();??
  10. ??????
  11. ????/*?Disable?interrupts.??*/??
  12. ????RAW_CRITICAL_ENTER();??
  13. ???
  14. ?????/*?First,?determine?if?there?are?enough?bytes?in?the?pool.??*/??
  15. ?????if?(memory_size?>=?pool_ptr?->raw_byte_pool_available)?{??
  16. ?????
  17. ????????/*?Restore?interrupts.??*/??
  18. ????????RAW_CRITICAL_EXIT();??
  19. ?????????/*?Not?enough?memory,?return?a?NULL?pointer.??*/??
  20. ?????????return???0;??
  21. ?????}??
  22. ???
  23. ?????/*?Walk?through?the?memory?pool?in?search?for?a?large?enough?block.??*/??
  24. ?????current_ptr?=??????pool_ptr?->raw_byte_pool_search;??
  25. ?????examine_blocks?=???pool_ptr?->raw_byte_pool_fragments?+?1;??
  26. ?????available_bytes?=??0;??
  27. ??????????
  28. ?????do?{??
  29. ??????
  30. ?????????/*?Check?to?see?if?this?block?is?free.??*/??
  31. ?????????if?(*((RAW_U32?*)?(current_ptr?+?sizeof(RAW_U8??*)))?==?RAW_BYTE_BLOCK_FREE)?{??
  32. ???????????
  33. ???
  34. ?????????????/*?Block?is?free,?see?if?it?is?large?enough.??*/??
  35. ???
  36. ?????????????/*?Pickup?the?next?block's?pointer.??*/??
  37. ?????????????next_ptr?=??*((RAW_U8??*?*)?current_ptr);??
  38. ???
  39. ?????????????/*?Calculate?the?number?of?byte?available?in?this?block.??*/??
  40. ?????????????available_bytes?=??next_ptr?-?current_ptr?-?sizeof(RAW_U8??*)?-?sizeof(RAW_U32);??
  41. ???
  42. ?????????????/*?If?this?is?large?enough,?we?are?done?because?our?first-fit?algorithm?
  43. ????????????????has?been?satisfied!??*/??
  44. ?????????????if?(available_bytes?>=?memory_size)?{??
  45. ???????????????
  46. ?????????????????/*?Find?the?suitable?position?*/??
  47. ?????????????????break;??
  48. ?????????????}??
  49. ??????????????????????????
  50. ?????????????else?{??
  51. ???????????????
  52. ?????????????????/*?Clear?the?available?bytes?variable.??*/??
  53. ?????????????????available_bytes?=??0;??
  54. ???
  55. ?????????????????/*?Not?enough?memory,?check?to?see?if?the?neighbor?is??
  56. ????????????????????free?and?can?be?merged.??*/??
  57. ?????????????????if?(*((RAW_U32?*)?(next_ptr?+?sizeof(RAW_U8??*)))?==?RAW_BYTE_BLOCK_FREE)?{??
  58. ???????????????????
  59. ?????????????????????/*?Yes,?neighbor?block?can?be?merged!??This?is?quickly?accomplished?
  60. ????????????????????????by?updating?the?current?block?with?the?next?blocks?pointer.??*/??
  61. ?????????????????????*((RAW_U8??*?*)?current_ptr)?=??*((RAW_U8??*?*)?next_ptr);??
  62. ???
  63. ?????????????????????/*?Reduce?the?fragment?number,?and?does?not?need?to?increase??available?bytes?since??
  64. ?????????????????????????they?are?already?there*/??
  65. ???????????????????????????
  66. ?????????????????????pool_ptr?->raw_byte_pool_fragments--;??
  67. ???
  68. ?????????????????????/*?Update?the?search?pointer,?if?it?is?involved?*/??
  69. ?????????????????????if?(pool_ptr?->raw_byte_pool_search?==??next_ptr)?{??
  70. ?????????????????????????pool_ptr?->raw_byte_pool_search?=??current_ptr;??
  71. ?????????????????????}??
  72. ??????????????????????????????????????????
  73. ?????????????????}??
  74. ?????????????????else?{??
  75. ???????????????????
  76. ?????????????????????/*?Neighbor?is?not?free?so?get?to?the?next?search?point*/??
  77. ?????????????????????current_ptr?=??*((RAW_U8??*?*)?next_ptr);??
  78. ???
  79. ?????????????????????/*?Reduse?the?examined?block?since?we?have?skiped?one?search?*/??
  80. ?????????????????????if?(examine_blocks)?{??
  81. ?????????????????????????examine_blocks--;??
  82. ?????????????????????}??
  83. ?????????????????}??
  84. ?????????????}??
  85. ?????????}??
  86. ?????????else??
  87. ?????????{??
  88. ???
  89. ?????????????/*?Block?is?not?free,?move?to?next?block.??*/??
  90. ?????????????current_ptr?=?*((RAW_U8??*?*)?current_ptr);??
  91. ?????????}???
  92. ???
  93. ?????????/*?finish?one?block?search*/??
  94. ?????????if?(examine_blocks)?{??
  95. ?????????????examine_blocks--;??
  96. ?????????}??
  97. ???????????
  98. ????????/*?Restore?interrupts?temporarily.??*/??
  99. ????????RAW_CRITICAL_EXIT();??
  100. ???
  101. ????????/*?Disable?interrupts.??*/??
  102. ???????RAW_CRITICAL_ENTER();??
  103. ???
  104. ?????????/*?Determine?if?anything?has?changed?in?terms?of?pool?ownership.??*/??
  105. ?????????if?(pool_ptr?->raw_byte_pool_owner?!=?raw_task_active)??
  106. ?????????{??
  107. ???
  108. ?????????????/*?Pool?changed?ownership?during?interrupts.so?we?reset?the?search*/??
  109. ???????????????????
  110. ?????????????current_ptr?=??????pool_ptr?->raw_byte_pool_search;??
  111. ?????????????examine_blocks?=???pool_ptr?->raw_byte_pool_fragments?+?1;??
  112. ???
  113. ?????????????/*?Setup?our?ownership?again.??*/??
  114. ?????????????pool_ptr?->raw_byte_pool_owner?=???raw_task_active;??
  115. ?????????}??
  116. ??????????????????
  117. ?????}?while?(examine_blocks);??
  118. ???
  119. ?????/*?Determine?if?a?block?was?found.??If?so,?determine?if?it?needs?to?be?
  120. ????????split.??*/??
  121. ?????if?(available_bytes)?{??
  122. ???????
  123. ?????????/*?Do?we?need?to?split?this?block?if?this?is?big?enough.*/??
  124. ?????????if?((available_bytes?-?memory_size)?>=?((RAW_U32)?RAW_BYTE_BLOCK_MIN))?{??
  125. ???????????
  126. ?????????????/*?Split?the?block.??*/??
  127. ?????????????next_ptr?=??current_ptr?+?memory_size?+?sizeof(RAW_U8??*)?+?sizeof(RAW_U32);??
  128. ???
  129. ?????????????/*?Setup?the?new?free?block.??*/??
  130. ?????????????*((RAW_U8??*?*)?next_ptr)?=??*((RAW_U8??*?*)?current_ptr);??
  131. ?????????????*((RAW_U32?*)?(next_ptr?+?sizeof(RAW_U8??*)))?=??RAW_BYTE_BLOCK_FREE;??
  132. ???
  133. ?????????????/*?Increase?the?total?fragment?counter.??*/??
  134. ?????????????pool_ptr?->raw_byte_pool_fragments++;??
  135. ???
  136. ?????????????/*?Update?the?current?pointer?to?point?at?the?newly?created?block.??*/??
  137. ?????????????*((RAW_U8??*?*)?current_ptr)?=?next_ptr;??
  138. ???
  139. ?????????????/*?Set?available?equal?to?memory?size?for?subsequent?calculation.??*/??
  140. ?????????????available_bytes?=??memory_size;??
  141. ?????????}??
  142. ???
  143. ????????/*?In?any?case,?mark?the?current?block?as?allocated.??*/??
  144. ????????*((RAW_U32?*)?(current_ptr?+?sizeof(RAW_U8??*)))?=??RAW_BYTE_BLOCK_ALLOC;??
  145. ???
  146. ????????/*?Reduce?the?number?of?available?bytes?in?the?pool.??*/??
  147. ????????pool_ptr?->raw_byte_pool_available?=??pool_ptr?->raw_byte_pool_available?-?available_bytes??
  148. ???????????????????????????????????????-?sizeof(RAW_U8??*)?-?sizeof(RAW_U32);??
  149. ???
  150. ????????/*?Adjust?the?pointer?for?the?application.??*/??
  151. ????????current_ptr?=??current_ptr?+?sizeof(RAW_U8??*)?+?sizeof(RAW_U32);??
  152. ??????????
  153. ?????}??
  154. ??????????
  155. ?????else?{??
  156. ??????????????
  157. ?????????/*?Set?current?pointer?to?NULL?to?indicate?nothing?was?found.??*/??
  158. ?????????current_ptr?=??0;??
  159. ?????}??
  160. ???
  161. ???
  162. ????/*?Restore?interrupts?temporarily.??*/??
  163. ????RAW_CRITICAL_EXIT();??
  164. ???
  165. ????/*?Return?the?searched?result*/??
  166. ????return?current_ptr;??
  167. ?}??
  168. ???
? ? 這個內存查找函數是這四個函數中最難的那個函數。看上去內容很多,其實我們可以分成兩個部分分析,其中do~while的這個部分就是負責查找合適的內存塊,后面的if~else部分是對分配的節點進行后續處理,下面我們可以詳細來看看,

? ? (1)驗證剩余空間是否滿足條件,不滿足立即返回;

? ? (2)遍歷所有的block節點,查找合適的節點,

? ? ? ? ? ? a、如果尋找到合適的節點,那么立即跳出循環;

? ? ? ? ? ? b、如果沒有尋找到合適的節點,可以做一些額外的工作,比如說對相連的free節點進行合并;

? ? ? ? ? ? c、循環的結束條件是examine_blocks全部遍歷結束,當然如果發現raw_byte_pool_owner不為當前線程,那么一切重來;

? ? (3)對于獲得的block節點,同樣有兩種方法處理,

? ? ? ? ? ? a、如果除去分配的空間,剩下的節點空間仍然很多,那么可以把當前節點拆分成兩個節點進行處理;

? ? ? ? ? ? b、如果剩下的空間本身就很有限,那么全部分配給當前線程算了。

? ? (4)函數返回,current_ptr包含了那個分配好的地址。


[cpp] view plaincopy
  1. RAW_U16??raw_byte_allocate(RAW_BYTE_POOL_STRUCT?*pool_ptr,?RAW_VOID?**memory_ptr,???
  2. ?????????????????????????????????????RAW_U32?memory_size,??RAW_U32?wait_option)??
  3. ?{??
  4. ???
  5. ????RAW_U16????????status;?????????????????/*?Return?status??????????????*/??
  6. ????RAW_U8?????????*work_ptr;???????????????/*?Working?byte?pointer???????*/??
  7. ????RAW_TASK_OBJ??*current_work_task;??
  8. ??????
  9. ????RAW_SR_ALLOC();??
  10. ???
  11. ????#if?(RAW_BYTE_FUNCTION_CHECK?>?0)??
  12. ???
  13. ?????if?(pool_ptr?==?0)?{??
  14. ??????????????
  15. ?????????return?RAW_NULL_POINTER;??
  16. ?????}??
  17. ???
  18. ???
  19. ?????if?(memory_ptr?==?0)?{??
  20. ??????????????
  21. ?????????return?RAW_NULL_POINTER;??
  22. ?????}??
  23. ???
  24. ?????if?(raw_int_nesting)?{??
  25. ???
  26. ????????if?(wait_option?!=?RAW_NO_WAIT)??
  27. ????????????return?RAW_NOT_CALLED_BY_ISR;??
  28. ???
  29. ????}??
  30. ???????
  31. ????#endif??
  32. ??????
  33. ?????/*?align?the?memory?size?to?4?byte*/??
  34. ??????????
  35. ?????memory_size?=?((memory_size?&?(~3u))?+4u);??
  36. ???
  37. ????current_work_task?=?raw_task_active;??
  38. ???
  39. ?????/*?Disable?interrupts.??*/??
  40. ????RAW_CRITICAL_ENTER();??
  41. ???
  42. ??????/*?Loop?to?handle?cases?where?the?owner?of?the?pool?changed.??*/??
  43. ?????do?{??
  44. ???????
  45. ????????/*?Indicate?that?this?thread?is?the?current?owner.??*/??
  46. ????????pool_ptr?->raw_byte_pool_owner?=??current_work_task;??
  47. ???
  48. ????????/*?Restore?interrupts.??*/??
  49. ????????RAW_CRITICAL_EXIT();??
  50. ??????
  51. ????????/*Search?for?free?memory*/??
  52. ????????work_ptr?=??raw_byte_pool_search(pool_ptr,?memory_size);??
  53. ???
  54. ????????/*?Disable?interrupts.??*/??
  55. ????????RAW_CRITICAL_ENTER();??
  56. ??????????
  57. ????????/*if?raw_byte_pool_owner?changed?and?we?have?not?got?memory?yet,?continue?tom?do?search*/??
  58. ?????}?while?((!work_ptr)?&&?(pool_ptr?->raw_byte_pool_owner?!=?current_work_task));??
  59. ???
  60. ??????????
  61. ???
  62. ?????/*?Determine?if?memory?was?found.??*/??
  63. ?????if?(work_ptr)?{??
  64. ??????????????
  65. ????????/*?Copy?the?pointer?into?the?return?destination.??*/??
  66. ????????*memory_ptr?=??(RAW_U8??*)?work_ptr;??
  67. ??????????
  68. ?????????RAW_CRITICAL_EXIT();??
  69. ????????/*?Set?the?status?to?success.??*/??
  70. ????????return??RAW_SUCCESS;??
  71. ???????????
  72. ?????}??
  73. ??????????
  74. ?????else?{??
  75. ???
  76. ?????????if?(wait_option)?{??
  77. ???
  78. ????????????/*system?is?locked?so?task?can?not?be?blocked?just?return?immediately*/??
  79. ????????????if?(raw_sched_lock)?{???
  80. ??????????????????
  81. ????????????????*memory_ptr???=?0;??
  82. ????????????????RAW_CRITICAL_EXIT();??????
  83. ????????????????return?RAW_SCHED_DISABLE;??????
  84. ????????????}??
  85. ???
  86. ????????????raw_task_active->msg?=?(RAW_VOID?*)memory_size;??
  87. ????????????raw_pend_object(&pool_ptr->common_block_obj,?raw_task_active,?wait_option);??
  88. ??????????????
  89. ?????????}??
  90. ??????????????????
  91. ????????else?{??
  92. ??????????????????
  93. ????????????*memory_ptr???=?0;??
  94. ????????????RAW_CRITICAL_EXIT();??
  95. ????????????/*?Immediate?return,?return?error?completion.??*/??
  96. ????????????return?RAW_NO_MEMORY;??
  97. ?????????}??
  98. ?????}??
  99. ???
  100. ?????/*?Restore?interrupts.??*/??
  101. ????RAW_CRITICAL_EXIT();??
  102. ???
  103. ????raw_sched();??
  104. ???
  105. ????RAW_CRITICAL_ENTER();??
  106. ??????
  107. ????*memory_ptr???=?0;??
  108. ????status?=?block_state_post_process(raw_task_active,?memory_ptr);??
  109. ??????
  110. ????RAW_CRITICAL_EXIT();????
  111. ??????
  112. ?????return??status;??
  113. ?}??
  114. ???
? ? 和內存查找函數相比,內存分配函數就簡單多了,

? ? (1)判斷參數的合法性;

? ? (2)循環查找鏈表節點,尋找合適的內存塊,注意循環的跳出條件,即work_ptr==NULL且沒有其他線程的干擾;

? ? (3)如果尋找到了合適的節點,那么沒有問題,反之需要根據wait_option判斷是否需要把自己掛起在等待隊列上;

? ? (4)線程再次得到運行的機會,memory_ptr中包含了內存地址,函數返回。


[cpp] view plaincopy
  1. RAW_U16??raw_byte_release(RAW_BYTE_POOL_STRUCT?*pool_ptr,?void??*memory_ptr)??
  2. ?{??
  3. ????RAW_U8??*work_ptr;???????????/*?Working?block?pointer??????*/??
  4. ??????
  5. ????LIST?????????????????????????????????????????*iter;??
  6. ????LIST?????????????????????????????????????????*iter_temp;??
  7. ????LIST?????????????????????????????????????????*byte_head_ptr;??
  8. ????RAW_TASK_OBJ?????????????????????????*task_ptr;??
  9. ???
  10. ????RAW_U8?need_sche?=?0;??
  11. ??????
  12. ????RAW_SR_ALLOC();??
  13. ???
  14. ???
  15. ????#if?(RAW_BYTE_FUNCTION_CHECK?>?0)??
  16. ???
  17. ????if?(pool_ptr?==?0)?{??
  18. ??????????????
  19. ?????????return?RAW_NULL_POINTER;??
  20. ?????}??
  21. ???
  22. ?????if?(memory_ptr?==?0)?{??
  23. ??????????????
  24. ?????????return?RAW_NULL_POINTER;??
  25. ?????}??
  26. ??????????
  27. ????#endif??
  28. ??????
  29. ????byte_head_ptr?=?&pool_ptr->common_block_obj.block_list;??
  30. ??????
  31. ????/*?Back?off?the?memory?pointer?to?pickup?its?header.??*/??
  32. ????work_ptr?=?(RAW_U8??*)?memory_ptr?-?sizeof(RAW_U8??*)?-?sizeof(RAW_U32);??
  33. ??????????????????
  34. ????/*?Disable?interrupts.??*/??
  35. ????RAW_CRITICAL_ENTER();??
  36. ???
  37. ????/*?Indicate?that?this?thread?is?the?current?owner.??*/??
  38. ????pool_ptr?->raw_byte_pool_owner?=??raw_task_active;??
  39. ???
  40. ?????/*?Release?the?memory.*/??
  41. ?????*((RAW_U32?*)?(work_ptr?+?sizeof(RAW_U8??*)))?=??RAW_BYTE_BLOCK_FREE;??
  42. ???
  43. ?????/*?Update?the?number?of?available?bytes?in?the?pool.??*/??
  44. ?????pool_ptr?->raw_byte_pool_available?=????
  45. ???????pool_ptr?->raw_byte_pool_available?+?(*((RAW_U8??*?*)?(work_ptr))?-?work_ptr);??
  46. ???
  47. ?????/*?Set?the?pool?search?value?appropriately.??*/??
  48. ?????pool_ptr?->raw_byte_pool_search?=??work_ptr;??
  49. ???
  50. ????iter?=?byte_head_ptr->next;??
  51. ??????
  52. ?????while?(iter?!=?byte_head_ptr)?{??
  53. ???
  54. ????????iter_temp?=?iter->next;??
  55. ????????task_ptr?=??list_entry(iter,?RAW_TASK_OBJ,?task_list);??
  56. ??????????
  57. ????????RAW_CRITICAL_EXIT();??
  58. ?????????/*?See?if?the?request?can?be?satisfied.??*/??
  59. ?????????work_ptr?=??raw_byte_pool_search(pool_ptr,?(RAW_U32)task_ptr->msg);?????
  60. ????????????RAW_CRITICAL_ENTER();??
  61. ????
  62. ??????????/*?If?there?is?not?enough?memory,?break?this?loop!??*/??
  63. ??????????if?(!work_ptr)?{??
  64. ?????????????break;??
  65. ??????????}??
  66. ???
  67. ????????????wake_send_msg(task_ptr,?(RAW_VOID?*)work_ptr);????
  68. ????????need_sche?=?1;??
  69. ????????iter?=?iter_temp;??
  70. ??????????
  71. ?????}??
  72. ???
  73. ????RAW_CRITICAL_EXIT();??
  74. ???
  75. ????if?(need_sche)?{??
  76. ??????????
  77. ????????raw_sched();??
  78. ??????????
  79. ????}??
  80. ??????
  81. ????return?RAW_SUCCESS;??
  82. ??????????
  83. ?}??
  84. ???
? ? 內存釋放后,本來我們只需要把內存節點的標志設置為RAW_BYTE_BLOCK_FREE即可。可是,我們還需要判斷當前是否存在等待喚醒的線程,是否可以為等待線程尋找到合適的內存節點,整個函數基本流程如下所示,

? ? (1)判斷參數合法性;

? ? (2)將當前block節點設置為可用,調整pool中的參數數據;

? ? (3)遍歷等待線程,并為之查找到合適的節點,尋找到節點跳出,遍歷完所有等待節點也跳出; ??

? ? (4)如果有線程被喚醒,那么需要重新調度,否則函數返回。

嵌入式操作系統內核原理和開發(優先級的修改)

之前在和rawos作者的閑聊中,rawos作者認為實時操作系統中最大的特色就是互斥量的問題。一開始,我對這個看法其實是有保留意見的,直到我看到了修改優先級的相關代碼,才開始對作者的看法有了很大的認同感。實話說,在嵌入式實時系統中修改優先級是一個很復雜的事情,為什么呢,因為這其中涉及到了互斥量的問題。我們大家都知道,在嵌入式系統中優先級反轉是一個繞不去的砍。低優先級的任務因為獲得了互斥量因而比高優先級的任務獲得了更多的運行機會,這從根本上違背了實時系統設計的初衷。所以,人們為了解決除了這一問題,提出了優先級互斥量和優先級繼承兩種方法。

?

??? 優先級互斥量的辦法比較簡單,ucos也是這么做的。我們在分配一個互斥量的時候,也給這個互斥量分配一定的優先級。任何獲得該互斥量的任務都會把自己的優先級修改為互斥量的優先級,這樣保證了獲得該優先級的任務可以在最短的時間內完成,盡快釋放資源。但是,這也會導致一個問題,那就是該互斥量需要占用一個優先級,而且比此優先級高的任務也沒辦法獲得該互斥量。另外一種方法就是優先級繼承的方法,簡單一點說就是任何對阻塞線程優先級的修改,都會導致擁有此互斥量的任務進行優先級的修改。閑話不多說,我們看看rawos上面的代碼是怎么描述的,

[cpp] view plaincopy
  1. RAW_U16?raw_task_priority_change?(RAW_TASK_OBJ?*task_ptr,?RAW_U8?new_priority,?RAW_U8?*old_priority)??
  2. {??
  3. ????RAW_U8?ret_pri;??
  4. ????RAW_U16?error;??
  5. ??????
  6. ????RAW_SR_ALLOC();??
  7. ??
  8. ????#if?(RAW_TASK_FUNCTION_CHECK?>?0)??
  9. ??????
  10. ????if?(task_ptr?==?0)?{??
  11. ??????????
  12. ????????return?RAW_NULL_OBJECT;??
  13. ????}??
  14. ??
  15. ????if?(old_priority?==?0)?{??
  16. ??????????
  17. ????????return?RAW_NULL_OBJECT;??
  18. ????}??
  19. ??????
  20. ????#endif????????
  21. ??
  22. ????/*Idle?task?is?not?allowed?to?change?priority*/??
  23. ????if?(task_ptr->priority?>=?IDLE_PRIORITY)?{??
  24. ??????????
  25. ????????return?RAW_CHANGE_PRIORITY_NOT_ALLOWED;??
  26. ????}??
  27. ??????
  28. ???/*Not?allowed?change?to?idle?priority*/??
  29. ????if?(new_priority?==?IDLE_PRIORITY)?{???????????????
  30. ??
  31. ????????return?RAW_CHANGE_PRIORITY_NOT_ALLOWED;??
  32. ????}??
  33. ??
  34. ??????
  35. ????RAW_CRITICAL_ENTER();??
  36. ??
  37. ????#if?(CONFIG_RAW_MUTEX?>?0)??
  38. ????ret_pri?=?chg_pri_mutex(task_ptr,?new_priority,?&error);??
  39. ??
  40. ????if?(error?!=?RAW_SUCCESS)?{??
  41. ????????goto?error_exit;??
  42. ????}??
  43. ??
  44. ????task_ptr->bpriority?=?new_priority;??
  45. ????new_priority?=?ret_pri;??
  46. ??????
  47. ????#else??
  48. ??????
  49. ????task_ptr->bpriority?=?new_priority;??
  50. ????#endif??
  51. ??
  52. ????*old_priority?=?task_ptr->priority;??
  53. ??
  54. ????error?=?change_interal_task_priority(task_ptr,?new_priority);??
  55. ??????
  56. ????if?(error?!=?RAW_SUCCESS)?{??
  57. ????????goto?error_exit;??
  58. ????}??
  59. ??
  60. ????RAW_CRITICAL_EXIT();??
  61. ??????
  62. ????do_possible_sche();????
  63. ??????????
  64. ????return?RAW_SUCCESS;??
  65. ??????
  66. ????error_exit:??
  67. ????RAW_CRITICAL_EXIT();??
  68. ????return?error;??
  69. ??????
  70. }??

??? 這個函數是系統本身提供的一個函數,內容不算少,但是重點可以放在兩個子函數上面。一個部分是函數chg_pri_mutex,另外一個函數是change_interal_task_priority。前者判斷當前優先級是否可以修改,后者判斷如何對當前的任務進行修改,后面我們會看到會對這個問題有一個詳細的說明。

[cpp] view plaincopy
  1. RAW_U8?chg_pri_mutex(RAW_TASK_OBJ?*tcb,?RAW_U8?priority,?RAW_U16?*error)??
  2. {??
  3. ????RAW_MUTEX???*mtxcb;??
  4. ????RAW_U8??hi_pri,?low_pri,?pri;??
  5. ????RAW_TASK_OBJ?*first_block_task;??
  6. ????LIST?*block_list_head;??
  7. ??????
  8. ????hi_pri??=?priority;??
  9. ????low_pri?=?0;??
  10. ??????
  11. ????mtxcb?=?(RAW_MUTEX??*)(tcb->block_obj);??
  12. ??????
  13. ????if?(mtxcb)?{??
  14. ??
  15. ????????/*if?it?is?blocked?on?mutex*/??
  16. ????????if?(mtxcb->common_block_obj.object_type?==?RAW_MUTEX_OBJ_TYPE)?{??
  17. ??????????????
  18. ????????????if?(mtxcb->policy?==?RAW_MUTEX_CEILING_POLICY)?{??
  19. ????????????????pri?=?mtxcb->ceiling_prio;??
  20. ??????????????????
  21. ????????????????if?(pri?>?low_pri)?{??
  22. ????????????????????low_pri?=?pri;??
  23. ????????????????}??
  24. ????????????}??
  25. ????????}??
  26. ????}??
  27. ??
  28. ????/*?Locked?Mutex?*/??
  29. ????pri?=?hi_pri;??
  30. ????for?(mtxcb?=?tcb->mtxlist;?mtxcb?!=?0;?mtxcb?=?mtxcb->mtxlist)?{??
  31. ????????switch?(mtxcb->policy)?{??
  32. ??????????????
  33. ??????????case?RAW_MUTEX_CEILING_POLICY:??
  34. ????????????pri?=?mtxcb->ceiling_prio;??
  35. ????????????if?(?pri?>?low_pri?)?{??
  36. ????????????????low_pri?=?pri;??
  37. ????????????}??
  38. ????????????break;??
  39. ??????????????
  40. ??????????case?RAW_MUTEX_INHERIT_POLICY:??
  41. ??????????????
  42. ????????????block_list_head?=?&mtxcb->common_block_obj.block_list;??
  43. ??????????????
  44. ????????????if?(!is_list_empty(block_list_head))?{??
  45. ????????????????first_block_task?=?list_entry(block_list_head->next,?RAW_TASK_OBJ,?task_list);???
  46. ????????????????pri?=?first_block_task->priority;??
  47. ????????????}??
  48. ??????????????
  49. ????????????break;??
  50. ??????????????
  51. ??????????default:??
  52. ????????????/*?nothing?to?do?*/??
  53. ????????????break;??
  54. ????????}??
  55. ????????if?(?pri?<?hi_pri?)?{??
  56. ????????????hi_pri?=?pri;??
  57. ????????}??
  58. ????}??
  59. ??
  60. ????if?(priority?<?low_pri)?{??
  61. ??????????
  62. ????????*error?=?RAW_EXCEED_CEILING_PRIORITY;??
  63. ????????return?RAW_EXCEED_CEILING_PRIORITY;??
  64. ????}??
  65. ??
  66. ????*error?=?RAW_SUCCESS;??
  67. ????return?hi_pri;??
  68. }??

??? 上面的代碼還是比較復雜的,我們看到其實任務的優先級不是可以隨便修改的,因為當前任務可能已經占有了許多互斥量資源,而這些互斥量資源其實是有約束條件的。如果占有的互斥量類型是那種帶優先級的互斥量,那么必須找出的那個最低優先級的互斥量,至少修改的任務優先級不能比它高。剩下的工作就是在繼承優先級的體制下尋找到最高的優先級,僅此而已。

[cpp] view plaincopy
  1. RAW_U16?change_interal_task_priority(RAW_TASK_OBJ?*task_ptr,?RAW_U8?new_priority)??
  2. {??
  3. ????RAW_U8?old_pri;??
  4. ??
  5. ????switch?(task_ptr->task_state)?{??
  6. ????????case?RAW_RDY:??
  7. ??????????????
  8. ????????????remove_ready_list(&raw_ready_queue,?task_ptr);??
  9. ????????????task_ptr->priority?=?new_priority;??
  10. ??????????????
  11. ????????????if?(task_ptr?==?raw_task_active)?{??
  12. ????????????????add_ready_list_head(&raw_ready_queue,?task_ptr);??
  13. ??????????????????
  14. ????????????}??
  15. ??????????????
  16. ????????????else?{??
  17. ??????????????
  18. ????????????????add_ready_list_end(&raw_ready_queue,?task_ptr);??
  19. ????????????}??
  20. ??????
  21. ????????????break;??
  22. ??
  23. ????????case?RAW_DLY:?????????????????????????????/*?Nothing?to?do?except?change?the?priority?in?the?OS_TCB?*/??
  24. ????????case?RAW_SUSPENDED:??
  25. ????????case?RAW_DLY_SUSPENDED:??
  26. ??????????????
  27. ????????????task_ptr->priority?=?new_priority;????????????????????????/*?Set?new?task?priority*/??
  28. ??????????????
  29. ????????????break;??
  30. ??
  31. ????????case?RAW_PEND:??
  32. ????????case?RAW_PEND_TIMEOUT:??
  33. ????????case?RAW_PEND_SUSPENDED:??
  34. ????????case?RAW_PEND_TIMEOUT_SUSPENDED:??
  35. ????????????old_pri?=?task_ptr->priority;??
  36. ????????????task_ptr->priority?=?new_priority;????
  37. ????????????change_pend_list_priority(task_ptr);??
  38. ??????????????
  39. ????????????#if?(CONFIG_RAW_MUTEX?>?0)??
  40. ????????????mtx_chg_pri(task_ptr,?old_pri);??
  41. ????????????#endif??
  42. ??????????????
  43. ????????????break;??
  44. ??
  45. ????????default:??
  46. ??????????????
  47. ????????????#if?(CONFIG_RAW_ASSERT?>?0)??
  48. ????????????RAW_ASSERT(0);??
  49. ????????????#endif??
  50. ??????????????
  51. ????????????return?RAW_STATE_UNKNOWN;??
  52. ????}??
  53. ??
  54. ????return?RAW_SUCCESS;??
  55. ??
  56. }??

??? 前面,我們說到了優先級的修改函數,而change_interal_task_priority就是完成這一功能的函數。當然,我們需要針對目前任務的狀態對任務的優先級進行修改,如果任務此時正在運行或者延遲運行,那么還好辦,關鍵是如果此時任務已經阻塞了,那么考慮的情況就多了。

[cpp] view plaincopy
  1. RAW_VOID?mtx_chg_pri(RAW_TASK_OBJ?*tcb,?RAW_U8?oldpri)??
  2. {??
  3. ????RAW_MUTEX???????*mtxcb;??
  4. ????RAW_TASK_OBJ????*mtxtsk;??
  5. ??
  6. ????mtxcb?=?(RAW_MUTEX??*)(tcb->block_obj);??
  7. ??????
  8. ????if?(mtxcb->common_block_obj.object_type?==?RAW_MUTEX_OBJ_TYPE)?{??
  9. ??????????
  10. ????????if?(mtxcb->policy?==?RAW_MUTEX_INHERIT_POLICY)?{??
  11. ????????????mtxtsk?=?mtxcb->mtxtsk;??
  12. ??????????????
  13. ????????????if?(mtxtsk->priority?>?tcb->priority)?{??
  14. ????????????????/*?Since?the?highest?priority?of?the?lock?wait?task?
  15. ?????????????????became?higher,?raise?the?lock?get?task?priority?
  16. ????????????????higher?*/??
  17. ????????????????change_interal_task_priority(mtxtsk,?tcb->priority);??
  18. ????????????}??
  19. ??
  20. ????????????/*the?highest?priority?task?blocked?on?this?mutex?may?decrease?priority?so?reset?the?mutex?task?priority*/??
  21. ????????????else?if(mtxtsk->priority?==?oldpri)?{??
  22. ??
  23. ????????????????release_mutex(mtxtsk,?0);??
  24. ????????????}??
  25. ??????????????
  26. ????????}??
  27. ????}??
  28. ??
  29. }??

??? mtx_chg_pri函數此時考慮的不光是它自己優先級的問題,它還需要考慮此時占有互斥量的這個任務優先級該怎么修改。我們進一步看看release_mutex下面做了哪些工作。

[cpp] view plaincopy
  1. static?RAW_VOID?release_mutex(RAW_TASK_OBJ?*tcb,?RAW_MUTEX?*relmtxcb)??
  2. {??
  3. ????RAW_MUTEX???*mtxcb,?**prev;??
  4. ????RAW_U8??newpri,?pri;??
  5. ????RAW_TASK_OBJ?*first_block_task;??
  6. ????LIST?*block_list_head;??
  7. ??????
  8. ????/*?(B)?The?base?priority?of?task?*/??
  9. ????newpri?=?tcb->bpriority;??
  10. ??
  11. ????/*?(A)?The?highest?priority?in?mutex?which?is?locked?*/??
  12. ????pri?=?newpri;??
  13. ????prev?=?&tcb->mtxlist;??
  14. ????while?((mtxcb?=?*prev)?!=?0)?{??
  15. ????????if?(mtxcb?==?relmtxcb)?{??
  16. ????????????/*?Delete?from?list?*/??
  17. ????????????*prev?=?mtxcb->mtxlist;??
  18. ????????????continue;??
  19. ????????}??
  20. ??
  21. ????????switch?(mtxcb->policy)?{??
  22. ??????????case?RAW_MUTEX_CEILING_POLICY:??
  23. ????????????pri?=?mtxcb->ceiling_prio;??
  24. ????????????break;??
  25. ??????????????
  26. ??????????case?RAW_MUTEX_INHERIT_POLICY:??
  27. ??????????????
  28. ????????????block_list_head?=?&mtxcb->common_block_obj.block_list;??
  29. ??????????????
  30. ????????????if?(!is_list_empty(block_list_head))?{??
  31. ????????????????first_block_task?=?list_entry(block_list_head->next,?RAW_TASK_OBJ,?task_list);???
  32. ????????????????pri?=?first_block_task->priority;??
  33. ????????????}??
  34. ??????????????
  35. ????????????break;??
  36. ??????????????
  37. ??????????default:??
  38. ????????????break;??
  39. ????????}??
  40. ????????if?(newpri?>?pri)?{??
  41. ????????????newpri?=?pri;??
  42. ????????}??
  43. ??
  44. ????????prev?=?&mtxcb->mtxlist;??
  45. ????}??
  46. ??
  47. ????if?(?newpri?!=?tcb->priority?)?{??
  48. ????????/*?Change?priority?of?lock?get?task?*/??
  49. ????????change_interal_task_priority(tcb,?newpri);??
  50. ????}??
  51. ??????
  52. }??
??? 這個函數的工作就是修改那個獲得互斥量的任務的優先級的,在尋找到最高優先級之后,那么又需要調用change_internall_task_priority函數了。有過遞歸函數編寫經驗的朋友就知道了,這其實就是一個典型的遞歸函數。change_internall_task_priority函數調用到release_mutex,然而release_mutex又調用到change_internall_task_priority,感覺上沒完沒了了,其實不是這樣。遞歸函數都是需要出口的,這個函數的出口就是change_internall_task_priority,一切等到獲得的那個任務不再是阻塞任務為止,整個修改的過程就結束了。當然,任務優先級恢復的工作也是非常麻煩的,不管是帶優先級的互斥量還是優先級繼承機制的互斥量,額外的優先級修改和計算都是必須的,不知道我講清楚了沒有。rawos在互斥量的最大進步就是進一步說明了擁有多互斥量的任務該如何修改優先級,當然了,函數迭代的過程多了堆棧使用的空間也多了,有沒有溢出的危險,這是我們需要考慮的另外一個重要因素。

嵌入式操作系統內核原理和開發(總結篇)

? 很多朋友都喜歡嵌入式操作系統的內容,但是如何實現和仿真這樣一個系統一直是困擾我們的難題。現在鄭重推薦一下raw-os系統,在我們的博客當中也多次提到了這個代碼,希望大家可以多多閱讀,不斷加深對os的認識。如果有可能,大家可以到 http://ishare.iask.sina.com.cn/f/33440944.html這里下載raw-os的vc6.0版本,單步調試每一行代碼,肯定會有所收獲。


(01)?嵌入式操作系統內核原理和開發(優先級的修改) ?

(02)嵌入式操作系統內核原理和開發(改進的鏈表內存分配算法)

(03)嵌入式操作系統內核原理和開發(等值block內存池設計)

(04)嵌入式操作系統內核原理和開發(線程狀態)

(05)嵌入式操作系統內核原理和開發(實時系統中的定時器)

(06)嵌入式操作系統內核原理和開發(延時操作)

(07)嵌入式操作系統內核原理和開發(實時調度)

(08)嵌入式操作系統內核原理和開發(消息隊列)

(09)嵌入式操作系統內核原理和開發(事件)

(10)嵌入式操作系統內核原理和開發(互斥量)

(11)嵌入式操作系統內核原理和開發(信號量)

(12)嵌入式操作系統內核原理和開發(最快、最優、最差內存分配算法)

(13)嵌入式操作系統內核原理和開發(基于鏈表節點的內存分配算法)

(14)嵌入式操作系統內核原理和開發(固定內存分配算法)

(15)嵌入式操作系統內核原理和開發(內存分配算法)

(16)嵌入式操作系統內核原理和開發(頭文件調整)

(17)嵌入式操作系統內核原理和開發(改進型優先級調度)

(18)嵌入式操作系統內核原理和開發(通用優先級調度)

(19)嵌入式操作系統內核原理和開發(多線程輪轉)

(20)嵌入式操作系統內核原理和開發(任務創建和堆棧溢出檢查)

(21)嵌入式操作系統內核原理和開發(線程切換)

(22)嵌入式操作系統內核原理和開發(系統中斷仿真)

(23)嵌入式操作系統內核原理和開發(基礎)

(24)嵌入式操作系統內核原理和開發(地址空間)

(25)嵌入式操作系統內核原理和開發(中斷)

(26)嵌入式操作系統內核原理和開發(cpu的那些事)

(27)嵌入式操作系統內核原理和開發(開篇)


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/449815.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/449815.shtml
英文地址,請注明出處:http://en.pswp.cn/news/449815.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java中對象比較

在Java之中不僅僅存在兩個數字與兩個字符串之間的比較&#xff0c;還存在兩個對象之間的比較。 眾所周知&#xff0c;兩個數字之間的比較我們使用“”&#xff0c;兩個字符串之間的比較我們使用“equals()”&#xff0c;那么兩個對象之間如何進行比較呢&#xff1f;既然要進行兩…

如何安裝pfbprophet

最近facebook出來了一個軟件包&#xff0c;可以用于時序數據預測&#xff0c;但是安裝上&#xff0c;遇到問題不小&#xff0c;本人也在安裝中遇到大大小小各種問題&#xff0c;然后查了各種資料&#xff0c;進行研究&#xff0c;終于找到一個最簡單的辦法。 不廢話&#xff0…

JeeSite 4.0 (1.0)開發環境部署運行

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 環境要求 1、Java SDK 1.8 下載 2、Eclipse IDE for Java EE Mars 2 (4.5.2) 下載&#xff08;依Eclipse舉例&#xff0c;IDEA雷同。&…

dojo 七 DOM dojo/dom

官方教程&#xff1a;Dojo DOM Functions對dom的使用&#xff0c;需要引用包dojo/dom。1.獲取節點&#xff0c;dom.byIdbyId中既可以傳遞一個字符串&#xff0c;也可以傳遞一個節點對象 require(["dojo/dom", "dojo/domReady!"], function(dom) {function …

quantaxis中使用docker安裝,出現的問題

本人在使用docker安裝quantaxis時&#xff0c;經常發生docker報錯&#xff0c;原因是與 wsl2沖突。之前不知道原因&#xff0c;直接就是卸載重裝&#xff0c;累死我了。現在知道后&#xff0c;將解決方案記下&#xff0c;幫助后來者繼續前行。 管理員打開cmd&#xff0c;輸入 …

并發編程(多進程1)

一 multiprocessing模塊介紹 python中的多線程無法利用多核優勢&#xff0c;如果想要充分地使用多核CPU的資源&#xff08;os.cpu_count()查看&#xff09;&#xff0c;在python中大部分情況需要使用多進程。Python提供了multiprocessing。 multiprocessing模塊用來開啟子進…

LInux 下文件包的使用

1 .deb   http://wiki.ubuntu.org.cn/MySQL%E5%AE%89%E8%A3%85%E6%8C%87%E5%8D%97 2 .rpm

@RequiresPermissions 注解說明

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 RequiresAuthentication 驗證用戶是否登錄&#xff0c;等同于方法subject.isAuthenticated() 結果為true時。 RequiresUser 驗證用戶…

千位分隔符轉換為數字

最近下載了akshare里面的數據&#xff0c;然后發現有些數據格式為1,300這種格式&#xff0c;為str格式&#xff0c;但是又無法直接強制轉換之類方式&#xff0c;特地尋求答案&#xff0c;并做筆記&#xff0c;留待自己與后來者一起避坑。 from locale import * atof(123,456)…

一本通1629聰明的燕姿

1629&#xff1a;聰明的燕姿 時間限制: 1000 ms 內存限制: 524288 KB【題目描述】 城市中人們總是拿著號碼牌&#xff0c;不停尋找&#xff0c;不斷匹配&#xff0c;可是誰也不知道自己等的那個人是誰。 可是燕姿不一樣&#xff0c;燕姿知道自己等的人是誰&#xff0c;…

IT職場人生系列之二十四:程序員如何增加收入

這是IT職場人生系列的第二十四篇。&#xff08;序言&#xff0c;專欄目錄&#xff09; 程序員的收入是廣受關注的問題&#xff0c;很多人從業3&#xff5e;5年之后就會遇到這個收入瓶頸。盡管物價不斷上漲&#xff0c;程序員尤其是初、中級程序員的收入不升反降。即使上次在某…

ASP 代碼當前記錄集不支持更新問題的解決辦法。

錯誤類型&#xff1a;ADODB.Recordset (0x800A0CB3)當前記錄集不支持更新。這可能是提供程序的限制&#xff0c;也可能是選定鎖定類型的限制。 /Model/manage/Admin_Admin.asp, 第 35 行 找到放在數據庫文件的--- 右鍵--》屬性---》安全----》添加IIS來賓用戶---》權限為&#…

@PathVariable 注解 說明

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 PathVariable 映射 URL 綁定的占位符 帶占位符的 URL 是 Spring3.0 新增的功能&#xff0c;該功能在SpringMVC 向 REST 目標挺進發展過…

數據清洗,篩選

本人在私募&#xff0c;負責數據收集以及清洗&#xff0c;就是包括收集數據&#xff0c;按照領導要求&#xff0c;選出滿足條件的數據&#xff0c;用于校驗策略是否正確。 現在就在這進行代碼上傳&#xff0c;即用于自己總結整理&#xff0c;也用于供大家學習了解&#xff0c;實…

JS媒體查詢

樣式的改變使用C3的媒體查詢 行為和功能的改變使用JS的媒體查詢 matchMedia()方法參數可寫任何一個CSSmedia規則&#xff0c;返回的是新的MediaQueryList對象&#xff0c;該對象有兩個屬性 media&#xff1a;查詢語句的內容matches&#xff1a;檢查查詢結果&#xff0c;返回boo…

Ruby初步介紹

Ruby是腳本語言,與傳統的C, Java不同的是,它不需要經過編譯,而是直接可以被執行 Ubuntu下執行第一個ruby腳本 print("Hello David, This is your first Ruby script.\n") davidubuntu:~/RubyTrain/Basic$ ruby Hello.rb 運行結果: Hello David, This is your first R…

C/C++ main用法總結

今天看到一篇很好的文章&#xff0c;詳細的講解了C、C中的main函數&#xff0c;以及returne的用法。轉載過來大家一起分享下。轉自&#xff1a;http://www.cnblogs.com/ct6816678/archive/2012/10/26/2741824.htmlreturn是C預定義的語句&#xff0c;當return語句提供了一個值時…

如何將數據寫入excel中,而不覆蓋原有數據

之前直接用pandas庫&#xff0c;然后to_excel&#xff08;&#xff09;&#xff0c;結果直接將原始數據直接覆蓋&#xff0c;幸虧有備份。&#xff08;友善提醒&#xff0c;做數據處理之前&#xff0c;先將數據本地備份一份&#xff0c;確認完全沒有問題&#xff0c;然后還是備…

對List集合中的元素進行排序

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 ollections對List集合中的數據進行排序 有時候需要對集合中的元素按照一定的規則進行排序&#xff0c;這就需要用到 Java中提供的對集合…

Jmeter----5.1 設置中文

注意&#xff1a;JMeter5需要Java8 以上&#xff0c;本文環境是Win7 64位 設置永久默認漢化&#xff1a;在Jmeter的安裝目錄下的bin目錄中找到 jmeter.properties這個文件&#xff0c;用文本編輯器打開。在#languageen下面插入一行languagezh_CN 這樣&#xff0c;再次打開Jmete…