OpenHarmony LiteOS-A内核文档之学习--系统调用(1)
OpenHarmony LiteOS-A内核文档之学习–系统调用
OpenHarmony LiteOS-A内核实现态与内核态的区分隔离,用户态程序不能直接访问内核资源,而系统调用则为用户态程序提供了一种访问内核资源、与内核进行交互的通道。如下图所示,用户程序通过调用System API(系统API,通常是系统提供的POSIX接口)进行内核资源访问与交互请求,POSIX接口内部会触发SVC/SWI异常,完成系统从用户态到内核态的切换,然后对接到内核的Syscall Handler(系统调用统一处理接口)进行参数解析,最终分发至具体的内核处理函数。
Syscall Handler的具体实现在kernel/liteos_a/syscall/los_syscall.c中OsArmA32SyscallHandle函数,在进入系统软中断异常时会调用此函数,并且按照kernel/liteos_a/syscall/syscall_lookup.h中的清单进行系统调用的入参解析,执行各系统调用最终对应的内核处理函数。
1. 涉及的系统调用文件目录介绍
先介绍下系统调用相关的内核态代码、用户态代码所在的文件目录。
1.1 syscall/syscall_lookup.h文件
kernel/liteos_a/syscall/syscall_lookup.h
文件中维护内核向用户态提供的系统调用接口。文件中包含虚拟文件系统VFS、动态加载DYNLOAD、PIPE、SHELL、LWIP、SECURITY_CAPABILITY等模块的系统调用接口,文件内容片段如下。每一个系统调用有宏函数SYSCALL_HAND_DEF
定义,包含系统调用编号、系统调用处理函数,返回值类型,系统调用处理参数数目,下文详细介绍。
......
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
中的系统调用初始化函数OsSyscallHandleInit
调用,调用代码如下。可以看出第一个参数是系统调用函数编号,编号定义在文件third_party/musl/porting/liteos_a/kernel/include/bits/syscall.h
;第二个是系统调用函数,函数原型声明在文件kernel/liteos_a/syscall/los_syscall.h
,函数实现一般在kernel/liteos_a/syscall
目录下的源代码文件中实现; 第三个是返回值类型,暂时没有使用;第四个函数是系统调用函数的参数数目。
下面看下宏函数的代码,⑴处把定义的系统调用函数都维护在全局数组g_syscallHandle
。⑵处由于参数数量不会特别大,记录系统调用处理函数参数数目的全局数组g_syscallNArgs
的类型是UINT8,每4个bit位维护一个系统调用的参数数目。
...
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
文件中维护内核的系统调用对接函数声明。函数实现一般在目录/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 系统调用函数编号
维护系统调用编号文件有2个,需要分别在用户态和内核态维护系统调用函数编号。除了文件porting/liteos_a/kernel/include/bits/syscall.h
中多个几个参数数目的宏定义外,下述两个文件的内容基本相同。
-
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. 系统调用开发示例
当需要新增一个系统调用接口时,可以参考下述步骤:
- 在LibC库中确定并添加新增的系统调用号。
- 在LibC库中新增用户态的函数接口声明及实现。
- 在内核系统调用头文件中确定并添加新增的系统调用号及对应内核处理函数的声明。
- 在内核中新增该系统调用对应的内核处理函数。
2.1 在LibC库中确定并添加新增的系统调用号
编辑文件porting/liteos_a/user/arch/arm/bits/syscall.h.in
,如下所示,其中⑴处的__NR_new_syscall_sample
为新增系统调用号。需要注意同时更新下⑵处的编号。
/* 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) /* 新增的系统调用号 __NR_new_syscall_sample:522 */
⑵ #define __NR_syscallend (__NR_OHOS_BEGIN + 23)
2.2 在LibC库中新增用户态的函数接口声明及实现。
系统调用提供基础的用户态程序与内核的交互功能,不建议开发者直接使用系统调用接口,推荐使用内核提供的对外POSIX接口。需要在LibC库中新增用户态接口的声明与实现。为了简化,我们在现成的一个源文件里增加一函数实现代码,如third_party/musl/porting/liteos_a/user/src/aio/aio.c
文件中增加:
/* 新增系统调用用户态的接口实现 */
void newSyscallSample(int num)
{
printf("user mode: num = %d\n", num);
__syscall(SYS_new_syscall_sample, num);
return;
}
- 点赞
- 收藏
- 关注作者
评论(0)