Ядро Linux (получение информации и управление). Процесс загрузки Linux

В многочисленном семействе операционных систем на основе GNU/Linux. Наверняка вы слышали, что оные коды являются открытыми, свободно распространяемыми и бесплатными. Дескать, бери кто хочешь, но только условия лицензии GPL соблюдай, что совсем нетрудно. Однако мало кто объясняет достаточно внятно, в чём же суть данного явления, в чём его смысл. Поэтому попытку такого объяснения осуществим мы.

Суть вкратце

Всё началось в 1991-м, когда финский студент Линус Торвальдс выложил в открытый доступ коды ядра новой операционной системы Linux. Почему в открытый? Потому что поддерживал . Но это, вы, пожалуй, и так знаете (или легко узнаете). Мы же обратим внимание на моменты, которые требуют чёткой классификации.

Linux

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

GNU

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

GNU/Linux - это уже ОС, а не просто ядро. Вместо GNU может быть что-то другое, например, Dalvik в Android .

Драйверы

Техника развивается, растёт количество компьютерных «железок», оборудование эволюционирует. И каждому изделию для работы нужен драйвер. Так вот, некоторые драйверы прикручиваются прямо к ядру. Если они свободные (Свободное ПО), как GNU и Linux, то и коды непременно открыты.

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

Приложения

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

Естественно, открыты коды всех вариантов - для всех поддерживаемых операционных систем. Та же самая ситуация - с различными утилитами.

Кто это изготавливает

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

Драйверы, если они свободные, тоже нередко изготавливаются сообществами. Для принтера, сканера, видеокарты, адаптера Wi-Fi... В общем, много для чего.

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

Иногда производители «железки» сами открывают код под какой-нибудь подходящей лицензией, той же GPL или BSD. Такие события обычно вызывают неописуемую радость сторонников Open Source.

Как вы уже догадываетесь, пользовательские приложения тоже создаются либо сообществами, либо энтузиастами-одиночками. Однако и коммерческие конторы любят рекламировать себя, давая народонаселению часть своей продукции в виде Свободного ПО. Яркий пример: офисный пакет OpenOffice.org долгое время выпускался компанией Oracle.

Более того, некоторые фирмы даже целые дистрибутивы делают. Red Hat, SuSE, Xandros берут деньги за бинарные сборки, готовые к употреблению, но коды прятать не имеют право. То есть, эти коды, как бы их ни переработали, должны оставаться открытыми. Таково требование лицензии GPL.

Кто этим пользуется

Смотрит программист на софт и думает: «Хорошая штука, но можно сделать лучше!» Качает с сайта разработчика архив с кодом - и совершенствует. К нему присоединяется группа специалистов, пожелавших участвовать, - и рождается новый проект.

Так появляются «форки» (от английского «fork», что в данном случае переводится как «ответвление»). Новые программы на кодовой базе уже существующих.

К примеру, из хорошего аудиоплеера Amarok сделан ещё лучший - Clementine. А из пакета офисных приложений OpenOffice.org - LibreOffice, бурно развивающийся и весьма перспективный.

Так вот, по такому принципу клонируются целые операционные системы. Из исходных кодов платной Red Hat Enterprise Linux компилируется бесплатная ОС CentOS . Конечно, боссы компании Red Hat наверняка кусают локти от досады, но сделать ничего не могут, поскольку исходный код им не принадлежит.

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

Заключение

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

Нет бинарной сборки важного для вас софта для конкретного дистрибутива GNU/Linux ? Драйвер не входит в состав ядра? Взяли архив с исходным кодом, распаковали, почитали инструкции по сборке, откомпилировали, установили - и пользуйтесь. Вы не зависите от производителя, не привязаны к конкретной операционной системе - это и есть настоящая свобода.

Предыдущие публикации:

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

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

5 Solutions collect form web for “Почему ядро ​​Linux называется «образ»?”

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

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

Изображение слова также имеет определение «Файл, содержащий всю информацию, необходимую для создания живой рабочей копии».

Это не означает, что «образ» – это всего лишь 1: 1 копия диска. Поскольку фотография представляет собой реальность точно так же, как и при съемке, изображение исполняемой программы (или ядра) представляет собой программу в состоянии, где она может загружаться (или распаковываться) в системной памяти точно так, как она есть, а затем дается контроль над ним. Затем эта программа может запускаться из этого состояния согласованным образом. Таким образом, образ ядра Linux представляет собой изображение (изображение состояния) ядра Linux, которое может запускаться само по себе, предоставляя ему контроль.

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

BIOS загружает загрузчик, который также является изображением, например, boot.img в случае grub . Этот boot.img не является файлом (если установлен grub); это имя для части, которая находится в главной загрузочной записи (MBR). Если вы выгрузите файл в файл, это будет образ в виде файла, который не будет записан на жесткий диск, но написанный в файле. Это также представление (изображение) самого раннего состояния, в котором grub может загрузить остальную часть себя. grub затем имеет свой механизм, как полностью загружать себя, загружая другие изображения. Это представлено различными этапами в grub . После этого загрузчик загружает образ ядра, заменяя себя извлеченным содержимым этого файла.

Древняя история. термин «изображение» происходит от старого термина «Digital Equipment Corporation» для вывода компилятора-> линкера. файл – это изображение, созданное путем интерпретации кода и т. д. через компоновщик для создания исполняемого «изображения» вашего дизайна.

В математике ядро ​​является прообразом подмножества образа некоторого отображения, были ли подмножество равно единичному элементу в codomain. Я уверен, что эти имена вытекают из математических понятий, поскольку они значительно связаны в разных областях математики. Учитывая, что Unix был получен в академической среде, возможно, что использование этого ядра и образа этих слов одинаково.

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

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

Ядро – в основном просто элементы из исходного набора, которые сопоставляются изображению, но только отображают элемент идентичности изображения. В основном элементы, которые отображают 0, как вещь в изображении.

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

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

Поэтому мы с полной уверенностью видим, что «ядро» и «образ» ОС полностью определены и имеют математическое значение. Независимо от, возможно, других видов использования терминов.

История и архитектурная организация

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

Краткий обзор истории Linux

Хотя Linux, по всей видимости, является самой популярной операционной системой с открытым исходным кодом, на самом деле ее история в сравнении с другими операционными системами относительно коротка. На заре компьютерной эры программисты разрабатывали свои программы для "голой" аппаратуры, используя языки, понятные для этой аппаратуры. В отсутствие операционной системы использовать всю большую и дорогую вычислительную машину в каждый конкретный момент времени могло только одно приложение (и один пользователь). Первые операционные системы были разработаны в 1950-е годы, чтобы облегчить жизнь разработчиков. В качестве примера можно назвать General Motors Operating System (GMOS), разработанную для IBM 701, и FORTRAN Monitor System (FMS), созданную North American Aviation для IBM 709.

В 1960-е годы в Массачусетском Технологическом институте (MIT) и в ряде компаний была разработана экспериментальная операционная система Multics (Multiplexed Information and Computing Service) для машины GE-645. Один из разработчиков этой ОС, компания AT&T, отошла от Multics и в 1970 году разработала свою собственную систему Unics. Вместе с этой ОС поставлялся язык C. При этом C был разработан и написан так, чтобы обеспечить переносимость разработки операционной системы.

Двадцать лет спустя Эндрю Танненбаум (Andrew Tanenbaum) создал микроядерную версию UNIX® под названием MINIX (minimal UNIX), которая могла работать на небольших персональных компьютерах. Эта операционная система с открытым исходным кодом вдохновила Линуса Торвальдса (Linus Torvalds) на разработку первой версии Linux в начале 1990-х (см. Рис. 1).

Рис. 1. Краткая история основных выпусков ядра Linux

Linux быстро превратился из инициативы энтузиаста-одиночки во всемирный проект, в котором участвуют тысячи разработчиков. Одним из важнейших решений в судьбе Linux стало принятие лицензии GNU General Public License (GPL). GPL защитила ядро Linux от коммерческой эксплуатации и одновременно открыла путь к использованию разработок сообщества пользователей проекта GNU, основанного Ричардом Столлменом (Richard Stallman), объемы кода которого значительно превосходят даже объем ядра Linux. Это позволило использовать в Linux такие полезные приложения, как комплекс компиляторов GNU Compiler Collection (GCC) и различные командные оболочки.

Введение в ядро Linux

Перейдем к общему обзору архитектуры операционной системы GNU/Linux. Операционную систему можно условно разделить на два уровня, как показано на Рис. 2.

Рис. 2. Фундаментальная архитектура операционной системы GNU/Linux

На верхнем уровне находится пользовательское пространство (пространство приложений). Здесь исполняются приложения пользователя. Под пользовательским пространством располагается пространство ядра. Здесь функционирует ядро Linux.

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

Ядро Linux можно, в свою очередь, разделить на три больших уровня. Наверху располагается интерфейс системных вызовов, который реализует базовые функции, например, чтение и запись. Ниже интерфейса системных вызовов располагается код ядра, точнее говоря, архитектурно-независимый код ядра. Этот код является общим для всех процессорных архитектур, поддерживаемых Linux. Еще ниже располагается архитектурно-зависимый код, образующий т.н. BSP (Board Support Package - пакет поддержки аппаратной платформы). Этот код зависит от процессора и платформы для конкретной архитектуры.

Свойства ядра Linux

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

В ядре Linux реализован целый ряд важных архитектурных элементов. И на самом общем, и на более детальных уровнях ядро можно подразделить на множество различных подсистем. С другой стороны, Linux можно рассматривать как монолитное целое, поскольку все базовые сервисы собраны в ядре системы. Такой подход отличается от архитектуры с микроядром, когда ядро предоставляет только самые общие сервисы, такие как обмен информацией. ввод/вывод, управление памятью и процессами, а более конкретные сервисы реализуются в модулях, подключаемых к уровню микроядра. Каждая из этих точек зрения имеет свои достоинства, но я здесь не буду вдаваться в это обсуждение.

С течением времени ядро Linux стало более эффективным с точки зрения использования памяти и процессорных ресурсов и приобрело исключительную стабильность. Однако самый интересный аспект Linux, учитывая размер и сложность этой системы - это ее переносимость. Linux можно откомпилировать для огромного количества разных процессоров и платформ, имеющих разные архитектурные ограничения и потребности. Например, Linux может работать на процессоре как с блоком управления памятью (MMU), так и без MMU. Поддержка процессоров без MMU реализована в версии ядра uClinux. Более подробную информацию см. в разделе " ".

Основные подсистемы ядра Linux

Давайте рассмотрим некоторые основные компоненты ядра Linux, следуя структуре, изображенной на рис. 3.

Рис. 3. Один из возможных взглядов на архитектуру ядра Linux

Интерфейс системных вызовов

SCI - это тонкий уровень, предоставляющий средства для вызова функций ядра из пространства пользователя. Как уже говорилось, этот интерфейс может быть архитектурно зависимым, даже в пределах одного процессорного семейства. SCI фактически представляет собой службу мультиплексирования и демультиплексирования вызова функций. Реализация SCI находится в./linux/kernel, а архитектурно-зависимая часть - в./linux/arch. Более подробные сведения об этом компоненте можно найти в разделе .

Управление процессами

Управление процессами сконцентрировано на исполнении процессов. В ядре эти процессы называются потоками (threads); они соответствуют отдельным виртуализованным объектам процессора (код потока, данные, стек, процессорные регистры). В пространстве пользователя обычно используется термин процесс , хотя в реализации Linux эти две концепции (процессы и потоки) не различают. Ядро предоставляет интерфейс программирования приложений (API) через SCI для создания нового процесса (порождения копии, запуска на исполнение, вызова функций Portable Operating System Interface ), остановки процесса (kill, exit), взаимодействия и синхронизации между процессами (сигналы или механизмы POSIX).

Еще одна задача управления процессами - совместное использование процессора активными потоками. В ядре реализован новаторский алгоритм планировщика, время работы которого не зависит от числа потоков, претендующих на ресурсы процессора. Название этого планировщика - O(1) - подчеркивает, что на диспетчеризацию одного потока затрачивается столько же времени, как и на множество потоков. Планировщик O(1) также поддерживает симметричные многопроцессорные конфигурации (SMP). Исходные коды системы управления процессами находятся в./linux/kernel, а коды архитектурно-зависимой части - в./linux/arch). Более подробную информацию об этом алгоритме см. в разделе .

Управление памятью

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

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

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

Виртуальная файловая система

Еще один интересный аспект ядра Linux - виртуальная файловая система (VFS), которая предоставляет общую абстракцию интерфейса к файловым системам. VFS предоставляет уровень коммутации между SCI и файловыми системами, поддерживаемыми ядром (см. Рис. 4).

Рис. 4. VFS предоставляет коммутационную матрицу между пользователями и файловыми системами

На верхнем уровне VFS располагается единая API-абстракция таких функций, как открытие, закрытие, чтение и запись файлов. На нижнем уровне VFS находятся абстракции файловых систем, которые определяют, как реализуются функции верхнего уровня. Они представляют собой подключаемые модули для конкретных файловых систем (которых существует более 50). Исходные коды файловых систем находятся в./linux/fs.

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

Сетевой стек

Сетевой стек по своей конструкции имеет многоуровневую архитектуру, повторяющую структуру самих протоколов. Вы помните, что протокол Internet Protocol (IP) - это базовый протокол сетевого уровня, располагающийся ниже транспортного протокола Transmission Control Protocol, TCP). Выше TCP находится уровень сокетов, вызываемый через SCI.

Уровень сокетов представляет собой стандартный API к сетевой подсистеме. Он предоставляет пользовательский интерфейс к различным сетевым протоколам. Уровень сокетов реализует стандартизованный способ управления соединениями и передачи данных между конечными точками, от доступа к "чистым" кадрам данных и блокам данных протокола IP (PDU) и до протоколов TCP и User Datagram Protocol (UDP). Исходные коды сетевой подсистемы ядра находятся в каталоге./linux/net.

Драйверы устройств

Подавляющее большинство исходного кода ядра Linux приходится на драйверы устройств, обеспечивающие возможность работы с конкретными аппаратными устройствами. В дереве исходных кодов Linux имеется подкаталог драйверов, в котором, в свою очередь, имеются подкаталоги для различных типов поддерживаемых устройств, таких как Bluetooth, I2C, последовательные порты и т.д. Исходные коды драйверов устройств находятся в./linux/drivers.

Архитектурно-зависимый код

Хотя основная часть Linux независима от архитектуры, на которой работает операционная система, в некоторых элементах для обеспечения нормальной работы и повышения эффективности необходимо учитывать архитектуру. В подкаталоге./linux/arch находится архитектурно-зависимая часть исходного кода ядра, разделенная на ряд подкаталогов, соответствующих конкретным архитектурам. Все эти каталоги в совокупности образуют BSP. В случае обычного настольного ПК используется каталог i386. Подкаталог для каждой архитектуры содержит ряд вложенных подкаталогов, относящихся к конкретным аспектам ядра, таким как загрузка, ядро, управление памятью и т.д. Исходные коды архитектурно-зависимой части находятся в./linux/arch.

Помимо переносимости и эффективности, ядро Linux обладает целым рядом других интересных функций, которые не были освещены в вышеприведенном рассмотрении.

Linux, как широко используемая на практике операционная система с открытым исходным кодом, является отличной испытательной площадкой для новых протоколов и их усовершенствований. Linux поддерживает большое количество сетевых протоколов, включая традиционный TCP/IP и его высокоскоростные расширения (для сетей быстрее Gigabit Ethernet и 10 GbE). Linux также поддерживает такие протоколы, как Stream Control Transmission Protocol (SCTP), реализующий множество дополнительных функций, отсутствующих в TCP (применяется в качестве альтернативного протокола транспортного уровня).

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

Еще одно недавнее усовершенствование Linux - возможность ее использования в качестве операционной системы для других операционных систем (т.н. гипервизора). Недавно в ядро было внесено усовершенствование, получившее название Kernel-based Virtual Machine (KVM, виртуальная машина на базе ядра). В результате этой модификации в пространстве пользователя был реализован новый интерфейс, позволяющий исполнять поверх ядра с поддержкой KVM другие операционные системы. В таком режиме можно не только исполнять другие экземпляры Linux, но и виртуализовать Microsoft® Windows®. Единственное ограничение состоит в том, что используемый процессор должен поддерживать новые инструкции виртуализации. Более подробную информацию см. в разделе .

Дальнейшее изучение

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

Представьте, что у вас имеется образ ядра Linux для телефона на базе Android, но вы не располагаете ни соответствующими исходниками, ни заголовочными файлами ядра. Представьте, что ядро имеет поддержку подгрузки модулей (к счастью), и вы хотите собрать модуль для данного ядра. Существует несколько хороших причин, почему нельзя просто собрать новое ядро из исходников и просто закончить на том (например, в собранном ядре отсутствует поддержка какого-нибудь важного устройства, вроде LCD или тачскрина). С постоянно меняющимся ABI ядра Linux и отсутствием исходников и заголовочных файлов, вы можете подумать, что окончательно зашли в тупик.

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

Конфигурация ядра

Первый шаг - найти исходники ядра наиболее близкие к тому образу ядра, насколько это возможно. Наверное, получение правильной конфигурации - наиболее сложная составляющая всего процесса сборки модуля. Начните с того номера версии ядра, который может быть прочитан из /proc/version . Если, как я, вы собираете модуль для устройства Android, попробуйте ядра Android от Code Aurora, Cyanogen или Android, те, что наиболее ближе к вашему устройству. В моем случае, это было ядро msm-3.0. Заметьте, вам не обязательно необходимо искать в точности ту же версию исходников, что и версия вашего образа ядра. Небольшие отличия версии, наиболее вероятно, не станут помехой. Я использовал исходники ядра 3.0.21, в то время как версия имеющегося образа ядра была 3.0.8. Не пытайтесь, однако, использовать исходники ядра 3.1, если у вас образ ядра 3.0.x.

Если образ ядра, что у вас есть, достаточно любезен, чтобы предоставить файл /proc/config.gz , вы можете начать с этого, в противном случае, вы можете попытаться начать с конфигурацией по умолчанию, но в этом случае нужно быть крайне аккуратным (хотя я и не буду углубляться в детали использования дефолтной конфигурации, поскольку мне посчастливилось не прибегать к этому, далее будут некоторые детали относительно того, почему правильная конфигурация настолько важна).

Предполагая, что arm-eabi-gcc у вас доступен по одному из путей в переменной окружения PATH, и что терминал открыт в папке с исходными файлами ядра, вы можете начать конфигурацию ядра и установку заголовочных файлов и скриптов:

$ mkdir build $ gunzip config.gz > build/.config # или что угодно, для того, чтобы приготовить.config $ make silentoldconfig prepare headers_install scripts ARCH=arm CROSS_COMPILE=arm-eabi- O=build KERNELRELEASE=`adb shell uname -r`
Сборка silentoldconfig , наиболее вероятно, спросит, хотите ли вы включить те или иные опции. Вы можете выбрать умолчания, но это вполне может и не сработать.

Можно использовать что-нибудь другое в KERNELRELEASE , однако это должно совпадать в точности с версией ядра, с которого вы планируете подгружать модуль.

Написание простого модуля

Чтобы создать пустой модуль, необходимо создать два файла: исходник и Makefile . Расположите следующий код в файле hello.c , в некоторой отдельной директории:

#include /* Needed by all modules */ #include /* Needed for KERN_INFO */ #include /* Needed for the macros */ static int __init hello_start(void) { printk(KERN_INFO "Hello world\n"); return 0; } static void __exit hello_end(void) { printk(KERN_INFO "Goodbye world\n"); } module_init(hello_start); module_exit(hello_end);
Поместите следующий текст в файл Makefile в той же директории:

Obj-m = hello.o
Сборка модуля достаточна проста, однако на данном этапе полученный модуль не сможет загрузиться.

Сборка модуля

При обычной сборки ядра система сборки ядра создает файл hello.mod.c , содержимое которого может создать различные проблемы:

MODULE_INFO(vermagic, VERMAGIC_STRING);
Значение VERMAGIC_STRING определяется макросом UTS_RELEASE , который располагается в файле include/generated/utsrelease.h , генерируемом системой сборки ядра. По умолчанию, это значение определяется версией ядра и статуса git-репозитория. Это то, что устанавливает KERNELRELEASE при конфигурации ядра. Если VERMAGIC_STRING не совпадает с версией ядра, загрузка модуля приведет к сообщению подобного рода в dmesg:

Hello: version magic "3.0.21-perf-ge728813-00399-gd5fa0c9" should be "3.0.8-perf"
Далее, также имеем здесь определение структуры модуля:

Struct module __this_module __attribute__((section(".gnu.linkonce.this_module"))) = { .name = KBUILD_MODNAME, .init = init_module, #ifdef CONFIG_MODULE_UNLOAD .exit = cleanup_module, #endif .arch = MODULE_ARCH_INIT, };
Само по себе, это определение выглядит безобидно, но структура struct module , определенная в include/linux/module.h , несет в себе неприятный сюрприз:

Struct module { (...) #ifdef CONFIG_UNUSED_SYMBOLS (...) #endif (...) /* Startup function. */ int (*init)(void); (...) #ifdef CONFIG_GENERIC_BUG (...) #endif #ifdef CONFIG_KALLSYMS (...) #endif (...) (... plenty more ifdefs ...) #ifdef CONFIG_MODULE_UNLOAD (...) /* Destruction function. */ void (*exit)(void); (...) #endif (...) }
Это означает, что для того, чтобы указатель init оказался в правильном месте, CONFIG_UNUSED_SYMBOLS должен быть определен в соответствии с тем, что использует наш образ ядра. Что же насчет указателя exit, - это CONFIG_GENERIC_BUG , CONFIG_KALLSYMS , CONFIG_SMP , CONFIG_TRACEPOINTS , CONFIG_JUMP_LABEL , CONFIG_TRACING , CONFIG_EVENT_TRACING , CONFIG_FTRACE_MCOUNT_RECORD и CONFIG_MODULE_UNLOAD .

Начинаете понимать, почему обычно предполагается использовать в точности те же заголовочные файлы, с которыми было собрано наше ядро?

Static const struct modversion_info ____versions __used __attribute__((section("__versions"))) = { { 0xsomehex, "module_layout" }, { 0xsomehex, "__aeabi_unwind_cpp_pr0" }, { 0xsomehex, "printk" }, };
Эти определения берутся из файла Module.symvers , который генеруется в соответствии с заголовочными файлами.

Каждая такая запись представляет символ, требуемый модулю, и то, какую сигнатуру должен иметь символ. Первый символ, module_layout , зависит от того, как выглядит struct module , то есть, зависит от того, какие опции конфигурации, упомянутые ранее, включены. Второй, __aeabi_unwind_cpp_pr0 , - функция, специфичная ABI ARM, и последний - для наших вызовов функции printk .

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

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

Hello: disagrees about version of symbol symbol_name
Что значит, что нам нужен правильный, соответствующий образу ядра, файл Module.symvers , которым мы не располагаем.

Изучаем ядро

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

Ядро использует следующую функцию для поиска в своей таблицы символов (в kernel/module.c):

Bool each_symbol_section(bool (*fn)(const struct symsearch *arr, struct module *owner, void *data), void *data) { struct module *mod; static const struct symsearch arr = { { __start___ksymtab, __stop___ksymtab, __start___kcrctab, NOT_GPL_ONLY, false }, { __start___ksymtab_gpl, __stop___ksymtab_gpl, __start___kcrctab_gpl, GPL_ONLY, false }, { __start___ksymtab_gpl_future, __stop___ksymtab_gpl_future, __start___kcrctab_gpl_future, WILL_BE_GPL_ONLY, false }, #ifdef CONFIG_UNUSED_SYMBOLS { __start___ksymtab_unused, __stop___ksymtab_unused, __start___kcrctab_unused, NOT_GPL_ONLY, true }, { __start___ksymtab_unused_gpl, __stop___ksymtab_unused_gpl, __start___kcrctab_unused_gpl, GPL_ONLY, true }, #endif }; if (each_symbol_in_section(arr, ARRAY_SIZE(arr), NULL, fn, data)) return true; (...)
Структура, используемая в данной функции, определена в include/linux/module.h:

Struct symsearch { const struct kernel_symbol *start, *stop; const unsigned long *crcs; enum { NOT_GPL_ONLY, GPL_ONLY, WILL_BE_GPL_ONLY, } licence; bool unused; };
Примечание: данный код ядра не изменился значительно за последние четыре года (видимо, с момента рассматриваемого релиза ядра 3.0, - прим. пер.).

То, что мы имеем выше в функции each_symbol_section - три (или пять, когда конфиг CONFIG_UNUSED_SYMBOLS включен) поля, каждое из которых содержит начало таблицы символов, ее конец и два флага.

Данные эти статичны и постоянны, что означает, что они появятся в бинарнике ядра как есть. Сканируя ядро на предмет трех идущих друг за другом последовательностей состоящих из трех указателей в адресном пространстве ядра и следом идущих значений типа integer из определений в each_symbol_section , мы можем определить расположение таблиц символов и сигнатур, и воссоздать файл Module.symvers из бинарника ядра.

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

Теги: Добавить метки

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

    Выполнение кода BIOS. Инициализация оборудования. Выбор загрузочного носителя. Считывание в ОЗУ начального загрузчика и передача управления на него. Начальный загрузчик обычно занимает один сектор на диске и ограничен размером 384 байт (512 байт – сектор диска, минус 128 байт – таблица разделов). В зависимости от типа загрузочного устройства загрузочный сектор может считываться из разных мест:

    • При загрузке с дискеты или НЖМД загрузчик читается из первого сектора физического носителя;
    • При загрузке с CD/DVD – из первого сектора образа загрузочного диска, размещённого в структуре данных CD;
    • При сетевой загрузке – из первого сектора образа загрузочного диска, скачиваемого с сервера по протоколу tftp.

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

    Считывание в память основного загрузчика (GRUB, LiLo, NTLDR) и выполнение его кода. Поскольку начальный загрузчик очень мал, то, как правило, в его код жестко прописывают сектора, из которых надо прочитать код основного загрузчика. На НЖМД это может быть пространство между МБР и первым разделом на диске (нулевая дорожка). На дискете и при использовании образа диска при загрузке с CD и по сети – основной загрузчик может располагаться сразу вслед за первичным загрузчиком и занимать весь объём образа.

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

    Вспомогательный диск необходим современным Linux системам из-за модульности ядра и содержит драйверы (ATA, NFS, RAID и т.п.), необходимые для получения доступа к основной файловой системе.

    На этом этапе создаётся процесс с pid=1 , в котором происходит выполнение скрипта init , находящегося в корневом каталоге вспомогательного диска. Параметры, передаваемые ядру, фактически передаются в init , как аргументы командной строки.

    Скрипт содержит загрузку необходимых драйверов в виде модулей ядра, создание временных файлов устройств в каталоге /dev для доступа к этим модулям, сканирование дисковых разделов для обнаружения и инициализации RAIDов и логических томов. После инициализации логических дисков делается попытка смонтировать корневую файловую систему, заданную параметром root= . В случае бездисковой сетевой загрузки делается попытка подключить корневой каталог по NFS.

    На экран выдаются сообщения о загрузке драйверов и о поиске виртуальных томов подсистемы LVM. Этап завершается перемонтированием корневого каталога на основную файловую систему и загрузку в процесс с pid=1 основной программы /sbin/init (или её аналога).

    В классическом UNIX"е и старых версиях Linux (примерно до 2012 года) программа init считывает конфигурационный файл /etc/inittab , инициализирует текстовые консоли и, как правило, запускает необходимые службы с помощью набора скриптов, расположенных в каталогах /etc/init.d и /etc/rc*.d . В современных дистрибутивах Linux в файле /sbin/init находится более современная программа запуска служб. Наиболее популярными из подобных программ являются upstart и systemd , которые позволяют существенно сократить время этого этапа загрузки.

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

Загрузчик GRUB

Загрузиться с установочного диска в режим восстановления - Rescue mode. Для этого в момент загрузки на приглашение boot: необходимо ввести linux rescue

Если всё пойдёт нормально, то корневой каталог основной системы будет смонтирован в /mnt/sysimage , загрузочный каталог в /mnt/sysimage/boot . Кроме того текущие каталоги /proc , /sys и /dev будут смонтированы в соответствующие подкаталоги /mnt/sysimage . Если это не случится, то придётся проделать эти операции вручную.

Когда все каталоги смонтированы, можно сменить корневой каталог

#если выяснится, что вы что-то забыли смонтировать, то можно выйти по ^D chroot /mnt/sysimage

и пересобрать initrd

#копируем старый файл cp -p /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r).img.bak #создаём новый dracut -f #если версия ядра в основной системе отличается от версии на установочном диске, указываем её явно dracut -f /boot/initramfs-2.6.32-358.el6.x86_64.img 2.6.32-358.el6.x86_64

#копируем старый файл cp -p /boot/initrd-$(uname -r).img /boot/initrd-$(uname -r).img.bak #создаём новый mkinitrd -f -v /boot/initrd-$(uname -r).img $(uname -r) #если версия ядра в основной системе отличается от версии на установочном диске, указываем её явно mkinitrd -f -v /boot/initrd-2.6.18-371.el5.img 2.6.18-371.el5

Cd / sync telinit 6

Полный пример с драйвером i2o_block (SCSI адаптер Adaptec 2010S), который не загружается автоматически. Пример выполняется в CentOS 5, поскольку в стандартном ядре CentOS 6 поддержка этого драйвера отключена.

После загрузки с CD в Rescue mode выдаётся сообщение, что Linux разделы не найдены и их надо монтировать самостоятельно.

#Загружаем драйвер insmod i2o_block #Проверяем, что всё сработало lsmod .... dmesg ... #Создаём файлы устройств на основе информации в dmesg mkdir /dev/i2o mknod /dev/i2o/hda b 80 0 mknod /dev/i2o/hda1 b 80 1 mknod /dev/i2o/hda2 b 80 2 #Активируем VolumeGroup lvm vgchange -a y #Монтируем тома mkdir /mnt/sysimage mount /dev/mapper/VolGroup00-LogVol00 /mnt/sysimage mount /dev/i2o/hda1 /mnt/sysimage/boot #Монтируем спецкаталоги mount --bind /proc /mnt/sysimage/proc mount --bind /dev /mnt/sysimage/dev mount --bind /sys /mnt/sysimage/sys

Далее по инструкции, только при создании образа диска надо указать программе mkinitrd дополнительную опцию --preload=i2o_block и отключить сервисы readahead , поскольку они приводят к зависанию драйвера i2o_block:

Chkconfig early-readahead off chkconfig later-readahead off

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

В этой лекции я расскажу, что делает классическая программа init в сочетании со скриптами rc.d в стиле System V (Систем пять). System V - это классическая версия UNIX на которой построены коммерческие UNIX.

Судя по названию, rc.d это некий каталог. Есть такая традиция UNIX - если вся конфигурация чего-либо умещается в один файл, и он называет config, то при разбиении его на отдельные файлы, которые подключаются к основному, создают каталог с аналогичным именем и добавляют к имени.d – config.d. Буква d означает, что это директория и там лежат вспомогательные части конфигурационного файла. У формата конфигурационных файлов программы init есть две традиции: вариант System V, в котором каждая деталь конфигурации держится в отдельном файле в каталоге rc.d, и традиция BSD систем, в которой есть один файл /etc/rc, содержащий много скриптов и переменных, которые отвечают за поведение системы.

В любом случае, при старте системы у нас создается процесс с PID=1, в котором запущена программа, которая называется init. Как вы видели в прошлый раз, если программу init убить, то ядро впадает в панику и прекращает всяческую работу.

Классический System V init читает файл /etc/inittab и выполняет ряд предписаний, которые прописаны в этом файле. Inittab этот текстовый файл каждая строка которого, это, по сути дела, одна команда или какое-то правило поведения. Inittab выглядит так:

id:3:initdefault:

si::sysinit:/etc/rc.d/rc.sysinit

l3:3:wait:/etc/rc.d/rc 3

ca::ctrlaltdel:/sbin/shutdown -t3 -r now

Вначале строки стоит метка. В чем большой смысл этой метки я не очень понимаю. Можно считать, что это простой текст и все. Вторым пунктом стоит либо так называемый уровень загрузки, либо пустое значение. Уровень загрузки - это либо одно число от 0 до 6, либо список чисел через запятую. Дальше идет некое действие. Действия бывают следующие: wait, respawn, sysinit, ctrlaltdel. Есть и другие действия, но это самые используемые. Наконец, в конце строки написана некая команда с именем исполняемого файла и аргументов, которые этой команде надо передать.

Действие sysinit выполняется однократно при старте системы.

Действие ctrlaltdel это на самом деле не совсем действие – это обработчик сочетания клавиш control alt del. Само нажатие перехватывается ядром системы, и информация об этом пересылается в процесс init, который должен выполнить определенную команду. Например, может быть выполнена команда shutdown, которая выполнит выключение компьютера. В принципе сюда можно прописать любую другую программу, например, echo, которая после нажатия control alt del будет выдавать на все терминалы системы какое-нибудь сообщение. камина консолью так

Действие wait означает, что необходимо запустить команду, дождаться пока она закончится и только после этого продолжить обработку следующих строк. Не знаю, могут ли запускаться такие действия в параллель. Скорее всего, нет.

Действие respawn означает, что надо запустить программу и не дожидаясь ее завершения, перейти в дальнейшем действиям. Если эта программа в последующем завершится, то необходимо ее рестартовать.

Итак, есть однократное выполнение с ожиданием результатов и многократное выполнение в асинхронном режиме – запустились, дождались пока закончить, запустили слова.

Уровни загрузки - это некая условность, которая позволяет управлять загружаемыми службами. Ближайший аналог в windows – это загрузка в безопасном режиме, когда грузится только ограниченное число драйверов и стартует минимальное количество служб, загрузка с отладкой, когда каждое действие дополнительно протоколируются и обычная полноценная загрузка.

В Linux по традиции выделяется 6 вариантов загрузки. Это деление довольно условно.

0 и 6 это выключение. 0 - полное выключение электричество, а 6 - режим перезагрузки.

4 в Linux вообще пропущен

Остаются четыре уровня загрузки:

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

3 - нормальный многопользовательский текстовый режим, когда запущены все службы, работает сеть, работают все драйверы.

2 - тоже текстовый режим, но без подключения сетевых дисков. Дело в том, что традиционные сетевая файловая система nfs, которая используется в UNIX, чрезвычайно устойчива к повреждениям сети. Если мы выключили файловый сервер или обрезали сетевой кабель, то сетевая файловая система nfs будет предпринимать многочисленные попытки восстановиться и эти попытки настолько длительны, что я ни разу не смог дождаться времени, когда же наконец появится сообщение об ошибке. Возможно это произойдёт через час, а может и через 6 часов. Всё это время драйвер nfs будет держать компьютер, не давая ничего сделать. Поэтому, если у нас упала сеть или файловый сервер в настройках написано, что при старте необходимо подмонтировать внешние диски, то попытка загрузится в полноценный режим приведёт к тому, что у вас все зависнет. Для этого случая и предусмотрен второй вариант загрузки - все как в третьем, только сетевые диски не подключаются. Сам сетевой адаптер работает, IP адрес назначается, интернет доступен.

5 - то же самое что и 3, но с запуском x window - графического интерфейса.

режим 2 включает себя 1 + многопользовательский режим. 3 включает 2 + монтирование сетевых файловых систем. Наконец, 5 включает в себя 3 + запуск графической подсистемы. Будет ли это реализовано последовательно или нет - это проблема дистрибутива. Вообще говоря, администраторы могут самостоятельно настроить файл inittab так, чтобы эти режимы запускались последовательно, а можно сделать так чтобы все было абсолютно независимо - переключаясь в очередной режим, убираем все что было сделано на предыдущем шаге, и настраиваем все с нуля.

Рассмотрим строки реального файла. Они очень просты.

l3:3:wait:/etc/rc.d/rc 3

Запускается какая-то программа, которая должна выполнить все необходимые действия, которые ожидаются на третьем уровне. Наверно, на третьем уровне нужно настроить сетевые интерфейсы, запустить драйвер терминалов, стартовать какие-то службы. Только после того, как всё этого завершится мы сможем работать в системе. Поскольку надо дождаться завершения запуска, мы выбираем действие wait.

Программа запуска называется rc и запускается с номером уровня в качестве параметра. Сама программа init достаточно простая. Она умеет построчно читать свой файл с простым синтаксисом и стартовать новые процессы, запуская какие-то вспомогательные программы. Вся логика уровней загрузки спрятана в скрипте rc. Запустив rc с параметром 3 мы перейдем на третий уровень, с параметром 5 - на пятый.

Программа rc тоже очень простая. Это скрипт который выполняет все файлы в каталогах, соответствующих уровню загрузки, например, /etc/rc3.d/. В этих каталогах находятся исполняемые файлы, которые принимают один параметр - либо start, либо stop. Если файл запущен с параметром start, то он стартует службу, если с параметром stop, то останавливает её. Например, network start будет настраивать сетевые интерфейсы, а network stop будет переводить интерфейсы в выключенное состояние. Кроме сетевых интерфейсов есть скрипты подключения/отключение сетевых файловых систем, запуска/остановки сервисов и т.д.

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

Скрипт rc просматриваем содержимого каталога rc3 и выбирает оттуда все файлы которые начинаются с буквы K (kill). Файлы упорядочиваются в порядке возрастания номера и выполняются с параметром stop. Потом те же действия выполняются с файлами на букву S (start), которые запускаются с параметром start. Вот в общем и вся процедура перехода на определенный уровень.

Можно предположить, что в каталоге /etc/rc0.d/ лежат только файлы, начинающиеся на букву K, поскольку при выключении надо все остановить, а в каталоге /etc/rc1.d/ будет один файл на буку S для запуска консоли администратора.

Для простоты программирования есть отдельный каталог /etc/init.d/, в котором лежат те же самые файлы только без буквы цифр в начале имени. На самом деле, файлы в каталогах уровней это просто символические ссылки на основные файлы. Так /etc/rc3.d/S10apache это ссылка на файл /etc/init.d/apache. Буквы и цифры в названии ссылок нужны для того, чтобы скрипт rc вызвал их в нужном порядке и с нужными аргументами.

В системах, которые построены по такому принципу, чтобы стартовать или остановить какую-либо службу в каталоге /etc/init.d/ надо найти файл который, который ей соответствует, и запустить его с параметром start или stop. Чем не нравится запускать службы именно таким способом - явно вызывая скрипты. Дело в том, что в командной строке linux замечательно работает автодополнение. С его помощью очень быстро можно ввести путь до файла запуска.

Чтобы спрятать от пользователя конкретную реализацию поверх системы скриптов и символических ссылок написаны две вспомогательные программы.

Программа chkconfig позволяет манипулировать символическими ссылками на соответствующие скрипты. Чтобы посмотреть, что стартует, а что останавливаться на каждом из уровней можно воспользоваться командой ls и выдать список скриптов в соответствующем каталоге, но проще воспользоваться командой chkconfig –list. Программа chkconfig пробегает по всем каталогам rc и выдает список того что стартует, а что останавливается на каждом уровне. Если мы хотим, чтобы при старте системы у нас что-то автоматически стартовала определенная службу мы выполняем chkconfig <имя службы> on и скрипт создает ссылку для запуска в нужном каталоге и с правильным именем. Запуск chkconfig <имя службы> off приводит к удалению ссылки для запуска и созданию ссылки для остановки. Таким образом программа chkconfig позволяет управлять списком служб, которые стартуют в момент старта системы.

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

В стартовых скриптах аргументы start и stop должны обрабатываться обязательно. Кроме того, можно придумать какие-то свои аргументы, которые будут делать что-то полезное.

В большинстве скриптов реализована опция status, которая показывает запущена служба или нет. Когда мы выполняем start, то скрипт после успешного запуска службы получает ее идентификатор PID и записывать его в определенный файл. По команде stop файл удаляется. Обычно такие файлы создаются в каталоге /var/run/. Команда status проверяет есть ли такой файл. Его нет, то сообщает, что служба не запущена. Если файл есть, то она извлекает из него идентификатор процесса и проверяет текущий список процессов. Если этот идентификатор присутствует все запущено, если программа по каким-то причинам поломалась, то статус выдаёт, что была сделана попытка запустить эту службу - файл существует, но сама служба не запущена.

Опция restart последовательно выполняет внутри скрипта две команды – сначала stop, а потом старт. Это совершенно необязательная команда - просто удобная. Наконец, есть службы, которые позволяет на ходу перечитать какие-то конфигурационные файлы. Для них добавляют команду reload, задачей которой является отправка службе сигнала о том, что конфигурация изменилась. Отдельный случай, команды save и load для сохранения конфигурации брандмауэра.

Если администратор системы вместо остановки или старта отдельных службы хочет всю систему перевести на определенный уровень, то этого можно достичь одним из двух способов. Можно вызвать прямо программу /sbin/init. Если ее вызвать с определенным числом в качестве параметра, то она выполнит все инструкцию из файла inittab, для которых прописывал соответствующий уровень. Если запустить, например, /sbin/init 1, то init найдет в своем конфигурационном файле все строчки, в которых есть уровень 1 и выполнит их. В некоторых системах команда shutdown реализована как /sbin/init 0, поскольку нулевой уровень соответствует остановке системы. В последнее время для перехода между уровнями появилась специальная программа под названием telinit, которая является ссылкой на init. Её задача – переслать процессу init сигнал о том, что администратор желает перейти на определенный уровень. telinit q сообщает init о том, что надо перечитать файл inittab. В старых системах это достигалось посылкой сигнала SIGHUP процессу с PID=1 (kill –HUP 1).

Ещё несколько строк в inittab, это запуск терминалов

1:2345:respawn:/sbin/mingetty tty1

Для того, чтобы обеспечить диалоговую доступ к системе, вы inittabе может присутствовать некоторое количество строчек такого рода. 2345 это уровни, на которых надо запускать команду, respawn означает, что программу надо перезапускать в случае завершения. Программа getty – это программа управления терминалом. Традиционно терминал в UNIX называется телетайпом, поскольку первыми терминалами были электрические пишущие машинка. Соответственно, tty это сокращение от телетайпа. Mingetty – программа, которая умеет работать с виртуальными терминалами на персональном компьютере. Она умеет настраивать драйвер терминала, а в качестве параметров получает имя устройства терминала, который надо настроить. В каталоге /dev/ есть файл устройства tty1, который соответствует первому виртуальному терминалу. Если бы у нас был модем и мы хотели бы инициализировать его момент загрузки, то могли бы вызвать getty с параметром ttyS0, который соответствует порту COM1. При инициализации модема можно было бы задать дополнительные параметры: скорость соединения 19200 бод, 7 или 8 бит в байте, четность, количество стоп-битов.

S0:2345:respawn:/sbin/getty ttyS0 19200 8 n 1

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

Текстовые пользовательские сеансы устроены на таких цепочках: сначала init делает свою копию и запускает в ней программу mingetty. Mingetty инициализирует терминал и клавиатуру, а потом запускает в том же процессе программу login. Login выводит на экран приглашения на ввод имени и пароля и, если все прошло успешно то назначает себе привилегии пользователя и в том же процессе, затирая самого себя, запускает интерпретатор пользователя, например, bash. Когда пользователь набирает команду exit, то интерпретатор завершает жизненный путь этого процесса. Когда процесс завершается, init получает об этом сигнал. Init смотрит, что полагается делать, видит действие respawn, снова запускает программу mingetty, которая заново инициализирует терминал и все повторяется. Таким образом каждый сеанс находится внутри одного процесса. Как только мы вышли из сеанса наш процесс закончился и тотчас же запустилась программа, которая почистит за нами терминал и восстановит все настройки по умолчанию.

В файле inittab есть есть ещё одно специальное ключевое слово initdefault - уровень по умолчанию. Если через ядро init получил параметр single, то мы загрузимся на уровень 1. Если через загрузчик ничего не передали, то используется значение по умолчанию. Если после установки графической оболочки оказалось, что наш компьютер слабоват для графики, то можно установит уровень по умолчанию на 3, и после следующей перезагрузки мы попадаем на третий уровень - то есть в текстовый режим. Установили систему без графического режима, потом доустановили все пакеты для x window, поменяли уровень по умолчанию на 5 и после следующей перезагрузки попали сразу в графический режим.

В этой системе скриптов иногда хочется сделать что-то свое, например, при старте удалить все файлы в каталоге /tmp/. Для этого есть отдельный файл под названием /etc/rc.local, который запускается после всех остальных. Это просто скрипт без параметров, в который можно прописать всё, что угодно. Например, на одном из моих роутеров в момент старта системы в этом файле прописываются таблицы маршрутизации. Мне было лень искать где находятся соответствующие стандартные скрипты из дистрибутива и проще оказалось прописать команды в rc.local.




Top