Самостройный Linux

Алексей Федорчук
2003-2004 гг

Этот материал сочинялся где-то на протяжении 2003 г. и по ряду причин так и остался неоконченным. Тем не менее, надеюсь, что кое-что здесь не утралито актуальности.

Я столько времени писал о всяких Linux’ах, а также Free- и прочих BSD, что однажды в голову закралась шальная мысль — а не замахнуться ли на сборку своей собственной системы? Той самой, которая станет очередной ступенькой на пути к недосягаемому идеалу? А что, и замахнемся, — ответил я себе, и, по мере сил и возможностей, приступил к изучению вопроса. Результаты вылились в нечто похожее на

Исторические изыскания

Оказалось, что я не одинок в своем стремлении: мысль стать обладателем собственной уникальной системы приходила в голову многим. В Сети обнаружились упоминания о двух проектах под значимыми названиями Linux from Scratch (что в данном случае я перевел бы как «Linux по сусекам скребеный», сокращенно — LFS) и BYO Linux (то есть нечто вроде — «Построй свой собственный Linux»).

Основоположник LFS — Герард Бикманс (Gerarg Beekmans). Устав, по его словам, от изобилия дистрибутивов, ни один из которых не удовлетворял его в полной мере, он решил собрать систему, в которой было бы только то, что он сам установил — и ни граном больше. Что и не замедлил претворить в жизнь.

Произведение Герарда назвать дистрибутивом в общепринятом понимании термина трудно. Это — не distribution на все случаи жизни, а, скорее, сборник рецептов, как, опираясь на исходники base Linux, построить собственную систему. Забегая вперед, замечу — построение это будет стопроцентно удачным только в том случае, если следовать писанию Герарда (знаменитой LFS Book, разные версии которой доступны и в русском переводе. И при этом пользоваться именно теми версиями базового софта, которые описаны им в соответствующей версии книги — в противном случае возможны всякие неожиданности.

Менее известен другой проект — BYOLinux. Начатый Джонатаном Торпом (Jonatan Thorpe) практически одновременно с LFS, ныне он прекратил свое развитие. Впрочем, описание Джонатана можно использовать как руководство к действию и до сих пор (с поправкой на версии софта, разумеется). Оно не столь детально, как LFS Book, однако, уже с силу этого, носит несколько более общий характер.

Однако выясняется, что исторически первым примером самостройного Linux’а был RockLinux Клиффорда Вольфа. Хотя и распространяется он обычно в виде прекомпилированных дистрибутивов, однако методически это — именно сборка «с нуля», дополненная системой автоматизированного обновления.

А вообще говоря, основоположник системного самосбора — не кто иной, как Линус Торвальдс: не из (собственноручно написанных) исходников ли собирал он свою первую Linux-систему? Впрочем, разработчики Free- и NetBSD также были вынуждены воссоздавать свои системы с нуля после запрета использования кода System V в BSD4.4…

Постановка проблемы

Как собирать Linux из исходников? Тут возникает извечная проблема курицы и яйца. Действительно, чтобы собрать компилятор, нужно как минимум иметь компилятор, а чтобы собрать шелл — требуется этот компилятор запустить (из командной строки шелла, разумеется). А и то, и другое требует библиотечных функций из главной системной библиотеки glibc. Которую, как нетрудно догадаться, также следует предварительно скомпилировать. Иными словами, как говорил мой бывший любимый начальник, «чтобы сварить суп из курицы, нужно как минимум иметь курицу». Что в данном случае означает — чтобы собрать работоспособную Linux-систему, ее уже нужно иметь в установленном (и работоспособном) виде.

Получается заколдованный круг, выход из которого — не столь очевиден. Линус на заре создания своей ОС решил эту систему просто: первые варианты Linux’а собирались под управлением другой операционной системы (волею судеб ею оказалась Minix знаменитого теоретика осестроения Энди Таненбаума). Однако ныне можно обойтись без этого, собирая Linux из Linux’а же.

Этим путем пошли и Герард, и Джонатан. То есть имея работоспособную Linux-машину в произвольном исполнении и полный набор исходников base Linux. Установленный дистрибутив может быть любым (при условии достаточной свежести компилятора gcc и библиотеки glibc, прочие компоненты не столь критичны). Ну и ядро родительской системы должно поддерживать все опции, желательные для системы дочерней (в первую очередь файловую систему, на которой предполагается разместить корень ее файловой иерархии).

По первоначальному методу Герарда сначала собирается (на отдельном дисковом разделе) некий самодостаточный минимум — bash, gcc, binutils и т.д., причем — статически слинкованные. Далее выполняется операция chroot и посредством этих программ собирается библиотека glibc. А потом — перекомпилируется, уже в динамически связанном виде, все ранее собранное хозяйство, плюс дособирается остальное из base Linux, ядро системы и средства загрузки. Причем позднее он перешел на двухэтапную сборку (т.н. метод pure LFS).

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

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

Мне же хотелось осуществить самострой системы а) идеологически чисто (то есть без заимствования компонентов родительской системы, б) из базовых компонентов произвольных (то есть наиболее свежих на текущий момент) версий (ведь не секрет, что многие из них обновляются достаточно часто), и, главное, в) на чистую машину. Не то чтобы у меня на винчестере не было какого-либо Linux’а. Более того, я даже осуществил самосбор, используя в разное время различные дистрибутивы (Gentoo, CRUX, Archlinux) дистрибутив в качестве родительских, — и осуществил успешно. Но вследствие этого успеха появилось желание разработать общий метод самосбора действительно с нуля (в том числе и на только что купленный компьютер).

Проблема казалась неразрешимой. Тут настало время вспомнить, как злые языки в нашей Партии (не подумайте плохого — геолого-поисковой) трактовали цитировавшуюся выше сентенцию нашего начальника. А толковали они ее в том смысле, что минимальным системным требованием для куриного супа является все же кошка. И, дабы «зарамсить проблему», я занялся поисками такой кошки.

Интуитивно было понятно, что собрать систему с нуля на голой машине можно было бы с помощью какого-либо LiveCD дистрибутива из числа расплодившихся в последнее время. Однако в большинстве из них декларировались в первую очередь легкость настройки и совместимость с оборудованием, а отнюдь не изобилие инструментальных средств. Однако и подходящий LiveCD нашелся быстро — некто lonix, сам собранный на базе LFS. Он комплектовался не только полным сборочным инструментарием, но и средствами настройки доступа к Сети — как через локалку, так (что было важно в моих условиях) и по модему: на тот случай, если в ходе сборки понадобится докачать какую-нибудь забытую мелочь.

Таким образом, Lonix идеально подходил на роль слесарного чемоданчика. Однако для этой цели подошел бы, вероятно, и knoppix. А также многие из более современных Live-систем…

Выбор компонентов

Это — второй вопрос системного самостроя. Очевидно, что в такой системе, как в рюкзаке хорошего туриста, не должно быть ничего лишнего (на то десятидисковые дистрибутивы имеются), но все необходимое — должно быть. То есть содержимое моего рюкзака должно обеспечивать не только загрузку системы, но и средства для ее наращивания, а также решения насущных практических задач. В результате сложился (пока у меня в голове) некоторый самодостаточный набор, который можно назвать Base Linux>.

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

В свою очередь, непременные компоненты также можно разделить на безальтерантивные и те, для которых возможен выбор из нескольких вариантов. В число первых попадают ядро Linux (еще бы — если бы у бабущки был… сами знаете что, она звалась бы дедушкой) и тесно связанный с ним комплекс утилит управления дисковыми разделами (linux-utils), загружаемыми модулями (modutils), обязательно задействуемыми файловыми системами (ext2fsprogs, procinfo и родственными, чисто волюртаристически я включил бы сюда и devfsd — без файловой системы устройств нынче не житье). Фактически нет выбора и для инструментов работы с файлами, от fileutils (в то время этот пакет еще не слился с sh-utils и textutils в единый coreutils) до архиватора tar и компрессоров gzip и bzip2 (причем последние два — не альтернатива, а неизбежность), утилит обработки текстов (grep, gawk, sed, diffutils, patch — в данном контексте они выступают как средства обеспечения работы установочных сценариев), базовых средств поддержки сети (netkit и т.д.). Ну и конечно инструментарий собственно для сборки (gcc, binutils, make и тому подобный) плюс основные системные библиотеки (от glibc до ncurses и zlib).

Альтернативно-обязательные компоненты определяются пользовательскими предпочтениями и потребностями. Среди них в первую очередь упомяну патчи ядра для расширения его функциональности. Далее — инструментарий для работы с файловыми системами, отличными от ext2fs, начиная с той же xfsprogs и заканчивая системами управления логическими томами (пакеты lvm-user и evms) и программными raid-массивами, пакеты управления консолью (традиционный выбор между kbd и console-tools), средства управления системными сообщениями (sysklogd или его более продвинутые аналоги типа metalog) и контроля над пользовательскими аккаунтами (впрочем, в Linux этой цели обычно служит пакет shadow).

Как ни странно, достаточно альтернативен и выбор средств обеспечения загрузки системы, начиная с собственно загрузчиков (GRUB или LILO, не говоря уже о сторонних), и заканчивая системами инициализации: конечно, в Linux обычно традиционно используется пакет sysvinit (стартовая система в стиле System V), но никто ведь не может запретить и старт в BSD-стиле.

Наконец, последний штрих к обязательному натюрморту — командная оболочка (шелл, по простому). Здесь теоретически также не обязательно замыкаться на bash, можно бы взять любую POSIX Shell-совместимую. Однако на практике весь процесс сборки Linux оказывается настолько тесно завязанным именно на bash-скриптинг, что альтернативный выбор выходит себе дороже (не зря же это была одна из первых программ, которую Линус запустил на своем ядре).

Что касается опциональной части Base Linux, то здесь по определению альтернативно все. Начать с наипервейшего пользовательского приложения — текстового редактора: для целей конфигурирования вполне можно ограничится чем-то предельно простым, типа nano. Если же требуется вся мощь системы обработки текста — на очереди будет скорее всего vim (к тому же — стандарт для всех Unix-систем вообще). Ну, а из промежуточных решений — я лично обеими руками за joe.

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

В итоге мы получаем список чуть более чем в полсотни пакетов, общий объем которых укладывается в сотню мегабайт. Их можно скачать заблаговременно. А можно, поскольку, как я уже сказал, Lonix позволяет настроить подключение к Сети, тянуть и по ходу дела. Возникает вопрос — какие версии? На мой взгляд — самые свежие из стабильных (или самые стабильные из свежих:-)). Правда, в этом случае схема установки LFS Герарда не сработает в полной мере (она довольно жестко привязана к версиям пакетов), и в нетривиальных ситуация придется искать собственное решение, но не это ли — самое интересное?

Так что остается засучить рукава и приступить к работе. Но сначала — важное предупреждение: будьте готовы уделить этому занятию достаточное количество времени. Даже если все пойдет как надо (а по первому разу на такое можно только надеяться, но никак не следует рассчитывать), сам по себе процесс компиляции займет несколько часов чистого времени на мощной машине. И не говорите, что вас не предупреждали…

А на кой все это?

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

Действительно, существует немалое количество прекрасных пакетных дистрибутивов, на инсталляцию которых в полном объеме нынче (с настройками) уходит меньше времени, чем на установку Windows. Есть и дистрибутивы Source Based, их развертывание требует несколько большего времени, но зато позволяет держать процесс (почти) под полным контролем. Стоит ли тратить время на собирание очередного велосипеда? Да еще и без гарантии (по первому разу, по крайней мере) адекватного затраченным усилиям результата…

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

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

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

Ну и последнее по счету — спортивный интерес. Хотя по значению этот резон я поставил бы на первое место. Ибо без него все остальные просто теряют силу.

Об источниках и исходниках

В первом варианте этих заметок я явно недостаточно уделил внимание а) источникам информации о системном самострое и б) принципам подбора пакетов. Задним числом исправляю упущение.

Для начала хотел бы подчеркнуть, что LFS Book Герарда — не догма, а руководство к действию (предваряемому размышлениями). По крайней мере, мне кажется, что автор рассматривал ее именно так. Действительно, мало радости, затратив полные астрономические сутки (а именно столько уйдет на самострой работоспособной системы при самом благоприятном расположении звезд), получить в точности то же самое, что получил Герард (или кто угодно другой) — тогда уж проще развернуть за считанные минуты тот же CRUX. Ибо суть самостроя — реализовать собственные представления об идеальной Linux-системе.

Во всяком случае, именно такой позиции придерживается уже сформировавшееся LFS community — это те, кто каждый собирает свой собственный Linux. И результатами своих изысканий члены его регулярно делятся посредством т.н. намеков — такой перевод слова hints кажется мне наиболее удачным в данном случае. Здесь можно найти намек на то, как прикрутить к файловую систему devfs, как мигрировать на XFS, как использовать GRUB вместо LILO, и многое, многое другое. В частности, для ярых нелюбителей SysV стиля (к коим отношу и себя), — как заменить Герардовский bootscripts на BSD init. Немало там можно найти и об особенностях установки отдельных приложений на голый base Linux.

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

Об ошибках при компиляции отдельных пакетов… Я собирал самостройный Linux несколько раз, используя как материнскую систему и Gentoo Linux, и Lonix (LiveCD, сам собранный по мотивам LFS), и CRUX. И, по моим наблюдениям, ошибки сборки а) жестко привязаны к конкретным версиям пакетов), и б) особенностям материнской системы (вероятно, к тому, как в ней собрана glibc). Так, из Gentoo мне не удалось собрать fileutils, причем не статически (об этом Герард предупреждает, и даже соответствующий патч у него есть), а именно динамически. Вернее, собираться-то они собирались, но при попытке дать команду ls появлялось сообщение об ошибке сегментации. И потому пришлось поначалу пользовать статически собранные версии первой стадии.

Так вот, для полного предотвращения воздействия glibc‘а материнской системы в hint’ах можно найти метод сборки «чистого» (pure, по утверждению авторов, неких Райана Оливера и Грега Шафера) LFS. Правда, при знакомстве с ним вспоминается анекдот про удаление гланд через… ну, сами знаете, через что, но — опять же по заявлению авторов, — работает (самому проверить пока не удалось). И, замечу задним числом, в текущих (5.X) версиях LFS Book именно метод pure LFS принят в качестве канонического.

Ну и о выборе пакетов… Канонический набор Герарда можно, с одной стороны, спокойно урезать за счет ed, bin86, MAKEDEV, а как показывает пример CRUX — и за счет Texinfo (info-документация, по моему скромному мнению, есть самое GNU’тое изобретение мрачных бородатых GNU-хакеров). С другой стороны, он (опять же ИМХО) должен быть расширен инструментарием для работы с файловыми системами, отличными от ext2fs, и, конечно же, логическими томами (LVM) — гораздо проще создавать файловые системы типа /usr/(local), /opt, /var или /home сразу на логике, чем потом заниматься их переносом. Опять же, devfs, на мой взгляд, ныне — абсолютно необходимый компонент Base Linux (попробуйте без нее регулярно работать с USB-накопителями). И, конечно же, замена для vim может быть любой — при условии, что это будет joe :-)(любой другой редактор потянет за собой дополнительные библиотеки).

Это я все к тому, что Base Linux — такая же системная целостность, как и Distributions FreeBSD. И должна включать в себя все то, что необходимо любому пользователю Linux, и исключать — то, что должно выбираться в индивидуальном порядке (или — отдаваться на откуп составителям дистрибутивов).

Так что можно выделить две составляющие успеха Linux-самостроя: 1) внимательное чтение предшественников (включая hint’ы) и 2) собственные размышления о том, какой бы вы хотели видеть идеальную систему.

Этапы большого пути

Итак, необходимость самостройного Linux’а осознана, конецепция сборки (в первом приближении) обдумана, пакеты собраны, LiveCD вставлен в привод, руки тянутся к трем сакраментальным клавишам для перезагрузки машины. Однако — по рукам себе, по рукам: прежде необходимо наметить план дальнейших мероприятий.

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

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

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

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

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

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

Вот теперь все цели ясны, задачи определены. За работу, товарищи!

Начало всех начал

Абу Талиб сказал: все имеет свое начало, и все начинается с дороги. В нашем случае первый шаг по этой дороге — разметка диска.

Собственно, ничего необычного тут нет — стратегия и тактика разбиения диска описывалась неоднократно. Два обязательных раздела — корневой и под своппинг. Необходимый объем для установки Base Linux (включая архивы исходников, место для временной их распаковки, и так далее) превышает полтора гигабайта. Плюс некий запас свободного пространства (как известно, производительность файловых операций в любых Unix-системах резко деградирует, если объем такового падает ниже 10%) — итого 2 Гбайт абсолютного минимума. Ну а swap-раздел — стандартно, между 128 Мбайт и 2 Гбайт (рекомендуется — удвоенный объем ОЗУ).

Впрочем, своя рука — владыка, и мы имеем возможность гибкого варьирования разделов. Например, можно создать маленький загрузочный раздел под /boot — это настоятельно рекомендуется, если в планах у нас использование GRUB в качестве начального загрузчика (а в моих планах — всегда именно так, никаких преимуществ перед ним у Lilo нынче не осталось).

Далее, и корневой раздел можно сделать не очень большим, выделив из его состава такие каталоги, как /var, /usr/X11R6, /usr/local и /opt. Последние три раздела в данном случае очень даже целесообразно обособить, ведь именно в них будет по умолчанию устанавливаться весь софт из числа Base Linux inside — мы же не для того собираем собственную систему, чтобы ограничится спартанскими минимумом обязательных компонентов. С другой стороны, в обособлении раздела под /usr в данном случае не видно резона — ведь большая часть Base Linux соберется именно в нем, после чего он станет своего рода read only. При большом объеме оперативной памяти нет необходимости и в отдельном разделе под /tmp — лучше будет в дальнейшем подмонтировать в него файловую систему tmpfs. Ну и конечно, каталог /home всегда должен лежать в отдельной патриции, это следует затвердить, как догмат веры.

С файловыми системами — также никакой специфики. Для раздела под /boot практически обязательна классика жанра — ext2fs. Под корень, если сделать его небольшим, также рекомендуется отвести (не извести) ext2fs или, для страховки, ее журналируемый вариант ext3fs. Файловые системы на прочих разделах — в соответствии с пристрастиями, потребностями и возможностями. Так, XFS не поддерживалась в то время каноническим ядром и, соответственно, дистрибутивом Lonix — но она и целесообразна для (очень) больших разделов, например, под /home, который на этом этапе отнюдь не обязательно задействовать.

Теперь раздел, которому суждено стать корневым в нашей грядущей системе, следует подмонтировать к Live файловой системы, загруженной с нашего CD. Создав предварительно для этого соответствующую точку монтирования (в Lonix для этой цели служит каталог /fake/needwrite — прочие ветви файловой системы отнюдь не Live, а, напротив, очень даже read only). Ну и переходим в этот раздел — например, cd /fake/needwrite/basix.

Следующий шаг — развертывание файловой иерархии, что мы будем делать более-менее в соответствии с заветами документа, описывающего одноименный стандарт — Filesystem Hierarchy Standard. А именно: в корне создаем файлового древа создаем каталоги

$ mkdir bin boot dev etc
home lib mnt opt root
sbin usr/{X11R,local} var

Напомню — все пути даются относительно точки монтирования будущего корневого раздела, в данном примере — /fake/needwrite/basix. Далее, при необходимости, подмонтируем к соответствующим точкам — opt, tmp, usr/{X11R,local}, var, — предопределенные для них разделы:

$ mount /dev/hd?# opt

и так далее. В каталогах usr, usr/X11R, usr/local развертываем их внутреннюю иерархию — с подкаталогами ~/bin, ~/etc, ~/include, ~/lib, ~/sbin, ~/share, ~/src и еще глубже (типа ~/share/man, ~/share/man/man1 и так далее).

Проделываем ту же процедуру для каталога opt — здесь согласно стандарту следует выполнить нечто вроде

$ mkdir opt/{bin,doc,include,info,lib,man}

Далее — каталоги var и tmp. В первом случае нам потребуются подкаталоги

$ mkdir var/{cache,lib,local,lock,log,opt,run,spool}

А вот каталог tmp оставляем пустым — просто как точку монтирования для грядущей tmpfs после того, как будет собрано ядро с поддержкой оной.

Теперь создаем символические ссылки — минимум одна, usr/tmp -> var/tmp, нам потребуется:

ln -s var/tmp usr

Наконец, изменяем атрибуты доступа для каталога /root — он должен быть закрыт для доступа обычных пользователей:

$ chmod 0750 root

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

$ chmod 1777 tmp var/tmp

Теперь можно приступить к развертыванию архивов исходников, предварительно скачав их из сети и разместив в каком-либо подходящем каталоге (например, usr/src — но обязательно в дереве каталога basix). Создаем каталог под собственно дерево исходников (скажем, usr/src/tmp) и переходим в него. Для статической сборки второго этапа все программы Base Linux нам не потребуются. Поэтому разворачиваем только необходимый минимум: bash, gcc, make, binutils и еще несколько (общим числом 18 — в него обязательно должен входить выбранный текстовый редактор). На этом подготовительный этап самостроя системы можно считать законченным.

Продолжение банкета

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

Чтобы статически связанные программы в дальнейшем не путались с нормально (то есть динамически) слинкованными (они будут собраны на одном из следующих этапов), отведем для них специальный каталог в дереве /mount_point/basix (будущем корне нашей системы), например, static. И предпишем, чтобы все именно в него помещались результаты компиляции.

Обе задачи — статическая линковка и размещение в каталоге /mount_point/basix/static для ряда наших программ достигаются указанием соответствующих опций конфигурирования:

$ ./configure --enable-static        --prefix=/mount_point/basix/static

Форма команды приведена в предположении, что мы находимся в корневом каталоге дерева исходников собираемой программы. Для некоторых программ (из нашего статического набора это binutils и gcc) сборку рекомендуется выполнять из специально созданного каталога, изолированного от дерева исходников (типа binutils-build и gcc-build, соответственно). В этом случае после создания такого каталога следует перейти в него, а команда конфигурирования примет вид вроде

$ ../binutils-X.XX/configure [options]

Однако многие из собираемых программ не реагируют на опцию конфигурирования --enable-static (это можно проверить по команде ./configure --help). В этом случае, понятно, она опускается, а статическая линковка задается на стадии компиляции.

Некоторые программы могут потребовать включения или отключения дополнительных опций. Из последних особенно важна опция --disable-nls, поскольку поддержка национальных языков а) не нужна на этом этапе и б) часто вызывает проблемы в статически слинкованных пакетах.

Компиляция программ и установка их в соответствующие ветви каталога basix/static выполняются, как обычно, последовательностью команд

$ make && make install

Для программ, не понимающих опции --enable-static, в строке make следует указать дополнительные флаги компиляции, предписывающие статическую сборку, в большинстве случаев таковым выступает флаг LDFLAGS=-static, или:

$ make LDFLAGS=-all-static

Префикс также может указываться как флаг компиляции, например:

$ make PREFIX=/mount_point/basix/static

Порядок при сборке статически слинкованных пакетов значения не имеет. По завершении мы получаем в каталоге static нечто вроде слепка корня — с подкаталогами bin, i686-pc-linux-gnu, include, info, lib, libexec, man, share, var. Ну а дерево исходников, из которых все это собиралось (то есть usr/src/tmp), лучше просто целиком удалить — собрать в нем же динамически связанные пакеты не рекомендуется.

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

И еще. Пожалуйста, не воспринимайте эту часть как руководство к действиям, которые описаны здесь в самом общем виде. Ибо, ей-же богу, не вижу смысла дублировать Герардово Писание или, паче того вывод ./configure --help для каждой программы. Так что именно к этим источникам и следует обратиться за деталями.

Определяющий этап

Для начала я должен извиниться перед читателями за некоторую скомканность изложения. Дело в том, что заметки эти были начаты до появления русского перевода LFS Book. И я собственно хотел в них совместить изложение рекомендаций Герарда с собственными впечатлениями от этого увлекательного процесса. Однако неожиданно поймал себя на мысли, что чем к более конкретным вещам я перехожу, тем больше скатываюсь на пересказ LFS Book. Что теперь не интересно даже тем, кто не очень хорошо владеет английским. Поэтому по возможности именно конкретные детали я буду опускать. Если что покажется неясным — обращайтесь к первоисточнику, информация в нем практически исчерпывающая. Хотя и касается лишь одного из возможных путей самостроя.

Определяющий этап самостроя — сборка главной системной библиотеки glibc. Однако прежде нам предстоит еще одно чрезвычайно ответственное дело — смена корневого каталога на /mount_point/basix, осуществляемая командой chroot. Выполнив ее, мы остаемся наедине с нашими статически собранными программами, и на помощь материнской LiveCD-системы рассчитывать уже не сможем. А потому нам следует озаботится созданием соответствуюшего окружения в новой среде обитания.

В этом окружении должны существовать: пути к исполнимым файлам относительно нового корня (то есть переменная PATH), и домашнему каталогу суперпользователя (переменная HOME), указание на тип терминала (переменная TERM), желательный вид приглашения командной строки (переменная PS1). Ну и о самой командной оболочке забывать не следует (ею на время будет /static/bin/bash — в отсчете от нового корня). Да и освободиться от прежнего окружения не помешает — дабы не путать новую шерсть со старой (это делается командой /static/bin/env -i — ясно, что она должна предществовать установке новых переменных). В итоге полная команда по смене корня приобретет вид вроде следующего:

$ chroot /mount_point/basix/static/bin/env -i
HOME=/root TERM=$TERM PS1='u:w$ '
PATH=/bin:/usr/bin:/sbin:/usr/sbin:/static/bin
/static/bin/bash --login

Сборка собственной системы дает практически неограниченные возможности по ее оптимизации. Правда, Герард Бикманс делать этого не рекомендует во избежание проблем. Однако буде такое желание все же возникнет — флаги оптимизации для компилятора gcc можно также определить в качестве переменных при смене корня, дабы потом каждый раз не вводить их вручную. Мой опыт показывает, что оптимизация под процессор (собиралось на Pentium 4) проходит безболезненно почти во всех случаях, немало способствуя итоговому быстродействию. Я использовал следующие значения флагов оптимизации:

CFLAGS="-march=pentium4 -O3 -pipe
 -fomit-frame-pointer"
CXXFLAGS="$CFLAGS"

При желании прибегнуть к более жестким флагам оптимизации настоятельно рекомендуется ознакомиться с документацией по gcc.

Итак, вводим команду chroot со всеми ее атрибутами и ждем Enter. Все, мосты сожжены: на время окружающий мир за пределами каталога basix для нас как бы перестал существовать. Правда, выход в него возможен через другие виртуальные консоли — благо lonix поддерживает их аж шесть штук. Но, во избежание путаницы, на это лучше не полагаться. Да и необходимости в том не возникнет — если, конечно, все предществующие шаги были сделаны правильно.

Еще одно необходимое подгтовительное действие — монтирование файловой системы proc как разделяемой в соответствующий каталог будущей корневой файловой системы:

$ mount proc /proc -t proc

Кроме того, вспоминаем, что не зря создавался нами каталог /mount_point/basix/tmp — очень не вредно было бы подмонтировать в него файловую систему tmpfs, если таковая поддерживается дром материнской системы, это сдорово увеличит скорость компиляции (для небольших пакетов — в разы):

# mount tmpfs /mnt/tmpfs -t tmpfs

Теперь Герард рекомендует создать несколько символических ссылок — /etc/mtab -> /proc/mounts, /bin/bash -> /static/bin/bash, /bin/sh -> /static/bin/bash. Не вижу причин не последовать его совету. Напомню только, что здесь и далее отсчет корня идет от того каталога, в который мы переместились командой chroot.

Вслед за этим создаются файлы /etc/passwd и /etc/group — командой echo или в текстовом редакторе (не зря же мы его также собрали статически. В первый заносится пока единственный аккаунт — для администратора (root), с символом x вместо пароля (он пока ни к чему). А состав групп — более или менее произвольный. Тот список групп, который дает Герард, используется его сценарием MAKEDEV, чтобы не ломать голову, сделаем, как приказано. При желании же проявить самодеятельность нужно помнить, что в списке групп обязательно должна присуствтвовать группа tty — иначе в дальнейшем не установятся некоторые пакеты (в частности, util-linux).

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

Теперь — внимание, последний из подготовительных перед сборкой glibc шагов. Распаковываем (непосредственно в каталоге /usr/src) архив выбранной нами версии ядра системы, получая каталог вида /usr/src/linux-2.6.XX, делаем на него символическую ссылку /usr/src/linux -> /usr/src/linux-2.6.XX.

Теперь т.н. заголовочные файлы ядра (header-файлы) следует скопировать туда, где они будут искаться при компиляции glibc (да и других пакетов тоже). Делать это следует очень внимательно, поэтому я не буду приводить соответствующих команд во избежание опечаток — лучше просто обратиться к труду Герарда. Где заодно можно прочитать обоснование того, почему header’ы следует именно копировать, а не устанавливать на них символические ссылки.

Теперь можно заняться собственно glibc. Распаковываем архив (например, в каталог /usr/src/tmp2), переходим в новообразованный каталог glibc-2.X.X и распаковываем в нем glibc-linuxthreads. А теперь потребуется внести некоторые изменения в исходники. У Герарда для этого предназначен специальный патч, однако он, естественно, привязан к конкретной версии glibc, мы же собраем ее произвольную (наиболее свежую на данный момент) версию. Так что придется вооружиться текстовым редактором (именно поэтому я настаивал на его включении в набор статически собранных пакетов).

Необходимость модификации исходников обусловлена следующим. Во-первых, в нашей недоношенной chroot-системе пакета glibc еще нет — и потому определение идентификатора пользователя по его имени (login’у) невозможно, а в исходниках glibc как аргумент команды chown фигурирует один пользователь (догадайтесь, како? — правильно, root). Так что все упонминания root’а следует заменить на его ID — то есть на 0.

Далее, при установке glibc используется Perl, если таковой доступен, и сслыки на его местонахождение даны в виде переменной $PERL. Однако в нашем случае Perl именно недоступен, поэтому переменную следует заменить на абсолютный путь — /usr/bin/perl, в результате он при конфигурировании просто игнорируется.

Итак, ищем, где же имеют место быть скараментальные символьные последовательности:

$ grep -R root *;
$ grep -R $PERL *

Наперед скажу, что в той версии glibc, которая собиралась мной (2.3.1), да и в более ранних, слово root мы найдем в файле login/Makefile, а переменную $PERL — в файле malloc/Makefile (команды даются в предположении, что мы находимся в каталоге glibc-2.3.X). Так что открываем их в текстовом редакторе и зменяем соответствующие вхождения, как сказано выше.

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