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

Gan*_*pur 6 c linux system-calls

对于以下代码段,

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

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

它如何检索系统调用号(__NR_read)?

pax*_*blo 13

我非常怀疑编译器知道它是一个系统调用.它更可能出现open在某个库中,并且库中的代码调用相关的内核接口.

简单程序的程序集输出:

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

是(删除不相关的位):

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.
Run Code Online (Sandbox Code Playgroud)

你会注意到的第一件事就是打电话open.换句话说,它是一个功能.没有int 80sysenter看不见,这是用于正确系统调用的机制(无论如何在我的平台上 - YMMV).

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

维基百科关于系统调用的摘录:

通常,系统提供位于正常程序和操作系统之间的库,通常是C库(libc)的实现,例如glibc.该库存在于OS和应用程序之间,并提高了可移植性.

在基于exokernel的系统上,该库作为中介尤其重要.在exokernel上,库将用户应用程序与极低级别的内核API隔离开来,并提供抽象和资源管理.

术语"系统调用"和"系统调用"通常被错误地用于引用C标准库函数,特别是那些充当相同系统调用的包装器的函数.对库函数本身的调用不会导致切换到内核模式(如果执行尚未处于内核模式)并且通常是正常的子例程调用(即,在某些ISA中使用"CALL"汇编指令).实际的系统调用确实将控制转移到内核(并且比抽象它的库调用更依赖于实现).例如,fork并且execve是GLIBC功能,在依次调用forkexecve系统调用.

并且,经过一些搜索后,该__open函数可以在io/open.c文件中的glibc 2.9中找到,并且weakref编辑为open.如果你执行:

nm /usr/lib/libc.a | egrep 'W __open$|W open$'
Run Code Online (Sandbox Code Playgroud)

你可以在那里看到它们:

00000000 W __open
00000000 W open
Run Code Online (Sandbox Code Playgroud)

  • @Ganesh,你_can_创建自己的系统调用.如果您不想提供C编译器可用的自己的包装器,您可以使用`syscall`从C代码中调用它们.或者如果你愿意,你可以自己内联组装int80. (2认同)

dor*_*ron 6

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

  • @Ganesh Kundapur是的.所有系统调用都包含在libc函数中.(如果你想自己建立系统调用,那么有一个通用的syscall()函数) (7认同)

bla*_*aze 1

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