本以為做個讀書筆記,誰知不懂的太多,抄書好了。
來源:《嵌入式實時操作系統-uc/os原理與實踐》 盧友亮 編著
一:操作系統
1:操作系統基本功能:
任務管理
CPU管理
內存管理 ----給任務分配內存空間,內存結束后釋放內存空間
文件管理----對文件存儲器的存儲空間進行組織,非配和回收,負責文件的存儲,檢索,共享和保護
I/O設備管理
第二章: 實時操作系統(RTOS)
一:概述:
(一):定義
是指當外界事件或數據產生時,能夠接受并以足夠快的速度予以處理,其處理的結果又能在規定的時間之內來控制生產過程或對處理系統做出快速響應,調度一切可利用的資源完成實時任務,并控制所有實時任務協調一致運行的操作系統。
硬實時操作系統:要求在規定的時間內必須完成操作
軟實時操作系統:只要按照任務的優先級,盡可能快地完成操作
(二):特征:
1:多任務:系統中多個任務同時運行。當時cpu只有一個,在某一個時刻,只有一個任務占有cpu,因此,多任務操作系統的核心任務之一是任務調度,為任務分配cpu時間。
2:多級中斷機制:
以確保對緊迫程度較高的實時時間進行及時響應和處理----例如:溫度超高的報警先處理
3:優先級調度機制:
越緊迫的任務優先級越高
任務管理模塊必須根據優先級調度任務,而又能保證任務在切換過程中不被破壞。
(三):任務
1:任務簡介
任務有 睡眠,就緒,運行,阻塞,掛起等多種狀態
典型的任務運行方式是循環。
例1:
標準的任務結構:(用戶任務)
void usertask(void *pParam) //參數是指針類型的
{
int i=*((int *)pParam); //將指針強制轉換為指向整數類型的指針,然后取改指針所指的內容,將它賦值給一個局部變量i
for( ; ; ) //進入無限循環
printf("\n\r%d\n",i++); //在循環體打印輸出當前的計數值i
OSTimeDly(OS_TICKS_PER_SEC); //調用操作系統演示函數,延時1s。1s后又可以獲得運行,繼續循環
}
注:OSTimeDly首先將本任務 阻塞掉,這樣,即使該任務優先級最高,也可以讓低優先級的任務獲得運行的機會。操作系統1s后使這個任務重新就緒,重新得到運行。
2:多任務:
優點:可以大大提高cpu的利用率,使應用程序分成多個程序模塊,實現模塊化,應用程序更易于設計和維護
例如:在ARM采集處理系統中,同時采集16路信號,又同時對多信號進行處理和傳輸,
可以創建16個任務負責16路信號的采集,
創建一個任務對信號進行處理,
再創建一個任務負責數據的傳輸。
例:2:
int main(int argc, char **argv)
{
int p[2];
p[0]=0;
p[1]=100;
OSInit(); //對ucosii內核進行初始化
OSTaskCreate(TaskStart,0,&TaskStk[0][TASK_STK_SIZE-1],TaskStart_Prio); //創建一個設置時鐘中斷的任務
//創建用戶任務,任務代碼是例1的usertask,參數是p,即指向p[0]的指針,優先級是5
OSTaskCreate(usertask,p,&TaskStk[2][TASK_STK_SIZE-1],5);
//創建用戶任務,任務代碼是例1的usertask,參數是p+1,即指向p[1]的指針,優先級是6
OSTaskCreate(usertask,p+1,&TaskStk[3][TASK_STK_SIZE-1],6);
OSStart(); //開始啟動多任務
return 0;
}
分析:
兩個用戶任務的優先級雖然不同,但是都執行延時操作,也就是說主動放棄cpu,因此可以輪流運行。
(如果沒有執行延時操作,把自己阻塞起來,在高優先級任務結束前,低優先級的任務是不能得到運行的)
運行結果: 第一個任務的參數是0,從0開始計數,第二個任務的參數是100,從100開始計數。
兩個任務輪流輸出結果到屏幕,多任務被調度運行了。
0
100
1
101
2
102
3
103
3:任務狀態:

1:睡眠態:
在調用 OSTaskCreate(任務創建函數)創建之前,處于睡眠狀態。
睡眠狀態的任務是不會運行的,操作系統也不會為他設置運行而準備的數據結構,沒有給它配置任務模塊。
2:就緒態
當操作系統調用 OSTaskCreate()創建一個任務后,任務進入就緒態。
圖中可以看到,任務也可以從其他狀態進入就緒態。
處于就緒態的任務 :操作系統已經為其運行配置好了任務控制模塊等數據結構。
當沒有比它優先級更高的任務或者優先級更高的任務處于阻塞態的時候,就可以被系統調度進入運行態,操作系統是調用任務切換函數完成的。
3:運行態:
運行態是任務真正占有cpu,得到運行。這時運行的代碼就是任務的代碼,如usertask。
處于運行態的任務如果運行完成,就會進入睡眠態。
如果更高優先級的任務搶占了cpu,就會轉到就緒態。
如果 因為等待某一事件,例如等待1s的時間,OSTimeDly(OS_TICKS_PER_SEC); 需要暫時放棄cpu的使用權而讓其他任務得到運行,就進入阻塞態。
當由于中斷的到來而使cpu進入中斷服務函數ISR,必然使正在運行過得任務放棄cpu而轉入中斷服務程序,這是被中斷的程序就會被掛起而進入掛起態。
4:阻塞態:
阻塞態對于操作系統的調度、任務的協調運行時很重要的。
圖中所示:并不是只有一個高優先級的任務在運行,是因為usertask在沒有事情可做,在等待1s的時候,不是強行運行代碼,而使把自己阻塞起來,使操作系統可以調度其他的任務。OSTimeDly(OS_TICKS_PER_SEC);
當任務在等待某些還沒有被釋放的資源或等待一定時間的時候,要阻塞起來,等到條件滿足的時候再重新回到就緒態,又能被操作系統調度以進入運行態。這是實時操作系統必須實現的功能之一。
注意:錯誤:一些不理解操作系統的讀者編程時候,在等待的時候常常使用for循環,不停的執行代碼而使cpu的利用率暴增,使系統的運行,甚至造成死機,十分惡劣。這樣不可取。
5:掛起態:
當任務在運行時,因為中斷的發生(例如定時器中斷每個時鐘滴答)中斷一次,被剝奪cpu的使用權而進入掛起狀。在中斷返回的時候,若該任務還是最高優先級的,則恢復運行。
如果不是這樣,就回到就緒態。
4:任務切換:-----Context Switch
任務切換時暫停一個任務的運行,云更新另一個處于就緒態的任務,暫停一個任務,以后又能恢復運行,必須考慮將這個任務的信息保存,而恢復運行的時候需要將這些信息恢復到運行環境。
因此,任務切換必須做環境的保存和恢復的操作。
環境的保存和恢復與任務有關,業余任務運行的硬件環境有關(PC和ARM有不同的寄存器,所以不同,因此涉及到匯編語言實現的最底層代碼。---因此需要一定的計算機原理或者嵌入式系統的基本知識。
多任務的關鍵在于.如何將調度,ucos采用的是可剝奪優先級調度算法。
多任務下,個任務還要按照一定的次序運行,因此存在同步問題,因此引入了信號量的改年來進行同步。
人物間有相互通信的需求,因此操作系統需要有郵箱、消息等用于通信的數據結構,以便多任務通信。
多個任務可能真多有限的資源而不發生沖突,因此操作系統還需要管理各任務,使他們能夠充分利用資源而不發生沖突,因此又產生了互斥,死鎖等概念。
5:可重入函數 與 不可重入函數 (任務應該調用可重入函數)
可重入函數: 指一個函數可以被多個任務調用。(不需要擔心在任務切換的過程中,代碼的執行會摻和啥呢個錯誤的結果)
不可重入函數:如果可能產生錯誤的結果,就是不可重入函數。
例3:
啟動兩個用戶任務:usertask1和usertask2 兩個任務都調用add2函數(將返回兩個參數相加的結果),使用了全局變量a和b。
void usertask1(void *pParam)
{
int sum;
for( ; ; )
{
printf("\ntask% call add2(1,2)\n",1);
sum=add2(1,2);
printf("\ntask% call add2(1,2) solution is %d\n",1,sum);
}
}
void usertask2(void *pParam)
{
int sum;
for( ; ; )
{
printf("\ntask% call add2(100,200)\n",2);
sum=add2(100,200);
printf("\ntask% call add2(100,200) solution is %d\n",2,sum);
}
}
a:
使用不可重入函數得到的錯誤結果:
int a,b; //定義全局變量,各個任務都可以訪問的全局變量
int add2(int p1,int,p2) //函數add2()實現對兩個參數相加,返回結果,因為使用了全局變量,所以在任務切換過程中,他得知可能改變
{
a=p1;
b=p2;
OSTimeDly(OS_TICKS_PER_SEC); //延時1s任務被阻塞,以保證任務被切換
return(a+b); //返回相加結果
}
運行結果:
task1 call add2(1,2) solution is 300
task2 call add2(100,200) solution is 3
task1 call add2(1,2) solution is 300
task2 call add2(100,200) solution is 3
分析:
結果完全錯誤。
usertask1的優先級為5,usertask2的優先級為6。
操作系統首先調度usertask1運行,對變量a、b進行賦值,運行結果為a=1,b=2.
然后調用OSTimeDly(OS_TICKS_PER_SEC);延時,usertask1被阻塞,操作系統進行一次任務切換。
usertask2得到尋釁,對a,b進行賦值,運行結果為a=100,b=200.
在經過一段延時后,usertask1被喚醒,重新得到運行,返回a+b的結果,于是有了1+2=300的錯誤結果。
打印出第一行后,usertask1仍然要進行循環,于是又調用add2,又給a、b進行賦值,運行結果為a=1,b=2,
再延時,進入usertask2延時結束得到運行,打印出了第二行100+200=3的錯誤結果。
b:使用可重入函數得到正確結果
int add2(int p1,int,p2) //函數add2()實現對兩個參數相加,返回結果,因為使用了全局變量,所以在任務切換過程中,他得知可能改變
{
int a,b;
a=p1;
b=p2;
OSTimeDly(OS_TICKS_PER_SEC); //延時1s任務被阻塞,以保證任務被切換
return(a+b); //返回相加結果
}
運行結果:
task1 call add2(1,2) solution is 3
task2 call add2(100,200) solution is 300
task1 call add2(1,2) solution is 3
task2 call add2(100,200) solution is 300
分析:
結果正確。
原因是add2函數采用了可重入代碼----------因此在實時多任務操作系統中,公用函數口用該采用可重入代碼。
|