Файлы ядра linux. Исходные коды Linux

09.04.2019

Состоящее почти из 20 миллионов строк кода ядро Linux является одним из самых крупных Opensource проектов в мире.

Что такое ядро

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

Типы ядер

Имеется три основных типа ядер — монолитные (monolithic ), микроядра (microkernel ) и гибридные (hybrid ).

К примеру Linux является монолитным ядром, тогда как OS X и Windows используют гибридные ядра.

Microkernel

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

Плюсы

  • переносимость
  • меньший размер занимаемой RAM и на жестком диске
  • безопасность

Минусы

  • в целом система может работать медленнее из-за дополнительных слоев программной абстракции между ядром и оборудованием
  • процессы могут тратить время на ожидание в очереди для получения информации

Monolithic ядра

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

Плюсы

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

Минусы

  • больший объем занимаемой памяти и жесткого диска
  • больше проблем с безопасностью

Гибридные ядра

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

Плюсы

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

Минусы

  • возможны недостатки в производительности
  • установка драйверов устройств зависит от пользователя и производителя оборудования

Файлы ядра Linux

В большинстве GNU/Linux -систем файлы ядра располагаются в каталоге /boot , например CentOS 6:

# ls -l /boot/ | grep linu -rwxr-xr-x 1 root root 4221232 Dec 15 23:48 vmlinuz-2.6.32-573.12.1.el6.x86_64 -rwxr-xr-x 1 root root 4221968 Feb 10 01:15 vmlinuz-2.6.32-573.18.1.el6.x86_64 -rwxr-xr-x 1 root root 4221776 Aug 14 2015 vmlinuz-2.6.32-573.3.1.el6.x86_64 -rwxr-xr-x 1 root root 4220144 Sep 23 01:29 vmlinuz-2.6.32-573.7.1.el6.x86_64 -rwxr-xr-x 1 root root 4220368 Nov 10 20:31 vmlinuz-2.6.32-573.8.1.el6.x86_64

Файл с vmlinuz в имени и есть файл ядра. Имя vmlinuz пришло из мира UNIX , в котором c 60-годов файл ядра назывался просто unix . Когда Linus Torvalds начал разработку Linux в 90-х — он назвал его просто linux .

Когда появилась реализация виртуальной памяти — к имени linux была добавлена приставка « vm » (virtual memory ). Так какое-то время файл ядра назывался просто vmlinux , однако рамзер файла постоянно увеличивался и со временем его стали сжимать а последняя буква в имени была заменена c x на z (zlib compression ). Ядро так же зачастую сжимается с помощью LZMA или BZIP2 и некоторые ядра называются просто zImage .

В каталоге /boot так же находятся файлы initrd.img-version (или initramfs-version), System.map-version и config-version . Файл initrd.img-version используется для первоначальной загрузки системы, во время которой распаковывается и загружается само ядро. Файл System.map используется для управления памятью перед загрузкой смого ядра, а файл config содержит в себе параметры ядра и список модулей для загрузки в ядро во время его компиляции.

Архитектура ядра Linux

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

Модули ядра Linux

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

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

Как правило — модули расширяют возможности ядра для работы с устройствами, файловыми системами и системными вызовами. LKM имеют рсширение файлов.ko:

# find /lib/modules/2.6.32-573.18.1.el6.x86_64/kernel/ -name "*.ko" -type f | wc -l 2033

Благодаря модульной структуре — вы можете настраивать ядро под себя, выбирая только необходимые модули в menuconfig , отредактировав файл /boot/config* или загружая и выгружая модули прямо во время работы с помощью утилит типа modprobe , insmod и rmmod .

Ядро не является чем-то волшебным, но является жизненно необходимым для работы любого компьютера. Ядро Linux отличается от ядер в Windows или OS X системах, так как включает в себя драйвера на уровне ядра системы и поддерживает многие возможности «из коробки».

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

Поскольку цель данной статьи - познакомить вас с ядром 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. Обязательно ознакомьтесь с разделом в конце данной статьи, где имеются ссылки на более подробную информацию по многим обсуждаемым здесь темам.

В данном пошаговом руководстве вы узнаете, как правильно собрать и установить ядро ветвей >2.6 в семействе ОС Ubuntu.

Шаг 1. Получение исходного кода ядра

Исходники ядра Ubuntu можно получить двумя способами :

    Установив архив из репозитория, с автоматическим наложением последних официальных патчей. При этом скачается пакет размером ~150 Мб в текущую папку. Чтобы получить исходники ядра, версия которого установлена на компьютере выполните команду: apt-get source linux-image-`uname -r`

    Или вместо `uname -r` можно указать конкретную версию из имеющихся в репозитории.

Список имеющихся в репозитории версий можно увидеть набрав команду: «apt-get source linux-image-» и, не нажимая Enter , нажать два раза клавишу Tab .

Не забудьте включить общий доступ к исходникам в репозитории (Параметры системы → Программы и обновления → Программное обеспечение Ubuntu → Исходный код). Из консоли это сделать можно раскомментировав строки начинающиеся с deb-src в файле /etc/apt/sources.list, а затем выполнить обновление командой: «sudo apt-get update».

    Самая свежая версия ядра доступна по git . Размер скачиваемого пакета ~500-800 Мб. git clone git://kernel.ubuntu.com/ubuntu/ubuntu-.git

    Где - имя релиза, например:

    Git clone git://kernel.ubuntu.com/ubuntu/ubuntu-xenial.git

Другие ядра

Также существуют ядра, работоспособность которых в Ubuntu не гарантируется. Например, известна проблема с рядом популярных системных приложений (в частности драйвера NVidia, VirtualBox), которые при своей установке компилируются под установленное ядро. Поэтому для их установки на ядро, нестандартное для данной версии Ubuntu (например, Ubuntu 16.04 идёт с ядром 4.4.0), может потребоваться их отдельная компиляция вручную или специальные патчи, а последние версии ядер с kernel.org приложение может вообще не поддерживать.

    Архив с базовой версий без патчей, т.е. например «4.8.0», «4.8.10»: sudo apt-get install linux-source

Распакуйте полученный архив, используя команды:

Cd ~/ tar -xjf linux-2.6.x.y.tar.bz2

Или в случае с linux-source:

Cd /usr/src tar -xjf linux-source-2.6.x.y.tar.bz2

Шаг 2. Получение необходимых для сборки пакетов

Данный шаг необходимо выполнить, только если ядро собирается на компьютере в первый раз

Выполните следующие команды для установки основных пакетов:

Sudo apt-get update sudo apt-get build-dep linux sudo apt-get install kernel-package

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

    oldconfig - файл конфигурации создаётся автоматически, основываясь на текущей конфигурации ядра. Рекомендуется для начинающих.

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

    menuconfig - псевдографический интерфейс ручной конфигурации, не требует последовательного ввода значений параметров. Рекомендуется для использования в терминале.

    xconfig - графический (X) интерфейс ручной конфигурации, не требует последовательного ввода значений параметров.

    gconfig - графический (GTK+) интерфейс ручной конфигурации, не требует последовательного ввода значений параметров. Рекомендуется для использования в среде GNOME.

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

В случае, если вы хотите использовать config , oldconfig , defconfig , localmodconfig или localyesconfig , вам больше не нужны никакие дополнительные пакеты. В случае же с оставшимися тремя вариантами необходимо установить также дополнительные пакеты.

menuconfig выполните следующую команду:

Sudo apt-get install libncurses5-dev

Для установки пакетов, необходимых для использования gconfig выполните следующую команду:

Sudo apt-get install libgtk2.0-dev libglib2.0-dev libglade2-dev

Для установки пакетов, необходимых для использования xconfig выполните следующую команду:

До Ubuntu 12.04: sudo apt-get install qt3-dev-tools libqt3-mt-dev

Sudo apt-get install libqt4-dev

Шаг 3. Применение патчей

Данный шаг не является обязательным.

Официальные патчи уже наложены на исходники, если ядро получалось описанной выше командой:

Apt-get source linux-image-`uname -r`

Если вы никогда до этого не применяли патчей к исходному коду, то выполните следующую команду:

Sudo apt-get install patch

Эта команда установит программу patch, необходимую для, как можно догадаться, применения патчей. Теперь скачайте файл патча в папку, куда вы распаковали ядро. Это может быть либо архивный файл (напр. Bzip2 или Gzip), либо несжатый patch-файл.

На данный момент подразумевается, что вы уже сохранили файл в ту папку, куда ранее распаковали ядро, и установили программу patch.
Если скачанный вами файл был в формате Gzip (*.gz), тогда выполните следующую команду для распаковки содержимого архива:

Gunzip patch-2.6.x.y.gz

Если скачанный вами файл был в формате Bzip2 (*.bz2), тогда выполните следующую команду для распаковки содержимого архива:

Bunzip2 patch-2.6.x.y.bz2

где 2.6.x.y - версия патча ядра. Соответствующие команды распакуют файл патча в папку с исходным кодом ядра. Прежде чем применить патч, необходимо удостовериться, что он заработает без ошибкок. Для этого выполните команду:

Patch -p1 -i patch-2.6.x.y --dry-run

где 2.6.x.y - версия патча ядра. Эта команда сымитирует применение патча, не изменяя сами файлы.

Если при её выполнении не возникнет ошибок, то изменения можно смело внедрять в сами файлы. Для этого выполните команду:

Patch -p1 -i patch-2.6.x.y

где 2.6.x.y - версия патча ядра. Если не было никаких ошибок, значит к исходному коду был успешно применён патч.

Внимание! Перед тем, как применять патч, проведите следующие действия: 1. Скачайте с http://www.kernel.org патч той же версии, что и ваших исходников. 2. Выполните следующую команду: patch -p1 -R

где 2.6.x.y - версия патча и ваших исходников

Шаг 4. Конфигурация будущей сборки ядра

Перейдите в папку, куда вы распаковали ядро, выполнив команду

Cd ~/linux-2.6.x.y

где 2.6.x.y - версия загруженного вами ядра.

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

    config - традиционный способ конфигурирования. Программа выводит параметры конфигурации по одному, предлагая вам установить для каждого из них свое значение. Вызывается командой make config

    oldconfig - файл конфигурации создаётся автоматически, основываясь на текущей конфигурации ядра. Рекомендуется для начинающих. Вызывается командой make oldconfig

    defconfig - файл конфигурации создаётся автоматически, основываясь на значениях по-умолчанию для данной конкретной архитектуры. Вызывается командой make defconfig

    menuconfig - псевдографический интерфейс ручной конфигурации, не требует последовательного ввода значений параметров. Рекомендуется для использования в терминале. Вызов: make menuconfig

    gconfig и xconfig - графические конфигураторы для ручной настройки. Вызов: make gconfig

    Make xconfig

    соответственно

    localmodconfig и localyesconfig - автоматические конфигураторы. Конфиг создается на основе вызванных в данных момент модулей и запущенного ядра. Разница между этими двумя конфигураторами в количестве модулей. В первом случае их будет не менее 50% ядра, а во-втором не больше 2 модулей. Вызов: make localmodconfig

    Make localyesconfig

    соответственно

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

Шаг 5. Сборка ядра

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

Fakeroot make-kpkg -j 5 --initrd --append-to-version=-custom kernel_image kernel_headers #-j <количество ядер процессора>+1

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

Шаг 6. Установка образов и заголовков ядра

Когда сборка ядра подошла к концу, в вашей домашней папке появятся два deb-пакета. Их и необходимо установить. Для этого выполните команды:

Cd ~/ sudo dpkg -i linux-image-2.6.x.y-custom_2.6.x.y-custom-10.00.Custom_arc.deb sudo dpkg -i linux-headers-2.6.x.y-custom_2.6.x.y-custom-10.00.Custom_arc.deb

где 2.6.x.y - версия собранного ядра, arc - архитектура процессора (i386 - 32-бит, amd64 - 64-бит).
Если вы не знаете точного названия пакета, выведите список файлов в домашнем каталоге командой

и найдите эти самые два пакета.

Шаг 7. Генерация начального RAM-диска

Для корректной работы Ubuntu требует наличия образа начального RAM-диска. Чтобы его создать, выполните команду:

Sudo update-initramfs -c -k 2.6.x.y-custom

где 2.6.x.y - версия собранного ядра.

Шаг 8. Обновление конфигурации загрузчика GRUB

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

Sudo update-grub

Файл menu.lst (для GRUB версии 1) или grub.cfg (для GRUB версии 2) обновится в соответствии с наличием установленных операционных систем и образов ядер.

Шаг 9. Проверка ядра

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

Uname -r

Она выведет на экран используемую версию ядра.

Если всё сделано правильно, то вы можете удалить архивы с исходным кодом и весь каталог linux-2.6.x.y в вашей домашней папке. Это освободит около 5 ГБ на вашем жёстком диске (размер освобождаемого пространства зависит от параметров сборки).

На этом процесс сборки и установки завершён, поздравляю!

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

Общая информация

Просмотреть список подключенных модулей в данный момент возможно с помощью команды :

Print-server:/tmp/123# lsmod Module Size Used by ipv6 235396 10 loop 12748 0 parport_pc 22500 0 parport 31084 1 parport_pc snd_pcm 62660 0 snd_timer 17800 1 snd_pcm snd 45636 2 snd_pcm,snd_timer soundcore 6368 1 snd snd_page_alloc 7816 1 snd_pcm psmouse 32336 0 serio_raw 4740 0 pcspkr 2432 0 i2c_piix4 7216 0 i2c_core 19828 1 i2c_piix4 ac 4196 0 button 6096 0 evdev 8000 0 ext3 105576 5 jbd 39476 1 ext3 mbcache 7108 1 ext3 sd_mod 22200 7 ide_cd_mod 27684 0 cdrom 30176 1 ide_cd_mod ata_generic 4676 0 ahci 23596 6 libata 140448 2 ata_generic,ahci scsi_mod 129548 2 sd_mod,libata dock 8304 1 libata e1000 102656 0 piix 6568 0 ide_pci_generic 3908 0 ide_core 96168 3 ide_cd_mod,piix,ide_pci_generic thermal 15228 0 processor 32576 1 thermal fan 4196 0 thermal_sys 10856 3 thermal,processor,fan

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

В примере также можно видеть, что соответствующими модулями осуществляется поддержка таких устройств как видео, SATA, SCSI, дискеты и звуковые карты, а также сетевые устройства, например, IPV6, поддержка файловых систем, такой как ext3, и Remote Procedure Call (RPC) компании Sun.

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

Команда modinfo выдает информацию об одном или нескольких модулях.

Kernel-server:/tmp/123$ /sbin/modinfo ipv6 filename: /lib/modules/2.6.26-2-686/kernel/net/ipv6/ipv6.ko alias: net-pf-10 license: GPL description: IPv6 protocol stack for Linux author: Cast of dozens depends: vermagic: 2.6.26-2-686 SMP mod_unload modversions 686

В приведенном примере видно, что команда modinfo показывает информацию о модуле ipv6, которая включает такие параметры как имя файла и путь, лицензия, описание, автор модуля и др. Параметры модуля могут различаться в зависимости от модуля.

Отдельно хотелось бы затронуть параметр filename, содержащий путь к файлу модуля и имя файла. Имя файла модуля ipv6 оканчивается на .ko , это говорит нам, что данный модуль относится к версии ядра 2.6 . В более ранней версии ядра - 2.4 , имена модулей оканчивались на .o ). Как видно, модуль расположен в подкаталогах каталога /lib/modules/2.6.26-2-686/, в данном пути, каталог 2.6.26-2-686 соответствует версии ядра (а так же выводу команды uname -r, что активно используется в написании скриптов). Структура подкаталогов указанного каталога отражает взаимосвязь модулей ядра и назначения модулей, думаю пример ниже это наглядно покажет:

Kernel-server:/tmp/123# ls -l /lib/modules/2.6.26-2-686/kernel/ итого 12 drwxr-xr-x 3 root root 1024 Окт 1 15:40 arch drwxr-xr-x 3 root root 4096 Окт 1 18:02 crypto drwxr-xr-x 54 root root 1024 Окт 1 15:40 drivers drwxr-xr-x 51 root root 3072 Окт 1 18:02 fs drwxr-xr-x 6 root root 1024 Окт 1 18:02 lib drwxr-xr-x 37 root root 1024 Окт 1 15:40 net drwxr-xr-x 11 root root 1024 Окт 1 18:02 sound

В примере видно, что модули ядра расположены по подкаталогам: fs , что наводит на мысль, что тут расположены модули файловой системы, sound - модули звуковых карт и так далее.

Как же нам узнать, какие модули ядра нужны , а какие можно удалить?

А все просто: если счетчик Used By равен нулю, то модуль ядра никем и ничем не используется. Соответственно, его можно удалить.

Удаление модуля ядра происходит командой rmmod module_name .

Удаленный модуль может понадобиться в процессе работы, для загрузки модуля необходимо выполнить команду: insmod /path/to/module.ko

Интересный пример использования insmod в купе с другими командами:

# uname -r 2.6.27-ovz-smp-alt9 # insmod /lib/modules/`uname -r`/kernel/drivers/block/floppy.ko # rmmod floppy # modinfo -F filename floppy /lib/modules/2.6.27-ovz-smp-alt9/kernel/drivers/block/floppy.ko # insmod $(modinfo -F filename floppy) # lsmod | grep floppy floppy 58244 0

Существует так же и другая команда для управления модулями : . Особенность данной команды в том, что она удаляет/добавляет модули с учетом зависимостей между модулями (зависимости между модулями прописаны в файле /lib/modules/версия/modules.dep ). Пример использования:

# modprobe -r vfat vfat: Device or resource busy # lsmod | grep fat vfat 13132 1 fat 38744 1 # umount /windows/D # modprobe -r vfat # modprobe -v --show vfat /sbin/insmod /lib/modules/2.4.21-37.0.1.EL/kernel/fs/fat/fat.o /sbin/insmod /lib/modules/2.4.21-37.0.1.EL/kernel/fs/vfat/vfat.o # lsmod | grep fat # modprobe -v vfat /sbin/insmod /lib/modules/2.4.21-37.0.1.EL/kernel/fs/fat/fat.o Using /lib/modules/2.4.21-37.0.1.EL/kernel/fs/fat/fat.o Symbol version prefix "" /sbin/insmod /lib/modules/2.4.21-37.0.1.EL/kernel/fs/vfat/vfat.o Using /lib/modules/2.4.21-37.0.1.EL/kernel/fs/vfat/vfat.o # lsmod | grep fat vfat 13132 0 (unused) fat 38744 0

Как уже выше было сказано, модули ядра имеют зависимости друг от друга, которые прописаны в файле /lib/modules/версия/modules.dep. Данный файл формируется командой depmod , которая при выполнении просматривает структуру каталогов /lib/modules/текущая_версия_ядра/ и формирует информацию о зависимостях.

Так же хочу отметить, что в Linux существует конфигурационный файл /etc/modules.conf, к которому обращается и modprobe и debmod . Данный файл в большинстве своем используется для корректировки алиасов модулей. Некоторые ОС используют другие конфигурационные файлы, таки как /etc/modprobe.conf или каталог с конфигурационными файлами - /etc/modprobe.d/.

Еще отличным источником информации о действующем ядре Linux является , который расположен в /boot/config-2.6.... Используя можно получить достаточно информации (например, поддерживает ли ядро файловую систему cifs):

Samba-server:~# grep CONFIG_SMB_FS /boot/config-2.6.32-5-686 # CONFIG_SMB_FS is not set samba-server:~# grep CONFIG_CIFS /boot/config-2.6.32-5-686 CONFIG_CIFS=m # CONFIG_CIFS_STATS is not set CONFIG_CIFS_WEAK_PW_HASH=y CONFIG_CIFS_UPCALL=y CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y # CONFIG_CIFS_DEBUG2 is not set CONFIG_CIFS_DFS_UPCALL=y CONFIG_CIFS_EXPERIMENTAL=y

На сегодня все. Как всегда - буду очень рад Вашим комментариям! В мы научимся собирать свое ядро.

С Уважением, Mc.Sim!

Представьте, что у вас имеется образ ядра 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 с тем, чтобы найти сжатый поток и получить из него распакованный образ.

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