在 Linux 6.8 中创建自定义系统调用

大家好!我是大聪明-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内核代码变化迅速。

相关推荐
朽棘不雕8 小时前
动态库的理解
linux
Escalating_xu8 小时前
【mmap 进程间通信】从共享内存到跨进程唤醒:用互斥锁、条件变量控制多个进程
java·linux·开发语言·jvm
djjjx.8 小时前
Linux工具篇 (三):make与Makefile
linux·服务器·makefile·make
zhangzhangkeji8 小时前
stm32 笔记 - 1:, 环境与项目搭建, 寄存器方式点灯
arm
xiaoye-duck8 小时前
《Linux 网络编程》深入理解 TCP 协议(五):滑动窗口、丢包重传与流量控制详解
linux·网络·tcp
昌原的儿子LEO8 小时前
【单片机入门】UART 串行异步通信核心考点梳理
linux·单片机·51单片机
团子股股东峥哥8 小时前
day36-RHEL-管理基本存储
linux·运维·服务器
Fcy6488 小时前
Linux下 网络计算器小程序(socket封装、协议自定义、业务逻辑解耦)
linux·网络·自定义协议
东城居士9 小时前
Linux驱动程序开发环境的快速搭建
linux·嵌入式系统
云栖梦泽9 小时前
网络设备驱动(3)
linux·运维·服务器·网络·嵌入式硬件