it-swarm-ru.tech

Как я могу найти реализации системных вызовов ядра Linux?

Я пытаюсь понять, как работает функция, скажем 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));

Теперь мне нужно посмотреть, в каком C-файле эта функция реализована. Из исходного каталога я попробовал

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);

Но ни один из них не соответствует определению в sys/stat.h.

Вопросы

  1. Какой файл имеет реализацию mkdir?
  2. С помощью определения функции, подобного приведенному выше, как я могу узнать, какой файл имеет реализацию? Есть ли какая-то схема, которой придерживается ядро ​​при определении и реализации методов?

ПРИМЕЧАНИЕ: я использую ядро ​​ 2.6.36-rc1 .

376
Navaneeth K N

Системные вызовы не обрабатываются как обычные вызовы функций. Для перехода из пользовательского пространства в пространство ядра требуется специальный код, в основном небольшой встроенный ассемблерный код, внедренный в вашу программу на сайте вызовов. Код на стороне ядра, который "ловит" системный вызов, - это тоже низкоуровневый материал, который вам, вероятно, не нужно глубоко понимать, по крайней мере, сначала.

В _include/linux/syscalls.h_ в вашем исходном каталоге ядра вы найдете это:

_asmlinkage long sys_mkdir(const char __user *pathname, int mode);
_

Затем в _/usr/include/asm*/unistd.h_ вы найдете это:

_#define __NR_mkdir                              83
__SYSCALL(__NR_mkdir, sys_mkdir)
_

Этот код говорит, что mkdir(2) - системный вызов # 83. То есть системные вызовы вызываются по номеру, а не по адресу, как при обычном вызове функции в вашей собственной программе или функции в библиотеке, связанной с вашей программой. Упомянутый выше встроенный склеивающий код сборки использует это для перехода от пространства пользователя к ядру, принимая ваши параметры вместе с ним.

Еще одно свидетельство того, что здесь немного странно, это то, что не всегда существует строгий список параметров для системных вызовов: например, open(2) может принимать 2 или 3 параметра. Это означает, что open(2)перегружено , функция C++, а не C, но интерфейс syscall является C-совместимым. (Это не то же самое, что и C функция varargs , которая позволяет одной функции принимать переменное число аргументов.)

Чтобы ответить на ваш первый вопрос, нет ни одного файла, в котором существует mkdir(). Linux поддерживает множество различных файловых систем, и у каждой есть своя реализация операции "mkdir". Уровень абстракции, который позволяет ядру скрывать все, что находится за одним системным вызовом, называется VFS . Итак, вы, вероятно, хотите начать копаться в _fs/namei.c_, с vfs_mkdir(). Реальные реализации низкоуровневого кода, модифицирующего файловую систему, находятся в другом месте. Например, реализация ext4 называется ext4_mkdir(), определенной в _fs/ext4/namei.c_ .

Что касается вашего второго вопроса, да, есть шаблоны для всего этого, но нет единого правила. Что вам действительно нужно, так это достаточно широкое понимание того, как работает ядро, чтобы выяснить, где вы должны искать какой-либо конкретный системный вызов. Не все системные вызовы включают VFS, поэтому их цепочки вызовов на стороне ядра не все начинаются с _fs/namei.c_. mmap(2), например, начинается с _mm/mmap.c_ , потому что это часть подсистемы управления памятью ("mm") ядра.

Я рекомендую вам получить копию " Понимание ядра Linux " от Bovet и Cesati.

388
Warren Young

Вероятно, это не дает прямого ответа на ваш вопрос, но я обнаружил, что strace действительно здорово работает, пытаясь понять базовые системные вызовы в действии, которые сделаны даже для самых простых команд оболочки. например.

strace -o trace.txt mkdir mynewdir

Система вызывает команду mkdir mynewdir будет сохранен в trace.txt для вашего удобства просмотра.

86
Banjer

Хорошим местом для чтения источника ядра Linux является перекрестная ссылка Linux (LXR) ¹. Поиски возвращают типизированные совпадения (прототипы функций, объявления переменных и т.д.) В дополнение к результатам поиска в свободном тексте, так что это удобнее, чем просто grep (и быстрее).

LXR не расширяет определения препроцессора. Системные вызовы имеют название, искаженное препроцессором повсюду. Однако большинство (все?) Системных вызовов определяются одним из SYSCALL_DEFINEx семейства макросов. Поскольку mkdir принимает два аргумента, ищите SYSCALL_DEFINE2(mkdir приводит к объявлению системного вызова mkdir :

SYSCALL_DEFINE2(mkdir, const char __user *, pathname, int, mode)
{
    return sys_mkdirat(AT_FDCWD, pathname, mode);
}

хорошо, sys_mkdirat означает, что это системный вызов mkdirat, поэтому нажатие на него приведет только к объявлению в include/linux/syscalls.h, но определение чуть выше.

Основная задача mkdirat - вызывать vfs_mkdir (VFS - это общий уровень файловой системы). Нажатие на это показывает два результата поиска: объявление в include/linux/fs.h, а определение несколько строк выше. Основная работа vfs_mkdir вызывать реализацию, специфичную для файловой системы: dir->i_op->mkdir. Чтобы выяснить, как реализовано это, вам нужно обратиться к реализации отдельной файловой системы, и нет строгого правила - это может быть даже модуль вне дерева ядра.

¹ LXR - это программа индексирования. Есть несколько веб-сайтов, которые предоставляют интерфейс для LXR, с немного различными наборами известных версий и немного другими веб-интерфейсами. Они имеют тенденцию приходить и уходить, поэтому, если тот, к которому вы привыкли, недоступен, выполните поиск в Интернете по запросу "перекрестные ссылки linux", чтобы найти другой.

56

Системные вызовы обычно заключаются в макрос SYSCALL_DEFINEx(), поэтому простой grep не находит их:

_fs/namei.c:SYSCALL_DEFINE2(mkdir, const char __user *, pathname, int, mode)
_

Окончательное имя функции после раскрытия макроса заканчивается на _sys_mkdir_. Макрос SYSCALL_DEFINEx() добавляет шаблонные вещи, такие как код трассировки, который должен иметь каждое определение системного вызова.

22
stefanha

Примечание: .h файл не определяет функция. Это объявлено в этом файле .h и определено (реализовано) в другом месте. Это позволяет компилятору включать информацию о сигнатуре (прототипе) функции, чтобы разрешить проверку типов аргументов и сопоставить типы возврата с любым контекстом вызова в вашем коде.

В общем случае .h (заголовочные) файлы в C используются для объявления функций и определения макросов.

mkdir в частности это системный вызов. Вокруг этого системного вызова может быть оболочка GNU libc) (почти наверняка так и есть). Можно найти истинную реализацию ядра mkdir в ядре путем поиска источников ядра и системных вызовов в частности.

Обратите внимание, что для каждой файловой системы также будет реализован некоторый код создания каталога. Уровень VFS (виртуальная файловая система) предоставляет общий API, к которому может обращаться уровень системных вызовов. Каждая файловая система должна зарегистрировать функции для вызова уровня VFS. Это позволяет различным файловым системам реализовывать свою собственную семантику для структуры каталогов (например, если они хранятся с использованием некоторой схемы хеширования, чтобы сделать поиск конкретных записей более эффективным). Я упоминаю об этом, потому что вы, скорее всего, отключите эти специфические для файловой системы функции создания каталогов, если будете искать в дереве исходных кодов ядра Linux.

17
Jim Dennis

Ни одна из найденных реализаций не соответствует прототипу в sys/stat.h. Может быть, поиск оператора включения с этим заголовочным файлом будет более успешным?

8
greg0ire

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

6
An̲̳̳drew