新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > setjmp構建簡單協作式多任務系統

        setjmp構建簡單協作式多任務系統

        作者: 時間:2004-12-10 來源:網絡 收藏
        摘要:討論一個利用標準C語言庫函烽實現查詢式,給出完整的內核和樣例程序并對源代碼進行說明。該具有易用的特點,只需要編寫存取堆棧指針的宏就可方便地移植到新的平臺上。文章詳述了的優缺點,討論一些性能擴展的方法。該內核適用于中小規模的嵌入式軟件。

        關鍵詞:式多 C語言

        引言

        本文介紹的是利用標準C語言庫函數實現的具備此特點的式多系統。從本質上講,實時多任務操作系統應該具備按照優先級搶占調度的內核。然而,在實際應用中,搶中式的多任務某種程序上帶來了用戶程序設計時數據保護的困難,并且,具備搶占功能的多任務內核設計時困難也比較多,這會增加操作系統自身的代碼,也使它在小資源單片機系統中應用較少;而協作多任務系統的調度只在用戶指定的時機發生,這會大大簡化內核和用戶系統的設計,尤其本文實現的系統通過條件查詢來放棄CPU,既符合傳統單片機程序設計的思維,又帶來了多任務、模塊化、可重入的編程便利。

        Setjmp是標準C語言庫函數的組成部分,它可以實現程序執行中的遠程轉操作。具體來說,它可以在一個函數中使用setjmp來初始化一個全局標號,然后只要該函數未曾返回,那么在其它任何地方都可以通過longjmp調用來跳轉到setjmp的下一條語句執行。實際上,setjmp函數將發生調用處的局部環境保存在一個jmp_buf的結構當中,只要主調函數中對應的內存未曾釋放(函數返回時局部內存就失效了),那么在調用longjmp的時候就可以根據已保存的jmp_buf參數恢復到setjmp的地方執行。我們的系統中就是分析了setjmp標準庫函數的特點,以的方式實現了協作式多任務。

        1 演示程序

        為了便于理解,首先給出多任務演示程序的源代碼。這個程序演示了協作式多任務切換、任務的動態生成、多任務共用代碼等功能,一共使用了init_coos初始化根任務(也就是C語言main函數)、creat_task創建新任務和WAITFOR查詢條件這3個基本的系統調用。由于面向嵌入式系統,因而程序不會中止并且運行中也沒有進行任何輸出,需要借助適合的調試工具來理解多任務系統的運行。

        example.c文件清單:

        #includestdlib.h>

        #include“co-os.h”

        void tskfunc1(int argc,void *argv);

        void tskfunc2(int argc,void *argv);

        void subfunc(void);

        volatile int cnt,test;

        int main(void){

        int i;

        init_coos(400);

        creat_tsk(tskfunc1,12,NULL,400);

        creat_tsk(tskfunc2,0,NULL,400);

        i=0;

        while(1){

        WAITFOR(cnt= =8);

        while(i++cnt)test=i;

        cnt++;

        }

        }

        void tskfunc1(int argc,void *argv){

        int i;

        static int creat=0;

        if(!creat){

        creat_tsk(tskfunc1,9,NULL,400);

        creat=1;

        }

        i=0;

        while(1){

        WAITFOR(cnt>argc);

        test=0x55;

        /*使用函數調用在子程序中測試WAITFOR*/

        subfunc();

        while(i++cnt)test=i^0xAA;

        }

        }

        void tskfunc2(int argc,void *argv){

        while(1){

        WAITFOR(++cnt>15);

        cnt=0;

        }

        }

        void subfunc(void){

        int i;

        WAITFOR(cnt5);

        for(i=0;i++)test=0x10*i;

        }

        2 內核構成

        內核包括一個供外部用戶程序包含的頭文件(co-os.h)和具體實現的源文件(co-os.c),它們提供了演示程序中用到的3個系統調用。

        內核的實現代碼假定了CPU堆棧是向下增長的,并且通過宏來直接操作堆棧指針。以下代碼在Microsoft VC6 for x86、Borland C++ Builder 5.5、SDS CrossCode7.0 for 68K和GCC3.2 for AVR四種平臺中測試過,只需在co-os.h頭文件中定義相應的平臺類型即可順利編譯。

        (1)co-os.h文件清單

        #includesetjmp.h>

        /*選擇X86_VC6,X86_BC5,AVR_GCC或M68H_SDS.*/

        #define X86_VC6

        #define MAX_TSK 10

        typedef struct {

        void (*entry)(int argc,void *argv);

        jmp_buf env;

        int argc;

        void *argv;

        }TVB;

        extern TCB tcb[MAX_TSK];

        extern int task_num,tskid;

        void init_coos(int mainstk);

        int creat_tsk(void(*entry)(int argc,void *argv),int argc,void *argv,int stksize);

        #define WAITFOR(condition)do{

        setjmp(tcb[tskid].env);

        if(!(condition)){

        tskid++;

        if(tskid>=task_num)tskid=0;

        longijmp(tcb[tskid].env,1);

        }

        }while(0)

        (2)co-os.c文件清單

        #include "co-os.h"

        #if defined(X86_VC6)||defined(X86_BC5)

        #define SAVE_SP(p) _asm mov p,esp

        #define RESTORE_SP(p) _asm mov esp,p

        #elif defined(AVR_GCC)

        #includeio.h>

        #define SAVE_SP(p) p=(int*)SP

        #define RESTORE_SP(p) SP=(int)p

        #elif defined(M68K_SDS)

        #define SAVE_SP(p) asm("MOVE.L A7,{"#p"}")

        #define RESTORE_SP(p) asm("MOVE.L {"#p"},A7")

        #endif

        TCB tcb[MAX_TSK];

        Int task_num=1;

        Int tskid;

        Static int stktop,oldsp;

        Void init_coos(int mainstk){

        SAVE_SP(stktop);

        stktop=stktop+sizeof(void(*)(void))/sizeof(int)

        -(mainstk+sizeof(int)-1)/sizeof(int);

        }

        int creat_tsk(void(*entry)(int argc,void *argv),

        int argc,void *argv,int stksize){

        if(task_num>=MAX_TSK)terurn-1;

        SAVE_SP(oldsp);

        RESTORE_SP(stktop);

        If(!setjmp(tcb[task_num].env)){

        RESTORE_SP(oldsp);

        tcb[task_num].entry=entry;

        tcb[task_num].argc=argc;

        tcb[task_num].argv=argv;

        task_num++;

        stktop-=(stksize+sizeof(int)-1)/sizeof(int);

        }

        else tcb[tskid].entry(tcb[tskid].argc,tcb[tskid].argv);

        return 0;

        }

        3 代碼說明

        任務代碼通過執行setjmp設置本任務下次查詢時的返回點,然后在等待條件放棄掉CPU跳轉到下一任務的返回點處執行。如此周而復始,讓各任務都獲得輪轉運行的機會,也要求各任務都需要主動通過等待條件的方式放棄掉CPU。系統中除了中斷服務程序之外,所有任務都是平等的,都應該遵循同樣的規則和其它任務一起協作運行。基本系統中沒有設計殺死任務的調用,這要求各任務都應當設計成某種形式的無限循環。

        任務中等待的條件可以是任務復雜的表達式或都函數調用,也可以是中斷服務程序設置的全局變量(注意加volatile定義)。一般在任務執行時會讓下次等待的條件不再滿足,避免某個任務一直霸占CPU將系統餓死。在嵌入式軟件中還經常會遇到任務定時啟動和超時等待在I/O操作上,在我們的系統中可以維護一個時間計數器,只需在適當的地方記錄時刻,然后在任務查詢條件中判斷當前計數器和記錄時刻之間的差值就可以了。

        內核實現的代碼則相當簡法。由于主要的保護和恢復任務現場的工作都由C語言標準庫setjmp實現了,我們就只需要操縱一下堆棧指針防止不同的任務使用了重疊的局部環境,這個工作在初始化和創建任務的時候通過預定義的兩個宏來實現。在init_coos函數中,記錄了主任務(main函數)保留mainstk字節堆棧后的新棧頂位置(stktop),然后在每次creat_task時都根據要求為每個任務保留stksize字節的堆棧并重新計算下一個stktop。在creat_task函數中利用了setjmp函數的返回值(直接返回時為0,longjmp跳轉返回時非0),使得一方面creat_task能正常回到調用者,又讓下次輪轉到新任務時能夠找到創建時的入口。

        co-os.h中定義的WAITFOR使用了一個do{…while(0)實現多語句宏的C語言小技巧,這樣能保證在任何情況下WAITFOR都可以如單條語句一樣在源程序中使用,需要擔心多了或者少了大括弧破壞if/else匹配之類的問題,并且,所有的編譯器都會優化掉這個假循環。

        為了盡量使程序并說明問題,以上代碼中沒有考慮中斷相關的數據保護問題。實際運行的系統中,如果首先寫堆棧指針不能一步完成(如AVR這樣的8位機),那么,在寫操作正在進行的時候絕對不能允許中斷;另外,在任務中查詢的條件如果和中斷有關,那么也必須考慮數據的完整性。

        4 性能分析

        所有的協作式多任務系統中任務切換時間都和用戶代碼(是否長期占用CPU)、就緒時刻有關,比標準系統略差的是,我們的簡單系統中不支持任務優先級,并且完成一輪查詢調度的時間還和任務數目有關。這也是為了達到簡單和可移植性目標而不得已作出的犧牲。在各任務間切換查詢條件通過C語言標準庫函數longjmp實現,一般來講longjmp函數要從內存中恢復大部分CPU寄存器,執行它也需要若干條指令的時間。

        為了面向嵌入式系統應用,任務控制塊(TCB)采用靜態數組來實現,這樣要求預先確定系統的最大任務數(co-os.h中的MAX_TSK)。如果需要,也可以通過環波鏈表來動態管理任務控制塊(TCB),這時可以簡單實現任務的動態創建和刪除,并且通過指針來訪問TCB也要比通過下標(tskid)略快一點。

        在某些情況下,如果在中斷返回后需要執行某關鍵任務,可以考慮通過設置“高級中斷”的方法來實現。具體地講,就是在中斷返回前改變返回地址到某函數入口(“高級中斷服務程序”),同時保留原返回地址到堆棧中,這樣在“高級中斷服務程序”完成后執行return就又回到了正常的多任務查詢流程。使用“高級中斷”時要注意現場保護的銜接,并且這種技巧顯然和CPU和體系結構有關。

        5 結論

        setjmp是標準C語言中用于遠程跳轉的庫函數,利用它可方便實現一個簡單易移植的協作式多任務系統。該系統功能完備、編程簡單、易于學習,適合一些中小規模的嵌入式軟件使用;并且,以此為基礎,還可以用一些與平臺相關的編程技巧提高其實時性和靈活性。



        評論


        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 德阳市| 沅陵县| 同心县| 三穗县| 江源县| 阳高县| 平乐县| 丰原市| 肥乡县| 五莲县| 岳西县| 莱西市| 建宁县| 龙岩市| 青阳县| 渑池县| 泉州市| 鲁山县| 敖汉旗| 和平区| 平塘县| 三明市| 石楼县| 巧家县| 金乡县| 公主岭市| 鄯善县| 翁源县| 龙井市| 咸阳市| 和顺县| 兰考县| 察隅县| 手游| 龙游县| 江西省| 江津市| 三台县| 华容县| 临桂县| 岳阳县|