任務,信號量、郵箱、隊列及其區別

一:UCOS是一種搶佔式的多任務操作系統,如果最高優先級的任務不主動放棄CPU的使用的話,其他任務是無法運行的,通常情況下,高優先級的任務在使用完CPU或其他資源後都要主動放棄,可以通過延時函數或者時等待一些信號量之類的讓自己掛起。但是如果最高優先級任務一直使用CPU,那就跟單任務沒有什麼區別了。

二:可以通過等待信號量,消息等是當前任務掛起,或者通過通過延時函數將任務掛起,從而讓其他優先級的任務運行。

UC/OS的信號量,消息隊列,郵箱的區別
    信號量像一把鑰匙,任務要運行下去,需先拿到這把鑰匙。
    消息郵箱是一個指針型變量。可以向一個任務或一箇中斷服務子程序發送一則消息(一個指針),同樣,一個或多個任務通過內核服務,可以接收這則消息。消息郵箱也可以當作只取2個值的信號量來用。
    消息隊列實際上是郵箱陣列。

任務

    看了《嵌入式實時操作系統uC/OS-II》也快一個星期了,期間斷斷續續的邊工作邊看,依照書的目錄,大致細分了系統各個功能:任務管理,時間管理,事件控制,信號量管理,互斥型信號量管理,事件標誌組管理,信息郵箱管理,信息隊列管理,內存管理等,這個次序也是以後要逐個理解學習的次序。

    現在下了個移植到51的源代碼,學習任務管理部分,試嘗編寫了個任務程序,爲了把能夠用上的函數使用上,程序就顯得有點臃腫,程序我保證是能夠通過編譯並且無錯誤的。

     程序粘貼上去 dat="dat總是變成dat"="dat",多了個"",如果大家要用這個程序例子,請去掉""。

 第一個程序dj:

 #include <INCLUDES.H> 
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

OS_STK TaskStk[3][MaxStkSize];//定義3個堆棧
void task0(void *dat);
void task1(void *dat);

long con1;
long con2;

main()
{
   OSInit();
   InitTimer0( );//初始化系統時鐘
   OSTaskCreate(task0,(void*)0,&TaskStk[0][0],5);//建立任務0 定義好它的優先級等參數
   OSTaskCreate(task1,(void*)0,&TaskStk[1][0],6);
   OSStart();

}

void task0(void *dat)//任務0
{
  dat="dat";  //防止編譯有錯誤
   
   while(1)
   {
    con1++;
   OSTimeDly(2);
   }

}

void task1(void *dat)
{
   u16 vison;
   dat="dat";
   while(1)
   {
    con2++;
    vison=OSVersion();//返回系統版本號

    OSTaskDel(6);//刪除任務1
    OSTimeDly(2);
 }
}

心得:

    根據作者建議,最高和最低優先級的任務最好不要使用,而用戶使用的任務多達56個,0表示最高優先級。

    建立任務的函數有OSTaskCreate和OSTaskCreateExt兩個,OSTaskCreateExt函數可以設置更多的任務細節。

    在 OSInit()和OSStart()之間最好建立任務和其他要初始化的函數,因爲啓動系統後,OSStart()將不再執行。

    OSTimeDly(2)表示將任務0延時2個時鐘節拍,在這2個節拍的時間裏,系統可以執行任務1;等待2個節拍時間後,系統才返回任務0執行。如果沒有延時,系統永遠不會執行任務1,而只執行任務0.這個很重要。

    想延時更長的時間可以OSTimeDlyHMSM(h,m,s,m)函數,可延時接近11天。

    用如果不想讓任務0等待2個節拍才返回的話,也可以用OSTimeDlyResume(5)直接恢復任務0運行,這裏的5是代表優先級爲5的任務;

    OSTaskDel(6)刪除優先級爲6的任務,這裏的刪除不是指刪除代碼,而是不再執行刪除的任務。

    OSVersion()返回系統版本號的函數,如果是200的話即是2.00版本。

    OSTaskSuspend(5)表示無條件暫停一個優先級爲5的任務,這個時候系統會重新調度,運行下一個優先級最高的任務,如果想恢復該任務,只能用OSTaskResume(5)來恢復。


信號量

    信號量的使用自己看了幾天仍然沒搞明白信號量,郵箱,和隊列的區別。現在是學習信號量的使用,苦於沒有現成的例子參考,只能一步步弄清楚它的,感覺外國人的思維還是和中國的有很大的出入,以至於學習一個國外的技術, 總是先翻譯成自己習慣的思維去領會,變成自己的東西。

    信號量程序dj:

   #include <INCLUDES.H> 
   typedef unsigned char u8;
   typedef unsigned int u16;
   typedef unsigned long u32;

   OS_STK TaskStk[3][MaxStkSize];//定義3個堆棧
   void task0(void *dat);//定義任務0
   void task1(void *dat);

   long con1=0;
   long con2=0; 
   OS_EVENT *sem;
   u8 err;

  main()
{
   OSInit();
   InitTimer0( );
   sem=OSSemCreate(1);//建立信號量
   OSTaskCreate(task0,(void*)0,&TaskStk[0][0],5);//建立任務 定義好它的優先級等參數
   OSTaskCreate(task1,(void*)0,&TaskStk[1][0],6);
   OSStart();
}

void task0(void *dat)//任務0
{  
   while(1)
    {
     con1++;
     OSSemPost(sem);//發送信號量
     OSTimeDly(2);
    }
}

void task1(void *dat)//任務1
{
   u16 value;
   u16 vison;
   while(1)
   {    
    value=OSSemAccept(sem);   //查看資源是否可以使用
    OSSemPend(sem,0,&err);    //等待接收信號 才往下執行任務
    con2++;
    }
  OSTimeDly(2);//延時
 }
}

心得:

    信號量的建立必須是在任務級中建立,信號量類型爲OS_EVENT ,信號量值可以爲1,0,0~65535的值,不同的值代表不同的意義。

    OSSemAccept(信號量)起查詢信號量作用,返回信號量的值。

    OSSemPend(sem,0,&err);將暫停當前任務,等待信號量的到來。如果 接收到一個信號量(如果信號量大於0) ,該信號量會自動減1,在例子中,信號量開始定義爲1,在任務1接收後,信號量會變爲0;例子中的OSSemPend(sem,timeout,&err),timeout代表等待timeout個信號量後還沒得到信號量,恢復運行狀態,如果timeout=0,表示無限等待信號量。


郵箱

    學習uCOSII到現在,開始對信號量,郵箱和隊列有了個大概的認識。如果有理解錯誤,請大家指出。

    信號量是一個觸發信號,也是一個計數器,等待接收信號的任務一般只有接收到信號纔可以執行,否則任務一直暫停。在uCOSII裏面,等待信號量的任務可以設置無限等待或等待若干個時鐘節拍後,任務自動恢復執行。具體看自己的要求來設置。

    郵箱是信號量的擴展,相當於把一個指針定義的變量從一個任務傳遞到另一個或多個任務中去,這個指針是先發到郵箱,然後等待任務從郵箱裏提取指針,這也就傳遞了指針指向的具體變量值。 等待郵箱的任務也是可以設置無限等待和等待若干個時鐘節拍後任務自動恢復執行。後面的隊列,我們也可以看得出它們規律,都有各自的建立,刪除,發送,接收,查詢等功能函數。

    隊列是多個郵箱的數組,可以看做是個指針數組,任務之間可以按照一定順序以指針定義的變量來傳遞,即是發送一個個指針給任務,任務獲得指針,來處理指向的變量。這個方式有先進先出,先進後出。這個後面再詳談。

    信號量,郵箱,隊列的最大不同在於它們發送的內容不同。我覺得這是最根本的區別。

郵箱實例dj:

 #include <INCLUDES.H> 
 typedef unsigned char u8;
 typedef unsigned int u16;
 typedef unsigned long u32;

 OS_STK TaskStk[3][MaxStkSize];//定義3個堆棧
 void task0(void *dat);//定義任務0
 void task1(void *dat);

 long con1=0;
 long con2=0;
 OS_EVENT *Come;
 u8 err;

 main()
 {
   OSInit();
   InitTimer0();
   Come=OSMboxCreate((void *)0);//建立郵箱

   OSTaskCreate(task0,(void*)0,&TaskStk[0][0],5);//建立任務 定義好它的優先級等參數
   OSTaskCreate(task1,(void*)0,&TaskStk[1][0],6);
   OSStart();
}

  void task0(void *dat)//任務0
 {   
   u16 buffer="0x0FFF";//要發送的信息

   while(1)
    {
     con1++;
     OSMboxPost(Come,&buffer);//通過郵箱發送信息(指針)給任務
     OSTimeDly(2);
    }
}

void task1(void *dat)
{
   u16 *msg;
   u16 c;
   while(1)
   {    
    msg=OSMboxPend(Come,0,&err);//等待接收郵箱信息(指針msg指向buffer) 
    c=*msg;//獲得指針指向的變量
    con2++;
    OSTimeDly(2);
 }

}

心得 :

    我們看到,郵箱傳遞了一個指針從任務0到任務1,任務1使用這個指針來獲得變量buffer進行處理。

    在這個例子裏,設置OSMboxPend(Come,0,&err)中的0表示無限等待郵箱有信息,如果沒有,任務1暫停;也可以設置成其他的值N,表示等待N個時鐘節拍後如果沒有收到信息,任務1自動執行。

    這裏沒用到的郵箱功能函數還有查詢郵箱是否有信息,通過郵箱發信息給多個任務等。


隊列

    對消息隊列的學習理解有點難,對技術來說,一本好的書一般是原理和例子相結合的,可惜我找到的很少。書上說消息隊列實際上是多個郵箱組成的數組,是一個列表。這個數組其實是個指針數組,裏面每個指針可以指向不同類型的變量,通過傳遞一個個指針,我們可以做到傳遞指針所指向的一個個變量。(順便複習下,一個郵箱只能傳遞一個指針,而隊列可傳遞多個)。

#include <INCLUDES.H> 
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

OS_STK TaskStk[3][MaxStkSize];//定義3個堆棧
void task0(void *dat);//定義任務0
void task1(void *dat);//定義任務1

long con1=0;
long con2=0;

OS_EVENT *Com1;    //定義一個指針

void *ComMsg1[3]; //定義一個指針數組 
u8 err;

u8 st[4]={0x01,0x02,0x03,0x04};//定義一個數組
u8 aa[4]={0x05,0x04,0x06,0x09};//定義一個數組
u8  a1=0xFF;//定義一個數

main()
{
   OSInit();//初始化ucosii
   InitTimer0();

   Com1=OSQCreate(&ComMsg1[0],3);//建立一個消息隊列(即爲數組指針) 消息內存大小爲3

   OSQPost(Com1,(void*)&st[0]);          //發送到隊列 
   OSQPost(Com1,(void*)&aa[0]);         //發送到隊列 
   OSQPost(Com1,(void*)&a1);             //發送到隊列 

   OSTaskCreate(task0,(void*)0,&TaskStk[0][0],5);//建立任務 定義好它的優先級等參數
   OSTaskCreate(task1,(void*)0,&TaskStk[1][0],6);
   OSStart();//系統啓動
}

void task0(void *dat)//任務0
{
//   dat="dat";//防止編譯有錯誤
   while(1)
    {
     con1++;
     OSTimeDly(2);
    }
}

void task1(void *dat)
{
   u8 *msg1;
   u8 cc,b;
//   dat="dat";//防止編譯有錯誤
   while(1)
   {    
   msg1=OSQPend(Com1,0,&err);
  cc=*msg1;
  b=*(msg1+1);
  con2++;
   OSTimeDly(2);
 }
}

心得:

   

Com1=OSQCreate(&ComMsg1[0],3),建立一個隊列,將*Com1指針將指向隊列指針數組*ComMsg1[3]的首地址。3表示內存大小。

    OSQPost(Com1,(void*)&st[0]),是以先入先出發送消息到隊列,由*ComMsg1[0]指向數組st的首地址,同時隊列消息數加一。

    OSQPost(Com1,(void*)&aa[0])發一個消息給隊列,此時是由*ComMsg1[1]指向aa數組的首地址,同時隊列消息數加1。; 依此類推,發下一個也是一樣。(如果隊列滿了,再發送消息到隊列的話,隊列將會出現錯誤)。

    msg1=OSQPend(Com1,0,&err);是等待消息隊列函數,獲得指針數組裏的第一個指針ComMsg1[0],該指針指向st[0],。cc=*msg1是通過指針獲得指向的具體值st[0]=0x01;如果再調用一次msg1=OSQPend(Com1,0,&err),將獲得指針數組裏下一個指針ComMsg1[1],依此類推。

    每調用一次OSQPend函數,該函數會將隊列的消息減1,Com1指向下一個消息指針*ComMsg1[i++],直到隊列完全沒有消息爲止。在例子中,只有3個消息,程序將執行3次任務1後,隊列已經沒有消息了,所以將任務1暫停。

原文地址:http://cspiao1986.blog.163.com/blog/static/701139742011413104555402


我自己的理解:信號量就是給某個任務傳遞一個數值,基本作用是提示接收該信號量的任務什麼時候可以執行。

  消息郵箱就是由某個任務傳遞一個指針(內存上指針指向的一片區域的變量類型基本相同)給一個或多個任務。

  消息隊列就是某個任務傳遞一堆指針(指針數組,數組中每個指針指向的變量類型可以不同)給一個或多個任務。

  三者的作用有待研究。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章