系统调用的好参考资料

6
我需要一份好的参考资料,最好有一些好的例子。我需要它是因为我开始使用NASM汇编器编写代码。我有这个参考:http://bluemaster.iu.hio.no/edu/dark/lin-asm/syscalls.html,它非常好用,但它有很多限制,因为它没有解释其他寄存器中的字段。例如,如果我正在使用write系统调用,我知道我应该在EAX寄存器中放置1,而ECX可能是指向字符串的指针,但EBX和EDX呢?我也希望能够得到解释,EBX决定输入(0表示stdin,1表示其他等等),EDX是要输入的字符串的长度等等。我希望你明白我的意思,我找不到任何这样的资料,所以才来写这篇文章。谢谢。
2个回答

12
Linux中的标准编程语言是C。因此,最好的系统调用描述将它们显示为可调用的C函数。通过了解如何将它们映射到汇编实际的系统调用,并给出它们作为C函数的描述,您将能够轻松地使用任何您想要的系统调用。
首先,您需要一个参考,以便查看所有系统调用,就像它们对C程序员一样。我知道的最好的参考是Linux man-pages project,特别是system calls部分。
让我们以write系统调用为例,因为它是您问题中的一个。正如您所看到的,第一个参数是有符号整数,通常是由open系统调用返回的文件描述符。这些文件描述符也可能已从父进程继承而来,就像通常发生在前三个文件描述符(0=stdin,1=stdout,2=stderr)中一样。第二个参数是指向缓冲区的指针,第三个参数是缓冲区的大小(作为无符号整数)。最后,该函数返回有符号整数,即写入的字节数,或者是错误的负数。
现在,如何将这映射到实际的系统调用呢?在32位x86上有许多方法可以进行系统调用(根据您的寄存器名称,您可能正在使用的就是它);请注意,在64位x86上完全不同(确保您在32位模式下汇编并链接32位可执行文件;参见此问题以获取出错示例)。在32位x86中最古老、最简单和最慢的方法是int $0x80方法。

对于int $0x80方法,您需要按照顺序将系统调用号放在%eax中,并将参数放在%ebx%ecx%edx%esi%edi%ebp中。然后调用int $0x80,系统调用的返回值在%eax中。请注意,此返回值与参考文献中所示的不同;参考文献显示C库如何返回它,但系统调用在出错时返回-errno(例如-EINVAL)。C库将把这个值移动到errno并在这种情况下返回-1。有关更多详细信息,请参见syscalls(2)intro(2)

因此,在write的例子中,您需要将write系统调用号放入%eax中,第一个参数(文件描述符号)放入%ebx中,第二个参数(指向字符串的指针)放入%ecx中,第三个参数(字符串的长度)放入%edx中。系统调用将在%eax中返回写入的字节数或错误号的负值(如果返回值介于-1和-4095之间,则为负数的错误号)。

最后,如何找到系统调用号?它们可以在/usr/include/linux/unistd.h中找到。在我的系统上,这只包括/usr/include/asm/unistd.h,最终包括/usr/include/asm/unistd_32.h,因此数字就在那里(对于write,您可以看到__NR_write4)。对于错误号也是同样的,它们来自/usr/include/linux/errno.h(在我的系统上,经过追踪包含链,我发现第一个错误号在/usr/include/asm-generic/errno-base.h中,其余在/usr/include/asm-generic/errno.h中)。对于使用其他常量或结构的系统调用,它们的文档会告诉您应查找哪些头文件以找到相应的定义。


现在,正如我所说的,int $0x80是最古老和最慢的方法。更新的处理器有特殊的系统调用指令,速度更快。为了使用它们,内核提供了一个虚拟动态共享对象(vDSO;它类似于共享库,但仅在内存中),其中包含一个函数,您可以调用该函数以使用最佳的硬件可用方法进行系统调用。它还提供了一些特殊函数,可以获取当前时间,甚至无需进行系统调用,以及其他一些功能。当然,如果您不使用动态链接器,则使用起来可能会有点困难。
还有另一种较旧的方法,vsyscall,它类似于vDSO,但使用固定地址的单个页面。这种方法已被弃用,在使用最新的内核时会在系统日志中产生警告,可以在引导时禁用,甚至可能在将来被删除。不要使用它。

1
这是我见过的最好的答案之一。 - user1655874

0

如果你下载了网页(就像第二段所建议的那样)和内核源代码,你可以点击“源码”列中的链接,直接转到实现系统调用的源文件。你可以阅读它们的C签名,看看每个参数是用来做什么的。

如果你只是想要快速参考,每个系统调用都有一个C库接口,名称与sys_相同。例如,你可以查看{{link1:man 2 lseek}}来获取关于sys_lseek参数的信息:

off_t lseek(int fd, off_t offset, int whence);

正如您所看到的,这些参数与您的 HTML 表格中的参数匹配:

%ebx           %ecx    %edx
unsigned int   off_t   unsigned int

当然,即使你在Google或任何源代码搜索引搜寻每个实现方式,你都可以找到直接的使用示例 - 我已经这样做了。我发帖的目的是为了跳过搜索和分析,提供一些漂亮的备忘单。谢谢。 - darko.pp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接