我阅读了LKD 1中的一些段落,但 我无法理解以下内容:
从用户空间访问系统调用 通常,C库提供对系统调用的支持。用户应用程序可以从标准标头中提取函数原型,并与C库链接以使用您的系统调用(或库例程,后者又使用syscall调用)。但是,如果您只是编写了系统调用,则怀疑glibc是否已支持它! 幸运的是,Linux提供了一组宏,用于包装对系统调用的访问。它设置寄存器内容并发出陷阱指令。这些宏命名为,其中介于零和六个之间。该数字与传递给syscall的参数的数量相对应,因为宏需要知道需要多少个参数,并因此将其压入寄存器。例如,考虑定义为_syscall _n_ () nopen() long open(const char *filename, int flags, int mode) 在没有显式库支持的情况下使用此系统调用的syscall宏将是 #define __NR_open 5 _syscall3(long, open, const char *, filename, int, flags, int, mode) 然后,应用程序可以简单地调用open()。 对于每个宏,都有2 + 2×n个参数。第一个参数对应于syscall的返回类型。第二个是系统调用的名称。接下来按照系统调用的顺序跟随每个参数的类型和名称。的__NR_open定义是在<asm/unistd.h>; 它是系统电话号码。的_syscall3宏展开与联汇编C函数; 该程序集执行上一节中讨论的步骤,将系统调用号和参数推送到正确的寄存器中,并发出软件中断以捕获到内核中。使用open()系统调用只需将这个宏放在应用程序中即可。 让我们编写宏以使用出色的新foo()系统调用,然后编写一些测试代码来展示我们的努力。 #define __NR_foo 283 __syscall0(long, foo) int main () { long stack_size; stack_size = foo (); printf ("The kernel stack size is %ld\n", stack_size); return 0; }
从用户空间访问系统调用
通常,C库提供对系统调用的支持。用户应用程序可以从标准标头中提取函数原型,并与C库链接以使用您的系统调用(或库例程,后者又使用syscall调用)。但是,如果您只是编写了系统调用,则怀疑glibc是否已支持它!
幸运的是,Linux提供了一组宏,用于包装对系统调用的访问。它设置寄存器内容并发出陷阱指令。这些宏命名为,其中介于零和六个之间。该数字与传递给syscall的参数的数量相对应,因为宏需要知道需要多少个参数,并因此将其压入寄存器。例如,考虑定义为_syscall _n_ () nopen()
_syscall _n_ ()
n
open()
long open(const char *filename, int flags, int mode)
在没有显式库支持的情况下使用此系统调用的syscall宏将是
#define __NR_open 5 _syscall3(long, open, const char *, filename, int, flags, int, mode)
然后,应用程序可以简单地调用open()。
对于每个宏,都有2 + 2×n个参数。第一个参数对应于syscall的返回类型。第二个是系统调用的名称。接下来按照系统调用的顺序跟随每个参数的类型和名称。的__NR_open定义是在<asm/unistd.h>; 它是系统电话号码。的_syscall3宏展开与联汇编C函数; 该程序集执行上一节中讨论的步骤,将系统调用号和参数推送到正确的寄存器中,并发出软件中断以捕获到内核中。使用open()系统调用只需将这个宏放在应用程序中即可。
__NR_open
<asm/unistd.h>
_syscall3
让我们编写宏以使用出色的新foo()系统调用,然后编写一些测试代码来展示我们的努力。
foo()
#define __NR_foo 283 __syscall0(long, foo) int main () { long stack_size; stack_size = foo (); printf ("The kernel stack size is %ld\n", stack_size); return 0; }
是什么 应用程序可以简单的调用open()是什么意思?
此外,对于最后一段代码,的声明在foo()哪里?以及如何使这段代码可编译和可运行?我需要包括哪些头文件?
1 Linux内核开发 ,作者:Robert Love。 在wordpress.com上的PDF文件(转到第81页); Google图书的结果。
首先,您应该了解linux内核的作用,并且应用程序 仅 通过系统调用 才能 与内核交互。
实际上,应用程序在内核提供的“虚拟机”上运行:它在用户空间中运行,并且只能(在最低计算机级别上)执行用户CPU模式所允许的由指令(例如SYSENTER或INT 0x80…)用于进行系统调用。因此,从用户级应用程序的角度来看,系统调用是原子的伪机器指令。
SYSENTER
INT 0x80
在Linux的大会HOWTO解释了如何一个系统调用可以在组件(即机器指令)的水平来完成。
的GNU库被提供对应于所述系统调用的C函数。因此,例如,开放函数是数字syscall上方的一小块胶水(即包装纸)NR__open(它使syscall然后更新errno)。应用程序通常在libc中调用此类C函数,而不是进行syscall。
NR__open
errno
您可以使用其他一些libc。例如,MUSL libc非常“简单”,其代码也许更易于阅读。它还将原始系统调用包装到相应的C函数中。
libc
如果添加自己的syscall,则最好也实现一个类似的C函数(在您自己的库中)。因此,您还应该为库提供一个头文件。
另请参见intro(2)和syscall(2)和syscalls(2)手册页,以及VDSO在syscalls中的作用。
请注意,系统调用不是C函数。他们不使用调用堆栈(甚至可以在没有任何堆栈的情况下调用它们)。syscall基本上是NR__openfrom 的数字<asm/unistd.h>,是一条SYSENTER机器指令,约定errno在C库包装中设置哪些寄存器在syscall的参数之前保留,哪些寄存器在syscall的结果(包括失败结果)之后保留。syscall)。syscall的约定不是ABI规范中C函数的调用约定(例如x86-64 psABI)。因此,您需要一个C包装器。