Второе издание
Скачать 3.09 Mb.
|
Ядро Linux в сравнении с классическими ядрами Unix Благодаря общему происхождению и одинаковому API, современные ядра Unix имеют некоторые общие характерные черты. За небольшими исключениями ядра Unix представляют собой монолитные статические бинарные файлы. Это значит, что они существуют в виде больших исполняемых образов, которые выполняются один раз и используют одну копию адресного пространства. Для работы операци- онной системы Unix обычно требуется система с контроллером управления стра- ничной адресацией памяти (memory management unit); это аппаратное обеспечение позволяет обеспечить защиту памяти в системе и предоставить каждому процессу уникальное виртуальное адресное пространство. В списке литературы приведены мои любимые книги по устройству классических ядер операционной системы Unix. Сравнение решений на основе монолитного ядра и микроядра Операционные системы, в соответствии с особенностями построения, можно разделить на две большие группы: с монолитным ядром и с микроядром. (Есть еще третий тип — экзоядро, кото- рое пока еще используется, в основном, только в исследовательских операционных системах, но уже начинает пробивать дорогу в большой мир.) Монолитное ядро является самым простым, и до 1980-х годов все ядра строились именно та- ким образом. Монолитное ядро реализовано в виде одного большого процесса, который вы- полняется в одном адресном пространстве, Такие ядра обычно хранятся на диске в виде одного большого статического бинарного файла. Все службы ядра существуют и выполняются в одном большом адресном пространстве ядра. Взаимодействия в ядре выполняются очень просто, по- тому что все, что выполняется в режиме ядра, — выполняется в одном адресном пространстве. Ядро может вызывать функции непосредственно, как это делает пользовательское приложение. Сторонники такой модели обычно указывают на простоту и высокую производительность моно- литных ядер. Микроядра не реализуются в виде одного большого процесса. Все функции ядра разделяются на несколько процессов, которые обычно называют серверами. В идеале, в привилегирован- ном режиме работают только те серверы, которым абсолютно необходим привилегированный режим. Остальные серверы работают в пространстве пользователя. Все серверы, тем не ме- нее, поддерживаются независимыми друг от друга и выполняются каждый в своем адресном пространстве. Следовательно, прямой вызов функций, как в случае монолитного ядра, невоз- можен. Все взаимодействия внутри микроядра выполняются с помощью передачи сообщений. Механизм межпроцессного взаимодействия (Inter Process Comrrmnication, IPC) встраивается в систему, и различные серверы взаимодействуют между собой и обращаются к "службам" друг друга путем отправки сообщений через механизм IPC. Разделение серверов позволяет предот- вратить возможность выхода из строя одного сервера при выходе из строя другого. Кроме того, модульность системы позволяет одному серверу вытеснять из памяти другого. Поскольку механизм IPC требует больше накладных расходов, чем обычный вызов функции, и при этом может потребоваться переключение контекста из пространства пользователя в про- странство ядра и наоборот, то передача сообщений приводит к падению производительности по сравнению с монолитными ядрами, в которых используются обычные вызовы функций. Введение в ядро Linux 29 В современных операционных системах с микроядром, большинство серверов выполняется в пространстве ядра, чтобы избавиться от накладных расходов, связанных с переключением контекста, кроме того, это дает потенциальную возможность прямого вызова функций. Ядро операционной системы Windows NT, а также ядро Mach (на котором базируется часть опера- ционной системы Mac OS X) - это примеры микроядер. В последних версиях как Windows NT, так и Mac OS X все серверы выполняются только в пространстве ядра, что является отходом от первоначальной концепции микроядра. Ядро ОС Linux монолитное, т.е. оно выполняется в одном адресном пространстве, в режиме ядра. Тем не менее ядро Linux позаимствовало некоторые хорошие свойства микроядерной модели: в нем используется преемптивное ядро, поддерживаются потоки пространства ядра и возможность динамической загрузки в ядро внешних бинарных файлов (модулей ядра). Ядро Linux не использует никаких функций микроядерной модели, которые приводят к снижению производительности: все выполняется в режиме ядра с непосредственным вызовом функций, вместо передачи сообщений. Следовательно, операционная система Linux — модульная, много- поточная, а выполнение самого ядра можно планировать. Прагматизм снова победил. По мере того как Линус и другие разработчики вносили свой вклад в ядро Linux, они принимали решения о том, как развивать ОС Linux без пренебрежения корнями, связанными с Unix (и, что более важно, без пренебрежения API ОС Unix). Поскольку операционная система Linux не базируется на какой-либо версии ОС Unix, Линус и компания имели возможность найти и выбрать наилучшее решение для любой проб- лемы и даже со временем изобрести новые решения! Ниже приводится анализ харак- теристик ядра Linux, которые отличают его от других разновидностей Unix. • Ядро Linux поддерживает динамическую загрузку модулей ядра. Хотя ядро Linux и является монолитным, оно дополнительно поддерживает динамиче- скую загрузку и выгрузку исполняемого кода ядра по необходимости. • Ядро Linux поддерживает симметричную многопроцессорную обработку (SMP). Хотя большинство коммерческих вариантов операционной системы Unix сей- час поддерживает SMP, большинство традиционных реализаций ОС Unix такой поддержки не имеет. • Ядро Linux является преемптивным. В отличие от традиционных вариантов ОС Unix, ядро Linux в состоянии вытеснить выполняющееся задание, даже если это задание работает в режиме ядра. Среди коммерческих реализаций ОС Unix преемптивное ядро имеют только операционные системы Solaris и IRIX. • В ядре Linux используется интересный подход для поддержки многопоточно- сти (threads): потоки ни чем не отличаются от обычных процессов. С точки зрения ядра все процессы одинаковы, просто некоторые из них имеют общие ресурсы. • В ядре Linux отсутствуют некоторые функции ОС Unix, которые считаются плохо реализованными, как, например, поддержка интерфейса STREAMS, или отвечают "глупым" стандартам. • Ядро Linux является полностью открытым во всех смыслах этого слова. Набор функций, реализованных в ядре Linux, — это результат свободной и откры- той модели разработки операционной системы Linux. Если какая-либо функ- ция ядра считается маловажной или некачественной, то разработчики ядра 30 Глава 1 не обязаны ее реализовать. В противоположность этому, внесение изменений при разработке ядра Linux занимает "элитарную" позицию: изменения должны решать определенную практическую задачу, должны быть логичными и иметь понятную четкую реализацию. Следовательно, функции некоторых современ- ных вариантов ОС Unix, такие как память ядра со страничной реализацией, не были реализованы. Несмотря на имеющиеся различия, Linux является опера- ционной системой со строгим наследованием традиций ОС Unix. Версии ядра Linux Ядро Linux поставляется в двух вариантах-: стабильном (stable) и разрабатывае- мом (development). Версии стабильного ядра - это выпуски продукции промыш- ленного уровня, которая готова для широкого использования. Новые стабильные версии ядра обычно выпускаются для исправления ошибок и для предоставления новых драйверов устройств. Разрабатываемые версии ядра, наоборот, подвержены быстрым изменениям. По мере того как разработчики экспериментируют с новыми решениями, часто вносятся радикальные изменения в ядро. Ядра Linux стабильных и разрабатываемых версий можно отличить друг от друга с помощью простой схемы присваивания имен (рис. 1.2.). Три числа, которые раз- деляются точкой, определяют версию ядра. Первое число - значение старшей (ma- jor) версии, второе - значение младшей (minor), третье число - значение редакции (выпуска, revision). Значение младшей версии также определяет, является ли ядро стабильным или разрабатываемым; если это значение четное, то ядро стабильное, а если нечетное, то разрабатываемое. Так, например, версия 2.6.0 определяет стабиль- ное ядро. Ядро имеет старшую версию 2, младшую версию 6 и редакцию 0. Первые два числа также определяют "серию ядер", в данном случае серия ядер — 2.6. Младшая версия равна 6 (это стабильное ядро) Старшая версия равна 2 2.6.0 Рис. 1.2. Соглашение о присваивании имен ядрам Разработка ядра соответствует различным фазам. Вначале разработчики ядра ра- ботают над новыми функциями, что напоминает хаос. Через определенное время ядро оказывается сформировавшимся, и в конце концов объявляется замораживание функций. Начиная с этого момента никакие новые функции не могут быть добавлены в ядро. Однако работа над сущестиующими функциями может быть продолжена. После того как ядро становится почти стабильным, осуществляется замораживание кода. В этом случае допускаются только исправления ошибок. Вскоре после этого (можно надеяться) ядро выпускается в виде первой, новой, стабильной версии. Например, при стабилизации серии ядер 2.5 получается серия 2.6. Введение в ядро Linux 31 Номер выпуска равен О Все это не правда По крайней мере - не совсем. Приведенное только что описание процесса разработки ядра технически правильное. Раньше процесс происходил именно так, как описано. Тем не менее летом 2004 года на ежегодном саммите для приглашенных разработчиков ядра Linux было при- нято решение продолжить разработку серии 2.6 ядра Linux и в ближайшем будущем не пере- ходить на серию разрабатываемого ядра 2.7. Такое решение было принято потому, что ядро 2.6 получилось хорошим; оно, в основном, стабильно и на горизонте нет никаких новых функций, которые требуют серьезного вторжения в ядро. Кроме того, и, возможно, это главное — существующая система поддержки, которая обеспечи- вается Линусом Тораальдсом и Эндрю Мортоном, работает чрезвычайно хорошо. Разработчики ядра уверены, что процесс разработки может продолжаться таким образом, что серия ядер 2.6 будет оставаться стабильной и в ней будут появляться новые возможности. Время рассудит, но уже сейчас результаты выглядят хорошо. Эта книга базируется на ядрах стабильной серии 2.6. Сообщество разработчиков ядра Linux Когда вы начинаете разрабатывать код ядра Linux, вы становитесь частью гло- бального сообщества разработчиков ядра Linux. Главный форум этого сообщества — список рассылки разработчиков ядра Linux (linux-kernel mailing list). Информация по по- воду подписки на этот форум доступна по адресу h t t p : / / v g e r . k e r n e l . o r g . Следует заметить, что это достаточно перегруженный сообщения список рассылки (количе- ство сообщений порядка 300 в день) и что другие читатели этого списка (разработ- чики ядра, включая Линуса) не очень склонны заниматься ерундой. Однако этот спи- сок рассылки может оказать неоценимую помощь в процессе разработки; здесь вы сможете найти тестологов, получить экспертную оценку и задать вопросы. В последних главах приведен обзор процесса разработки ядра и более полное описание того, как успешно принимать участие в деятельности сообщества разра- ботчиков ядра. Перед тем как начать Эта книга посвящена ядру Linux: как оно работает, почему оно работает и чему следует уделить внимание. Далее будут описаны принципы работы и реализация основных подсистем ядра, а также интерфейсы и программная семантика. Эта книга касается практических вопросов, и в ней используется подход на основании золотой серединки указанных выше направлений. Такой интересный подход в сочетании с анекдотами из личной практики автора и советами по хакерским приемам позволяет быть уверенным в том, что книга станет хорошим стартом. Я надеюсь, что у читателей есть доступ к системе Linux и дереву исходного кода ядра. В идеале предполагается, что читатель— это пользователь операционной си- стемы Linux, который уже "копался" в исходном программном коде, но все же нуж- дается в некоторой помощи для того, чтобы все связать воедино. В принципе, чита- тель может и не быть пользователем Linux, но хочет разобраться в устройстве ядра из чистого любопытства. Тем не менее, для того чтобы самому научиться писать программы — исходный код незаменим. Исходный программный код свободно досту- пен— пользуйтесь им! Удачи! 32 Глава 1 2 Начальные сведения о ядре Linux В этой главе будут рассмотрены основные вопросы, связанные с ядром Linux: где получить исходный код, как его компилировать и как инсталлировать новое ядро. После этого рассмотрим некоторые допущения, связанные с ядром Linux, от- личия между ядром и пользовательскими программам, а также общие методы, кото- рые используются в ядре. Ядро имеет интересные особенности, которые отличают его от других программ, но нет таких вещей, в которых нельзя разобраться. Давайте этим займемся. Получение исходного кода ядра Исходный программный код последней версии ядра всегда доступен как в виде полного архива в формате tar (tarball), так и виде инкрементной заплаты по адресу http://www.kernel.org. Если нет необходимости по той или другой причине работать со старыми версиями ядра, то всегда нужно использовать самую последнюю версию. Архив kernel.org — это то место, где можно найти как само ядро, так и заплаты к нему от ведущих разработчиков. Инсталляция исходного кода ядра Архив исходного кода ядра в формате tar распространяется в сжатых форматах GNU zip (gzip) и bzip2. Формат bzip2 наиболее предпочтителен, так как обеспечи- вает больший коэффициент сжатия по сравнению с форматом gzip. Архив ядра в формате bzip2 имеет имя l i n u x - x . у . z . t a r . b z 2 , где х, у, z — это номер соответ- ствующей версии исходного кода ядра. После загрузки исходного кода его можно декомпрессировать очень просто. Если tar-архив сжат с помощью GNU zip, то необ- ходимо выполнить следующую команду. $ tar xvzf linux-x.у.z.tar.gz Если сжатие выполнено с помощью bzip2, то команда должна иметь следующий вид. $ tar xvjf linux-x.у.z.tar.bz2 Обе эти команды позволяют декомпрессировать и развернуть дерево исходных кодов ядра в каталог с именем l i n u x - x . y . z . Где лучше инсталлировать и изменять исходный код Исходный код ядра обычно инсталлируется в каталог / u s r / s r c / l i n u x . Заметим, что это де- рево исходного кода нельзя использовать для разработок. Версия ядра, с которой была ском- пилирована ваша библиотека С, часто связывается с этим деревом каталогов. Кроме того, чтобы вносить изменения в ядро, не обязательно иметь права пользователя root, вместо этого лучше работать в вашем домашнем каталоге и использовать права пользователя root только для инсталляции ядра. Даже при инсталляции нового ядра каталог / u s r / s r c / l i n u x лучше оставлять без изменений. Использование заплат В сообществе разработчиков ядра Linux заплаты (patch) — это основной язык об- щения. Вы будете распространять ваши изменения исходного кода ядра в виде заплат и получать изменения кода от других разработчиков тоже в виде заплат. При дан- ном рассмотрении наиболее важными являются инкрементные заплаты (incremental patch), которые позволяют перейти от одной версии ядра к другой. Вместо того что- бы загружать большой архив ядра, можно просто применить инкрементную заплату и перейти от имеющейся версии к следующей. Это позволяет сэкономить время и пропускную способность каналов связи. Для того чтобы применить инкрементную заплату, находясь в каталоге дерева исходных кодов ядра, нужно просто выполнить следующую команду. $ patch -p1 < ../patch-х.у.z Обычно заплата для перехода на некоторую версию ядра должна применяться к предыдущей версии ядра. В следующих главах использование заплат рассматривается более подробно. Дерево исходных кодов ядра Дерево исходных кодов ядра содержит ряд каталогов, большинство из которых также содержит подкаталоги. Каталоги, которые находятся в корне дерева исходных кодов, и их описание приведены в табл. 2.1. Некоторые файлы, которые находятся в корне дерева исходных кодов, также заслуживают внимания. Файл COPYING — это лицензия ядра (GNU GPL v2). Файл CREDITS — это список разработчиков, которые внесли большой вклад в разработку ядра. Файл MAINTAINERS — список людей, которые занимаются поддержкой подси- стем и драйверов ядра. И наконец, Makefile — это основной сборочный файл ядра. Сборка ядра Сборка ядра достаточно проста. Это может показаться удивительным, но она даже более проста, чем компиляция и инсталляция других системных компонентов, как, например библиотеки g l i b c . В ядрах серии 2.6 встроена новая система конфи- гурации и компиляции, которая позволяет сделать эту задачу еще проще и является долгожданным улучшением по сравнению с серией ядер 2.4. 34 Глава 2 Таблица 2 . 1 . Каталоги в корне дерева исходных кодов ядра Так как доступен исходный код ядра Linux, то, это означает, что есть возможность сконфигурировать ядро перед компиляцией. Есть возможность скомпилировать под- держку только необходимых драйверов и функций. Конфигурация ядра— необходи- мый этап перед тем, как его компилировать. Поскольку в ядре бесчисленное количе- ство функций и вариантов поддерживаемого аппаратного обеспечения, возможностей по конфигурации, мягко говоря, много. Конфигурация управляется с помощью опций конфигурации в виде CONFIG_FEATURE. Например, поддержка симметричной мно- гопроцессорной обработки (Symmetric multiprocessing, SMP) устанавливается с по- мощью опции CONFIG SMP. Если этот параметр установлен, то поддержка функций SMP включена. Если этот параметр не установлен, то функции поддержки SMP от- ключены. Все конфигурационные параметры хранятся в файле . c o n f i g в корневом каталоге дерева исходного кода ядра и устанавливаются одной из конфигурацион- ных программ, например, с помощью команды make xconfig. Конфигурационные параметры используются как для определения того, какие файлы должны быть ском- пилированы во время сборки ядра, так и для управления процессом компиляции че- рез директивы препроцессора. Конфигурационные переменные бывают двух видов: логические (boolean) и пере- менные с тремя состояниями (instate). Логические переменные могут принимать значения yes и по. Такие переменные конфигурации ядра, как CONFIG_PREEMPT, обычно являются логическими. Конфигурационная переменная с тремя состояния- ми может принимать значения yes, no и module. Значение module отвечает кон- фигурационному параметру, который установлен, но соответствующий код должен компилироваться как модуль (т.е. как отдельный объект, который загружается дина- мически). Драйверы устройств обычно представляются конфигурационными пере- менными с тремя состояниями. Начальные сведения о ядре Linux 35 Каталог Описание arch crypto Documentation drivers fs include init ipc kernel lib mm net scripts security sound usr Специфичный для аппаратной платформы исходный код Криптографический API Документация исходного кода ядра Драйверы устройств Подсистема VFS и отдельные файловые системы Заголовочные файлы ядра Загрузка и инициалиэация ядра Код межпроцессного взаимодействия Основные подсистемы, такие как планировщик Вспомогательные подпрограммы Подсистема управления памятью и поддержка виртуальной памяти Сетевая подсистема Сценарии компиляции ядра Модуль безопасности Linux Звуковая подсистема Начальный код пространства пользователя (initramfs) Конфигурационные параметры могут иметь целочисленный, или строковый, тип. Эти параметры не контролируют процесс сборки, а позволяют указать значения, ко- торые встраиваются в исходный код ядра с помощью препроцессора. Например, с помощью конфигурационного параметра можно указать размер статически выделен- ного массива. Ядра, которые включаются в поставки ОС Linux такими производителями, как Novell и Redhat, компилируются как часть дистрибутива. В таких ядрах обычно име- ется большой набор различных функций и практически полный набор всех драй- веров устройств в виде загружаемых модулей. Это позволяет получить хорошее базовое ядро и поддержку широкого диапазона оборудования. К сожалению, как раз- работчикам ядра, вам потребуется компилировать свои ядра и самим разбираться, какие модули включать, а какие нет. В ядре поддерживается несколько инструментов, которые позволяют выполнять конфигурацию. Наиболее простой инструмент — это текстовая утилита командной строки: • make config Эта утилита просматривает все параметры один за другим и интерактивно запра- шивает у пользователя, какое значение соответствующего параметра установить — yes, no или module {для переменной с тремя состояниями). Эта операция требует длительного времени, и если у вас не почасовая оплата, то лучше использовать утили- ту на основе интерфейса ncurses: make menuconfig или графическую утилиту на основе системы X11: make xconfig или еще более удобную графическую утилиту, основанную на библиотеке gtk+ make gconfig Эти утилиты позволяют разделить все параметры по категориям, таким как Processor Features (Свойства процессора) и Network Devices (Сетевые устройства). Пользователи могут перемещаться по категориям и, конечно, изменять значения конфигурационных параметров. Команда $ make defconfig позволяет создать конфигурационный файл, который будет содержать параметры, используемые по умолчанию для текущей аппаратной платформы. Хотя эти параме- тры и достаточно общие (ходят слухи, что для аппаратной платформы i386 исполь- зуется конфигурация Линуса), они являются хорошей стартовой точкой, если вы никогда перед этим не занимались конфигурацией ядра. Чтобы все сделать быстро, необходимо выполнить эту команду, а потом проверить, включена ли поддержка всех нужных аппаратных устройств. Конфигурационные параметры содержатся в корне дерева каталогов исходного кода ядра в файле с именем .config. Для вас может показаться более простым, так же как и для большинства разработчиков, непосредственно редактировать этот кон- фигурационный файл. Достаточно легко проводить поиск в этом файле и изменять значение конфигурационных параметров. После внесения изменений в конфигура- 36 Глава 2 ционный файл или при использовании существующего конфигурационного файла для нового дерева каталогов исходного кода ядра, необходимо активизировать и об- новить конфигурацию с помощью команды: make oldconfig Кстати, перед сборкой ядра эту команду также необходимо выполнить. После того как конфигурация ядра выполнена, можно выполнить сборку с помощью команды: make В отличие от предыдущих серий ядер, в версии 2.6 больше нет необходимости выполнять команду make dep перед сборкой ядра, так как создание дерева зависи- мостей выполняется автоматически. Также не нужно указывать цель сборки, напри- мер bzlmage, как это было необходимо для более ранних версий. Правило, записан- ное в файле с именем Makefile, которое используется по умолчанию, в состоянии обработать все! |