实现系统调用时,如何将系统调用号暴露给用户空间?

When implementing a system call, how do you expose the system call number to userland?

我正在摆弄系统调用。我添加了两个新的,并通过调用 syscall.

来验证它们是否有效

我希望系统调用编号位于 header 中,这样用户空间就不必明确知道系统调用编号。

arch/x86/syscalls/syscall_64.tbl 我有:

317     64      krun_read_msrs                  sys_krun_read_msrs
318     64      krun_reset_msrs                 sys_krun_reset_msrs

一些 grepping 表明 kbuild 有 auto-generated 新系统调用的宏:

$ ag __NR_krun *
arch/x86/include/generated/uapi/asm/unistd_64.h
321:#define __NR_krun_read_msrs 317
322:#define __NR_krun_reset_msrs 318

文件名表明我不需要手动添加条目,但这与 Linux 内核文档所说的相反:

Some architectures (e.g. x86) have their own architecture-specific syscall tables, but several other architectures share a generic syscall table. Add your new system call to the generic list by adding an entry to the list in include/uapi/asm-generic/unistd.h:

#define __NR_xyzzy 292
__SYSCALL(__NR_xyzzy, sys_xyzzy)

好吧,我的系统调用是 x86_64 特定的,因为它们读取和写入仅在 Intel 芯片中发现的 MSR。所以在这之后,我开始挖掘,看看是否可以为我的 amd64 系统找到 arch-specific header。

您可能希望它在 arch/x86_64 之下,但其中根本没有包含。所以我假设 x86_64 继承自 x86。既然如此,arch-specific header 应该是:

arch/x86/include/uapi/asm/unistd.h

如果你打开它,它只是一个基于 arch 的小包装器:

# ifdef __i386__                                                                
#  include <asm/unistd_32.h>                                                    
# elif defined(__ILP32__)                                                       
#  include <asm/unistd_x32.h>                                                   
# else                                                                          
#  include <asm/unistd_64.h>                                                    
# endif

所以这大概是为了获取 /usr/include/x86_64-linux-gnu/asm/unistd.h 而设计的,但这还不包括我的新系统调用编号。

我希望 headers_install 目标安装新的 headers(也许),但遗憾的是它没有。

我很困惑。我是否应该手动将新系统调用添加到文件中?如果是哪个文件?如果没有,我如何在标准位置将 auto-generated __NR_* 宏暴露给用户空间?

谢谢

好吧,我有一个部分答案。部分因为它是特定于 Debian 的。

如果您在内核源代码中使用 make deb-pkg 目标,则会在 parent 目录中创建 .deb 个包。如果您随后安装这些,那么您的 headers 就会安装到系统中。

在为我的内核执行上述操作后:

$ grep krun /usr/include
/usr/include/asm/unistd_64.h:#define __NR_krun_read_msrs 317
/usr/include/asm/unistd_64.h:#define __NR_krun_reset_msrs 318

内核构建过程分配的实际系统调用编号是内核构建过程的一部分...所以你不会得到实际的最终编号,但只能在一个已经构建的内核。构建您的内核,您将在构建的头文件中看到实际分配。恐怕您已经尝试在干净的内核源代码中搜索它们,这就是您找不到合适的包含文件的原因。

另一方面,隐藏实际的系统调用号是很常见的,它是由一些包含上述内核头文件的包装程序完成的,并使用 #defined 符号来表示调用号进行 __SYSCAL(...) 实际调用。这通常甚至是必要的,因为每个系统调用通常都有不同的接口。您使用的所有正常系统调用都在 stdlib 中包含了它们的包装器,但新的不会包含在内。这里有两种方法:修补标准 C 库以包含(并在 /usr/include 中的某处为函数原型编写标准头文件) 以包含包装文件 mySysCall.o(此名称方便您使用)在您要使用新系统调用的所有程序中。