Linux src что это

Обновлено: 06.07.2024

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

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

Файловая система /var содержит файлы, которые изменяются в ходе работы системы. Например, там лежат каталоги буферов для почты, новостей, печати и т.п., файлы протоколов, форматированных man-страниц и временные файлы.

Файловая система /home содержит все домашние каталоги пользователей системы, то есть, все реальные данные системы. Отделение пользовательских каталогов от системы упрощает резервирование данных. Если пользователей много, эта файловая система может быть разделена на несколько (например, /home/students и /home/teacher).

Хотя различные части выше были названы файловыми системами, это не значит, что они фактически находятся на отдельных дисках. Они могут храниться на одном диске, если речь идето небольшом сервере или личном однопользовательском компьютере. Дерево каталогов может быть также разделено по-другому, в зависимости от того, насколько большие диски использованы, и как распределено место на них для различных целей, тем не менее, важно, чтобы все стандартные имена работали; даже если, скажем, /var и /usr фактически на том же самом разделе, имена /usr/lib/libc.a и /var/log/messages должны работать, например, перемещая файлы из /var в /usr/var, и делая /var ссылкой на /usr/var.

Структура файловой системы в Unix группирует файлы по их назначению, то есть, все команды находятся в одном месте, все файлы данных в другом, документация в третьем и так далее. Альтернативный вариант группировал файлы согласно программе, которой они принадлежат, то есть, все Emacs-файлы были бы в одном каталоге, весь TeX в другом, и так далее. Проблема с последним подходом состоит в том, что это делает трудным совместно использовать файлы (каталог программ часто содержит статические, общие, изменяющиеся и личные файлы), и иногда даже находить файлы, например, man-страницы в огромном числе мест, благодаря чему поиск их программой просмотра сильно осложняется.

Корневая файловая система

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

Ниже приведено описание основных каталогов верхнего уровня.

Из готового тарбола (если он учитывает все нужные патчи) можно получить исполняемые программы с помощью следующих команд:

1 разархивировать тарбол:

Создаем папку, куда будем разархивировать тарбол,

Копируем туда тарбол

Непосредственно разархивируем в папку:

- разархивация архивов типа tar.gz и tgz

- разархивация архивов типа tar.bz и tbz

файлы разархивируются в текущую папку (для работы с архивами очень удобно использовать Midnight Commander - MC - свободный клон NC)

Переходим в папку с разархивированным тарболом

2 конфигурим пакет

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

3 Создаем пакет:

4 Устанавливаем пакет

(последний этап можно теоретически сделать и в ручную скопировав файлы в нужные места) Хочу подчеркнуть, что это наиболее опасный этап - перед ним желательно позаботится о том, как восстановить старый пакет, если в новом есть ошибки.

7.2 Установка программ из сорца (.src.rpm)

Программисты создают проект программы (например с помощью Kdevelop), в котором есть все makefile и файлы конфигурации (configure), а потом упаковывают их в тарболы. В случае доработки пакета создаются Patch-и к исходным текстам, которые заменяют одни строки текста программ на другие. Тарболы и прикладываемые к ним patch-и упаковываются в пакеты-сорцы (.SRC.RPM)(бывают и другие системы пакетов - но я говорю о дистрибутивах на основе RPM - Red Hat, Mandrake, SuSe). RPM-пакет - это особо организованный архив, в который помимо данных (тарбола и патчей - для сырца, необходимых программ - для бинарного RPM) упакованы скрипты установки и обновления. C помощью сорца можно создать бинарный RPM - т.е. такой RPM, в котором упакованы исполняемые пакеты. Причем, если RPM создан на текущей машине, он теоретически будет наилучшим образом подходить к текущей конфигурации пакетов (именно поэтому многие администраторы наиболее важные пакеты собирают из сырцов заново на своей машине). В результате установки сорца- в директорию /usr/src/RPM/source помещаются все необходимые тарболы (обычно один) и патчи (может быть много, а может быть и не одного - все зависит от разработчика и составителя конкретного RPM). - В директорию /usr/src/RPM/spec помещается установочный скрипт (файл с расширением spec) в котором разработчик RPM помещает все действия по установке пакета - разархивирование тарбола, накладывания патчей, транслирование и т.д. Разработано уже много макросов для spec-файлов. С наиболее старыми из них и общей теорией их построения а также опциями команды rpm можно познакомится в RPM-HOWTO . При построении пакета все операции с исходным текстом программ обычно (но не всегда) помещаются в /usr/src/RPM/builder, а новые полученные пакеты (новый сырец и новый бинарник) помещаются соответственно в /usr/src/RPM/RPMS и /usr/src/RPM/SRPMS. Получить из установленного сорца соответствующий пакет можно с помощью команды

7.3 Установка программ из пакетов .RPM

В дистрибутивах Red Hat, Mandrake, Suse, AltLinux, ASP Linux и некоторых других, программы (состоящие, как правило, из нескольких файлов) распространяются объединенными в пакеты формата RPM (RedHat Packet Manager). С помощью программы rpm можно легко устанавливать, модифицировать, удалять и создавать пакеты программного обеспечения, а также получать о них разнообразную информацию. Все эти дистрибутивы (кроме программы начальной установки) состоят из таких пакетов. Каждый пакет определяется именем программы, номером ее версии и номером версии релиза этой программы дистрибутива, а также архитектурой пакета. Например, bash-2.0.5-alt2.i586.rpm: в этом пакете

  • имя - bash,
  • номер версии - 2.0.5,
  • номер релиза - alt2,
  • архитектура - i586.

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

Далее приводятся возможные параметры.

Установка пакета.

Вы можете установить программу, используя опцию -i (опции -v и -h выставлены здесь для того, чтобы включить визуальное отображение процесса установки). Например, для того, чтобы установить klyx, наберите:

(настоящее имя зависит от версии программы на доступном носитеое). Заметим, что ipl6mdk означает, что пакет был модифицирован ALT Linux Team (ранее - IPLabs Linux Team) для русской редакции, это его шестая сборка, он входит в дистрибутив Mandrake. i586 указывает на то, что он скомпилирован для процессоров не ниже Pentium(tm). Наличие в имени пакета аббревиатуры alt2 означает, что пакет был собран ALT Linux Team и это его вторая сборка.

Обновление пакета.

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

Удаление пакета.

Если вы желаете удалить пакет из системы, внимательно введите:

то есть, например, для пакета klyx:

Если в процессе удаления пакета произойдет нарушение зависимостей, программа rpm сообщит об этом.

Информация о пакете.

Вы можете запросить у rpm ряд полезной информации о пакете, не устанавливая его - например, бывает удобно просмотреть список всех файлов пакета или краткое описание его возможностей. Для этого используйте опцию -q (query, запрос). -qi используется для получения некоторой информации о ранее установленном пакете; -qip используется для еще не установленных пакетов. В этом случае вы должны указать полный путь и имя пакета (например, /mnt/cdrom/Mandrake/RPMS/klyx-0.10.9-ipl6mdk.i586.rpm); -ql используется для того, чтобы просмотреть список файлов пакета. Добавьте p, если пакет еще не был установлен; -qa выдает список всех установленных пакетов (не нужно указывать имя пакета).

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

Если надо установить два или более пакетов, зависящих друг от друга, то установите их одновременно:

Часто бывает удобнее, однако, применять программы специально созданные для данного дистрибутива, например, rpmdrake, разработанную MandrakeSoft, kpackage из KDE, gnorpm из GNOME или систему apt (AltLinux). В дистрибутивах, родственных Mandrake удобнее управлять пакетами через графическую оболочку rpmdrake, которую можно запустить через панель управления DrakConf (находящуюся на рабочем столе). Можно выбрать два режима работы - установка или удаление - при помощи кнопок в правом верхнем углу. Выделив пакет, можно получить информацию о нем, входящих в его комплект файлах, а также некоторую другую. Нажав кнопку "Удалить выбранное" или "Установить выбранное", можно удалить или установить выбранные пакеты. Часто бывает так, что требуемый пакет для нормального функционирования требует другие; в этом случае программа предложит вам установить или удалить еще несколько пакетов. При удалении пакетов необходимо соблюдать осторожность, чтобы не удалить важные части системы, например пакеты kernel или glibc. Для использования функции обновления пакетов необходимо указать программе через меню Файл|Настройки дополнительный источник пакетов, в качестве которого может выступать как ресурс Internet, так и локальный каталог или диск CD-ROM. Установку пакетов весьма удобно выполнять и через консольную программу urpmi - с тем отличием, что все действия будут выполняться менее наглядно. Для установки пакетов, поставляемых ALT Linux Team, можно даже запускать программу urpmi не от имени суперпользователя, а от обычного пользователя; единственное, что необходимо сделать для этого ? добавить его в группу urpmi. Для получения дополнительной информации наберите man rpm.


Любой программист знает, что теоретически он может внести свой посильный вклад в развитие Linux ядра. С другой стороны, подавляющее большинство уверено, что занимаются этим исключительно небожители, а процесс контрибьюта в ядро настолько сложен и запутан, что обычному человеку разобраться в нём нет никакой возможности. А значит, и надобности.
Сегодня мы попробуем развеять эту легенду и покажем, как абсолютно любой инженер при наличии достойной идеи, воплощённой в коде, может предложить ее на рассмотрение Linux community для включения в ядро.

0. Подготовка

Как и перед любой инженерной операцией, всё начинается с подготовки своего рабочего места. И первейшее здесь действие — это завести себе аккаунт с адекватным именем. В идеальном мире это будет просто транскрипция имени и фамилии. Если за учётку вроде MamkinC0d$r или Developer31337 в других местах пальцем в вас тыкать не будут, то правила LKC (Linux kernel community) такое прямо запрещают — инкогнито контрибьютить в ядро не принято.

Далее вам понадобится место на локальной машине. Сама папка Linux со скачанными исходниками весит чуть меньше 3-х гигов. Но если ядро пробовать собирать, то вместе с модулями займёт все 30 GB.

Захотелось собрать несколько веток? Умножаем 30 на число веток.
И помним — скорость сборки прямо связана с количеством доступных ядер! Больше ядер — быстрее соберётся. Так что не стесняйтесь выделять под это самую мощную машину.

1. Mail

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

Но, как принято в уютном мирке ядра, Линус хлопнул кулаком по столу — и все пишут письма. Возможно, буквально завтра это изменится, но на момент выхода статьи это письма и только письма.

Какой email-client выбрать — есть рекомендации. Самым рекомендуемым почтовым агентом для LKC остаётся mutt. Да, тот самый текстовый почтовый клиент, от которого сводит олдскулы. Для начала mutt нужно поставить (я думаю, со своим пакетным менеджером вы и сами справитесь), а потом задать параметры в файле

Но почты недостаточно. Без Git никуда.

2. Git

Прежде чем что-то делать с исходниками ядра, нужно настроить Git. Можно конфигурировать файлы напрямую, но есть упрощающая жизнь утилита git config, через которую можно регулировать все аспекты работы Git'a.

Внутри есть три уровня настроек: общие для всех пользователей системы и для всех репозиториев (git config --system), общие для всех репозиториев конкретного пользователя (git config --global), отдельные для каждого репозитория (git config --local).

Глобальные настройки хранятся в /etc/gitconfig, настройки пользователя в

/.config/git/config, а настройки отдельных репозиториев хранятся в файле config в каталоге .git/config.

В общем случае будет достаточно законфигурировать файл для пользователя

/.gitconfig . Основная идея: при отправке коммитов должно отображаться ваше корректное имя.

signOff обязателен, чтоб в коммитах была информация об авторе. По идее, надо бы, чтобы коммиты подписывались. Была тут недавно статья на эту тему.

Отправка патча выполняется командой git send-email. У git send-email есть несколько параметров с участием smtp, которые можно (и нужно) переопределить.

Можно задавать пароль к почте через параметр --smtp-pass=p4ssw0rd или вообще захардкорить в конфиге, но это это для тех, кому терять нечего. Но если каждый раз вводить пароль лень, то есть некий хак: если username был задан (через --smtp-user или sendmail.smtpUser), а пароль не указан, тогда пароль получается через git-credential.

Итак, окно в большой мир прорубили. Можно переходить к воплощению своей грандиозной идеи в коде.

3. Coding

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

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

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

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

Если бродить вслепую по гуглу не хочется, то вся максимально полезная информация по ядру сконцентрирована тут. Прочитать стоит действительно всё. Особенно правильно будет начать с How To о том, как правильно коммуницировать. Потому что мейнтейнеры, как правило, люди весьма занятые, и вникать в невнятно составленные письма им никакого интереса. Да и вам будет обидно, если из-за плохого описание ваше детище не примут в апстрим.

И вот свой небольшой и эффективный код вы написали, отладили, всё протестировали и готовы отправлять на рассмотрение. Но не спешите этого делать. Для начала обязательно проверьтесь на code style. В этом вам поможет ./script/checkpatch.pl . Для этого сделаем патч и отправим его на проверку.

После того, как пройдёт первое удивление и вы доустановите необходимые компоненты для python2 типа ply и git (который у меня так и не установился), наступит чудесное время исправления ошибок и ворнингов. По результатам которых вы а) поймёте, что красивый код писать вы не умеете б) потеряете всякое желание что-то куда-то отправлять. Ведь даже если отбросить все шутки, ещё можно как-то смириться с тем, что длина строк ограничена 100 символами (это начиная с версии 5.7, раньше так было вообще 80). Но вот такие места оставляют неизгладимое впечатление:

Для .h файлов строка с информацией о лицензии должна быть в ремарках / * */, а для *.c файлов должна быть в ремарках //. Такое запросто выбьет кого угодно из душевного равновесия. Вопрос: "Зачем?!" до сих пор болтается в моей голове, хотя есть вера в то, что это не просто ошибка в скриптах.

Кстати, чтобы просто проверить один файл достаточно вызвать

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

Ещё важное замечание: clang-format добрался и до ядра Linux. Файл .clang-format расположился в корне ядра в кучке с другими конфигами. Но я не советую добавлять его в хук для git. Лучше всего корректно настроить среду разработки и запомнить code style. Лично мне не понравилось как он делает переносы длинных строк. Если вдруг строка оказалась длиннее допустимой, то скорее всего функция, в которой эта строка расположилась, является кандидатом на рефакторинг, и лучше его не откладывать. С другой стороны, если у вас много уже готового кода который нужно адаптировать для ядра — clang-format может сильно облегчить вам задачу.

4. Kernel build

Несмотря на то, что процесс описан в других статьях тут и тут, я все же повторюсь.

По шагам процесс сборки ядра довольно прост, если не вдаваться в детали. Для начала ставим необходимые пакеты (использовался Debian 10):

Это без компилятора и обычного для С/С++ разработчика набора программ.
Запускаем конфигурацию:

Тут есть интересный аспект: в качестве шаблона будет браться config ядра от вашего боевого ядра, которое, скорее всего, подготовлено дистрибьютером. Для Debian 10 сборка проходит успешно, если в конфиге потереть информацию о встраиваемых в ядро сертификатах.

Перед попыткой собрать проверьте, что нужные программы уже установлены. Список тут. Чтобы собрать само ядро:

Этого достаточно для проверки собираемости, но недостаточно для запуска ядра в системе, так как без модулей ядро на реальной системе практически беспомощно.

Если какой-то модуль не собирается, просто вырубите его в ближайшем Makefile-е (если 100% уверены, что не пытались в нём что-то улучшить). Наверняка он вам не пригодится, и тратить время на исправления смысла нет.

Теперь можно деплоить то, что получилось, на эту же систему.

Хотя, конечно, экспериментировать с ядром на той же машине, где ведётся разработка — дело рискованное.

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

На моей системе загрузчик после установки ядра автоматически обновился. Если у вас этого не произошло, то это делается это на Debian-подобных системах командой:

Update: Как верно заметил gavk, ядро давно уже умеет собирать пакеты, причём как для deb, так и для rpm.
Команда

выводит весь ассортимент. Так что команда

должна собрать пакет с ядром.

5. Patches

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

Ещё можно комментарии к коммиту дополнить в человеческом текстовом редакторе.

И теперь его можно оформить в виде того самого письма. Правила хорошего тона, или best practice, если угодно — это 75 символов на строку.

В результате получите два файла. В первом 000-cover-letter.patch нужно указать заголовок письма "Subject" и основное описание патча. В описании патча пишем, для чего он создавался, кому он сделает жизнь на нашей планете лучше и каким образом. Только не словоблудим про космические корабли в Большом театре, а пишем лаконично и по делу. И не в коем случае не пишите корпоративную лабуду а-ля "Без этого патча мой бизнес встанет, меня уволят, а дети мои умрут от голода". Нет, строго по существу: "Увидел вот такую проблему вот тут, починить решил вот таким образом, исходники патча прилагаю". Всё, вы восхитительны! А если не превысили 75 символов на строку, то восхитительны в квадрате.

А ещё один волшебный скриптик ./scripts/getmaintainers.pl <patch file> позволит узнать, кому письмо отправлять.

И вот он, момент отправления письма, ради которого всё и затевалось:

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

6. Debuging

И чуть-чуть про отладку. Бонус "на сладкое" для начинающих разработчиков ядра, так сказать.

Как правило, при ошибке вы получаете лог с calltrace-ом. Там указываются имена функций и смещения. Примерно вот так:

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

Важно, чтобы в модуле сохранились символы (stripped модуль вам тут не поможет).

Выполнив команду list

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


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

Модуль ядра Linux — это скомпилированный двоичный код, который вставляется непосредственно в ядро Linux, работая в кольце 0, внутреннем и наименее защищённом кольце выполнения команд в процессоре x86–64. Здесь код исполняется совершенно без всяких проверок, но зато на невероятной скорости и с доступом к любым ресурсам системы.

Не для простых смертных

Написание модуля ядра Linux — занятие не для слабонервных. Изменяя ядро, вы рискуете потерять данные. В коде ядра нет стандартной защиты, как в обычных приложениях Linux. Если сделать ошибку, то повесите всю систему.

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

Можно в основном забыть традиционные парадигмы разработки приложений. Кроме загрузки и выгрузки модуля, вы будете писать код, который реагирует на системные события, а не работает по последовательному шаблону. При работе с ядром вы пишете API, а не сами приложения.

У вас также нет доступа к стандартной библиотеке. Хотя ядро предоставляет некоторые функции вроде printk (которая служит заменой printf ) и kmalloc (работает похоже на malloc ), в основном вы остаётесь наедине с железом. Вдобавок, после выгрузки модуля следует полностью почистить за собой. Здесь нет сборки мусора.

Необходимые компоненты

Прежде чем начать, следует убедиться в наличии всех необходимых инструментов для работы. Самое главное, нужна машина под Linux. Знаю, это неожиданно! Хотя подойдёт любой дистрибутив Linux, в этом примере я использую Ubuntu 16.04 LTS, так что в случае использования других дистрибутивов может понадобиться слегка изменить команды установки.

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

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

Установка среды разработки

На Ubuntu нужно запустить:


Устанавливаем самые важные инструменты разработки и заголовки ядра, необходимые для данного примера.

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

Начинаем

Приступим к написанию кода. Подготовим нашу среду:


Запустите любимый редактор (в моём случае это vim) и создайте файл lkm_example.c следующего содержания:


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

  • В include перечислены файлы заголовков, необходимые для разработки ядра Linux.
  • В MODULE_LICENSE можно установить разные значения, в зависимости от лицензии модуля. Для просмотра полного списка запустите:


Если мы запускаем make , он должен успешно скомпилировать наш модуль. Результатом станет файл lkm_example.ko . Если выскакивают какие-то ошибки, проверьте, что кавычки в исходном коде установлены корректно, а не случайно в кодировке UTF-8.

Теперь можно внедрить модуль и проверить его. Для этого запускаем:


Если всё нормально, то вы ничего не увидите. Функция printk обеспечивает выдачу не в консоль, а в журнал ядра. Для просмотра нужно запустить:


Вы должны увидеть строку “Hello, World!” с меткой времени в начале. Это значит, что наш модуль ядра загрузился и успешно сделал запись в журнал ядра. Мы можем также проверить, что модуль ещё в памяти:


Для удаления модуля запускаем:


Если вы снова запустите dmesg, то увидите в журнале запись “Goodbye, World!”. Можно снова запустить lsmod и убедиться, что модуль выгрузился.

Как видите, эта процедура тестирования слегка утомительна, но её можно автоматизировать, добавив:


в конце Makefile, а потом запустив:


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

Теперь у нас есть полностью функциональный, хотя и абсолютно тривиальный модуль ядра!

Немного интереснее

Копнём чуть глубже. Хотя модули ядра способны выполнять все виды задач, взаимодействие с приложениями — один из самых распространённых вариантов использования.

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

Вероятно, раньше вы уже имели дело с файлами устройств. Команды с упоминанием /dev/zero , /dev/null и тому подобного взаимодействуют с устройствами “zero” и “null”, которые возвращают ожидаемые значения.

В нашем примере мы возвращаем “Hello, World”. Хотя это не особенно полезная функция для приложений, она всё равно демонстрирует процесс взаимодействия с приложением через файл устройства.

Вот полный листинг:

Тестирование улучшенного примера


Теперь после запуска make test вы увидите выдачу старшего номера устройства. В нашем примере его автоматически присваивает ядро. Однако этот номер нужен для создания нового устройства.

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


(в этом примере замените MAJOR значением, полученным в результате выполнения make test или dmesg )

Параметр c в команде mknod говорит mknod, что нам нужно создать файл символьного устройства.

Теперь мы можем получить содержимое с устройства:


или даже через команду dd :


Вы также можете получить доступ к этому файлу из приложений. Это необязательно должны быть скомпилированные приложения — даже у скриптов Python, Ruby и PHP есть доступ к этим данным.

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

Заключение

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

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

Читайте также: