Еще в первый год моего знакомства с Ubuntu, мне стало интересно, как же работает ядро этой ОС. Я решил со всей серъезностью во всем разобратся, скачал 80-мегабайтовый архив исходников, и… всё! Я не знал ни с чего начать, ни чем закончить. Открывал поочереди случайные файлы и тут же терялся. Думаю такое случалось с каждым бывалым линуксоидом. Сейчас же я набрался опыта и хотел бы им поделиться.
В этой статье я раскажу, как я искал код системного вызова mkdir.
Начнем с того, что функция mkdir определена в sys/stat.h. Прототип выглядит следующим образом:
/* Create a new directory named PATH, with permission bits MODE. */
extern int mkdir (__const char *__path, __mode_t __mode)
__THROW __nonnull ((1));
Этот заголовочный файл является частью стандарта POSIX. Linux является почти полностью POSIX-совместим, а значит должен реализовывать mkdir именно с такой сигнатурой.
Но даже зная сигнатуру, найти собственно код системного вызова вовсе не просто…
И в правду ack "int mkdir" вернет:
security/inode.c
103:static int mkdir(struct inode *dir, struct dentry *dentry, int mode)
tools/perf/util/util.c
4:int mkdir_p(char *path, mode_t mode)
tools/perf/util/util.h
259:int mkdir_p(char *path, mode_t mode);
Понятно, что ни одна сигнатура полностью не совпадает. Где же находится реализация функции mkdir? Каков алгоритм нахождения реализаций системных вызовов в ядре Linux?
Системные вызовы работают не так, как обычные функции. Точне функциями они вовсе не являются. Для выполнений системного вызова нужно немного ассемблерного кода. По большому счету, в регистр EAX кладется номер системного вызова (кстати говоря, к системным вызовам обращаются по номеру, а не по адресу), в остальные регистры кладутся аргументы: первый в EBX, второй в ECX, третий в EDX, четвертый в ESX, пятый в EDI. Кстати, именно по этому системный вызов не может иметь более 5 аргументов. После расположения всех нужных значений программа, которыя хочет сделать системный вызов, выполняет 128-е прерывание (на асемблере: int 0x80). Прерывание, переводит процессор в режим ядра и передает управление по заранее оговореному с ядром адресу. Как видим, системные вызовы работают на более низком уровне, чем С-функции.
Номер любого системного вызова можно найти в usr/include/asm*/unistd.h:
#define __NR_mkdir 83
__SYSCALL(__NR_mkdir, sys_mkdir)
Тобиш системный вызов mkdir имеет номер 83.
Если вы програмировали в user space под Linux, вы скорее всего знаете, что как правило для выполнения системных вызовов таки используются C-функции. Откуда они берутся? Те функции — это просто обертки из библиотеки GNU libc. Каждому системному вызову соответствует функция-обертка. Сами же функции делают все те же прерывания.
Read more: Habrahabr.ru
QR: