一尘不染

如何从用户空间访问系统调用?

linux

我阅读了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;
}

是什么 应用程序可以简单的调用open()是什么意思?

此外,对于最后一段代码,的声明在foo()哪里?以及如何使这段代码可编译和可运行?我需要包括哪些头文件?


1 Linux内核开发 ,作者:Robert Love。
在wordpress.com上的PDF文件(转到第81页);
Google图书的结果


阅读 314

收藏
2020-06-02

共1个答案

一尘不染

首先,您应该了解linux内核的作用,并且应用程序
通过系统调用 才能 与内核交互。

实际上,应用程序在内核提供的“虚拟机”上运行:它在用户空间中运行,并且只能(在最低计算机级别上)执行用户CPU模式所允许的由指令(例如SYSENTERINT 0x80…)用于进行系统调用。因此,从用户级应用程序的角度来看,系统调用是原子的伪机器指令。

Linux的大会HOWTO解释了如何一个系统调用可以在组件(即机器指令)的水平来完成。

GNU库被提供对应于所述系统调用的C函数。因此,例如,开放函数是数字syscall上方的一小块胶水(即包装纸)NR__open(它使syscall然后更新errno)。应用程序通常在libc中调用此类C函数,而不是进行syscall。

您可以使用其他一些libc。例如,MUSL libc非常“简单”,其代码也许更易于阅读。它还将原始系统调用包装到相应的C函数中。

如果添加自己的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包装器。

2020-06-02