Как компилятор узнает, что используемая вами функция является системным вызовом? - PullRequest
5 голосов
/ 23 августа 2010

Для следующего фрагмента кода,

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

Как компилятор узнает, что чтение - это системный вызов, а не какая-либо библиотечная функция?

Как получить номер системного вызова (__NR_read)?

Ответы [ 5 ]

13 голосов
/ 23 августа 2010

Я очень сомневаюсь, что компилятор знает, что это системный вызов. Гораздо более вероятно, что open находится где-то в библиотеке, а код внутри библиотеки вызывает соответствующий интерфейс ядра.

Вывод сборки из простой программы:

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

is (ненужные биты удалены):

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 80 или sysenter, который является механизмом, используемым для правильных системных вызовов (в любом случае, на моей платформе - YMMV).

Функции-оболочки в libc - это место, где выполняется фактическая работа с доступом к интерфейсу системного вызова.

Отрывок из Википедии о системных вызовах :

Обычно системы предоставляют библиотеку, которая находится между обычными программами и операционной системой, обычно это реализация библиотеки C (libc), например, glibc. Эта библиотека существует между ОС и приложением и увеличивает переносимость.

В системах, основанных на exokernel, библиотека особенно важна как посредник. В exokernels библиотеки защищают пользовательские приложения от API ядра очень низкого уровня и обеспечивают абстракции и управление ресурсами.

Термины «системный вызов» и «системный вызов» часто неправильно используются для обозначения стандартных функций библиотеки C, особенно тех, которые выполняют функцию оболочки для соответствующих системных вызовов с тем же именем. Вызов самой функции библиотеки не вызывает переключение в режим ядра (если выполнение не было уже в режиме ядра) и обычно является обычным вызовом подпрограммы (то есть с использованием инструкции сборки "CALL" в некоторых ISA). Реальный системный вызов передает управление ядру (и в большей степени зависит от реализации, чем абстрагирующий его библиотечный вызов). Например, fork и execve являются функциями GLIBC, которые, в свою очередь, вызывают системные вызовы fork и execve.

И, после небольшого поиска, функция __open найдена в glibc 2.9 в файле io/open.c и weakref 'преобразована в open. Если вы выполните:

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

вы можете увидеть их там:

00000000 W __open
00000000 W open
6 голосов
/ 23 августа 2010

read - это вызов библиотеки для компилятора.Так уж получилось, что реализация libc определяет read для генерации программного прерывания с правильным номером.

2 голосов
/ 23 августа 2010

Компилятор может видеть объявление этой функции и генерирует объектный код, который вызывает эту функцию.

Попробуйте скомпилировать с gcc -S, и вы увидите что-то вроде:

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

Системный вызов сделан из реализации read (2) библиотеки C.

EDIT: в частности, GNU libc (что, вероятно, то, что у вас есть в Linux), устанавливает отношения между syscallномера и имена функций в glibc-2.12.1/sysdeps/syscalls.list.Каждая строка этого файла преобразуется в исходный код на ассемблере (на основе sysdeps/unix/syscall-template.S), компилируется и добавляется в библиотеку при сборке libc.

1 голос
/ 23 августа 2010

Ниже приведена реализация чтения в бионическом режиме для 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.

1 голос
/ 23 августа 2010

open () - это библиотечная функция, она находится в libc.a / libc.so

...