大家好!我是大聪明-PLUS!
你是否曾经想过创建自己的系统调用?也许你曾被布置过这样的作业,也许你出于好奇尝试过,又或许只是为了学习一些关于内核的新知识。无论如何,系统调用都是深入了解 Linux 的绝佳途径。
❯ 本指南有哪些吸引人的地方?
关于这个主题,市面上也有其他一些指南,但问题在于它们没有跟上内核发展的步伐------内核发展太快了。很多指南不仅过时,而且还包含大量错误。这就是我决定写这篇文章的原因,同时也是为了找出并修正这些错误。
❯ 准备系统以进行内核编译
在基于 Red Hat/Fedora/Open SUSE 的发行版上,您可以简单地执行以下操作:
`Sudo dnf builddep kernel
Sudo dnf install kernel-devel`
在基于 Debian/Ubuntu 的发行版中:
`sudo apt-get install build-essential vim git cscope libncurses-dev libssl-dev bison flex`
❯ 我们得到核心
我们将克隆内核源代码分支,具体目标是 6.8 版本。但是,这里描述的说明也应该适用于更新的内核版本(当然,除非内核开发人员再次更改整个过程)。
`git clone --depth=1 --branch v6.8 https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git`
理想情况下,克隆版本的内核版本应该与当前安装的内核版本不相上下。
您可以使用以下命令检查当前内核版本:
`uname -r`
❯ 创建一个新的系统调用
让我们执行以下代码:
`cd linux
make mrproper
mkdir hello
cd hello
touch hello.c
touch Makefile`
因此,我们将hello在加载的内核代码中直接创建一个名为" "的目录,并在该目录中放入两个文件------hello.c系统调用代码和一个 Makefile,该 Makefile 以规则的形式规定了如何创建同一个文件。
用你最喜欢的文本编辑器打开它hello.c,并将以下代码写入其中:
`#include <linux/kernel.h>
#include <linux/syscalls.h>
SYSCALL_DEFINE0(hello) {
pr_info("Hello World\n");
return 0;
}`
内核日志将显示" Hello World"。
由于我们只想在屏幕上显示信息,我们将使用n=0
接下来,将以下文本添加到 Makefile 文件中:
`obj-y := hello.o`
现在:
`cd ..
cd include/linux/`
让我们打开此目录中的文件" " syscalls.h,并添加:
`asmlinkage long sys_hello(void)`

这是我们上面创建的系统调用函数的原型。
打开内核根目录(cd ../..)下的文件" Kbuild",并在文件末尾添加以下内容:
`obj-y += hello/`

这样我们就告诉内核构建系统编译我们刚刚包含的目录。
处理完这个问题后,我们还需要将系统调用指定为描述架构的表格中的一个新条目。
每个 CPU 架构都有其特定的系统调用,因此程序需要指明我们正在运行的架构。
文件x86_64内容如下:
`arch/x86/entry/syscalls/syscall_64.tbl`
您应该在此处添加系统调用条目。请记住,您只能为其分配一个当前未被使用的编号,并且切勿使用表注释中禁止使用的编号。
当时我的房间号是462,所以就添加了一条新的入住记录,如下所示:
`462 common hello sys_hello`

这里,462 映射到我们的系统调用,这是两种架构的常见做法。我们的系统调用名为 hello,其入口点为sys_hello。
❯ 编译并安装新内核
让我们运行本节列出的命令。
既然我们已经讨论完了所有允许的事情,接下来让我们看看禁止的事情吧 ;)
`cp /boot/config-$(uname -r) .config
make olddefconfig
make -j $(nproc)
sudo make -j $(nproc) modules_install
sudo make install`
在这里,我们复制当前加载内核的配置文件,告诉构建系统使用与配置文件中相同的值,其他所有参数保持默认值。然后,我们根据可用核心数(由 nproc 指定)构建支持并行处理的内核。之后,我们自行承担风险安装已构建的内核。
编译内核需要很长时间,所以不妨给自己泡杯咖啡,欣赏一下终端里一行行的命令吧。
整个过程可能需要几个小时,具体取决于您的系统速度。您的电脑风扇可能会高速运转以防止过热(是的,我经历过这种情况)。
❯ 最有趣的是:我们使用了一个新的系统调用
现在我们的系统调用已经集成到内核中,让我们重新启动系统,并确保在启动时从 grub 中选择新的(修改过的)内核。

启动后,让我们编写一个 C 程序来使用这个系统调用。
让我们创建一个名为" "的文件,例如,test.c该文件将包含以下内容:
`#include <stdio.h>
#include <sys/syscall.h>
#include <unistd.h>
int main(void) {
printf("%ld\n", syscall(462));
return 0;
}`
这里,请替换 462 为您在表格中为新系统调用指定的编号。
编译并运行程序。
`make test
chmod +x test
./test`
如果一切顺利," "将出现在命令行中0,并且系统调用的输出将在所有内核日志中可见。
我们通过以下方式访问日志dmesg
`sudo dmesg | tail`
瞧,您将在这里看到系统调用输出的消息。
恭喜你,你做到了!但请允许我提醒你以下几点:
-
编译内核需要很长时间;
-
新编译的内核会占用相当大的空间,所以请确保你有足够的空间;
-
Linux内核代码变化迅速。