【问题标题】:When implementing a system call, how do you expose the system call number to userland?在实现系统调用时,如何将系统调用号暴露给用户空间?
【发布时间】:2017-12-30 09:21:25
【问题描述】:

我在摆弄系统调用。我添加了两个新的,并通过调用 syscall 验证了它们的工作原理。

我希望系统调用号位于标头中,以便用户空间不必明确知道系统调用号。

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 已经为新的系统调用自动生成了宏:

$ 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 Kernel 文档所说的相反:

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 的,因为它们读取和写入仅在英特尔芯片中发现的 MSR。因此,在此之后,我开始挖掘是否可以为我的 amd64 系统找到特定于架构的标头。

您可能希望它位于arch/x86_64 之下,但其中根本没有包含。所以我假设 x86_64 继承自 x86。在这种情况下,特定于架构的标题应该是:

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 目标安装新的标头(也许),但可惜它没有。

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

谢谢

【问题讨论】:

  • 如果您在库中实现包装函数来调用系统调用,则不一定需要。毕竟,这是 C 库在 POSIXy 系统上所做的大部分工作。您可以让库头文件公开系统调用号(适用于当前架构)。要向所有 Linux 用户包含新的系统调用,您需要将补丁推送到 Linux 内核(通过 LKML)和 GNU C 库或特定发行版的上游,以便他们将更改添加到其系统头文件.
  • 是的,这不太可行,因为系统调用不适合一般用途。
  • 什么(不可行),将系统调用包装成函数? (如果你的意思是把它推到上游,我同意。)不过,你真的应该考虑将系统调用包装到函数中;即使只是作为头文件中的静态内联函数,也要根据拱门和字长(使用预处理器宏)选择正确的系统调用号。与修改后的内核(或内核修改)一起提供 extra 头文件比在标准头文件中包含额外项要容易得多。
  • 拥有包装函数会很好,是的,但第一步确实是尝试导出__NR_* 和朋友。您建议的包装器功能是否不依赖于那些?我只需要某种方式知道系统调用号,它可能因架构或内核版本而异。
  • 啊,现在我明白了。问题是,大多数 Linux 发行版不使用内核提供的用户空间头文件;相反,它们使用 C 库提供的头文件,以及安装的任何其他开发库。例如,Debian(以及 Debian 衍生产品,如 Ubuntu、Mint 等)将内核头文件保持在 /usr/src/linux-headers-$(uname -r)/(当前的头文件);当前运行的内核的系统调用号在/usr/src/linux-headers-$(uname -r)/uapi/asm-generic/unistd.h 中。包装函数可以使用uname()C库函数来获取当前[...]

标签: c linux linux-kernel system-calls


【解决方案1】:

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

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

对上述内核执行此操作后:

$ 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

【讨论】:

    【解决方案2】:

    内核构建过程分配的实际系统调用号是内核构建过程的一部分...因此您不会获得实际的最终数字,而只能在已构建的内核上获得。构建你的内核,你会在一个构建的头文件中看到实际的分配。恐怕您试图在干净的内核源代码中搜索它们,这就是您找不到正确包含文件的原因。

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

    【讨论】:

      猜你喜欢
      • 2012-09-28
      • 2011-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-25
      • 2014-10-15
      • 2017-02-14
      • 2018-04-04
      相关资源
      最近更新 更多