当前位置:   article > 正文

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

简要分析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);
}
  • 1
  • 2
  • 3
  • 4

其中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__)
  • 1
  • 2

可以看到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__))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

可以看到__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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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

__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)
  • 1
  • 2
  • 3

与老版本内核不同的是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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/214535
推荐阅读
相关标签
  

闽ICP备14008679号