OpenHarmony LiteOS-A內(nèi)核文檔之學習--系統(tǒng)調(diào)用
OpenHarmony LiteOS-A內(nèi)核實現(xiàn)態(tài)與內(nèi)核態(tài)的區(qū)分隔離,用戶態(tài)程序不能直接訪問內(nèi)核資源,而系統(tǒng)調(diào)用則為用戶態(tài)程序提供了一種訪問內(nèi)核資源、與內(nèi)核進行交互的通道。如下圖所示,用戶程序通過調(diào)用System API(系統(tǒng)API,通常是系統(tǒng)提供的POSIX接口)進行內(nèi)核資源訪問與交互請求,POSIX接口內(nèi)部會觸發(fā)SVC/SWI異常,完成系統(tǒng)從用戶態(tài)到內(nèi)核態(tài)的切換,然后對接到內(nèi)核的Syscall Handler(系統(tǒng)調(diào)用統(tǒng)一處理接口)進行參數(shù)解析,最終分發(fā)至具體的內(nèi)核處理函數(shù)。

Syscall Handler的具體實現(xiàn)在kernel/liteos_a/syscall/los_syscall.c中OsArmA32SyscallHandle函數(shù),在進入系統(tǒng)軟中斷異常時會調(diào)用此函數(shù),并且按照kernel/liteos_a/syscall/syscall_lookup.h中的清單進行系統(tǒng)調(diào)用的入?yún)⒔馕觯瑘?zhí)行各系統(tǒng)調(diào)用最終對應的內(nèi)核處理函數(shù)。
1. 涉及的系統(tǒng)調(diào)用文件目錄介紹
先介紹下系統(tǒng)調(diào)用相關的內(nèi)核態(tài)代碼、用戶態(tài)代碼所在的文件目錄。
1.1 syscall/syscall_lookup.h文件
kernel/liteos_a/syscall/syscall_lookup.h文件中維護內(nèi)核向用戶態(tài)提供的系統(tǒng)調(diào)用接口。文件中包含虛擬文件系統(tǒng)VFS、動態(tài)加載DYNLOAD、PIPE、SHELL、LWIP、SECURITY_CAPABILITY等模塊的系統(tǒng)調(diào)用接口,文件內(nèi)容片段如下。每一個系統(tǒng)調(diào)用有宏函數(shù)SYSCALL_HAND_DEF定義,包含系統(tǒng)調(diào)用編號、系統(tǒng)調(diào)用處理函數(shù),返回值類型,系統(tǒng)調(diào)用處理參數(shù)數(shù)目,下文詳細介紹。
- ......
- SYSCALL_HAND_DEF(__NR_write, SysWrite, ssize_t, ARG_NUM_3)
- SYSCALL_HAND_DEF(__NR_open, SysOpen, int, ARG_NUM_7)
- SYSCALL_HAND_DEF(__NR_close, SysClose, int, ARG_NUM_1)
- SYSCALL_HAND_DEF(__NR_creat, SysCreat, int, ARG_NUM_2)
- ......
文件syscall/syscall_lookup.h被文件syscall/los_syscall.c中的系統(tǒng)調(diào)用初始化函數(shù)OsSyscallHandleInit調(diào)用,調(diào)用代碼如下??梢钥闯龅谝粋€參數(shù)是系統(tǒng)調(diào)用函數(shù)編號,編號定義在文件third_party/musl/porting/liteos_a/kernel/include/bits/syscall.h;第二個是系統(tǒng)調(diào)用函數(shù),函數(shù)原型聲明在文件kernel/liteos_a/syscall/los_syscall.h,函數(shù)實現(xiàn)一般在kernel/liteos_a/syscall目錄下的源代碼文件中實現(xiàn); 第三個是返回值類型,暫時沒有使用;第四個函數(shù)是系統(tǒng)調(diào)用函數(shù)的參數(shù)數(shù)目。
下面看下宏函數(shù)的代碼,⑴處把定義的系統(tǒng)調(diào)用函數(shù)都維護在全局數(shù)組g_syscallHandle。⑵處由于參數(shù)數(shù)量不會特別大,記錄系統(tǒng)調(diào)用處理函數(shù)參數(shù)數(shù)目的全局數(shù)組g_syscallNArgs的類型是UINT8,每4個bit位維護一個系統(tǒng)調(diào)用的參數(shù)數(shù)目。
- ...
- static UINTPTR g_syscallHandle[SYS_CALL_NUM] = {0};
- static UINT8 g_syscallNArgs[(SYS_CALL_NUM + 1) / NARG_PER_BYTE] = {0};
- ...
- void OsSyscallHandleInit(void)
- {
- #define SYSCALL_HAND_DEF(id, fun, rType, nArg) \
- if ((id) < SYS_CALL_NUM) { \
- ⑴ g_syscallHandle[(id)] = (UINTPTR)(fun); \
- ⑵ g_syscallNArgs[(id) / NARG_PER_BYTE] |= ((id) & 1) ? (nArg) << NARG_BITS : (nArg); \
- } \
- #include "syscall_lookup.h"
- #undef SYSCALL_HAND_DEF
- }
- LOS_MODULE_INIT(OsSyscallHandleInit, LOS_INIT_LEVEL_KMOD_EXTENDED);
1.2 syscall/los_syscall.h文件
kernel/liteos_a/syscall/los_syscall.h文件中維護內(nèi)核的系統(tǒng)調(diào)用對接函數(shù)聲明。函數(shù)實現(xiàn)一般在目錄/kernel/liteos_a/syscall下的源代碼文件中。該文件主要被如下源文件include使用。
- ./kernel/liteos_a/fs/vfs/vfs_cmd/vfs_shellcmd.c:50:#include "los_syscall.h"
- ./kernel/liteos_a/kernel/extended/blackbox/los_blackbox_core.c:46:#include "los_syscall.h"
- ./kernel/liteos_a/kernel/user/src/los_user_init.c:33:#include "los_syscall.h"
- ./kernel/liteos_a/syscall/fs_syscall.c:49:#include "los_syscall.h"
- ./kernel/liteos_a/syscall/los_syscall.c:38:#include "los_syscall.h"
- ./kernel/liteos_a/syscall/net_syscall.c:37:#include "los_syscall.h"
1.3 系統(tǒng)調(diào)用函數(shù)編號
維護系統(tǒng)調(diào)用編號文件有2個,需要分別在用戶態(tài)和內(nèi)核態(tài)維護系統(tǒng)調(diào)用函數(shù)編號。除了文件porting/liteos_a/kernel/include/bits/syscall.h中多個幾個參數(shù)數(shù)目的宏定義外,下述兩個文件的內(nèi)容基本相同。
- third_party/musl/porting/liteos_a/user/arch/arm/bits/syscall.h.in
- third_party/musl/porting/liteos_a/kernel/include/bits/syscall.h
2. 系統(tǒng)調(diào)用開發(fā)示例
當需要新增一個系統(tǒng)調(diào)用接口時,可以參考下述步驟:
- 在LibC庫中確定并添加新增的系統(tǒng)調(diào)用號。
- 在LibC庫中新增用戶態(tài)的函數(shù)接口聲明及實現(xiàn)。
- 在內(nèi)核系統(tǒng)調(diào)用頭文件中確定并添加新增的系統(tǒng)調(diào)用號及對應內(nèi)核處理函數(shù)的聲明。
- 在內(nèi)核中新增該系統(tǒng)調(diào)用對應的內(nèi)核處理函數(shù)。
2.1 在LibC庫中確定并添加新增的系統(tǒng)調(diào)用號
編輯文件porting/liteos_a/user/arch/arm/bits/syscall.h.in,如下所示,其中⑴處的__NR_new_syscall_sample為新增系統(tǒng)調(diào)用號。需要注意同時更新下⑵處的編號。
- /* OHOS customized syscalls, not compatible with ARM EABI */
- #define __NR_OHOS_BEGIN 500
- #define __NR_pthread_set_detach (__NR_OHOS_BEGIN + 0)
- ......
- #define __NR_sysconf (__NR_OHOS_BEGIN + 21)
- ⑴ #define __NR_new_syscall_sample (__NR_OHOS_BEGIN + 22) /* 新增的系統(tǒng)調(diào)用號 __NR_new_syscall_sample:522 */
- ⑵ #define __NR_syscallend (__NR_OHOS_BEGIN + 23)
2.2 在LibC庫中新增用戶態(tài)的函數(shù)接口聲明及實現(xiàn)。
系統(tǒng)調(diào)用提供基礎的用戶態(tài)程序與內(nèi)核的交互功能,不建議開發(fā)者直接使用系統(tǒng)調(diào)用接口,推薦使用內(nèi)核提供的對外POSIX接口。需要在LibC庫中新增用戶態(tài)接口的聲明與實現(xiàn)。為了簡化,我們在現(xiàn)成的一個源文件里增加一函數(shù)實現(xiàn)代碼,如third_party/musl/porting/liteos_a/user/src/aio/aio.c文件中增加:
- /* 新增系統(tǒng)調(diào)用用戶態(tài)的接口實現(xiàn) */
- void newSyscallSample(int num)
- {
- printf("user mode: num = %d\n", num);
- __syscall(SYS_new_syscall_sample, num);
- return;
- }
2.3 在內(nèi)核系統(tǒng)調(diào)用頭文件中新增系統(tǒng)調(diào)用號
在內(nèi)核系統(tǒng)調(diào)用頭文件中新增系統(tǒng)調(diào)用號,如下所示,在third_party/musl/porting/liteos_a/kernel/include/bits/syscall.h文件中,__NR_new_syscall_sample為新增系統(tǒng)調(diào)用號。用戶態(tài)代碼和內(nèi)核態(tài)代碼增加系統(tǒng)調(diào)用號方式相同,編號相同。
- /* OHOS customized syscalls, not compatible with ARM EABI */
- #define __NR_OHOS_BEGIN 500
- #define __NR_pthread_set_detach (__NR_OHOS_BEGIN + 0)
- ...
- #define __NR_sysconf (__NR_OHOS_BEGIN + 21)
- #define __NR_new_syscall_sample (__NR_OHOS_BEGIN + 22) /* 新增的系統(tǒng)調(diào)用號 __NR_new_syscall_sample:522 */
- #define __NR_syscallend (__NR_OHOS_BEGIN + 23)
- ...
在kernel/liteos_a/syscall/syscall_lookup.h中,增加一行,如下。
- ...
- /* 當前現(xiàn)有的系統(tǒng)調(diào)用清單 */
- SYSCALL_HAND_DEF(__NR_chown, SysChown, int, ARG_NUM_3)
- SYSCALL_HAND_DEF(__NR_chown32, SysChown, int, ARG_NUM_3)
- #ifdef LOSCFG_SECURITY_CAPABILITY
- SYSCALL_HAND_DEF(__NR_ohoscapget, SysCapGet, UINT32, ARG_NUM_2)
- SYSCALL_HAND_DEF(__NR_ohoscapset, SysCapSet, UINT32, ARG_NUM_1)
- #endif
- /* 新增系統(tǒng)調(diào)用 */
- SYSCALL_HAND_DEF(__NR_new_syscall_sample, SysNewSyscallSample, void, ARG_NUM_1)
- ...
2.4 在內(nèi)核中新增系統(tǒng)調(diào)用對應的處理函數(shù)
需要在內(nèi)核中新增系統(tǒng)調(diào)用函數(shù)聲明及函數(shù)實現(xiàn),并加入編譯構建文件。首先,如下所示,在kernel/liteos_a/syscall/los_syscall.h中,SysNewSyscallSample為新增系統(tǒng)調(diào)用的內(nèi)核處理函數(shù)聲明。
- ...
- /* 當前現(xiàn)有的系統(tǒng)調(diào)用內(nèi)核處理函數(shù)聲明清單 */
- ...
- extern int SysTimerGettime64(timer_t timerID, struct itimerspec64 *value);
- ...
- /* 新增的系統(tǒng)調(diào)用內(nèi)核處理函數(shù)聲明 */
- extern void SysNewSyscallSample(int num);
- ...
然后在kernel/liteos_a/syscall目錄下新建個源文件syscall_demo.c,新增系統(tǒng)調(diào)用的內(nèi)核處理函數(shù)實現(xiàn)如下。
- #include "los_printf.h"
- /* 新增系統(tǒng)調(diào)用內(nèi)核處理函數(shù)的實現(xiàn) */
- void SysNewSyscallSample(int num)
- {
- PRINTK("kernel mode: num = %d\n", num);
- return;
- }
最后,在文件kernel/liteos_a/syscall/BUILD.gn中增加對syscall_demo.c源文件的編譯管理。
- import("//kernel/liteos_a/liteos.gni")
- module_switch = defined(LOSCFG_KERNEL_SYSCALL)
- module_name = get_path_info(rebase_path("."), "name")
- kernel_module(module_name) {
- sources = [
- "fs_syscall.c",
- ......
- "syscall_demo.c"
- ]
- }
2.5 調(diào)用并驗證
參考官網(wǎng)Hello World示例,修改示例applications/sample/camera/apps/src/helloworld.c,增加系統(tǒng)調(diào)用內(nèi)容newSyscallSample(10)。
- #include <stdio.h>
- #include "syscall.h"
- /* 新增系統(tǒng)調(diào)用用戶態(tài)的接口實現(xiàn) */
- void newSyscallSample(int num)
- {
- printf("user mode: num = %d\n", num);
- __syscall(SYS_new_syscall_sample, num);
- return;
- }
- int main(int argc, char **argv)
- {
- printf("\n************************************************\n");
- printf("\n\t\tHello OHOS!\n");
- printf("\n************************************************\n\n");
- newSyscallSample(10);
- return 0;
- }
在工程根目錄,hb set選擇產(chǎn)品ipcamera_hispark_taurus,然后hb build構建,使用Device Tool工具燒錄Hi3516開發(fā)板,運行在串口得到輸出結果如下。用戶態(tài)接口與內(nèi)核態(tài)接口均有輸出,證明系統(tǒng)調(diào)用已使能。

3. 小結
本文學習小型系統(tǒng)內(nèi)核開發(fā)-系統(tǒng)調(diào)用官網(wǎng)文檔,并在Hi3516開發(fā)板上實際驗證操作,通過動手更好的掌握了系統(tǒng)調(diào)用。