semm7
VxWorks下采用C++構建Application可以使得程序更加利於維護,利用其提供的STL支持,可以省去大量的底層工作,大大加速軟件的開發進度。
1 異常類VxError
首先封裝異常類VxError,當程序出現異常時,向外層調用者拋出壹個該類的對象,調用者采用try-catch clause捕獲該異常對象,進行異常處理。
由於該類針對的是系統運行時產生的異常,故考慮由C++標準異常類中的runtime_error類派生;VxWorks內核采用設置全局變量errno的方式記錄系統運行中產生的錯誤,所以將int ?errNum 作為該類的成員變量,用以記錄異常發生時的errno值。源代碼如下:
#include?<stdexcept>
#include?"errnoLib.h"
class?VxRunTimeError?:?public?runtime_error?{
protected:
int?errNum;
public:
VxRunTimeError(const?string?msg?=?"")?:?runtime_error(msg),?errNum(errnoGet?())
{
}
int?getErrNum(void)
{
return?errNum;
}
};
2 任務類VxTask
任務類VxTask用以封裝VxWorks的task,本來考慮將任務的入口函數作為該類的純虛成員函數,用戶只要在該類的派生類中重載該純虛函數就能實現自己的VxWorks task,但由於taskSpawn()的入口函數的參數類型是FUNCPTR(typedef ?int (*FUNCPTR) (...)),而指向VxTask類成員函數的指針類型為int (VxTask:: *ptr)(…),編譯器不支持這兩種類型之間的強制類型轉換,所以只能換種思路——用壹個名為Runnable的類專門封裝task的入口函數,同時提供壹個靜態成員函數完成對該入口函數的調用,而該靜態成員函數地址可以轉換成FUNCPTR類型,供taskSpawn()調用。在VxTask類中實現關於task的系統調用。部分源代碼如下:
class?Runnable?{protected:
virtual?void?run()?=?0;
public:
static?void?entry(Runnable?*?Run)
{
Run->run();
}
virtual?~Runnable()
{
}
};
class?VxTask?{
protected:
char?*name;
int?tid;
public:
VxTask(char*?Name,?int?Arg1?,?FUNCPTR?Entry?=?(FUNCPTR)Runnable::entry,?int?Pri?=?150,?int?Opt?=?VX_FP_TASK,?int?StackSize?=?2000000,
int?Arg2?=?0,?int?Arg3?=?0,?int?Arg4?=?0,?int?Arg5?=?0,?int?Arg6?=?0,?int?Arg7?=?0,?int?Arg8?=?0,?int?Arg9?=?0,?int?Arg10?=?0)?:?name(Name){
if(Entry?==?NULL)?{
throw(VxRunTimeError("Task?Creat?Fail:?Entry?Can't?be?NULL!"));
}
tid=taskSpawn(Name,Pri,Opt,StackSize,Entry,Arg1,Arg2,?Arg3,Arg4,Arg5,Arg6,Arg7,Arg8,Arg9,Arg10);
if(tid?==?ERROR)?{
throw(VxRunTimeError("Task?Spawn?Fail!"));
}
}
~VxTask()
{
if(taskDelete(tid)?==?ERROR)?{
throw(VxRunTimeError("Task?Delete?Error:?Task?delete?fail!"));
}
}
void?suspend()
{
if(taskSuspend(tid)?==?ERROR)?{
throw(VxRunTimeError("Task?Suspend?Error:?Task?suspend?fail!"));
}
}
void?resume()
{
if(taskResume(tid)?==?ERROR)?{
throw(VxRunTimeError("Task?Resume?Error:?Task?resume?fail!"));
}
}
int?getTid()
{
return?tid;
}
};
使用時首先派生Runnable的子類,重載其run()成員函數,然後將該子類的對象指針賦給VxTask的構造函數,用戶task就跑起來了:
class?MyRunnable?:?public?Runnable{
void?run()?{
while(1)?{
cout<<"Hello?VxWorks?Task?World!"<<endl;
taskDelay(sysClkRateGet());
}
}
};
void?myMain()
{
MyRunnable?myRun;
VxTask?task(“tMyRun”,?(int)&myRun);
While(1)?{
taskDelay(sysClkRateGet());
}
}
在shell中sp myMain可以看到預期效果,但如果myMain()中去掉最後的while(1)循環,就只會在輸出窗口中看壹次Hello VxWorks Task World!輸出。Why?(提示:VxTask的析構函數!)
3 中斷類VxInt
中斷類VxInt與VxTask類似,同樣用Runnable的派生類封裝入口函數,VxInt類實現中斷系統調用:
typedefvoid?(**VOIDFUNCPTRPTR)?(...);class?VxInt?{
protected:
int?intNum;
public:
VxInt(int?IntNum,?int?Arg?=?0,?VOIDFUNCPTR?Entry?=?(VOIDFUNCPTR)Runnable::entry)?:?intNum(IntNum)
{
if(intConnect((VOIDFUNCPTRPTR)INUM_TO_IVEC(intNum),?Entry,?Arg)?==?ERROR)?{
throw(VxRunTimeError("Interrupt?Connect?Fail!"));
}
}
};
與task不同,中斷服務程序(ISR)中不能調用可能被阻塞的函數,這點需要在重載Runnable派生類中的run()成員函數時引起註意。
4 看門狗類 VxWatchDog
VxWorks中的看門狗實際上是利用系統時鐘中斷來定時執行某個函數的,所以被看門狗執行的函數是運行在中斷的上下文(Context)中,而不是任務的上下文中,故該函數中也不能調用帶有阻塞功能的函數。所以VxWatchDog的實現與中斷類VxInt類似:
class?VxWatchDog?{
WDOG_ID?id;
int?delay;
public:
VxWatchDog(int?Delay,?Runnable?*EntryObj)?:?delay(Delay)
{
id?=?wdCreate();
if(id?==?NULL)?{
throw(VxRunTimeError("Watch?Dog?Creat?Fail!"));
}
if(wdStart(id,?delay,?(FUNCPTR)Runnable::entry,(int)EntryObj)?!=?OK)?{
throw(VxRunTimeError("Watch?Dog?Start?Fail!"));
}
}
void?cancel()
{
if(wdCancel(id)?!=?OK)?{
throw(VxRunTimeError("Watch?Dog?Cancel?Fail!"));
}
}
WDOG_ID?getId()
{
return(id);
}
~VxWatchDog()
{
if(wdDelete(id)?!=?OK)?{
throw(VxRunTimeError("Watch?Dog?Delete?Fail!"));
}
}
};
wdStart(WDOG_ID Id, int Delay, FUNCPTR Ptr, int Para )只會讓函數Ptr在延時Delay ticks後執行壹次,要周期性地執行Ptr,需要在Ptr中遞歸調用wdStart()。那能否這樣實現呢:
class?WdRun?:?public?Runnable?{protected:
void?run()?{
logMsg("Hello?Watch?Dog?World!",0,0,0,0,0,0);
VxWatchDog?wtDog(sysClkRateGet(),?this);
}
};
上述程序試圖在入口函數中產生壹個VxWatchDog類的對象,並用this指針初始化該對象,以期達到每秒鐘執行壹次入口函數的目的。但是不要忘了該入口函數是運行在中斷的上下文中的,不允許動態地產生或刪除對象,所以采用這種方法實現周期性執行作業並不可行。
為了在入口函數中調用wdStart(),且要避免動態地生成VxWatchDog對象,需要在Runnable派生類的成員變量中包含壹個VxWatchDog指針,通過該指針調用所指對象的wdStart()。為此,需要在VxWatchDog類中增加成員函數:
VxWatchDog?::VxWatchDog(int?Delay)?:?id(wdCreate()),?delay(Delay)
{
if(id?==?NULL)?{ throw(VxRunTimeError("Watch?Dog?Creat?Fail!"));}
} void?VxWatchDog?::start(Runnable?*EntryObj) { if(wdStart(id,?delay,?(FUNCPTR)Runnable::entry,?(int)EntryObj)?!=?OK)?{throw(VxRunTimeError("Watch?Dog?Start?Fail!"));
}
}
class?WdRun?:?public?Runnable?{
protected:
VxWatchDog?*dog;
virtual?void?run()?{
logMsg("Hello?Watch?Dog?World!",0,0,0,0,0,0);
dog->start(this);
}
public:
WdRun(VxWatchDog?*Dog)?:?dog(Dog)
{
}
};
void?myMain()
{
VxWatchDog?wtDog(sysClkRateGet());WdRun?run(&wtDog);
wtDog.start(&run);
while(1)?{
taskDelay(sysClkRateGet());
cout<<"In?Main!"<<endl;
}
}
在shell中輸入sp myMain,可以看到預期輸出。
5 信號量類 VxSem
VxWorks信號量包括互斥信號量、二進制信號量和計數信號量,這三種信號量除了創建時調用各自的創建函數,其它操作具有相同的接口,所以考慮采用VxSem類作為信號量基類,提供統壹的信號量操作接口,VxSemM、VxSemB、VxSemC三個派生類分別封裝了三種信號量的創建函數:
class?VxSem?{
protected:
SEM_ID?id;
public:
VxSem(SEM_ID?Id)?:?id(Id)
{
}
virtual?~VxSem()
{
if(semDelete(id)?==?ERROR)?{
throw(VxRunTimeError("Semaphore?Delete?Fail!"));
}
}
void?take(int?TimeOut?=?WAIT_FOREVER)
{
if(semTake(id,WAIT_FOREVER)?==?ERROR)?{
throw(VxRunTimeError("Semaphore?Take?Fail!"));
}
}
void?give()
{
if(semGive(id)?==?ERROR)?{
throw(VxRunTimeError("Semaphore?Give?Fail!"));
}
}
void?flush()
{
if(semFlush(id)?==?ERROR)?{
throw(VxRunTimeError("Semaphore?Flush?Fail!"));
}
}
SEM_ID?getId()
{
return?id;
}
};
class?VxSemB?:?public?VxSem?{
public:
VxSemB(int?Opts?=?SEM_Q_FIFO,?SEM_B_STATE?State?=?SEM_EMPTY)?:?VxSem(semBCreate?(Opts,?State))
{
if(id?==?0)?{
throw(VxRunTimeError("Binary?Semaphore?Creat?Fail!"));
}
}
};
class?VxSemM?:?public?VxSem?{
public:
VxSemM(int?Opts?=?SEM_Q_PRIORITY?|?SEM_INVERSION_SAFE?|?SEM_DELETE_SAFE)?:?VxSem(semMCreate?(Opts))
{
if(id?==?0)?{
throw(VxRunTimeError("Mutual-exclusion?Semaphore?Creat?Fail!"));
}
}
};
class?VxSemC?:?public?VxSem?{
public:
VxSemC(int?Opts,?int?Cnt)?:?VxSem(semCCreate?(Opts,?Cnt))
{
if(id?==?0)?{
throw(VxRunTimeError("Counting?Semaphore?Creat?Fail!"));
}
}
};