天天看點

嵌入式作業系統VxWorks簡介(轉貼)

VxWorks作業系統是美國WindRiver公司于1983年開發的一種嵌入式實時作業系統(RTOS),是嵌入式開發環境的關鍵組成部分。

實時作業系統和分時作業系統的差別

        從作業系統能否滿足實時性要求來區分,可以把作業系統分成分時作業系統和實時作業系統。

        分時作業系統按照相等的時間片排程程序輪流運作,分時作業系統由排程程式自動計算程序的優先級,而 不是由使用者控制程序的優先級。這樣的系統無法實時響應外部異步事件。

        實時作業系統能夠在限定的時間内執行完所規定的功能,并能在限定的時間内對外部的異步事件作出響應。分時系統主要應用于科學計算和一般實時性要求不高的場合。實時性系統主要應用于過程控制,資料采集,通信,多媒體資訊處理等對時間敏感的場合。

VxWorks的特點

-可靠性

-實時性

實時性是指能夠在限定時間内執行完規定的功能并對外部的異步事件作出響應的能力。實時性的強弱是以完成規定功能和作出響應時間的長短來衡量的。

 VxWorks的實時性做的非常好,其系統本身的開銷很小,程序排程,程序間通信,中斷處理等系統公用程式精練而有效,它們造成的延遲很短。VxWorks提供的多任務機制中對任務的控制采用了優先級搶占(Preemptive Priority Scheduling)和輪轉排程(Round-Robin Scheduling)機制,也充分保證了可靠的實時性。

-可裁減性

使用者在使用作業系統時,并不是作業系統中的每一個部件都要用到。例如圖形顯示,檔案系統以及一些裝置驅動在某些嵌入式系統中往往并不使用。

VxWorks由一個體積很小的核心及一些可以根據需要進行定制的系統子產品組成。VxWorks核心最小為8KB。

對一個實時核心的要求

一個實時作業系統核心需要滿足許多特定的實時環境所提出的基本要求,這些包括:

-多任務:由于真實世界的事件的異步性,能夠運作許多并發程序或任務是很重要的。

-搶占排程:真實世界的事件具有繼承的優先級,在配置設定CPU的時候要注意這些優先級。當一個高優先級的任務變成可執行态,它會立即搶占目前正在運作的較低優先級的任務。

任務間的通訊與同步:在一個實時系統中,可能有許多任務作為一個應用的一部分執行。系統必須提供這些任務間的快速且功能強大的通信機制。核心也要提供為了有效地共享不可搶占的資源或臨界區所需的同步機制。

任務與中斷之間的通信:盡管真實世界的事件通常作為中斷方式到來,但是為了有效的排隊,優先化和減少中斷延時,我們通常希望在任務級處理響應的工作。是以需要複雜任務級和中斷級之間存在通信。

二、系統程式設計方法

實時系統主要包括:多任務排程(采用優先級搶占方式),任務間的同步和程序間的通信機制。

一個多任務環境允許實時應用程式以一套獨立任務的方式構築,每個任務擁有獨立的程序和它自己的一套系統資源。程序間通信機制使得這些任務的行為同步、協調。wind使用中斷驅動和優先級的方式。它縮短了上下文轉換的時間開銷和中斷時延。在VxWorks中,任何例程都可以被啟動成為一個單獨的任務,擁有它自己的上下文和堆棧。還有一些其他任務機制可以使任務挂起、繼續、删除、延時或改變優先級。

另一個重要内容是:硬體中斷處理。硬體産生中斷,統治系統調用相應的中斷曆程(ISR),為了系統得到盡快的響應,ISR在它自己獨立的上下文和堆棧中運作。它的優先級高于任何任務優先級。

中斷延遲(Interupt Latency) 中斷延遲是指從硬體中斷發生到開始執行中斷處理程式第一條指令之間的這段時間。

優先級驅動(Priority-Driver)優先級驅動是指多任務系統中,目前運作的任務總是具有最高優先級的就緒任務。

多任務排程

兩種方式:優先搶占和輪轉排程(Preemptive Prioriy, Round-Robin Scheduling)

優先搶占(Preemptive Priority):每一個任務都有一個優先級,系統核心保證優先級最高的任務運作于CPU。如果由任務優先級高于目前的任務優先級,系統立刻儲存目前任務的上下文,切換到優先級高的上下文。

搶占(Preemptive):搶占是指系統處于核心态運作時,允許任務的重新排程。換句話說就是指正在執行的任務可以被打斷,讓另一個任務運作。搶占提高了運用對異步事件的響應性能力。作業系統核心可搶占,并不是說任務排程在任何時候都可以發生。例如當一個任務正在通過一個系統調用通路共享資料時,重新排程和中斷都被禁止。

任務上下文(Task Context):任務上下文是指任務運作的環境。例如,針對x86的CPU,任務上下文可包括程式計數器,堆棧指針,通用寄存器的内容。

上下文切換(Context Switching):多任務系統中,上下文切換是指CPU的控制權由運作的任務轉移到另外一個就緒任務時所發生的事件,目前運作任務轉為就緒(或者挂起,删除)狀态,另一個被標明的就緒任務成為目前任務。上下文切換包括目前任務的運作環境,恢複将要運作任務的運作環境。上下文的内容依賴于具體的CPU.

輪換排程(Round-Robin Scheduling):使所用相同優先級,狀态為ready的任務公平分享CPU(配置設定一定的時間間隔,每個任務輪流享有CPU)。

系統由256個優先級,從0到255,0為最高,255為最低。任務在被建立時設定了優先級。也可以用taskPrioritySet()來改變任務的優先級。

任務的主要狀态為:READY, PEND, DELAY, SUSPEND

輪轉排程(Round-Robin):輪轉排程可以擴充到優先搶占方式中,當多個任務優先級相同的情況下 ,輪換排程算法使任務按平等的時間片運作于CPU,共享CPU。避免一個任務長時間占用CPU,而導緻其他任務不能運作。可以用kernelTimeSlice()來定義時間長度。

taskLock()和taskUnlock()用來取消優先搶占方式,和恢複優先搶占方式。

注意:一個任務可以調用taskDelete()删除另一個任務,但是如果一個目前正在運作的任務被删除後,該任務的記憶體沒有釋放,而其他任務不知道,依然在等待,結果導緻系統stop。用taskSafe()和taskUnsafe()來保證正在運作的任務不被删除。

用法如下:

taskSafe();

semTake(semId,WAIT_FOREVER);

.....critical region

semGive(semId);

semGive(semId);

taskUnsafe();

任務間的同步和程序間協調

 信号量作為任務間同步和互斥的機制。在wind核中有幾種類型的信号量,它們分别針對不同的應有需求;二進制信号,計數信号量,互斥信号量和POSIX信号量。所有的這些信号量是快速和高效的,它們除了被運用在開發設計程序中外,還被廣泛應有在VxWorks高層應有系統中。對于程序間通信,wind核也提供了如消息隊列,管道,套接字和信号等機制。

任務間的同步和程序間協調的幾種方式:

1. 記憶體共享(shared memory),對簡單的資料共享而言

2. 信号量(semaphore),基本的互斥和同步

3. 消息隊列(message queues)和管道(Pipe),單個CPU中,任務間的消息傳遞

4. 套接字(Socket)和遠端調用(Remote procedure calls),相對于網絡任務間的通信。

5. 信号(Signals),出錯處理(Exception handling)

記憶體共享(Shared Memory)

任務間通信最通常的方式是通過共享的資料結構進行通信。因為所有VxWorks的任務存在于一個單一的 線性位址空間,任務間共享資料。全局變量,線性隊列,環形隊列,連結清單,指針都可以被運作在不同上下文的代碼所指。

互斥(Mutual Exclusion)

互斥是用來控制多任務對共享資料資料進行串行通路的同步機制。在多任務應用中,當兩個或多個任務同時通路共享資料時,可能會造成資料破壞。互斥使它們串行地通路資料,進而達到保護資料的目的。

解決互斥的幾種方法:

1. 關部中斷的方法(intLock):能解決任務和中斷ISR之間産生的互斥。

funcA()

{

           int lock =intLock();

           ...critical region that cannot be interrupted

            intUnlock(lock);

}

但在實時系統中采取這個方法會影響系統對外部中斷及時響應和處理能力。

2. 關閉系統優先級(taskLock):關閉系統優先級,這樣在目前任務執行時,除了中斷外,不會有其他優先級高的任務來搶占CPU,影響目前程式的運作。

    funcA()

   {

           taskLock();

           ....critial region that cannot be interrupted

           taskUnlock();

    }

這種方法阻止了高優先級的任務搶先運作,在實時環境中也是不适合的,除非關閉優先級的時間特别短。

信号量(Semaphore):信号量是解決互斥和同步協調程序的最好的方法

VxWorks信号量提供最快速的任務間通信機制,它主要用于解決任務間的互斥和同步。針對不同類型的問題,有以下三種信号量:

二進制信号量(binary)使用最快捷、最廣泛,主要用于同步或互斥

互斥信号量(mutual exclusion)特殊的二進制信号量,主要用于優先級繼承,安全删除和回溯;

計數器信号量(counting)和二進制信号量類似,保持信号量被釋放(gaven)的次數,主要用于保護一個資源的多個例程(multiple instance of a resource)

信号量控制,函數介紹:

semBCreate()配置設定并初始化一個二進制信号量

semMCreate()配置設定并初始化一個互斥信号量

semCCreate()配置設定并初始化一個計數器信号量

semDelete()終止一個自由的信号量

semTake()占有一個信号量

semGive()釋放一個信号量

semFlush()解鎖所有等待信号量的任務

semBCreate(),semMCreate(),and semCCreate()傳回一個信号量ID作為其它後續任務使用該信号量的句柄。當一個信号量被建立,它的隊列(queue)類型就被确定。等待信号量的任務隊列以優先級的高低排列(SEM_Q_PRIORITY),或者一個先到先得的方式排列(SEM_Q_FIFO)。

當一個Semaphore建立時,指定了任務隊列的種類。

A. semBCreate(SEM_Q_PRIORIY, SEM_FULL), SEM_Q_PRIORITY指明處于等待狀态的任務在等待隊列中以優先級的順序排列

B. semBCreate(SEM_Q_FIFO, SEM_FULL), SEM_Q_FIFO指明處于等待狀态的任務在等待隊列中以先進先出的順序排列。

互斥程序(Mutual Exclusion)

互斥信号量有效的内鎖對共享資源的進入,與屏蔽中斷(disabling interrupt)和優先級鎖定(preemptive locks)相比,二進制信号量将互斥的範圍限制在僅與其有關的資源上。從技術上說,建立一個信号量來保護(guarding)資源。信号量初始化位可用的(FULL)。

當一個Semaphore建立時,指定了這個semaphore是用在解決互斥還是用于同步任務

A. semBCreate(SEM_Q_FIFO, SEM_FULL), SEM_FULL指明用于任務間互斥

     SEM_ID semMutex;

     semMutex = semBCreate(SEM_Q_PRIORITY, SEM_FULL);

    當一個任務要進入資源,首先要得到一個信号量(take that semaphore),隻要有任務在使用這個信号量,其它的要進入資源的任務要停止執行(blocked from execution),當這個任務完成了對資源的使用,它會釋放信号量,允許另一個任務要使用資源。

semTake(semMutex, WAIT_FOREVER);

...critical region, only accessible by a single task at a time.

semGive(semMutex);

同步協調程序(Synchronization)

B. semBCreate(SME_Q_FIFO, SEM_EMPTY), SEM_EMPTY指明用于任務間同步

#include "vxWorks.h"

#include "semLib.h"

SEM_IDsyncSem;

init(int someIntNum);

{

   intConnect(INUM_TO_IVEC(someIntNum), eventInterruptSvcRout, 0);

   syncSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY);

  taskSpawn("sample", 100, 0, 20000, task1, 0, 0, 0, 0, 0, 0, 0, 0, 0,0);

}

task1(void)

{

   ...

   semTake(syncSem, WAIT_FOREVER);

      printf("task 1 got eht semaphoere/n");

}

eventInteruptSvcRout(void)

{

   ...

   semGive(syncSem);

   ...

}

semTake(semID, time out)-----有Semaphore空閑,就Take,如果沒有,由time out定,逾時則向下執行。

2. 互斥信号量

互斥信号量是一個特殊的二進制信号量,設計用于優先級繼承,安全删除和回歸。

互斥信号量的使用基本和二進制信号量類似的。但是以下不同:

.僅僅被用做互斥

.隻能被使用它的任務釋放。(It  can be  given only by the task that took it.)

.ISR不能釋放它

.不能使用函數semFlush().

優先級反轉(Priority Inversion)

優先級反轉是指一個任務等待比它優先級低的任務釋放資源而被阻塞,如果這時有中等優先級的就緒 任務,阻塞會進一步惡化。優先級繼承技術可用于解決優先級反轉問題。

Priority inversion arises when a higher-priority task is forced to wait an indefinite period of time for

a lower-priority task to complete.

優先級繼承(priority inheritance)

優先級繼承可用來解決優先級反轉問題。當優先級反轉發生時,優先級較低的任務被暫時地提高它的優先級,使得該任務能盡快執行,釋放出優先級較高的任務所需要的資源。

 The mutual-exclusion semaphore has the option SEM_INVERSION_SAFE,which enables a priority-inheritance algoithm. The priority-inheritance protocol assures that a task that owns a resource executes at the priority of the highest-priority task blocked on that resource. Once the task priority

計數信号量(Counting Semaphores)

計數信号量是任務同步和互斥的另一種實作形似。計數信号量除了保留信号量被釋放的次數以外和二進制信号量是一樣的。每次信号量被釋放(gaven)一次,計數增加;每次信号量被占用(taken)一次,計數減少;當計數減少到0時,要求得到信号量的任務被阻塞(blocked)。二進制信号量是如果一個信号量被釋放,有一個任務阻塞等待,則這個任務被unblock。而計數信号量如果一個信号量被釋放,沒有任務阻塞等待,則計數增加。這說明一個被釋放兩次的計數信号量可以被占用(taken)兩次,沒有阻塞。

消息隊列(Message queues)

現實的實時應用由一系列互相獨立又協同工作的任務組成。信号量為任務間同步和聯鎖提供了高效機制。

在VxWorks中,用于單一CPU任務之間通信主要(primary)的機制是消息隊列。

消息隊列允許一定數量不同長度的消息進行排列。任何任務或中斷服務程式(ISR)能夠發送消息給消息隊列。任何任務可以從消息隊列接收消息。多任務可以從同一消息隊列發送和接收消息。兩個任務之間的全雙工(Full-duplex)通信需要針對不同方向的兩個消息隊列。

消息隊列函數介紹

msgQCreate()建立并初始化一個消息隊列

msgQDelete()終止并釋放一個消息隊列

msgQSend()發送一個消息到消息隊列

msgQReceive()從消息隊列接收一個消息

消息隊列由函數msgQCreate(MAX_MSGS, MAX_MSG_LEN, MSG_Q_PRIORITY)建立。它的參數MAX_MSGS指定了消息隊列中可允許最多可以排列的消息數和每個消息允許的最大位元組數MAX_MSG_LEN。

一個任務或中斷服務程式(ISR)用函數msgQSend()發送一個消息到消息隊列。如果沒有任務等待消息隊列的消息,這個消息被添加到消息緩存的隊列裡。如果某些任務已經在等待消息隊列中的消息,消息立刻被傳遞到第一個等待消息的任務。

一個任務用函數msgQReceive()從消息隊列得到一個消息。如果消息隊列緩存中有消息存在,第一個消息立刻出列并回到調用處(caller)。如果沒有消息存在,則任務(calling task)停止(blocks)并被添加到等待消息的任務隊列中。這個等待的任務隊列按照優先級或先進先出(FIFO)規則排列,這個規則有消息隊列建立時所指定的。

等待時間限制(time out)

msgQSend()和msgQReceive()都有時間限制參數。當發送一個消息,如果消息隊列緩沖這時沒有空間,這個 參數指定允許等待的時間(ticks),直到隊列緩存有空間來接收消息。當接收消息時,如果消息隊列沒有消息,這個參數指定允許等待的時間(ticks),直到消息隊列有消息。

#include "vxWorks.h"

#include "msgQLib.h"

#define MAX_MSGS (10)

#define MAX_MSG_LEN (100)

MSG_Q_ID myMsgQId;

task2(void)

{

    char msgBuf[MAX_MSG_LEN];

    if(MsgQReceive(myMsgQId, msgBuf, MAX_MSG_LEN, WAIT_FOREVENR) == ERROR)

                  reutrn (ERROR);

      printf("Message form task 1:/n %s/n", msgBuf);

}

#define MESSAGE "Greeting from Task 1"

task1(void)

{

  if(myMsgQId = msgQCreate(MAX_MSGS, MAX_MSG_LEN, MSG_Q_PRIORIYT)) == NULL)

                  return (ERROR);

    if(msgSend(myMsgQId, MESSAGE, sizeof(MESSAGE), WAIT_FOREVER, MSG_PRI_NORMAL) == ERROR)

    return (ERROR);

}

管道(Pipes)

   管道對消息隊列提供了一個可供選擇的接口,VxWorks的I/O系統。管道是虛拟的I/O裝置,由驅動pipeDrv管理。函數pipeDevCreate()建立一個管道裝置,這個調用指定管道的名字,能被排列的最多消息數,和每個消息允許的長度。

status = pipeDevCreate("/pipe/name", max_msgs, max_length);

被建立的管道是一個通常命名(named)的I/O裝置,任務能用标準的I/O函數打開,讀,寫管道,并能調用ioctl例程。當任務試圖從一個空的管道中讀取資料,或向一個滿的管道中寫入資料時,任務被阻塞。和消息隊列一樣,ISR可以向管道寫入,但是不能從管道讀取。

作為I/O裝置,管道提供了消息隊列沒有的重要特性,調用select()