Откуда компилятор узнает, что вы использовали функцию системный вызов?

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

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

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

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

6
задан P Shved 23 August 2010 в 12:50
поделиться

5 ответов

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

1
ответ дан 8 December 2019 в 05:19
поделиться

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

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

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

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

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

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

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

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

, вы увидите их там:

00000000 W __open
00000000 W open
13
ответ дан 8 December 2019 в 05:19
поделиться

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

6
ответ дан 8 December 2019 в 05:19
поделиться

Ниже приведена реализация read в bionic (эквивалент 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
ответ дан 8 December 2019 в 05:19
поделиться

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

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

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

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

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

2
ответ дан 8 December 2019 в 05:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: