Руководство FreeBSD

         

Как все это устроено?


FreeBSD поддерживает абстракцию, называемую ``загрузчик исполнимых классов'', который фактически является первой стадией системного вызова execve(2).

На самом деле, FreeBSD имеет несколько загрузчиков вместо одного, который, в случае неудачи, выполняет программу как сценарий (скрипт).

Исторически сложилось, что единственный загрузчик в UNIX® системах проверял ``магическое число'' (чаще всего первые 4 или 8 байт файла), чтобы определить, известен ли формат исполняемого файла системе, и если да, то вызвал соответствующий загрузчик.

Если файл не опознавался системой как исполнимый, execve(2) возвращал ошибку, и текущий командный интерпретатор начинал выполнять файл как скрипт.

Позднее, был модифицирован, так, чтобы проверять первые два символа в файле, и если они оказывались :\n, то файл выполнялся как сценарий для csh(1) (утверждается, что SCO были первыми, кто сделал эту модификацию).

FreeBSD ведет себя по-другому: пробегает по списку загрузчиков, включая специальный #! загрузчик, который вызывает нужный интерпретатор или /bin/sh, если не нашел подходящего.

Формат исполняемого файла FreeBSD определяет по ``магическому числу''. На этой стадии пока не различается, для какой операционной системы предназначен файл (Linux, Solaris™, или любой другой, использующей ELF-формат исполняемых файлов).

Далее, ELF-загрузчик определяет ``марку'' (специальный комментарий; отсутствует в исполняемых файлах SVR4/Solaris) исполняемого файла, то есть для какой операционной системы он предназначен.

Соответственно, Linux программы должны быть ``маркированы'' для Linux (например, с помощью утилиты brandelf(1)):

# brandelf -t Linux file

Когда ELF-загрузчик находит ``марку'' Linux, он заменяет соответствующий указатель в структуре proc. Все системные вызовы индексируются через этот указатель (в традиционной UNIX системе это массив sysent[], содержащий системные вызовы). Некоторые особые ситуации и системные вызовы обрабатываются специальным модулем ядра поддержки Linux.

Плюс ко всему, Linux эмулятор динамически ``изменяет корень'' файловой системы при поиске файлов; фактически так же, как и параметр union при монтировании файловых систем (не путать с unionfs!).
Сперва, файл ищется в каталоге /compat/linux/original-path и только затем, в случае неудачи, в /original-path. Это дает возможность Linux программам выполнять FreeBSD команды, если не найдется соответствующих Linux команд. Например, скопировав FreeBSD uname(1) в каталог /compat/linux/bin/, можно ``заставить'' Linux программы сообщать, что они запускаются под FreeBSD.

На самом деле, ядра FreeBSD и Linux во многом похожи: системные операции, виртуальная память, система сигналов и сообщений, межпроцессное взаимодействие и прочее. Разница в том, что FreeBSD программы обращаются к системным вызовам FreeBSD, Linux программы соответственно к системным вызовам Linux. Во многих операционных системах прошлого адреса системных вызовов были зашиты в статический глобальный массив sysent[], вместо обращения по указателю в структуре proc, который инициализируется динамически, позволяя таким образом запускать программы, написанные для разных операционных систем.

В чем же разница между системными вызовами Linux и FreeBSD? Фактически никакой. Единственное различие (на данный момент, в будущем все может и, вероятно, изменится), пожалуй, в том, что функции системных вызовов FreeBSD зашиты в ядро, а для Linux они могут быть либо в ядре, либо в динамически загружаемом модуле.

Можно ли назвать это эмуляцией? Нет. Это реализация ABI, а не эмуляция. Как таковой, эмулятор (или симулятор) отсутствует.

В таком случае, почему же тогда говорят ``Linux эмуляция''? Чтобы ``насолить'' FreeBSD?!. На самом деле, это вопрос терминологии: не существовало слова, которое бы точнее описывало этот процесс. Нельзя сказать, что FreeBSD запускает приложения Linux (без перекомпиляции или загрузки соответствующего модуля ядра). Поэтому и придумали термин ``Linux эмуляция''.


Содержание раздела