10.8. Дополнительные сведения

Если вы интересуетесь, как обеспечивается двоичная совместимость с Linux, этот раздел для вас. Большинство материала взято из электронного письма, адресованного Terry Lambert в Список рассылки, посвящённый неформальным беседам о FreeBSD (ID письма: <199906020108.SAA07001@usr09.primenet.com>).

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

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

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

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

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

По умолчанию скрипт выполнялся ''текущим командным интерпретатором''.

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

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

Для поддержки Linux ABI FreeBSD ищет магическое число, соответствующее двоичному файлу ELF (на этой стадии не различаются FreeBSD, Solaris™, Linux или любая другая ОС поддерживающая формат ELF).

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

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

# brandelf -t Linux file

Когда это сделано, загрузчик ELF выявит марку Linux в файле.

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

Вектор системных вызовов Linux содержит, среди прочего, список записей sysent[], адреса которых находятся в модуле ядра.

При выполнении системного вызова из двоичного файла Linux, код обработчика разыменовывает указатель на функцию системного вызова из структуры proc, и получает точки входа системных вызовов Linux, а не FreeBSD.

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

Фактически, имеется ядро Linux в ядре FreeBSD; различные базовые функции, реализующие все услуги ядра, идентичны как в записях таблицы системных вызовов FreeBSD, так и в записях таблицы системных вызовов Linux: операции с файловой системой, виртуальная память, средства доставки сигналов, System V IPC ... Единственное отличие в том, что FreeBSD-программы получают интерфейсные функции FreeBSD, а Linux-программы получают интерфейсные функции Linux (в большинстве более старых ОС есть только их собственные интерфейсные функции: функции берутся из статического глобального массива структур sysent[], а не из массива, полученного разыменованием динамически проинициализированного указателя в структуре proc процесса, выполняющего вызов).

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

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

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

Этот, и другие документы, могут быть скачаны с ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

По вопросам, связанным с FreeBSD, прочитайте документацию прежде чем писать в <questions@FreeBSD.org>.
По вопросам, связанным с этой документацией, пишите <doc@FreeBSD.org>.
По вопросам, связанным с русским переводом документации, пишите в рассылку <frdp@FreeBSD.org.ua>.
Информация по подписке на эту рассылку находится на сайте проекта перевода.

Обновлено: 12.03.2015