给linux添加系统调用——从源头说起
来源:互联网 发布:软件需求说明书案例 编辑:程序博客网 时间:2024/05/17 15:05
给linux添加系统调用,网上一搜一大堆,我很久之前也试图添加过并且按照网上的教程一步一步做,但是都没有成功过,因为网上的教程大体都是一个样子,一份创造多分副本,照着做也不明白为什么,而且linux内核版本从2.6跨越到3.x之后的目录结构的差异也比较大,同一种方法在另一个内核中不见得就能够行得通。本文讲解的内核采用的linux2.6.32.63版本。
本文试图从系统调用的概念入手,然后再来添加系统调用。理解了这个原理远比照着教程做出效果来却不知其所以然更重要。
linux通过int 0x80指令产生中断,内核会查找中断向量表来执行相应的中断函数。在中断函数中内核通过查找函数指针数组,取出下标为%eax的指针元素并执行,这就是系统调用的主要步骤。
int 0x80是一条汇编指令,通常用在内联汇编中,如(参见《程序员的自我修养》第四章:最小的程序):
char *str = "Hello world!\n";asm( "movl $13, %%edx \n\t" "movl %0, %%ecx \n\t" "movl $1, %%ebx \n\t" "movl $4, %%eax \n\t" "int $0x80 \n\t" ::"r"(str): "edx", "ecx", "ebx");内联汇编语法可以参考《linuxC编程一站式学习》第19章:C内联汇编。
上面的代码实现了write的系统调用,当执行到int 0x80的时候产生中断,中断函数执行4号系统调用,在32位系统上该函数为sys_write(),它有三个参数,分别是%ebx、%ecx、%edx,它们的值是1、str、13,这样内核执行的函数就是sys_write(1, str, 13)。即向标准输出文件写str,执行后我们会在屏幕上面看到"Hello world"的字样。
这样看来是不是很简单?
x86的中断向量表在linux源码的arch/x86/kernel/traps.c里可见一部分,函数trap_init()用于初始化中断向量表,在它的末尾可以看到:
函数set_system_trap_gate用于设置某个中断号上的处理程序,在该函数中还有其他几种设置方式,这些方式适用范围也不一样,这里我们关心的是SYSCALL_VECTOR和system_call这两个符号。在arch/x86/include/asm/irq_vectors.h中可以找到:
可以知道0x80号中断执行的函数就是system_call,在arch/x86/kernel/entry_64.S我找到了该函数的入口(32位路径为arch/x86kernel/entry_32.S):
可以看到截图倒数第二行:call *sys_call_table(,%rax,8) 这条指令的意思就是call *(sys_call_table+8*%rax)。sys_call_table是一个函数指针数组,每个指针占8位,取其中第%rax个指针并执行。这就是我们用户态所谓的中断调用号,上例中我们的中断调用号是4,在32位系统上是sys_write的系统调用。
sys_call_table数组定义在文件arch/x86/kernel/syscall_64.c中,其定义如下:
很明显这是个指针数组,它的赋值方式很特别,举个例子:
type_t array[ARRAYSIZE + 1] = { [0 ... ARRAYSIZE] = element, [0] = element1, [1] = element2, ...};结果中的0和1元素会被覆盖,其他的元素都是element。这样的语法在c++是不支持的。我们看看#include <asm/unistd_64.h>里面有什么:
将上图中的宏展开后得到和上例形式相同的数组,这两张图里面用到了很多的宏定义,读者可以留心观察一番。最终sys_call_table数组就包含了该文件所列举的所有系统调用函数。细心的读者肯定已经发现了这个数组的大小__NR_systcall_max+1了,我们添加系统调用并不需要改动这个值,看看它的定义arch/x86/kernel/asm-offsets_64.c:
所以sys_call_table数组的大小就是#include <asm/unistd.h>里面的函数的个数,如果需要添加自己的函数在这个头文件添加即可。细心的读者已经发现我上图在末尾添加的自己的函数。到这里基本上就大功告成了,读者们可以理一理思路。
剩下的工作就是声明并定义我们添加的函数。内核提供了一个宏定义来定义系统调用SYSCALL_DEFINEx(),我们只需要是用这个宏来定义自己的系统调用。定义的位置最好跟你的函数实现的功能关联,比如你要实现文件读写相关的功能那么你可以定义在fs/open.c等文件中,下面是我的定义fs/open.c:
系统调用的声明在include/linux/syscalls.h:
编译安装内核(网上的方法很多可以参考网上的最后可以执行make modules_install install来完成自动安装)。
安装完毕!
测试用户态程序:
成功添加,读者可以在此基础上添加更多的功能。
- 给linux添加系统调用——从源头说起
- 给linux添加系统调用
- 给linux系统添加系统调用_1_先从模块开始
- 给linux系统添加系统调用_2_修改内核
- 给linux添加一个系统调用(linux-4.10.1)
- 从系统卡说起
- 从系统说起
- 从源头掌控质量——北京源品汇科技有限公司
- 添加Linux系统调用
- Linux添加系统调用
- 添加Linux系统调用
- Linux添加系统调用
- Linux添加系统调用
- Linux添加系统调用
- 《Linux设计与实现》笔记——系统调用工作原理、添加系统调用的过程
- 理解AngularJS——从WPF说起
- 为linux添加系统调用
- 添加自定义Linux系统调用
- 英巴卡迪诺发布2015 年产品发展与中国市场策略
- Hadoop QuickStart VM
- QTP中经常使用的VB语句
- MVC的URL路由规则
- xcode 各种配置设置
- 给linux添加系统调用——从源头说起
- 控制actionbar中的向上键
- Balanced Binary Tree
- 查询本机的某个端口被什么程序或者进程占用
- 磁盘I/O性能影响
- 浅谈企业应用软件架构设计过程
- kmp中next数组的运用
- HDOJ 2046 骨牌铺方格
- JavaScript Promises