17.08.13

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

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





Для загрузки ядра "напрямую", без initrd, всего-то и требуется, чтобы в него была жестко (не модулями!) встроена поддержка корневой файловой системы и дискового контроллера, на котором она расположена. И для спокойствия души это следует проконтролировать. Заодно можно избавиться от поддержки ряда устройств, отсутствующих в подавляющем большинстве компьютеров — правда, большинство из них и так включены как модули, и вроде бы есть не просят, но зачем держать у себя заведомо ненужные вещи?
Кроме того, на современной машине, часто имеющей в своём составе только SATA-накопители (в том числе и SATA-приводы оптических дисков), открывается уникальная возможность освободить ядро и от поддержки массы морально устаревших чипсетов и интерфейсов, тем самым существенно (процентов на 20-25%) сократив объем файла образа ядра.
Для чего это нужно? Теоретически небольшое ядро должно не только быстрее грузиться (во что свой вклад внесёт и отсутствие initrd), но и быстрее выполнять свои обязанности, потребляя при этом меньше ресурсов (в первую очередь памяти — мы ведь помним по первой заметке, что отличие ядра от прочих программ в том и состоит, что оно должно всегда находиться в оперативной памяти).
В первый эффект действительно можно было бы поверить, хотя он не так уж принципиален: при нормальной эксплуатации настольной Unix-машины (о ноутбуках здесь речь не идёт, у них свои требования) в домашних условиях она загружается не чаще, чем раз в сутки, а в служебных — так и много реже.
А вот что касается быстродействия... В старину, когда компьютеры были медленными, а памяти в них было мало, такой эффект действительно имел место быть, и автор этих строк неоднократно утверждал, "что всё это истина".
Однако прошли годы, формальное быстродействие компьютеров возросло во многие разы, объемы оперативной памяти — на порядки. И нынче, если разницу в скорости загрузки ядра большого и ядра маленького еще можно увидеть невооруженным глазом, то на реальных задачах она будет видна разве что в телескоп, обращенный к "Пяти звёздочкам".
При этом следует учесть, что понятия ядра большого и малого следует трактовать в разумных пределах: образ умолчального ядра из дистрибутива Zenwalk имеет размер несколько менее 2 Мбайт, а самое аскетическое ядро для полнофункциональной системы общего назначения даже самому аскетичному дзэн-буддистскому анахорету вряд ли удастся вписать менее чем в один мегабайт. И разница между ними на фоне нескольких гигабайт системной памяти кажется ничтожной.
И тем не менее, занятие по оптимизации ядра не такое уж бессмысленное, как может показаться.
Во-первых, в некоторых случаях оно действительно может дать некоторый прирост быстродействия, а упрощение любой системы всегда снижает её ненадёжность.
Во-вторых, при этом приобретается опыт, который может пригодиться в ряде ситуаций (со временем некоторые из них мы рассмотрим).
И в-третьих, это действительно спорт — увлекательный и азартный. Так что продолжим наши игры с ядром...
Для начала убедимся, что в нашем ядре имеется всё, что нужно для его автономной, без initrd, загрузки. И тут перво-наперво нам потребуется пункт Device drivers из главного меню скрипта конфигурирования (рис. 1).

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

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

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

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

Рис. 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).

Рис. 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).

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

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

Рис. 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); впрочем, обычно это уже сделано по умолчанию.

Рис. 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 Гбайт, что можно считать реальным результатом (в предположении, конечно, что раз мы накупили столько памяти, то она нам на самом деле нужна — например, для работы с очень большими изображениями).
Во-вторых, отрицательный результат — это тоже результат. Который говорит, что без очень веских причин не стоит заниматься самодеятельностью, а надо брать то, что дают: майнтайнеры дистрибутивов тоже не лаптем щи хлебают, и в большинстве случаев собранные ими ядра более чем подходят для практической работы.
В-третьих же, мы приобрели уверенность в том, что если те самые веские причины появятся, пересобрать ядро будет вполне по силам. И это, пожалуй, самое главное...