共计 2140 个字符,预计需要花费 6 分钟才能阅读完成。
我采纳的零碎是 Ubuntu20.04,内核版本为 5.10.56,体系结构为 x86_64。
增加零碎调用是在内核源码树中操作,所有版本的内核源代码都能够在 Linux 内核官方网站中找到,能够去自行下载。
1. 注册零碎调用号
在 Linux 中,每个零碎调用都被赋予了一个零碎调用号。这样,通过这个举世无双的号就能够关联系统调用。当用户空间的过程执行一个零碎调用的时候,这个零碎调用号就用来指明到底是要执行哪个零碎调用;过程不会提及零碎调用的名称。
内核记录了零碎调用表中的所有已注册过的零碎调用的列表,存储在 sys_call_table
中。每一种体系结构中,都明确定义了这个表,在本零碎 x86_64 中,它定义于 arch/x86/entry/syscalls/syscall_64.tbl
。
零碎调用表中每一项都以下由四个元素组成:
<number> <abi> <name> <entry point>
其中,<num>
代表零碎调用号,例如在 x86_64 架构中 open 的零碎调用号就是 5;<abi>
即 x86_64 架构的 ABI,其含意 application binary interface(应用程序二进制接口);<name>
是零碎调用的名字;<entry point>
代表零碎调用在内核的接口函数,在 <name>
前加 sys_
即可。
在零碎调用表中最初一项填入这四个元素即可注册零碎调用。
2. 申明零碎调用函数原型
为了保障增加的零碎调用能被找到并且调用,须要在 include/linux/syscalls.h
中申明该零碎调用的函数原型。
每个零碎调用都对应一个内核服务例程来实现该零碎调用的具体性能,其命名格局都是以 sys_
结尾。这是 Linux 中所有零碎调用都应该恪守的命名规定,例如零碎调用 bar()
在内核中也实现为 sys_bar()
函数。
函数申明中的 asmlinkage
限定词是一个编译指令,用于告诉编译器仅从堆栈中提取该函数的参数,而不是从寄存器中,因为在执行服务例程之前零碎曾经将通过寄存器传递过去的参数值压入内核堆栈了。所有的零碎调用都须要这个限定词。
其次,零碎调用函数返回值类型是 long
。为了保障 32 位和 64 位零碎的兼容性,零碎调用在用户空间和内核空间有不同的返回值类型,在用户空间为 int
,在内核空间为 long
。
3. 增加零碎调用函数的定义
申明了该零碎调用的原型之后,须要实现该零碎调用,这只有把它放进 kernel/
下的一个相干文件就能够了,比方 sys.c
,它蕴含了各种各样的零碎调用,我这里就在 kernel/sys.c
中增加其定义。
SYSCALL_DEFINEn
是用来定义零碎调用的一个宏,其中 n
为该零碎调用参数的个数,例如这个定义是两个参数,即为 SYSCALL_DEFINE2
,宏里的参数顺次为函数名以及所有参数的参数类型和参数名,开展后的代码如下:
asmlinkage long sys_compareint(int arg1, int arg2)
printk()
函数运行在内核态,是在内核中运行的向控制台输入显示的函数,终端可能看不到输入,这与本身的操作系统无关。如果终端看不到输入则能够应用 dmesg -c
命令来打印以后零碎的日志信息,即可看到 printk()
函数的输入。
4. 编译并装置此内核
往此内核源码树中增加完零碎调用之后须要编译能力应用,而这样就须要编译整个内核。如果此版本的内核已在零碎中装置并且没有批改此内核的其余货色,只增加了零碎调用,则不须要执行编译并装置一个新内核的所有步骤,只须要执行以下步骤:
(1)配置内核
在内核源码树下执行如下命令:
make menuconfig
此时会呈现一个配置窗口,如果是第一次装置此则能够应用零碎中原有的配置或者应用默认配置,我这里间接应用之前装置此内核时的 .config
文件的配置即可,间接退出不作批改。
(2)编译内核
内核配置完后,编译内核,生成启动映像文件 bzlmage,位于 arch/x86_64/boot/bzlmage
。执行命令如下:
make ARCH=x86_64 bzImage -j4
此过程须要较长时间,因为开启了四线程编译,大概须要二十分钟即可。
(3)装置内核
因为此版本的内核已在零碎中装置过,而且未对内核做其余批改,所以亲测能够跳过装置模块,间接装置内核即可。执行命令如下:
make install
(4)重启零碎
因为新版的内核在装置完之后会主动配置 grub
疏导程序,所以间接重启零碎 reboot
即可。
重启零碎并抉择此内核进入零碎之后,就能够应用新增加的零碎调用了。
通常,零碎调用靠 C 库反对。用户程序通过蕴含规范头文件并和 C 库链接,就能够应用零碎调用(或者调用库函数,再由库函数理论调用)。但这里只将零碎调用增加到了内核中,并不能应用 glibc 库。而对此,Linux 自身提供了一个 syscall()
函数,用于间接对系统调用进行拜访。将想要调用的零碎调用的零碎调用号和参数传入 syscall()
中即可应用此零碎调用。
参考资料
[1] OS 试验一 | linux 内核编译及增加零碎调用 – 知乎
[2] linux 增加零碎调用总结 (内核版本 4.4.4) – CSDN 博客
[3] X86_64 架构减少一个零碎调用 – BiscuitOS
[4] Linux 下零碎调用的三种办法 - hazir – 博客园