简要分析Linux 4.19内核ARM64架构系统调用(syscall)的定义

我们以mount系统调用为例, 自顶向下分析它的实现

最外层的mount系统调用定义如下

SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, char __user *, type, unsigned long, flags, void __user *, data)
{
    return ksys_mount(dev_name, dir_name, type, flags, data);
}

其中ksys_mount()mount功能的具体实现, 与系统调用本身的定义关系不大, 涉及系统调用定义最关键的就是宏定义SYSCALL_DEFINE5, 我们继续看SYSCALL_DEFINE5的实现, 为方便阅读我删除了无关紧要的代码

#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINEx(x, sname, ...) __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

可以看到SYSCALL_DEFINE5最终会调用__SYSCALL_DEFINEx, 而__SYSCALL_DEFINEx也是一个宏定义, 该定义的实现是与架构相关的, ARM64的实现在/arch/arm64/include/asm/syscall_wrapper.h, 同样为方便阅读我删除了无关紧要的代码

#define __SYSCALL_DEFINEx(x, name, ...)                        \
    static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));    \
    asmlinkage long __arm64_sys##name(const struct pt_regs *regs)        \
    {                                    \
        return __do_sys##name(SC_ARM64_REGS_TO_ARGS(x,__VA_ARGS__));    \
    }                                    \
    static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))

可以看到__SYSCALL_DEFINEx实现了一个函数__arm64_sys##name, 对应本例是__arm64_sys_mount, 这个函数就是我们的系统调用, 系统调用表sys_call_table中存放的就是这个函数的地址; 实际上__arm64_sys_mount系统调用的实现就是直接调用了__do_sys_mount, 而__SYSCALL_DEFINEx也帮我们写好了函数__do_sys_mount定义的开头部分

让我们回到最开头的系统调用宏SYSCALL_DEFINE5, 现在我们可以把这个宏展开如下

static inline long __do_sys_mount(char __user * dev_name, char __user * dir_name, char __user * type, unsigned long flags, void __user * data);   
asmlinkage long __arm64_sys_mount(const struct pt_regs *regs)       
{                                   
    return __do_sys_mount(dev_name, dir_name, type, flags, data);   
}                                   
static inline long __do_sys_mount(char __user * dev_name, char __user * dir_name, char __user * type, unsigned long flags, void __user * data)
{
    return ksys_mount(dev_name, dir_name, type, flags, data);
}

可以看到实际系统调用的实现如下

__arm64_sys_mount(const struct pt_regs *regs)
  |--> __do_sys_mount(dev_name, dir_name, type, flags, data)
     |--> ksys_mount(dev_name, dir_name, type, flags, data)

与老版本内核不同的是4.19内核的所有系统调用都只有一个参数:const struct pt_regs *regs(事实上这是从4.17内核开始做的改动), 并且通过宏SC_ARM64_REGS_TO_ARGSregs中的参数转换成传统的系统调用参数, 实际上就是把regs中的0-5号寄存器的值分别当做传统系统调用函数的第1-6个参数

至此, 我们简要分析了定义一个系统调用的关键步骤, 当然我们没有过多的讨论具体细节, 有兴趣的读者可以参考下面的资料深入研究

参考资料
https://elixir.bootlin.com/linux/v4.19.274/source/fs/namespace.c#L3052
https://elixir.bootlin.com/linux/v4.19.274/source/include/linux/syscalls.h#L218
https://elixir.bootlin.com/linux/v4.19.274/source/arch/arm64/include/asm/syscall_wrapper.h

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值