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