Лишнему в ядре не место

Алексей Федорчук
3 сентября 2008 г

До сих пор все конкретные примеры конфигурирования ядра относились к дистрибутиву Zenwalk. Штатный метод его загрузки — использование инициирующего диска в памяти, initrd. Однако на более-менее стандартном «железе» и при использовании одной из общепринятых файловых систем в качестве корневой ядро с дистрибутивного носителя, устанавливаемое по умолчанию, может загрузиться и без него — правда, при этом потеряются стартовые красоты типа сплэш-картинки.

Для загрузки ядра «напрямую», без initrd, всего-то и требуется, чтобы в него была жёстко (не модулями!) встроена поддержка корневой файловой системы и дискового контроллера, на котором она расположена. И для спокойствия души это следует проконтролировать. Заодно можно избавиться от поддержки ряда устройств, отсутствующих в подавляющем большинстве компьютеров — правда, большинство из них и так включены как модули, и вроде бы есть не просят, но зачем держать у себя заведомо ненужные вещи?

Кроме того, на современной машине, часто имеющей в своём составе только SATA-накопители (в том числе и SATA-приводы оптических дисков), открывается уникальная возможность освободить ядро и от поддержки массы морально устаревших чипсетов и интерфейсов, тем самым существенно (процентов на 20-25%) сократив объем файла образа ядра.

Для чего это нужно? Теоретически небольшое ядро должно не только быстрее грузиться (во что свой вклад внесёт и отсутствие initrd), но и быстрее выполнять свои обязанности, потребляя при этом меньше ресурсов (в первую очередь памяти — мы ведь помним по первой заметке, что отличие ядра от прочих программ в том и состоит, что оно должно всегда находиться в оперативной памяти).

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

А вот что касается быстродействия… В старину, когда компьютеры были медленными, а памяти в них было мало, такой эффект действительно имел место быть, и автор этих строк неоднократно утверждал, «что всё это истина».

Однако прошли годы, формальное быстродействие компьютеров возросло во многие разы, объемы оперативной памяти — на порядки. И нынче, если разницу в скорости загрузки ядра большого и ядра маленького ещё можно увидеть невооружённым глазом, то на реальных задачах она будет видна разве что в телескоп, обращенный к «Пяти звёздочкам».

При этом следует учесть, что понятия ядра большого и малого следует трактовать в разумных пределах: образ умолчального ядра из дистрибутива Zenwalk имеет размер несколько менее 2 Мбайт, а самое аскетическое ядро для полнофункциональной системы общего назначения даже самому аскетичному дзэн-буддистскому анахорету вряд ли удастся вписать менее чем в один мегабайт. И разница между ними на фоне нескольких гигабайт системной памяти кажется ничтожной.

И тем не менее, занятие по оптимизации ядра не такое уж бессмысленное, как может показаться.

Во-первых, в некоторых случаях оно действительно может дать некоторый прирост быстродействия, а упрощение любой системы всегда снижает её ненадёжность.

Во-вторых, при этом приобретается опыт, который может пригодиться в ряде ситуаций (со временем некоторые из них мы рассмотрим).

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

Для начала убедимся, что в нашем ядре имеется всё, что нужно для его автономной, без initrd, загрузки. И тут перво-наперво нам потребуется пункт Device drivers из главного меню скрипта конфигурирования (рис. 1).

kern-21Рис. 1. Находим драйверы устройств…

В развернувшемся подменю (рис. 2) взгляд сразу останавливается на пунктах Block devices и Misc devices.

kern-22Рис. 2. … и начинаем со второстепенных

Раскрыв первый из них (рис. 3), мы можем освободиться от поддержки флоппи-дисковода и еще некоторых столь же нужных устройств, заодно проверив, что поддержка устройств loopback-типа наличествует — в виде модуля, как на приведенной картинке, или встроенной. Она подчас необходима — например, для монтирования образов CD/DVD, — но необходима достаточно редко, и потому модульная её поддержка предпочтительна.

kern-23Рис. 3. Убираем поддержку ненужных блочных устройств…

Также не лишней может оказаться поддержка низкоскоростных USB-устройств и пакетной записи на CD/DVD (запись в формате UDF) — разумеется, модулями, ибо и то, и другое отнюдь не предмет каждодневого назначения. А вот поддержку RAM-дисков, коль скоро мы решили обходиться без initrd, искореняем как класс, полностью (рис. 4).

kern-24Рис. 4. … в том числе RAM-дисков

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

kern-25Рис. 5. Поддержка лишних лаптопов нам тоже ни к чему

Теперь поднимаемся в меню на уровень выше и приступаем к главному делу, требующему особого внимания.

Зона особого внимания концентрируется в трёх последовательных пунктах (см. рис. 2):

ATA/ATAPI/MFM/RLL support
SCSI device support
Serial ATA (prod) and Parallel ATA (experimental) drivers

До недавнего времени главным из них был первый, ибо все основные накопители в настольных машинах подсоединялись к IDE-разъёмам (ныне за ними закрепилось погоняло — накопители с PATA-интерфейсом). Устройства SCSI выполняли сугубо вспомогательные функции — под них маскировались флэшки, встроенные накопители цифровых камер, а одно время и пишущие CD/DVD-приводы. Что же до SATA-дисков, то первое после своего появления время их поддержка обеспечивалась опциями пункта первого (хотя сами они номенклатурно тоже примазывались к славе SCSI-устройств).

Ныне совместная поддержка SATA- и PATA-накопителей обеспечивается так называемой подсистемой libdata SATA, использующей подсистему SCSI (таким образом, PATA-диски и компакт-приводы также попали в SCSI-номенклатуру), в результате чего на первые роли при конфигурировании вышли пункты 3 и 2. Соответственно, с них-то мы и начнём.

В пункте про SCSI-устройства нам, по большому счёту, необходимы только такие опции:

  • общая поддержка SCSI-устройств (SCSI device support);
  • поддержка SCSI-винчестеров (SCSI disk support);
  • поддержка оптических накопителей SCSI (SCSI CDROM support).

Причём все они должны быть встроены в ядро (рис. 6).

kern-26Рис. 6. Поддержка SCSI — всё, что нужно для счастья

Если наша система загружается с аппаратного SATA RAID, аналогично, видимо, нужно поступить с пунктом RAID Transport Class — я этого не проверял по причине неиспользования аппаратных RAID-массивов, а подсказка по этому пункту словообилием не отличается.

Наконец, в некоторых случаях может потребоваться еще и драйвер родовых устройств SCSI (т.н. SCSI generic — sg); соответствующую опцию подключаем в качестве модуля.

В результате всех этих действий в итоговом файле .config по завершении настройки должны присутствовать такие строки:

CONFIG_SCSI=y
CONFIG_SCSI_DMA=y
...
CONFIG_BLK_DEV_SD=y
...
CONFIG_BLK_DEV_SR=y
...
CONFIG_CHR_DEV_SG=m

При использовании аппаратного RAID к ним, видимо, добавится строка

CONFIG_RAID_ATTRS=y

И это всё, что необходимо для счастья: прочие опции, включая бессчетное число низкоуровневых SCSI-драйверов (SCSI low-level drivers), можно безбоязненно отключить.

Итак, верхний уровень обслуживания SCSI-номенклатуры включён. Тперь озаботимся потребностями уровня более низкого — то есть собственно SATA-устройств, ради которых всё и затевалось. Для чего, открыв пункт Serial ATA (prod) and Parallel ATA (experimental) drivers (рис. 7), начинаем убирать в нём поддержку всех чипсетов и контроллеров, кроме имеющихся в наличии. В нашем случае это будет встроенная в ядро поддержка чипсетов Intel, при использовании чипсетов Nvidia или ATI/AMD аналогично следует поступить с ними. И, разумеется, надо оставить поддержку родового класса ATA-устройств (Generic ATA support).

kern-27Рис. 7. Оставляем только необходимые устройства SATA

А что же пункт поддержки устройств ATA/ATAPI/MFM/RLL? Он нам больше не нужен, и поэтому мы отключаем его напрочь.

Вообще-то в пункте Device Drivers из главного меню есть где порезвиться шаловливым ручонкам. Однако это рукоблудие мы оставим для другого раза. А в контексте настоящего разговора остановимся ещё только на одном его подпункте — поддержке многодисковых устройств (Multiple devices driver support), под которыми понимаются программные RAID-массивы и тома системы LVM. Если ни то, ни другое не используется, то весь этот подпункт можно истребить на корню. Если используется как загрузочное устройство — встроить в ядро. Если загрузки ни с того, ни с другого типа устройств не намечается — можно подключить и как модули. Причем для soft-RAID в любом случае достаточно ограничиться поддержкой массивов только того уровня, который будет использоваться на деле — например, RAID-0 под файловую систему для домашнего каталога (рис. 8).

kern-28Рис. 8. Модульная поддержка RAID-0

Покончив с устройствами, которые потенциально могут выступать в качестве загрузочных, и заодно искоренив устройства заведомо ненужные, переходим к не менее важному моменту — поддержке файловых систем, за которую отвечает одноименный ( File systems) пункт главного меню (рис. 9).

kern-29Рис. 9. Поддержка файловых систем

Как уже говорилось, для «лобовой» загрузки системы необходимо, чтобы в ядро была жестко встроена поддержка файловой системы, выступающей в качестве корневой — иначе неизбежно сообщение о невозможности смонтировать последнюю с переходом к «ядерной панике».

Убеждаемся, что в этом отношении всё нормально: в дистрибутивном ядре Zenwalk’а встроены все пригодные для несения корня файловой иерархии файловые системы —ext2/ext3, ReiserFS, XFS, лишь JFS задействована как модуль. И теперь можем как отключить заведомо ненужные файловые системы (я, например, отключаю вышеупомянутую JFS и всё, связанное с NFS), так и подключить дополнительные (так, для любителей экспериментов доступна экспериментальная поддержка файловой системы ext4).

Второстепенные файловые системы, типа ISO-9660 и UDF для оптических дисков и FAT/VFAT — для накопителей типа флэшек, целесообразно подключить как модули. Вероятно, это верно и в отношении NTFS — за неактуальностью изучением этого вопроса не занимался.

А вот что заслуживает отдельного внимания — это поддержка псевдофайловых систем (или файловых псевдо-систем, иногда не вполне точно называемых виртуальными). Здесь следует позаботиться о включении в ядро поддержки таких файловых систем, как /proc и всех ее разновидностей, sysfs и собственно виртуальной файловой системы в оперативной памяти (Virtual memory file system), именуемой также shm fs или tmpfs (рис. 10); впрочем, обычно это уже сделано по умолчанию.

kern-30Рис. 10. Поддержка псевдо-файловых систем

Наконец, последнее, что имеет отношение к файловым системам — пункт Native language support. Он не влияет ни на локаль, ни на поддержку национальных языков системой и приложениями, а отвечает только за возможность восприятия имен файлов, записанных в наборах символов, отличных от ASCII. Если следовать советам резонных людей и не иметь дела с файлами, именованными чем бы то ни было кроме чистой «латиницы» (они будут прочитаны в любом случае), этот пункт можно просто игнорировать. Но, поскольку мы живём не в безвоздушном пространстве, время от времени устройства с такими файлами будут попадать в наши руки. Поэтому весь этот мункт целесообразно включить как модуль, и модульно же задействовать отдельные его подпункты, имеющие практическое значение в наших условиях, как то:

  • Codepage 437 (США и Канада);
  • Codepage 866 («альтернативная» кириллица);
  • Windows CP1251 (кодировка сами знаете чего);
  • ASCII — чисто для порядка;
  • NLS ISO 8859-1, или Latin 1 — аналогично;
  • NLS ISO 8859-5 (бывшая кириллица по ГОСТу, используемая в Solaris) — на всякий пожарный случай;
  • NLS KOI8-R (до недавнего времени самая распространенная кодировка кириллицы в свободных Unix’ах и Интернете);
  • NLS KOI8-U/RU (её аналог для братских Украины и Белоруссии);
  • NLS UTF-8 — тот самый универсальный железный конь, который постепенно идёт на смену разномастным табунам национальных чарсетов.

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

Для начала — про обещанное уменьшение объема файла образа: да, что есть, то есть: если дистрибутивное ядро Zenwalk’а тянуло на 1,9 Мбайт (а его модификация, собранная в прошлой заметке, была почти такой же), то нынче мы видим образ размером 1,6 Мбайт. За счёт этого мы вправе ожидать некоторого выигрыша в скорости загрузки машины, не так ли?

Нет, не так. Система о нашем праве не подозревает и грузится с завидно постоянной скоростью, вне зависимости от ядра (замерялось секундомером от выбора в меню GRUB до предложения авторизоваться, для умолчального ядра, ядра, собранного в прошлой заметке, и ядра нынешнего): около 13 секунд при загрузке в чистую консоль (runlevel по умолчанию — 3) и примерно 20 секунд — при загрузке в gdm (runlevel 4).

Неожиданный результат? Если подумать, то нет. Потому что приходится констатировать: на современных машинах разница в скорости загрузки собственно ядрер различного размера столь мала, что полностью нивелируется временем подгрузки модулей, запуска стартовых серверов, обращения к DHCP, синхронизации с серверами точного времени по NTP. Не говоря уже о времени старта X-сервера и менеджера сеансов при runlevel 4…

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

Возражение отметается с негодованием простым вопросом: а за каким зелёным нам нужна загрузка без необходимых модулей, сети и прочих требуемых при реальной работе сервисов? Вот если бы мы были участниками соревнования на скорость старта машины — тогда да; но ведь таковые пока в олимпийскую программу не включили. А ядро нами пересобиралось вроде как для ускорения повседневной деятельности.

Кстати, а ускорилась ли эта самая повседневная деятельность на новом ядре? Проверяем, в качестве мерила её взяв всё ту же компиляцию ядра (ну есть люди, для которых компиляция составляет если не всю работу, то существенную её часть). И приходим к неутешительным результатам: 15:22 при выполнении команды make all и 7:56 — make -j4 all.

Сравниваем это с показателями из предыдущей заметки и делаем вывод: сокращение объема ядра за счет отключения неиспользуемых опций в современных условиях не даёт никакого прироста быстродействия.

Выходит, что всё, чем мы занимались, зря? Не совсем. Во-первых, мы всё-таки получили (правда, ещё в прошлой заметке) доступ к памяти более 4 Гбайт, что можно считать реальным результатом (в предположении, конечно, что раз мы накупили столько памяти, то она нам на самом деле нужна — например, для работы с очень большими изображениями).

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

В-третьих же, мы приобрели уверенность в том, что если те самые веские причины появятся, пересобрать ядро будет вполне по силам. И это, пожалуй, самое главное…