编译器如何知道你使用的函数是系统调用?

对于下面的代码片段,

int n; char buf[100]; int fd = open ("/etc/passwd", O_RDONLY); n = read ( fd, buf, 100); 

编译器如何知道read是一个系统调用而不是任何库函数?

如何检索系统呼叫号码( __NR_read )?

open()是一个库函数,它位于libc.a / libc.so中

我非常怀疑编译器知道这是一个系统调用。 open在某个库中的可能性更大,库中的代码调用相关的内核接口。

简单程序的汇编输出:

 #include <stdio.h> int main (void) { int fd = open("xyz"); return 0; } 

是(不相关的位被删除):

 main: pushl %ebp ; stack frame setup. movl %esp, %ebp andl $-16, %esp subl $32, %esp movl $.LC0, (%esp) ; Store file name address. call open ; call the library function. movl %eax, 28(%esp) ; save returned file descriptor. movl $0, %eax ; return 0 error code. leave ; stack frame teardown. ret .LC0: .string "xyz" ; file name to open. 

你会注意到的第一件事是有一个open电话 。 换句话说,这是一个功能。 有没有一个int 80sysenter在视线,这是用于正确的系统调用的机制(无论如何我的平台 – YMMV)。

libc中的包装函数是访问系统调用接口的实际工作完成的地方。

系统调用维基百科摘录:

通常,系统提供一个位于正常程序和操作系统之间的库,通常是C库(libc)的一个实现,比如glibc。 该库存在于操作系统和应用程序之间,增加了可移植性。

在以exokernel为基础的系统中,图书馆作为中间人尤为重要。 在exokernels上,库将用户应用程序从最底层的内核API中提取出来,并提供抽象和资源管理。

术语“系统调用”和“系统调用”通常不正确地用于指代C标准库函数,特别是那些充当相同系统调用的相同名称的包装的函数。 对库函数本身的调用不会导致切换到内核模式(如果执行不在内核模式下),并且通常是一个正常的子例程调用(即在某些ISA中使用“CALL”汇编指令)。 实际的系统调用确实将控制转移给内核(并且比库调用抽象化更依赖于实现)。 例如, forkexecve是GLIBC函数,后者又调用forkexecve系统调用。

而且,经过一番搜索之后,在io/open.c文件的glibc 2.9中找到了__open函数, __openopen 。 如果您执行:

 nm /usr/lib/libc.a | egrep 'W __open$|W open$' 

你可以看到他们在那里:

 00000000 W __open 00000000 W open 

就编译器而言,read是一个库调用。 libc实现定义了读取以产生具有正确数字的软件中断。

编译器可以在这个函数中看到这个函数的声明,并且它会生成一个调用该函数的对象代码。

尝试使用gcc -S编译,您将看到如下所示的内容:

 movl $100, %edx movq %rcx, %rsi movl %eax, %edi call read 

系统调用是由C库的read(2)实现的。

编辑:具体来说,GNU libc(这可能是你在Linux上的),建立系统调用号码和glibc-2.12.1/sysdeps/syscalls.list函数名称之间的关系。 该文件的每一行都被转换为汇编语言源代码(基于sysdeps/unix/syscall-template.S ),编译并在libc生成时添加到库中。

以下是仿生读取的Android实现(与libc相当的Android)

 /* autogenerated by gensyscalls.py */ #include <sys/linux-syscalls.h> .text .type read, #function .globl read .align 4 .fnstart read: .save {r4, r7} stmfd sp!, {r4, r7} ldr r7, =__NR_read swi #0 ldmfd sp!, {r4, r7} movs r0, r0 bxpl lr b __set_syscall_errno .fnend 

你可以看到它将__NR_read加载到r7中,然后调用SWI,SWI是将处理器切换到内核模式的软件中断。 所以编译器需要对如何进行系统调用一无所知,libc负责处理。