- OsArmA32SyscallHandle
当不同消息ID对应不同的处理函数时,c语言可以抽象函数指针,来归一化处理。
假如需要不同入参的处理函数时,鸿蒙的系统调用提供了另一种方法。
源码链接
VOID OsArmA32SyscallHandle(TaskContext *regs)
{
UINT32 ret;
UINT8 nArgs;
UINTPTR handle;
UINT32 cmd = regs->reserved2;
if (cmd >= SYS_CALL_NUM) {
PRINT_ERR("Syscall ID: error %d !!!\n", cmd);
return;
}
handle = g_syscallHandle[cmd];
nArgs = g_syscallNArgs[cmd / NARG_PER_BYTE]; /* 4bit per nargs */
nArgs = (cmd & 1) ? (nArgs >> NARG_BITS) : (nArgs & NARG_MASK);
if ((handle == 0) || (nArgs > ARG_NUM_7)) {
PRINT_ERR("Unsupported syscall ID: %d nArgs: %d\n", cmd, nArgs);
regs->R0 = -ENOSYS;
return;
}
OsSigIntLock();
switch (nArgs) {
case ARG_NUM_0:
case ARG_NUM_1:
ret = (*(SyscallFun1)handle)(regs->R0); // 为何0入参的函数可以这样调用
break;
case ARG_NUM_2:
case ARG_NUM_3:
ret = (*(SyscallFun3)handle)(regs->R0, regs->R1, regs->R2); // 同上 同下?? why
break;
case ARG_NUM_4:
case ARG_NUM_5:
ret = (*(SyscallFun5)handle)(regs->R0, regs->R1, regs->R2, regs->R3, regs->R4);
break;
default:
ret = (*(SyscallFun7)handle)(regs->R0, regs->R1, regs->R2, regs->R3, regs->R4, regs->R5, regs->R6);
}
regs->R0 = ret;
OsSigIntUnlock();
return;
}
上图中的系统调用有0~7个接口的情况,代码通过01、23、45、67来进行区分调用,
问题1:0和1时、2和3时入参个数不一样,为何能够编译通过??
问题2:函数调用为何能正常工作??
下边是我的理解
问题1:因为(*(SyscallFun1)handle)的强转将handle转化为了有一个入参的函数,所以就算handle对应SyscallFun0的类型,也不会编译报错
问题2:函数调用前,会将指定的入参入栈,在函数执行中,去栈中取所需的入参,因此,当handle是SyscallFun0的类型时,虽然regs->R0已经在栈中了,但是handle执行中并不会去使用它。
那么问题又来了,顺着这种思路,case就不需要区分了, 统一按照最长入参个数的函数类型来调用即可。
验证:
#include <stdio.h>
typedef unsigned int UINT32;
typedef UINT32 (*SyscallFun3)(UINT32, UINT32, UINT32);
typedef UINT32 (*SyscallFun2)(UINT32, UINT32);
typedef UINT32 (*SyscallFun1)(UINT32);
typedef UINT32 (*SyscallFun0)();
UINT32 fun0()
{
printf("%s \n", __FUNCTION__);
}
UINT32 fun1(UINT32 a)
{
printf("%s %d\n", __FUNCTION__, a);
}
UINT32 fun2(UINT32 a, UINT32 b)
{
printf("%s %d %d \n", __FUNCTION__, a, b);
}
UINT32 fun3(UINT32 a, UINT32 b, UINT32 c)
{
printf("%s %d %d %d\n", __FUNCTION__, a, b, c);
}
UINT32 *pArr[4] = {
(UINT32 *)fun0,
(UINT32 *)fun1,
(UINT32 *)fun2,
(UINT32 *)fun3,
};
int test(UINT32 nArgs)
{
UINT32 * pFun = pArr[nArgs];
switch (nArgs) {
case 0:
case 1:
case 2:
case 3:
((SyscallFun3)pFun)(nArgs, nArgs, nArgs); // 去掉switch 仅保留该句也是可行的
break;
}
}
int main()
{
printf("Hello, World! \n");
test(0);
test(1);
test(2);
test(3);
return 0;
}
实际输出:推荐一个在线C语言工具
Hello, World!
fun0
fun1 1
fun2 2 2
fun3 3 3 3
至于为何鸿蒙代码没有这样实现,应该是出于节省栈空间的考虑,毕竟连g_syscallNArgs这种变量都能省则省,8bit的数据拆成2个4bit来用