从用户空间访问系统调用
通常,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()的声明在哪里?并且如何使这段代码可运行?我需要包括哪些头文件?
@R_502_6120@ 您首先应该了解 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 – 如何从用户空间访问系统调用?全部内容,希望文章能够帮你解决linux – 如何从用户空间访问系统调用?所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)