解建偉 賴前程 曹成軍 張興旺
(中國電器科學研究院有限公司 廣東 廣州 510300)
VxWorks自定義動態(tài)系統(tǒng)調用的應用
解建偉 賴前程 曹成軍 張興旺
(中國電器科學研究院有限公司 廣東 廣州 510300)
系統(tǒng)調用是用戶模式程序訪問內核服務、硬件資源的接口。自定義的系統(tǒng)調用在VxWorks程序開發(fā)中有著非常重要的作用。通過自定義系統(tǒng)調用可以讓用戶模式的程序訪問更多的內核函數、硬件資源,可以擴展程序的應用范圍。簡單論述VxWorks中自定義系統(tǒng)調用的概念,描述系統(tǒng)調用的過程,分步介紹自定義動態(tài)系統(tǒng)調用的實現過程。最后介紹應用程序中動態(tài)系統(tǒng)調用的使用方法。
VxWorks 自定義 系統(tǒng)調用
VxWorks是美國風河公司設計開發(fā)的嵌入式實時操作系統(tǒng),它支持多種處理器平臺,具有較高的可擴展性與安全性。VxWorks在6.0版本之前的系統(tǒng)只提供一個內存地址空間,用戶程序和操作系統(tǒng)程序沒有做隔離,所有的任務都運行在特權模式。盡管這種形式為程序開發(fā)提供了比較好的性能和靈活性,但也使內核與應用程序在內存空間存在相互干擾的可能。在6.0版本以后,操作系統(tǒng)開始支持實時進程RTP(real-time processes),它支持應用程序在用戶模式運行,并且是與操作系統(tǒng)內核完全隔離的運行模型。這種模型是專門為滿足硬實時操作系統(tǒng)對確定性和速度的要求而設計[1]。
VxWorks中的RTP在很多方面都與Unix和Linux的用戶態(tài)的進程類似,每個進程都有自己的地址空間,包括可執(zhí)行代碼、數據堆棧以及自身相關的管理資源[2]。這使得系統(tǒng)的安全性得到極大提高,同時系統(tǒng)調用接口為用戶態(tài)的應用程序提供了訪問內核的接口[3]。系統(tǒng)調用常用于RTP程序訪問內核服務,CPU外設或其他硬件資源。
需要說明的是應用編程接口(API)與系統(tǒng)調用是不同的,前者是一個函數定義,說明如何獲得一個給定的服務,而后者是通過軟中斷向內核發(fā)出一個明確的模式切換請求[4]。由于特權模式的內核程序和用戶模式程序有不同的指令集和內存管理單元MMU(memory management unit)設置,應用程序運行在用戶模式時不能直接訪問內核函數和數據結構。用戶模式程序執(zhí)行系統(tǒng)調用函數時系統(tǒng)從用戶模式切換到CPU特權級別的內核模式,當執(zhí)行完函數后再恢復到用戶模式[5]。VxWorks中系統(tǒng)調用的模式切換過程對使用者來說是透明的[1]。
在RTP程序中使用實時系統(tǒng)原有的系統(tǒng)調用與內核交互時,時常會出現不滿足設計需求或性能指標的情況[6]。通過增加自定義系統(tǒng)調用,我們可以根據自己的需求,來給內核增加特定的功能,滿足性能指標和設計需求[7]。
在VxWorks中的系統(tǒng)調用一般可分為兩大類,一類是操作系統(tǒng)定義的系統(tǒng)調用,像exit、create、open、semGive等都是系統(tǒng)調用;另外一類是用戶自定義的系統(tǒng)調用。VxWorks中的自定義系統(tǒng)調用又分為靜態(tài)自定義系統(tǒng)調用和動態(tài)自定義系統(tǒng)調用兩種。這兩種系統(tǒng)調用的實現方式不同,性能都基本一致,但是它們各有優(yōu)缺點。
自定義的靜態(tài)系統(tǒng)調用的優(yōu)勢在于它是與操作系統(tǒng)源碼一起編譯,可根據需要修改系統(tǒng)原有的系統(tǒng)調用函數;在使用時與普通的函數調用方式一致。不足之處是修改定義系統(tǒng)調用要對系統(tǒng)核心文件作修改,編譯調試,這會帶來比較大的工作量,一個小小的失誤往往會使系統(tǒng)不穩(wěn)定甚至崩潰,系統(tǒng)可靠性和穩(wěn)定性不容易保證;同時增加、修改系統(tǒng)調用都比較麻煩,使得系統(tǒng)可維護性與可移植性將會降低[8]。
動態(tài)的系統(tǒng)調用只需要在運行時向系統(tǒng)注冊自定義系統(tǒng)調用,不需要像自定義靜態(tài)系統(tǒng)調用那樣修改VxWorks源代碼文件,不需要與操作系統(tǒng)一起重新編譯生成[2]。其優(yōu)勢是不需要對系統(tǒng)核心代碼做更改,減少了對內核穩(wěn)定性的影響;同時對新增系統(tǒng)調用的修改、更新、刪除都比較方便。不足之處在于一般不能通過名字直接訪問動態(tài)系統(tǒng)調用函數,在應用的時候需要知道自定義動態(tài)系統(tǒng)調用的組編號和索引位置,然后統(tǒng)一由syscall函數調用。
2.1 分析與設計
在VxWorks6.9實時操作系統(tǒng)中用戶自定義的系統(tǒng)調用主要有3個元素:
(1) 系統(tǒng)調用。執(zhí)行系統(tǒng)調用的是使用匯編語言編寫的一個函數,它通過C語言調用。區(qū)別不同系統(tǒng)調用的方式是在調用時使用系統(tǒng)調用函數名字和系統(tǒng)調用編號,這是系統(tǒng)開發(fā)人員定義的。
(2) 捕獲處理。當用戶RTP程序訪問系統(tǒng)調用時會執(zhí)行捕獲指令處理,它將調用的程序從用戶模式切換到特權(內核)模式執(zhí)行。系統(tǒng)調用編號傳遞給內核去識別關聯(lián)的函數。在內核中,捕獲處理從用戶堆?;蚣拇嫫骺臻g拷貝系統(tǒng)調用參數到內核堆棧空間,然后調用系統(tǒng)調用關聯(lián)匹配的處理函數。
(3) 處理函數。系統(tǒng)調用的處理函數是系統(tǒng)開發(fā)者編寫的程序,它只有一個結構體變量參數,結構體成員就是系統(tǒng)調用的參數[3]。當系統(tǒng)從處理函數返回,調用任務將從特權(內核)模式切換到用戶模式。
用戶自定義動態(tài)系統(tǒng)調用程序的實現一般可以分為以下3個步驟:
(1) 設計參數結構,編寫處理函數;
(2) 添加系統(tǒng)調用處理表結構信息;
(3) 注冊系統(tǒng)調用函數及相關設置。
VxWorks文檔建議,對于添加動態(tài)系統(tǒng)調用的方法最好使用統(tǒng)一的命名約定,使得系統(tǒng)調用的邏輯一致性和代碼的清晰度最好。命名約定如表1所示。
表1 命名約束
設計的實時控制系統(tǒng)中需要實現RTP應用程序讀寫FPGA中的數據動態(tài)系統(tǒng)調用,其中FPGA是掛載在處理器的IFC(Integrated Flash Controller)接口上通過絕對地址訪問。
根據命名約束,在內核任務程序中需要實現兩個自定義動態(tài)系統(tǒng)調用get_fpga_registerSc和set_fpga_registerSc函數,并為它們注冊合適的系統(tǒng)調用編號,檢查系統(tǒng)調用的注冊信息。最后在RTP程序中通過使用此動態(tài)統(tǒng)調用訪問FPGA的寄存器內容,檢查執(zhí)行正確性。
2.2 實現過程
第一步是設計、編寫動態(tài)系統(tǒng)調用函數,及其參數結構。自定義讀寫函數的系統(tǒng)調用其聲明如下:
int set_fpga_registerSc(SysCallSetRegScArgs *pArgs);
int get_fpga_registerSc(SysCallGetRegScArgs *pArgs);
其中動態(tài)系統(tǒng)調用定義必須要有整型的返回值,參數是通過指向參數結構體的指針來進行傳遞。自定義動態(tài)系統(tǒng)調用函數最多可以有8個參數,即最多可以只有8個結構體成員。每個參數長度都是本機字大小(32位處理器參數長度就是32位,64位處理器參數長度就是64位),如果在32位系統(tǒng)上要傳遞64位參數需要特別處理。get_fpga_registerSc系統(tǒng)調用有2個參數,其傳遞的參數結構體如下:
typedef struct{
unsigned int index;
unsigned short* pV;
} SysCallGetRegScArgs;
函數get_fpga_registerSc的執(zhí)行是在內核中完成,執(zhí)行時所有參數在使用前必須做邊界值檢查,驗證使用的內存地址,以及檢查數據結構的有效性。對內存地址的驗證是實際大小的緩沖大小,而不是最大緩沖大小。也就是說RTP程序分配了一個20個字節(jié)的緩沖區(qū),那么系統(tǒng)調用函數應該驗證這20個字節(jié)的內存,如果系統(tǒng)調用函數驗證這個緩沖的最大長度(比如64個字節(jié))可能會因為只有20個字節(jié)被分配而出錯。解決這個問題的最好方法是通過參數告訴系統(tǒng)調用函數使用的緩沖空間的大小信息。
對動態(tài)系統(tǒng)調用函數get_fpga_registerSc的實現如下:
int get_fpga_registerSc(SysCallGetRegScArgs *pArgs)
{
if(pArgs->index > FPGA_AGREED_MAX_SIZE)
{
errno = EMSGSIZE;
return ERROR;
}
if (scMemValidate (pArgs->pV, sizeof(unsigned short), SC_PROT_WRITE) == ERROR)
{
errno = EINVAL;
return ERROR;
}
if(FPGA_GetValue(pArgs->index,pArgs->pV)==ERROR)
{
errno = EINVAL;
return ERROR;
}
return OK;
}
在執(zhí)行時首先驗證讀取的FPGA地址是否已經超出了可訪問范圍,然后使用scMemValidate函數來驗證第二個參數地址寫入的有效性,最后讀取FPGA指定地址的數據并返回。系統(tǒng)調用內存驗證函數scMemValidate的第一個參數是驗證的起始地址,第二個是驗證地址的數據或結構的長度,第三個參數是地址空間訪問模式,它分為讀、寫、以及線程安全的讀寫3種模式。這3種模式中的讀、寫可以組合使用,而線程安全的讀寫不可以組合使用。
在參數檢查、內存驗證、以及執(zhí)行過程中如果出現了錯誤,需要根據具體情況對errno設置一個合適的異常值,然后返回錯誤-1(ERROR),如果正常則返回0(OK)。返回ERROR時內核的異常值將被拷貝到調用的任務進程的errno中;如果沒有錯誤,這時只是將值拷貝到調用用戶模式的任務。
第二步是添加自定義的系統(tǒng)調用函數表。注冊VxWorks動態(tài)系統(tǒng)調用前必須在程序源碼文件中包含自定義動態(tài)系統(tǒng)調用的處理程序表SYSCALL_RTN_TBL_ENTRY。表中每一項都由SYSCALL_DESC_ENTRY()宏關聯(lián)一個系統(tǒng)調用,在VxWorks6.9版本的系統(tǒng)中此宏定義有三個參數(如果是其它版本,定義會略有不同),示例如下:
LOCAL _WRS_DATA_ALIGN_BYTES(16) SYSCALL_RTN_TBL_ENTRY pRtnTbl [NUM_RTN] = {
SYSCALL_DESC_ENTRY (set_fpga_registerSc, ″set_fpga_register″, 2),
SYSCALL_DESC_ENTRY (get_fpga_registerSc, ″get_fpga_register″, 2)
};
系統(tǒng)調用表的_WRS_DATA_ALIGN_BYTES(16)修飾是告訴編譯、鏈接器數組按照16字節(jié)對齊以提高性能,這個修飾是可選的。NUM_RTN定義的值等于2,表示數組中的宏元素個數。SYSCALL_DESC_ENTRY宏的第1個參數是執(zhí)行系統(tǒng)調用的函數指針,第2個是對應的系統(tǒng)調用名字,第3個參數系統(tǒng)調用參數個數。
第三步是注冊系統(tǒng)調用。定義的處理程序表需要關聯(lián)到合適的系統(tǒng)調用的組中,完成注冊工作后才能使用。注冊的每個系統(tǒng)調用都必須有唯一的系統(tǒng)調用編號。執(zhí)行動態(tài)系統(tǒng)調用時,編號傳遞給內核,然后使用它來識別和執(zhí)行匹配的系統(tǒng)調用處理程序。每個系統(tǒng)調用的編號是32 bit整數,由兩部分組成的:一個是具有10 bit長度的系統(tǒng)調用的組數,另外一個是具有6 bit的系統(tǒng)調用程序編號,如圖1所示。
圖1 系統(tǒng)調用編號
據定義可知,此系統(tǒng)最多允許有1024個組編號,每個組最多有64個程序編號,總共可以容納65 536個系統(tǒng)調用。根據系統(tǒng)定義,從第2組到第7組是自定義使用區(qū)間,其他組為系統(tǒng)使用或保留區(qū)間。
系統(tǒng)調用的注冊需要使用syscallGroupRegister函數,此函數第1個參數是指定注冊組編號,第2個參數是組名字,第3個參數是函數個數,第4個參數是處理程序表地址,第5個參數是強制覆蓋開關。注冊示例代碼如下:
syscallGroupRegister (2, ″fpgaIF_ScGroup″, NUM_RTN, pRtnTbl, FALSE);
注冊函數將pRtnTbl注冊到了第2組自定義系統(tǒng)調用中,并為之取名為fpgaIF_ScGroup,注冊組內有2(NUM_RTN)個系統(tǒng)調用函數,即set_fpga_registerSc和get_fpga_registerSc函數,程序編號在組內分別是0和1。系統(tǒng)調用的編號不是必須順序的,在多個編號之間的定義是允許留空白。注冊系統(tǒng)調用后的fpgaIF_ScGroup系統(tǒng)調用接口視圖如圖2所示。
圖2 fpgaIF_ScGroup視圖
2.3 檢查系統(tǒng)調用
為了方便檢查注冊的自定義系統(tǒng)調用,需要配置操作系統(tǒng)包含INCLUDE_SHOW_ROUTINES組件,使用它檢查自定義系統(tǒng)調用。編譯并執(zhí)行本文中實現的自定義動態(tài)系統(tǒng)調用注冊程序及必要的組件。操作系統(tǒng)正確運行后,在調試終端使用syscallShow函數命令檢查系統(tǒng)調用的詳細信息。syscallShow函數有兩個參數,第一個參數是組號,第二個參數是顯示級別,顯示級別0表示只輸出組內信息,1表示輸出全部信息。
圖3 系統(tǒng)調用信息
圖3中,在調試終端使用syscallShow查看自定義系統(tǒng)調用的第2組,以級別1顯示信息??梢钥吹阶缘南到y(tǒng)調用的組名為fpgaIF_ScGroup,以及組內提供的系統(tǒng)調用的名字、地址、參數個數等信息與設計完全符合。
在RTP程序中執(zhí)行系統(tǒng)調用,如果是靜態(tài)的自定義系統(tǒng)調用可以直接通過函數名調用,使用上與普通函數調用沒有任何區(qū)別;如果使用自定義動態(tài)系統(tǒng)調用,則需要統(tǒng)一使用syscall接口函數來訪問。不管是什么樣的系統(tǒng)調用,在調用時產生的模式切換是透明的。
動態(tài)系統(tǒng)調用接口syscall函數有9個參數,其中前8個參數是系統(tǒng)調用傳入的參數,最后1個參數是系統(tǒng)調用的編號,編號的計算可以使用宏SYSCALL_NUMBER指定組號與程序號替代。程序中動態(tài)系統(tǒng)調用的應用封裝如下:
#define SC_GET_REG SYSCALL_NUMBER (2, 1)
int RtpGetRegister(int i, int *pV)
{
if(syscall (i, pV,0,0,0,0,0,0, SC_GET_REG) == ERROR)
{
printf (″syscall() returned err. errno = %#x ”, errno);
return ERROR;
}
return OK;
}
根據定義可知,RtpGetRegister 是封裝使用動態(tài)系統(tǒng)調用函數,其中的syscall執(zhí)行了第2組中的第1個系統(tǒng)調用(此系統(tǒng)調用函數就是本文中定義的get_fpga_registerSc函數)并將參數i值和pV指針傳入函數,執(zhí)行函數時首先驗證i,pV的有效性,然后讀取FPGA的i地址的值放入pV指向的內存空間,最后返回執(zhí)行結果。如果執(zhí)行系統(tǒng)調用錯誤將返回-1(ERROR),此時可以從errno獲取到系統(tǒng)調用返回的錯誤號并根據需要處理此錯誤;如果執(zhí)行系統(tǒng)調用正常將會返回0(OK)。
在VxWorks系統(tǒng)中自定義系統(tǒng)調用可以讓用戶動態(tài)地應用程序獲得更多的資源訪問權限,這既可以保證應用程序的安全性又擴展了其應用范圍。
為操作系統(tǒng)增加動態(tài)系統(tǒng)調用,可以使開發(fā)人員專注于如何設計、完善自定義的系統(tǒng)調用函數,無需對操作系統(tǒng)源碼作修改。這樣不會增加操作系統(tǒng)內核不穩(wěn)定的風險,同時又減少了開發(fā)工作量,增強了自定義系統(tǒng)調用的可維護性。
[1]WindRiverSystemsInc.VxWorksApplicationProgrammer’sGuide6.9[Z]. 2013.
[2]WindRiverSystemsInc.VxWorksKernelProgrammer’sGuide6.9[Z].2013.
[3] 葛仁北. 系統(tǒng)調用與操作系統(tǒng)安全[J].計算機工程與應用,2002,38(19):97-99,128.
[4]BovetDP,CesatiM.深入理解Linux內核[J].陳莉君,張瓊聲,張宏偉,譯.3版. 北京:中國電力出版社,2007:397-398.
[5] 謝錦濱, 王晨, 張申生. 系統(tǒng)調用重定向的研究與應用[J].計算機應用與軟件,2006,23(3):4-6.
[6]WindRiverSystemsInc.VxWorksBenchmarkDataSheetforVxWorks6.9 1.2GHzP2020RDB[DB].2011.
[7] 胡盼盼.Linux下系統(tǒng)調用原理解析及增加系統(tǒng)調用的方法[J].計算機系統(tǒng)應用,2007,16(8):109-112.
[8] 羅忠海, 劉心松.UNIX環(huán)境中動態(tài)擴充系統(tǒng)調用的功能[J].計算機工程與設計,1999,20(1):1-6.
APPLICATION OF VXWORKS DYNAMICAL CUSTOM SYSTEM CALLS
Xie Jianwei Lai Qiancheng Cao Chengjun Zhang Xingwang
(ChinaNationalElectricApparatusResearchInstituteCo.,Ltd.Guangzhou510300,Guangdong,China)
The user mode application through system calls interface can access the kernel services and hardware resources. Custom system calls in VxWorks has a very important role in application development. Applications of user mode are able to access more kernel functions or other hardware resources through custom system calls, extending the scope of application. Thus, the concept of custom system calls in VxWorks and the system calls process are described, then the implementation process of the dynamical custom system calls is introduced step by step. Finally, the method of using dynamical custom system calls in application program is introduced.
VxWorks Custom System calls
2015-12-11。解建偉,工程師,主研領域:嵌入式軟件與驅動開發(fā)。賴前程,工程師。曹成軍,高工。張興旺,教授級高工。
TP316.2
A
10.3969/j.issn.1000-386x.2017.02.043