История возникновения
Система Linux возникла как вариант операционной системы UNIX, предназначенный для персональных компьютеров с IBM-совместимой архитектурой. Первоначальная версия была написана Линусом Торвальдсом (Linus Torvalds), финским студентом, изучающим теорию вычислительных машин. В 1991 году Торвальдс представил в Internet первую версию системы Linux. С тех пор множество людей, сотрудничая посредством Internet, развивают Linux под руководством ее создателя. Благодаря тому что система Linux является бесплатной и можно беспрепятственно получить ее исходный код, она стала первой альтернативой рабочим станциям UNIX, предлагавшимся фирмами Sun Microsystems, Digital Equipment Corp (теперь Compaq) и Silicon Graphics. На сегодняшний день Linux является полнофункциональной системой семейства UNIX, способной работать на всех этих и других платформах.
Залогом успеха системы Linux является то, что она бесплатно распространяется при поддержке Фонда бесплатно распространяемых программ (Free Software Foundation — FSF). Целью этой организации является создание надежного аппаратно-независимого программного обеспечения, которое было бы бесплатным, обладало высоким качеством и пользовалось широкой популярностью среди пользователей. Фонд предоставляет инструменты для разработки программного обеспечения под эгидой общедоступной лицензии GNU (GNU Public License — GPL). Таким образом, система Linux в таком виде, в котором она существует сегодня, является продуктом, появившимся в результате усилий Торвальдса, а затем и многих других его единомышленников во всем мире, и распространяющимся в рамках проекта GNU.
Linux используется не только многими отдельными программистами; она проникла и в корпоративную среду [MANC00]. В основном это произошло благодаря высокому качеству ядра операционной системы Linux, а не из-за того, что эта система является бесплатной. В эту популярную версию внесли свой вклад многие талантливые программисты, в результате чего появился впечатляющий технический продукт. К достоинствам системы Linux можно отнести то, что она является модульной и легко настраивается. Благодаря этому можно достичь высокой производительности ее работы на самых разнообразных аппаратных платформах. К тому же получая в свое распоряжение исходный код, производители программного обеспечения могли улучшать качество приложений и служебных программ, с тем чтобы они удовлетворяли определенным требованиям. В этой книге изложены подробности внутреннего устройства ядра операционной системы Linux.
Модульная структура
Ядра большинства версий операционной системы UNIX являются монолитными. Напомним, что монолитное ядро — это ядро, которое виртуально включает в себя все возможности операционной системы в виде одного большого блока кода, который запускается как единый процесс в едином адресном пространстве. Все функциональные компоненты такого ядра имеют доступ ко всем его внутренним структурам данных и ко всем программам. При внесении изменений в любую из частей типичной монолитной операционной системы все ее модули и подпрограммы необходимо повторно компоновать и переустанавливать, а перед тем как изменения вступят в силу, систему нужно будет перезагрузить. В результате все модификации, такие, как добавление драйвера нового устройства или новых функций файловой системы, усложняются. Особенно остро эта проблема стоит в системе Linux, глобальную разработку которой выполняют объединенные на добровольных началах группы независимых программистов.
Для решения этой проблемы операционная система Linux организована в виде набора относительно независимых блоков, которые называются загружаемыми модулями (loadable modules) [GOYE99]. Загружаемые модули Linux имеют две отличительные особенности.
Динамическое связывание. Любой модуль ядра может быть загружен в память и подсоединен к ядру в то время, когда само ядро уже находится в памяти и выполняется. Любой модуль может быть также отсоединен от ядра и удален из памяти в любой момент времени.
Стековая организация. Модули организованы в виде определенной иерархической структуры. Отдельные модули могут выполнять роль библиотек при обращении к ним модулей более высоких уровней в рамках этой структуры; они сами также могут обращаться к модулям на более низких уровнях.
Динамическое связывание [FRAN97] облегчает настройку системы и экономит память, которую занимает ядро. В системе Linux программа пользователя или сам пользователь может загружать или выгружать модули с помощью команд insmod и rmmod. Само ядро управляет работой отдельных функций и по мере надобности загружает нужные модули или выгружает те, нужда в которых уже отпала. Кроме того, стековая организация позволяет задавать зависимости модулей, что дает два основных преимущества.
Код, являющийся общим для набора однотипных модулей (например, драйверы похожих устройств), можно поместить в один модуль, что позволяет сократить количество повторений.
Ядро может проверить наличие в памяти нужных модулей, воздерживаясь от выгрузки модуля, который нужен для работы других, зависимых от него, и загружая вместе с новым требуемым модулем все необходимые дополнительные модули.
На примере, приведенном на рис. 2.18, показаны структуры, которые используются операционной системой Linux для управления модулями. На рисунке приведен список модулей ядра после загрузки модулей FAT и VFAT. Каждый модуль задается двумя таблицами: таблицей модулей и таблицей символов. В таблицу модулей входят перечисленные ниже элементы.
next. Указатель на следующий модуль. Все модули организованы в виде связанного списка. Этот список начинается псевдомодулем (на рис. 2.18 он не показан).
ref. Список модулей, которые используются данным модулем.
symtab. Указатель на таблицу символов данного модуля.
name. Имя модуля.
size. Размер модуля в страницах памяти.
addr. Начальный адрес модуля.
state. Текущее состояние модуля.
* cleanup!). Указатель на программу, которая запускается при выгрузке данного модуля.
Модуль Модуль
Рис. 2.18. Операционная система Linux. Пример списка модулей ядра
Таблица символов определяет символы, контролируемые данным модулем и используемые где-либо еще. В таблицу входят такие элементы.
- size. Полный размер таблицы.
- n_symbols. Количество символов.
- n_ref s. Количество ссылок.
- symbols. Таблица символов.
- references. Список модулей, зависящих от данного.
На рис. 2.18 модуль VFAT загружается после модуля FAT и зависит от него.