快捷搜索:  汽车  科技

linux系统原理解析(追根究底Linux系统调用)

linux系统原理解析(追根究底Linux系统调用)看到这或许很多人(包括我)会觉得诧异,为何要如此复杂呢,后来查资料,发现这是由于之前64位Linux存在CVE-2009-2009的漏洞,简单说就是32位参数存放在64位寄存器,修改符号扩展可能导致产生一个非法内存地址,从而导致系统崩溃或者提升权限。 为了修复这个问题,把寄存器高位清零即可,但做起来比较困难,为了做尽可能少的修改,将调用参数统一采用使用long型来接收,再强转为相应参数。 窥见一斑,可见Linux大师们精湛的宏定义,已经用得出神入化。sys_kill,通过别名机制等同于SyS_kill().内核提供用户空间程序与内核空间进行交互的一套标准接口,这些接口让用户态程序能受限访问硬件设备,比如申请系统资源,操作设备读写,创建新进程等。用户空间发生请求,内核空间负责执行,这些接口便是用户空间和内核空间共同识别的桥梁,这里提到两个字“受限”,是由于为了保证内核稳定性,而不能让用户空间

linux系统原理解析(追根究底Linux系统调用)(1)

来源:Gityuan

原文地址:http://gityuan.com/2016/05/21/syscall/

引言:分析Android源码的过程中,要想从上至下完全明白一行代码,往往涉及app、framework、native一直到kernel,可能迷失到代码世界,明白了系统调用原理,或许能帮你峰回路转,找到进入kernel函数的入口。本文主要讲解ARM架构相关源码:

/bionic/libc/kernel/uapi/asm-arm/asm/unistd.h /bionic/libc/arch-arm/syscalls/kill.S /kernel/arch/arm/kernel/calls.S /kernel/arch/arm/include/Uapi/asm/unistd.h /kernel/include/uapi/asm-generic/unistd.h /kernel/include/linux/syscalls.h /kernel/kernel/signal.c /kernel/arch/arm/kernel/entry-common.S /kernelarch/arm/kernel/entry-armv.S

一、Syscall意义

内核提供用户空间程序与内核空间进行交互的一套标准接口,这些接口让用户态程序能受限访问硬件设备,比如申请系统资源,操作设备读写,创建新进程等。用户空间发生请求,内核空间负责执行,这些接口便是用户空间和内核空间共同识别的桥梁,这里提到两个字“受限”,是由于为了保证内核稳定性,而不能让用户空间程序随意更改系统,必须是内核对外开放的且满足权限的程序才能调用相应接口。

  • kill添加了sys_前缀,声明sys_kill()函数;

  • 定义SYSC_kill()函数和SyS_kill()函数;

  • sys_kill,通过别名机制等同于SyS_kill().

  • 看到这或许很多人(包括我)会觉得诧异,为何要如此复杂呢,后来查资料,发现这是由于之前64位Linux存在CVE-2009-2009的漏洞,简单说就是32位参数存放在64位寄存器,修改符号扩展可能导致产生一个非法内存地址,从而导致系统崩溃或者提升权限。 为了修复这个问题,把寄存器高位清零即可,但做起来比较困难,为了做尽可能少的修改,将调用参数统一采用使用long型来接收,再强转为相应参数。 窥见一斑,可见Linux大师们精湛的宏定义,已经用得出神入化。

    如果觉得很复杂,那么可以忽略这个宏定义,只要记住SYSCALL_DEFINE2(kill pid_t pid int sig) 基本等价于 asmlinkage long sys_kill(int pid int sig) 就足够了。

    四、总结

    4.1 内核空间

    • 系统调用的函数原型的指针:位于文件/kernel/arch/arm/kernel/calls.S,格式为CALL(sys_xxx),指定了目标函数的入口地址。

    • 系统调用号的宏定义:位于文件/kernel/arch/arm/include/Uapi/asm/unistd.h,记录着内核空间的系统调用号,格式为#define__NR_xxx (__NR_SYSCALL_BASE [num])

    • 系统调用的函数声明:位于文件/kernel/include/linux/syscalls.h,格式为asmlinkage long sys_xxx(args ...);

    • 系统调用的函数实现:不同函数位于不同文件,比如kill()位于/kernel/kernel/signal.c文件,格式为SYSCALL_DEFINEx(x sname ...)

    前面这4步都是在内核空间相关的文件定义,有了这些,那么内核就可以使用相应的系统调用号。

    4.2 用户空间

    • 系统调用号的宏定义:位于文件/bionic/libc/kernel/uapi/asm-arm/asm/unistd.h,记录着用户空间的系统调用号,格式为#define__NR_xxx (__NR_SYSCALL_BASE [num])。这个文件就是由内核空间同名的头文件自动生成的,所以该文件与内核空间的系统调用号是完全一致。

    • 汇编定义相关函数的中断调用过程:位于文件/bionic/libc/arch-arm/syscalls/xxx.S,比如kill()位于kill.S,格式为:

    ENTRY(xxx) mov ip r7 ldr r7 =__NR_xxx swi #0 mov r7 ip cmn r0 #(MAX_ERRNO 1) bxls lr neg r0 r0 b __set_errno_internal END(xxx)

    当然kill()方法还有函数声明,有了这些,用户空间也能在程序中使用系统调用。明白了这些过程,那么自己新添加系统调用其实也并不是多困难的一件事,新增系统调用号还需要修改syscalls总个数,但强烈不建议自己新增系统调用号,尽量保持与linux kernel主线一致,兼容性更好,所以就不进一步介绍新增流程了。

    Vanilla社区发起⦗晨读计划⦘,每天坚持积累一点,今天的努力至少让我们比昨天更进一步。

    ⦗晨读计划⦘ 期待你的加入... ...

    Vanilla:基于OpenResty的高性能Web应用开发框架

    我们的Vanilla-OpenResty

    我们的QQ群:205773855、481213820、34782325

    晨读计划

    2016/06/07

    猜您喜欢: