Для следующего фрагмента кода,
int n;
char buf[100];
int fd = open ("/etc/passwd", O_RDONLY);
n = read ( fd, buf, 100);
Как компилятор узнает, что чтение - это системный вызов, а не какая-либо библиотечная функция?
Как получить номер системного вызова ( __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 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
чтение - это вызов библиотеки с точки зрения компилятора. Так уж получилось, что реализация libc определяет чтение для генерации программного прерывания с правильным номером.
Ниже приведена реализация 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.
Компилятор может видеть объявление этой функции в и генерировать объектный код, который вызывает эту функцию.
Попробуйте скомпилировать с помощью 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.