Журнал LinuxFormat - перейти на главную

LXF73:Вскрываем ядро

Материал из Linuxformat
Перейти к: навигация, поиск

Содержание

Вскрываем ЯДРО

Знаток ядра Грег Кроа-Хартман (Greg Kroah-Hartman) заглянул внутрь последнего ядра, чтобы показать вам, что заставит ваш ПК работать на полную мощность.

«С технической точки зрения, я верю, что ядро будет сохранять те же черты, а всё действительно интересное будут происходить в пространстве пользователя» Линус Торвальдс (Linux Torvalds), 2001 год.

Несмотря на то, о что думает доброжелательный «хранитель сообщества», ядро Linux демонстрирует, что оно является очень интересной и важной частью системы. На самом нижнем уровне, ядро отвечает за нормальную работу аппаратного обеспечения (того, на что вы, обычно, тратите много денег). Это позволяет вам запускать ваш любимый web-браузер, клиент электронной почты, программу IRC, трехмерную «стрелялку» и т. д., одновременно. Без него не было бы Linux.

С появлением ядер серии 2.6 в конце 2003 года, разработчики ядра предоставили миру предельно стабильную, поддерживающую многопроцессорность операционную систему, работающую быстрее любой другой ОС на широком спектре типов процессоров. Какая еще операционная система может масштабироваться для использования что в крохотном MP3-плеере или встроенном в робота контроллере, что в крупнейших суперкомпьютерах (некоторые с более чем 512 процессорами)? Все это возможно только благодаря самому ядру: остальные компоненты, работающие на более высоких уровнях — лишь надстройки над ним.

«Продвинутые» особенности ядра

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

Ядро 2.6.13 было выпущено 28 августа 2005 года. В него было добавлено около 200 000 новых строк кода, и столько же строк было модифицировано. большинство изменений направлено на исправление ошибок и повышение стабильности; но 2.6.13 также содержит несколько новых и интересных функций, которые могут сделать использование ПК под управлением Linux заметно лучше, причем для каждого.

для разработчиков

Inotify

для такой простой концепции (позволить пользователю знать обо всех изменениях, которые выполняются в файлах), Inotify было уделено достаточно большое внимание. Все сводится к одному инструменту — Beagle. Beagle — это приложение Gnome, которое позволяет вам искать на вашем компьютере практически всё — независимо от типа файла, в любом месте вашей системы. С Beagle вы можете легко находить документы, письма, историю браузера, диалоги IRC и многое другое (см. http://beaglewiki.org).

если говорить об инструментах, подобных Beagle, то чтобы они работали эффективно и знали, какие изменения произошли на вашем Пк в последнее время, им нужен некоторый метод получения информации обизменениях в системе, альтернативный сканированию всего жесткого диск каждый раз, когда вы выполняете поиск. Inotify - это часть ядра, которая предоставляет эту функциональность для пользовательских запросов. Прежние версии ядер имели механизм, называемый Dnotify, но он был неуклюж и работал недостаточно хорошо. Inotify решает все проблемы, присущие Dnotify, и позволяет наблюдать за отдельными файлами наряду с целыми деревьями каталогов.

Kexec и Kdump

Функции Kexec и Kdump уже продолжительное время включаются в ядра дистрибутивов, но только сейчас они нашли место в основной ветви ядра. Kexec позволяет пользователям загружать и запускать другое ядро Linux, не требуя перезагрузки работающего ядра в BIOS — благодаря этому экономится много времени при рестарте. Kdump использует эту особенность, чтобы снимать дампы ядра системы в случае краха, позволяя разработчикам потом проводить анализ системы.

Пользователи, которые хотят минимизировать перерыв в работе при обновлении ядра (никто не хочет ждать и изучать сообщения BIOS о повторной проверке шины SCSI) могут использовать Kexec, чтобы свести время простоя к минимуму.

для пользователей

bind и unbind

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

Версия 2.6.13 дает пользователю возможность связывать (bind) отдельные устройства с определенным драйвером и освобождать (unbind) их. Это позволяет администраторам больших систем блокировать отдельные устройства, загрузить новый драйвер и затем связать его с этим устройством, без отсоединения других устройств, которые управляются первоначальным драйвером. Просто представьте себе всю суету, присущую обновлению большой дисковой системы, и вы поймете, что это Вещь с большой буквы.

Devfs мертва

Файловая система devfs отключена с версии 2.6.13. Это было сделано для того, чтобы перед полным удалением devfs дать пользователям, которые все еще используют ее, некоторый «переходный период». Тем, кто до сих пор использует devfs, следует перейти на udev. Может показаться странным, что мы считаем удаление старой неиспользуемой системы шагом вперед, однако правда заключается в том, что devfs довольно отвратительна. Она чрезвычайно раздута, затрудняет чтение кода ядра, и чем раньше мы от нее избавимся, тем лучше.

Улучшенные очереди ввода-вывода

Планировщик ввода-вывода, реализующий алгоритм «Completely Fair Queuing» (полностью справедливая очередь) и являющийся, возможно, лучшим на сегодняшний день, был усовершенствован, чтобы обеспечить более эффективное управление интервалами времени (timeslices). Также в этой области были добавлены два новых системных вызова, позволяющие пользовательским программам изменять приоритеты ввода-вывода различных процессов в системе. Это позволит администраторам более легко подстраивать производительность различных программ.

для встроенных систем

Xtensa

В ядро 2.6.13 была добавлена полностью новая архитектура ЦПУ: Xtensa. Xtensa — это встраиваемый 32-битный процессор, который может быть размещен в SoC-системах с широким спектром встроенных функций - телефоны, PDA, TV-приемники (set-top boxes), MP3-плееры, и прочие подобные устройства.

С этой новой архитектурой Linux теперь поддерживает 24 основных типа процессоров, и еще большее число подтипов. Хорошая новость для встроенных устройств. На заметку тем, кто любит хвастаться - Linux поддерживает больше различных процессоров, чем любая другая операционная система (пожалуй, NetBSD может составить ему конкуренцию в этой области, — прим. ред.). добавьте этот факт к тому, что Linux поддерживает больше внешних устройств, чем любая другая ОС, и станет понятно, почему Linux считается наиболее гибким и мощным ядром.

Выполнить «на месте»

Файловая система ext2 теперь позволяет программам исполняться «на месте» (inplace) вместо предварительной загрузки в оперативную память. Это очень полезно на встроенных системах, которые имеют ограниченный объем ОЗУ, но большой объем ПЗУ или Flash-памяти — таких как мобильные телефоны.

Регулируемая частота

Мы установили частоту прерываний ядра 2.6 на уровне 1000 Гц, что порождало некоторые жалобы от пользователей. Теперь эта частота может быть установлена на этапе компиляции в три различных значения, в зависимости от того, как именно будет использоваться компьютер:

  • 100 Гц. Это лучшее решение для маломощных устройств.
  • 200 Гц. Идеально для серверов.
  • 1000 Гц. Оптимально для настольных систем, или других систем, требующих быстрой, интерактивной реакции на события.
«Добровольно-вытесняющая» многозадачность

Те пользователи, кто чувствовал себя некомфортно с ядром, реализующим вытесняющую многозадачность (full pre-emption), теперь получили другую опцию: «добровольно-вытесняющую» многозадачность (voluntary pre-emption). Она позволяет ядру прерывать процессы в ряде известных «точек явного прерывания» (explicit preemption points), которые выбираются таким образом, чтобы уменьшить латентность перепланировки различных программ. Приложения получают более низкое время отклика, хотя это достигается за счет некоторого снижения производительности.


Начнем, пожалуй

Вашим программистским пальцам не терпится начать? Углубимся в новое ядро и рассмотрим две самые замечательные функции.

Итак, мы охватили множество интересных теоретических вопросов — и вы, вероятно, ждете практических способов, как можно начать использовать эти новые возможности ядра прямо сейчас.

Ну что ж, начнем: если вы хотя бы в общих чертах разбираетесь в языке C, вы можете «сразиться» с Inotify, изучив новые функции ядра для добавления и удаления точек слежения (watches) за файлами.

Если же вы не настолько бесстрашны, читайте врезку «Привязка драйверов вручную», чтобы узнать, как можно настроить драйвер, чтобы он автоматически подгружался при подключении устройств типа USB Flash.


Внутри Inotify

Функция Inotify добавляет три новых системных вызова:

int inotify_init(void);
int inotify_add_watch(int fd, const char
*path, __u32 mask);
int inotify_rm_watch(int fd, __u32 mask);

Выступая как замена для Dnotify, Inotify работает с сущностями, называемыми точками слежения (watch). Watch — это пара «объект-маска», которая описывает событие, о наступлении которого пользователь хотел бы получать уведомления. Объект — это файл или каталог (представлен дескриптором открытого файла), а маска — битовое описание событий. Р азличные типы событий, которые можно отслеживать, приведены ниже:

  • IN_ACCESS: к файлу был доступ;
  • IN_MODIFY: файл был изменен;
  • IN_ATTRIB: были изменены метаданные файла;
  • IN_CLOSE_WRITE: файл был закрыт с записью всех изменений;
  • IN_CLOSE_NOWRITE: файл был закрыт без записи изменений;
  • IN_OPEN: файл был открыт;
  • IN_MOVED_FROM: файл был перемещен из положения X;
  • IN_MOVED_TO: файл был перемещен в положение Y;
  • IN_CREATE: файл в каталоге был создан;
  • IN_DELETE: файл в каталоге был удален;
  • IN_DELETE_SELF: объект наблюдения был удален.

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

Чтобы создать точку слежения и зарегистрировать ее в ядре, нужно получить дескриптор с помощью вызова функции inotify_init(), например, так:

int fd = inotify_init();

Затем, используя этот новый файловый дескриптор, вы можете добавить watch к нему, используя функцию inotify_add_watch():

int wd = inotify_add_watch(fd, path, mask);

где fd — дескриптор, возвращенный функцией inotify_init(); path — путь к файлу или каталогу, за которым вы хотели бы наблюдать; и mask — тип события, которое вы будете отслеживать.

Функция inotify_add_watch() возвращает указатель на дескриптор watch, который должен быть сохранен, чтобы правильно выполнить очистку.

Чтобы удалить точку слежения, которая уже создана, используйте функцию inotify_rm_watch():

int retval = inotify_rm_watch(fd, wd);

где fd — первоначальный файловый дескриптор, возвращенный inotify_init(), и wd — дескриптор watch, возвращенный при вызове inotify_add_watch().

как только watch будет зарегистрирован в ядре, получить события можно простым вызовом read() для файлового дескриптора, соответствующего этой точке слежения (и всем другим точкам слежения, которые были зарегистрированы для данного процесса).

Структура данных, которые могут быть получены от ядра, описывается следующим кодом на C:

struct inotify_event {
__s32 wd; /* дескриптор точки слежения */
__u32 mask; /* маска */
__u32 cookie; /* cookie для синхронизации событий */
__u32 len; /* длинна имени (с завершающим нулем) */
char name[0]; /* место для размещения имени */
};

Названия полей говорят сами за себя, за исключением поля cookie. если это поле имеет ненулевое значение, это значит, что для данного объекта возникло несколько событий одновременно. В качестве примера можно привести переименование файла. если мы будем наблюдать за каталогом, в котором будет переименован файл (mv foo baz), произойдут два события: IN_MOVE_FROM и IN_MOVE_TO. Оба они будут иметь одинаковое значение cookie, что позволит пользовательской программе скоординировать эти события.

Файловый дескриптор, возвращаемый inotify_init(), позволяет использовать вызовы select() и poll(), так что блокирующий вызов read() не является необходимым.

когда программа, удерживающая первоначальный файловый дескриптор, возвращенный inotify_init(), завершает работу (или если этот файловый дескриптор будет закрыт), все точки слежения, которые были зарегистрированы ядром, уничтожаются и очищаются соответствующим образом.

В качестве очень простого примера программы, который показывает, как регистрировать события и читать их по мере наступления, посмотрите пакет inotify-utils, который можно найти на странице http://www.kernel.org/pub/linux/kernel/people/rml/inotify/utils.

Поиски стабильности

Лоскутные заплатки, «горячие» исправления, два дерева становятся одним… Как процесс разработки ядра изменился в лучшую сторону за последние пять лет.

«Проблема, существующая в настоящее время, заключается в том, я думаю, что качество ядра не настолько высоко, как должно быть. В каждом очередной версии мы довольно часто откатываемся назад из-за новых ошибок». Эндрю Мортон (Andrew Morton), OSDL, август 2005 г.

До того, как было выпущено ядро 2.6, было достаточно легко определить, какая ветвь разработки ядра была стабильной, а какая — экспериментальной: вторая цифра в номере версии говорила обо всем. Четные номера были стабильными, нечетные — экспериментальными, то есть ветви 2.0, 2.2 и 2.4 были стабильными, а 2.1, 2.3 и 2.5 — для разработчиков. Каждая ветвь имела свои индивидуальные выпуски: например, 2.4.12 и 2.4.13 — это различные выпуски в рамках ветви 2.4.

Все это изменилось: теперь есть только одна ветвь — ветвь 2.6.

Путаница с обратным портированием

Раньше, в процессе разработки ветки 2.5 (с конца 2001 до 2003 года), в экспериментальное ядро был добавлен ряд важных изменений. Эти изменения были столь хороши, что как пользователи, так и дистрибьюторы в равной степени хотели их использовать, даже учитывая, что ядро было все еще нестабильно (то есть имело нечетный номер 2.5).

Крупные поставщики дистрибутивов поручили своим инженерам портировать эти функции в свои, основанные на ветви 2.4, ядра. Поставщикам приложений нужно было теперь определять, какие новые особенности стали доступны в этих «самописных» ядрах.

Проблема возникла, когда пользователи этих дистрибутивов оказались вынуждены доверять своим дистрибьюторам поддержку «лоскутных» ядер, поскольку сообщество разработчиков не желало ничего делать с ними. Спустя некоторое время такая поддержка стала очень сложной задачей и превратилась в настоящий инженерный кошмар (просто сравните деревья исходных кодов ядер Red Hat Enterprise Linux 3 и SUSE Linux Enterprise Server 8 с деревом kernel.org, на котором они основаны).

Спустя почти год с момента разработки серии ядер 2.6, разработчики ядра Linux встретились, чтобы обсудить, что они хотели бы сделать для следующей экспериментальной серии (2.7).

Оказывается, все разработчики в действительности хотели бы оставить все как есть, и не желали столкнуться с такой же проблемой, как путаница с обратным портированием в ядре 2.4, которая, между прочим, «помогла» растянуть цикл разработки 2.6 более чем на два года.

Когда они собрались за круглым столом и посмотрели, как работает ядро 2.6, они решили оставить его единственной ветвью и объявить, что это будет одновременно и стабильное, и экспериментальное ядро.

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


устаревшие…

Так что разработчики ядра начали выпускать новые ядра каждые два или три месяца, с относительно большим числом изменений в каждом. Это удавалось в течение нескольких месяцев, пока люди не осознали, что если для определенного выпуска ядра возникала проблема безопасности или необходимость в исправлении какой-либо ошибки, то могло пройти несколько месяцев, прежде чем появлялось обновленное ядро, устраняющее эти проблемы. И когда новая версия ядра выходила в свет, появлялся следующий ряд изменений, некоторые из которых также требовали исправления ошибок…

Чтобы обойти эту проблему, была создана «стабильная» (-stable) ветвь версий ядра со следующей идеей: двое разработчиков каждые две недели (или около того) подготавливают небольшой выпуск ядра и ничего не добавляют, кроме очевидных исправлений ошибок и проблем безопасности. Эти выпуски маркировались четвертым символом в номере версии, которые теперь имели формат «2.6.X.Y», где Y — номер стабильной версии ядра. Например, ядро 2.6.13.1 — это первый «стабильный» выпуск на ветви 2.6.13.

Подстройка версий

Все это было замечательно, но промежуток между стабильными версиями ядра, казалось, все возрастал. Потребовался один месяц, чтобы пройти путь от 2.6.1 до 2.6.2, но теперь мы должны ждать почти четыре месяца между версиями. кроме того, не похоже, чтобы кто-то действительно тестировал промежуточные версии «кандидатов в релиз» (release candidate), которые выпускались между основными выпусками.

кое-что изменилось. Вот как выглядит этот процесс сейчас: в первую неделю после выхода ядра все ответственные разработчики различных подсистем подают свои основные изменения для объединения. Это могут быть новые функции, исправления ошибок или переписанные секции кода. После этого выпускается настоящий «release candidate», и все разработчики усаживаются и работают над приданием выпуску стабильности. Ошибки отслеживаются на сайте http://bugzilla.kernel.org, а разработчикам временно запрещается добавлять новые функции. В течение следующих нескольких недель ядро становится достаточно стабильным, чтобы выпустить его в свет, и весь цикл начинается сначала. По идее, это должно сохранять короткий цикл разработки и обеспечивать лучшее выявление любых возможных ошибок.


Взгляд в будущее

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

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

Первый — это флаг, который устанавливается всякий раз, когда загружается модуль с закрытым кодом. Это позволяет сообществу разработчиков легко опознать и отказаться от поддержки всех пользователей с такими модулями, поскольку в этом случае невозможно точно определить, где находится источник проблемы (в секции с закрытым кодом или в остальной части ядра).

Почти все новые функции ядра явно помечаются компилятором как доступные только из открытого GPL-кода. Этот подход запрещает любому модулю с закрытым кодом использовать их интерфейсы, и постепенно будет «душить» все их функциональные возможности. Хороший пример этого - базовые интерфейсы для драйверов и sysfs, которые недоступны для модулей с закрытым кодом.

буква закона

Видимо, этого было недостаточно, чтобы переубедить заблудших программистов, и разработчиками ядра был подан судебный иск в отношении поставщиков Linux, создающих закрытые модули (поскольку повторное распространение закрытых модулей незаконно, как определено в лицензии GPL). Оказалось, что это весьма эффективно, поскольку никакие Linux-компании не хотели иметь дело с законом в этой связи.

Теперь перейдем к третьему средству сдерживания. Основные претензии по поводу процесса разработки ядра предъявлялись производителями драйверов, являвшимися внешними по отношению к дереву исходного кода ядра и содержащими проприетарные наработки — такими, например, как NVIDIA.

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

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

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

Чтобы узнать подробное описание причин, по которым Linux не может сохранять стабильный интерфейс ядра, прочитайте документ http://lxr.linux.no/source/Documentation/stable_api_nonsense.txt.

Появится ли когда-либо 2.7?

В прошлом, когда разработчики ядра решили ограничиться одним деревом исходного кода, упоминался ряд серьезных изменений, которые могли бы поспособствовать ответвлению 2.7 от основного ядра. две из таких особенностей — это четырехуровневые таблицы страниц и переписанная подсистема времени ядра. Однако дерево 2.6 сейчас уже имеет четырехуровневые таблицы (позволяющие использовать терабайты виртуальной памяти), а также имеется переписанный код для подсистемы времени ядра, ожидающий включения в будущие версии.

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

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


Персональные инструменты
купить
подписаться
Яндекс.Метрика