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

前端之家收集整理的这篇文章主要介绍了linux – 如何从用户空间访问系统调用?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我在LKD中看到一些段落,我只是不明白下面的内容

用户空间访问系统调用

通常,C库提供对系统调用支持.用户应用程序可以从标准头部拉入功能原型,并与C库链接以使用系统调用(或库例程,反过来使用您的系统调用).但是,如果刚刚编写系统调用,glibc已经支持它了,这是值得怀疑的!

值得庆幸的是,Linux提供了一组用于封装系统调用访问的宏.它设置寄存器内容并发出陷阱指令.这些宏命名为_syscalln(),其中n在0到6之间.该数字对应于传入syscall的参数数量,因为该宏需要知道要预期的参数以及因此推入寄存器.例如,考虑系统调用open(),定义为

long open(const char *filename,int flags,int mode)

在没有显式库支持的情况下使用此系统调用的系统调用宏将是

#define __NR_open 5
_syscall3(long,open,const char *,filename,int,flags,mode)

然后,应用程序可以简单地调用open().

对于每个宏,有2个2xn参数.第一个参数对应于系统调用的返回类型.第二个是系统调用名称.接下来按照系统调用顺序按照每个参数的类型和名称. __NR_open定义在;它是系统调用号码. _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()的声明在哪里?并且如何使这段代码可运行?我需要包括哪些头文件

解决方法

您首先应该了解 linux kernel的作用,该应用程序只能通过 system calls与内核进行交互.

实际上,应用程序在内核提供的“虚拟机”上运行:它在user space中运行,并且只能在(最低机器级)执行user CPU mode中允许的机器指令集(由SYSENTER或INT 0x80 …)用于进行系统调用.因此,从用户级应用程序的角度来看,系统调用是原子伪机器指令.

Linux Assembly Howto解释了如何在程序集(即机器指令)级别完成系统调用.

GNU libc提供了与系统调用对应的C函数.所以例如open功能是一个很小的胶水(即一个包装),在数字NR__open的系统调用之上(它正在使系统调用然后更新errno).应用程序通常在libc中调用这样的C函数,而不是执行系统调用.

你可以使用一些其他的libc.例如,MUSL libc是一个“简单的”,其代码可能更容易阅读.它还将原始系统调用包装到相应的C函数中.

如果你添加自己的系统调用,你最好还要实现一个类似的C函数(在你自己的库中).所以你也应该有一个头文件为您的库.

另见intro(2)syscall(2)syscalls(2)手册页,以及VDSO in syscalls的作用.

请注意,syscalls不是C函数.它们不使用调用堆栈(甚至可以在没有堆栈的情况下调用它们).系统调用基本上是一个数字,例如来自< asm / unistd.h>的NR__open,一个SYSENTER机器指令,具有关于在系统调用的参数之前保存哪些寄存器的SYSENTER机器指令,以及在系统调用的结果(包括失败的结果,在C库中设置errno包裹系统调用).系统调用的约定不是ABI规范中C函数调用约定(例如x86-64 psABI).所以你需要一个C包装.

猜你在找的Linux相关文章