LXF129:kernelhack

Материал из Linuxformat.

Перейти к: навигация, поиск
Вскрываем ядро

Содержание

ЯДРО ЖДЕТ ПОМОЩИ ОТ ТЕБЯ

А ТЫ записался в армию Linux? Командир ядра Грег Кроа-Хартман проведет курс молодого бойца...

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

Но если все прекрасно работает, что тогда исправлять?.. Не отчаивайтесь: разработчики ядра Linux рады любой помощи, и в дереве исходников хватает кода, ожидающего пересмотра. Один из примеров – ветка drivers/staging/: она содержит кучу драйверов, не удовлетворяющих стандартам ядра. Драйверы поместили сюда, чтобы другие разработчики могли помочь привести их в порядок для дальнейшего включения в основную часть дерева ядра Linux.

Каждый драйвер в директории drivers/staging/ содержит в файле TODO список вещей, которые надо сделать, чтобы код мог переместиться в основную ветку ядра. Большинство драйверов содержат в своем TODO строчку

- fix checkpatch.pl issues

Разберемся, что это означает и что тут можно сделать. Каждый объемный проект нуждается в выработке единого стиля кодирования, для выживаемости при совместной работе множества программистов. Мечта (и задача) любого разработчика ядра Linux – привлечь других к помощи по поиску проблем в своем коде, и хранение кода в общепринятом формате позволяет всем легко брать его, модифицировать и отмечать ошибки. Так как каждая строчка до ее включения в код ядра просматривается как минимум двумя разработчиками, важно придерживаться выработанного стиля.

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

Из малого желудя...

Одно из лучших руководств по Git поставляется с самим Git. Вы можете прочитать его, запустив

$ man gittutorial

после того, как установите Git на свой ПК.

Итак, запустите и установите Git на вашу Linux-систему, используя свой любимый менеджер пакетов, затем выполните клонирование основного репозитория ядра Linux:

$ mkdir ~/linux
$ cd ~/linux
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git

Внутри директории linux/ должна создаться поддиректория linux-2.6. Все, чем мы с вами займемся, будет происходить в данном каталоге, поэтому для начала перейдем в него:

$ cd ~/linux/linux-2.6

Теперь, имея сырой исходный код, как собрать его и установить его в вашу систему? Это более объемная задача, выходящая за рамки этой статьи. К счастью, на эту тему есть книга Linux Kernel in a Nutshell, ее можно найти в свободном доступе онлайн на http://www.kroah.com/lkn/.

Задание правил

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

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

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

checkpatch.pl

Руководствуясь этими несложными правилами пробелов и фигурных скобок, давайте запустим скрипт checkpatch.pl на каком-нибудь коде и посмотрим, что он нам выдаст:

$ ./scripts/checkpatch.pl --help
Usage: checkpatch.pl [OPTION]... [FILE]...
Version: 0.30
Options:
-q, --quietquiet
--no-tree	 run without a kernel tree
--no-signoff	 do not check for ‘Signed-off-by’ line
--patch		 treat FILE as patchfile (default)
--emacs	 emacs compile window format
--terse		 one line per report
-f, --file	 treat FILE as regular source file
--subjective, --strict enable more subjective tests
--root=PATH	 PATH to the kernel tree root
--no-summary	 suppress the per-file summary
--mailback	 only produce a report in case of
warnings/errors
--summary-file	 include the filename in summary
--debug KEY=[0|1] turn on/off debugging of KEY, where
KEY is one of ‘values’, ‘possible’, ‘type’, and ‘attr’ (default is
all off)
--test-only=WORD report only warnings/errors containing
WORD literally
-h, --help, --version display this help and exit When FILE is -
read standard input.

Мы будем чаще всего использовать --terse и --file: они позволяют видеть проблемы в более простом отчете и работать со всем файлом, а не отдельной поправкой.

Итак, давайте прихватим какой-нибудь файл из ядра и посмотрим, что о нем скажет checkpatch.pl:

$ ./scripts/checkpatch.pl --file --terse drivers/staging/comedi/
drivers/ni_labpc.c drivers/staging/comedi/drivers/ni_
labpc.c:4: WARNING: line over 80 characters
...
drivers/staging/comedi/drivers/ni_labpc.c:486: WARNING:
braces {} are not necessary for single statement blocks
... 
drivers/staging/comedi/drivers/ni_labpc.c:489: WARNING:
braces {} are not necessary for single statement blocks
...
drivers/staging/comedi/drivers/ni_labpc.c:587: WARNING:
suspect code indent for conditional statements (8, 0)
...
drivers/staging/comedi/drivers/ni_labpc.c:743: WARNING:
printk() should include KERN_ facility level
drivers/staging/comedi/drivers/ni_labpc.c:750: WARNING:
kfree(NULL) is safe this check is probably not required
...
drivers/staging/comedi/drivers/ni_labpc.c:2028: WARNING:
EXPORT_SYMBOL(foo); should immediately follow its
function/variable
total: 0 errors, 76 warnings, 2028 lines checked

Я удалил из предыдущего вывода множество предупреждений: их целых 76, и все они являются вариантами приведенных выше.

Как видите, инструмент checkpatch.pl говорит, где код превышает 80-символьный лимит и где используются лишние скобки, а также показывает другие ненужные вещи, которые следует удалить из файла.

Теперь, зная, что нужно делать, откройте свой любимый текстовый редактор и исправьте что-нибудь: например, проблему с фигурными скобками (см. врезку) – это нетрудно. Взглянем на исходный код: строки 486–490 выглядят так:

if (irq) {
             printk(“, irq %u”, irq);
      }
      if (dma_chan) {
             printk(“, dma %u”, dma_chan);
      }

Простое удаление лишних фигурных скобок даст нам такой результат:

if (irq)
             printk(“, irq %u”, irq);
      if (dma_chan)
             printk(“, dma %u”, dma_chan);

Сохраните файл и запустите checkpatch вновь для проверки, что предупреждение исчезло:

$ ./scripts/checkpatch.pl --file --terse drivers/staging/comedi/drivers/ni_labpc.c | grep 486
$

И, конечно, надо собрать файл и проверить, что ничего не сломалось:

$ make drivers/staging/comedi/drivers/ni_labpc.o
CHK include/linux/version.h
CHK include/generated/utsrelease.h
CALL scripts/checksyscalls.sh
CC [M] drivers/staging/comedi/drivers/ni_labpc.o

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

Про Git

Вероятно, главное, что следует помнить, работая с Git – то, что ни в коем случае нельзя соваться в ту ветку (branch), которой занимается Линус, так называемую ‘master’. Надо создать собственное ответвление и работать в нем. Это гарантирует, что у вас без осложнений применятся изменения, появившиеся в ветви Линуса. Чтобы создать новую ветку с именем ‘tutorial’ и «выписать» ее (check out), выполните следующие действия:

$ git branch tutorial
$ git checkout tutorial

Вот и все. Теперь вы в ветви ‘tutorial’ вашего репозитория ядра, что видно из следующей команды:

$ git branch
master
* tutorial

Звездочка * перед именем ‘tutorial’ показывает, что вы в правильной ветви. Теперь-то и вносите изменения в код ядра!

===Другие радости Git

Если вы редактируете файл внутри репозитория Git, ваши изменения отслеживаются Git. Вы можете видеть это, запустив git status:

$ git status
# On branch tutorial
# Changed but not updated:
# (use “git add <file>...” to update what will be committed)
# (use “git checkout -- <file>...” to discard changes in working directory)
#
# modified: drivers/staging/comedi/drivers/ni_labpc.c
#
no changes added to commit (use git add and/or git commit -a)

Данный вывод показывает, что мы находимся в ветви с именем ‘tutorial’ и имеем на текущий момент один модифицированный файл, ni_labpc.c. Попросив Git показать нам, что изменилось, мы увидим следующие строки: мы увидим следующие строки:

$ git diff
diff ­­git a/drivers/staging/comedi/drivers/ni_labpc.c b/
drivers/staging/comedi/drivers/ni_labpc.c
index dc3f398..a01e35d 100644
­­­ a/drivers/staging/comedi/drivers/ni_labpc.c
+++ b/drivers/staging/comedi/drivers/ni_labpc.c
@@ ­483,12 +483,10 @@ int labpc_common_attach(struct
comedi_device *dev, unsigned long iobase,
      printk(“comedi%d: ni_labpc: %s, io 0x%lx”, dev­>minor,
thisboard­>name,
            iobase);
­     if (irq) {
+      if (irq)
             printk(“, irq %u”, irq);
­     }
­     if (dma_chan) {
+      if (dma_chan)
             printk(“, dma %u”, dma_chan);
­     }
      printk(“\n”);
      if (iobase == 0) {

Это формат, применяемый инструментом patch для автоматического внесения изменений в код. + или - в начале строк показывают, что строки будут удалены или добавлены. Чтение вывода изменений в формате diff скоро станет для вас естественным; в этом формате и нужно отправлять правки куратору [maintainer] ядра для их применения.

Фигурные скобки

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

if (error != ­ENODEV) {
       foo();
       bar();
 }

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

if (error != ­ENODEV) {
       foo();
       bar();
 } else {
       report_error();
       goto exit;
 }

Если скобки в утверждении не нужны, не ставьте их совсем:

if (error != ­ENODEV)
       foo();
 else
      goto exit;

Единственное исключение для открывающих скобок – это описание функции; тут их следует помещать в новой строке, вот так:

int function(int *baz)
 {
       do_something(baz);
       return 0;
 }

Описания, описания, описания

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

Вот пример изменений, не так давно внесенных в дерево ядра Linux:

USB: otg: Fix bug on remove path without transceiver
In the case where a gadget driver is removed while no
transceiver was found at probe time, a bug in otg_put_
transceiver() will trigger.
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
Acked-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
--- a/drivers/usb/otg/otg.c
+++ b/drivers/usb/otg/otg.c
@@ -43,7 +43,8 @@ EXPORT_SYMBOL(otg_get_transceiver);
void otg_put_transceiver(struct otg_transceiver *x)
{
- put_device(x->dev);
+ if (x)
+ put_device(x->dev);
}

Первая строка – краткое описание того, какую часть ядра изменяет правка и, вкратце, что она делает:

USB: otg: Исправление ошибки, возникающей при удалении устройства без трансивера

Затем идет более подробный параграф, описывающий, зачем нужны изменения:

Если драйвер устройства удаляется, а трансивер при зондировании обнаружен не был, возникает ошибка в otg_put_ transceiver().

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

Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
Acked-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

Термин ‘Signed-off-by:’ [Подписано к выпуску мной] ссылается на право данного разработчика внести данные изменения, причем они предлагаются под лицензией, приемлемой для добавления в дерево исходных текстов ядра Linux.

Это соглашение называется Developer’s Certificate of Origin [Сертификат разработчика о происхождении]; его полная версия находится в файле Documentation/SubmittingPatches дерева исходных текстов ядра Linux.

Если коротко, Developer’s Certificate of Origin содержит следующее:

  1. Эти изменения создал я; или
  2. Основано на предыдущей работе с совместимой лицензией; или
  3. Предоставлено мне по (1), (2), или (3) и не модифицировалось
  4. Это общественный вклад.

Смысл соглашения очень прост: это гарантия, что все участники знают о легальности внесения изменений. Каждый человек, к которому попадает поправка, по мере прохождения цепочки разработчик–куратор добавляет к ней свой ‘Signed-off-by’ до ее внесения в дерево исходных текстов. Так гарантируется, что каждая строка кода в ядре Linux может быть отслежена назад вплоть до разработчика, который ее создал, и разработчика, который ее проверил.

Теперь, зная структуру поправок, мы может создать свою. Сперва велим Git зафиксировать [commit] сделанные нами изменения:

$ git commit drivers/staging/comedi/drivers/ni_labpc.c

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

# Please enter the commit message for your changes. Lines
starting with ‘#’ will be ignored, and an empty message
aborts the commit.
# Explicit paths specified without -i nor -o; assuming --only
paths...
# On branch tutorial
# Changes to be committed:
# (use “git reset HEAD <file>...” to unstage)
#
# modified: drivers/staging/comedi/drivers/ni_labpc.c

Задаем для поправки строку описания [все должно быть на английском, – прим. пер.]:

Staging: comedi: fix brace coding style issue in ni_labpc.c
[исправление проблемы стиля использования фигурных скобок в ni_labpc.c]

а затем сообщаем подробности:

This is a patch to the ni_labpc.c file that fixes up a brace
warning found by the checkpatch.pl tool
[Это поправка для файла ni_labpc.c, исправляющая проблему
с фигурными скобками, найденную инструментом checkpatch.pl]

Затем добавляем строку Signed-off-by:

Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

и уже после этого сохраняем файл. Git должен внести изменения, напечатав следующее:

[tutorial 60de825] Staging: comedi: fix brace coding style issue in ni_labpc.c
1 files changed, 2 insertions(+), 4 deletions(-)

Если вы вызовете команду git show HEAD для просмотра самых последних изменений, она должна показать ваш «коммит» полностью:

$ git show HEAD
commit 60de825964d99dee56108ce4c985a7cfc984e402
Author: Greg Kroah­Hartman <gregkh@suse.de>
Date: Sat Jan 9 12:07:40 2010 ­0800
    Staging: comedi: fix brace coding style issue in ni_labpc.c
    This is a patch to the ni_labpc.c file that fixes up a brace
warning found by the checkpatch.pl tool
Signed­off­by: My Name <my_name@my_email_domain>
diff ­­git a/drivers/staging/comedi/drivers/ni_labpc.c b/
drivers/staging/comedi/drivers/ni_labpc.c
index dc3f398..a01e35d 100644
­­­ a/drivers/staging/comedi/drivers/ni_labpc.c
+++ b/drivers/staging/comedi/drivers/ni_labpc.c
@@ ­483,12 +483,10 @@ int labpc_common_attach(struct
comedi_device *dev, unsigned long iobase,
printk(“comedi%d: ni_labpc: %s, io 0x%lx”, dev­>minor,
thisboard­>name,
     iobase);
­      if (irq) {
+       if (irq)
printk(“, irq %u”, irq);
­      }
­      if (dma_chan) {
+       if (dma_chan)
printk(“, dma %u”, dma_chan);
­      }
printk(“\n”);
if (iobase == 0) {

Итак, вы создали свою первую поправку к ядру!

Внесем поправку в дерево ядра

А как внести созданную поправку в дерево ядра? Разработка ядра Linux по прежнему осуществляется через электронную почту, включая и поправки, и рецензирование.

Сперва экспортируем нашу поправку в формат, используемый в письмах куратору, ответственному за их внесение. Для этого в Git опять-таки есть команда, format-patch; ею и воспользуйтесь:

$ git format-patch master..tutorial
0001-Staging-comedi-fix-brace-coding-style-issue-in-ni_la.patch

Этой командой мы создадим все поправки, содержащие различия между веткой ‘master’ (та самая ветка Линуса – помните, что я говорил в начале?) и вашей частной веткой ‘tutorial’.

Там содержится только одно изменение – наша поправка. Теперь она в файле 0001-Staging-comedi-fix-brace-coding-style-issue-in-ni_la.patch в нашей директории в формате, пригодном к пересылке.

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

$ ./scripts/checkpatch.pl 0001-Staging-comedi-fix-brace-
coding-style-issue-in-ni_la.patch
total: 0 errors, 0 warnings, 14 lines checked
0001-Staging-comedi-fix-brace-coding-style-issue-in-ni_la.patch has no obvious style problems and is ready for submission.

Все прекрасно…

… но кому посылать поправку? Разработчики ядра весьма упростили и это, предусмотрев скрипт, который сообщает, кого оповестить. Он называется get_maintainer.pl и содержится в той же поддиректории scripts/ в дереве исходных кодов ядра. Скрипт изучает файлы, которые вы модифицировали поправкой, и сравнивает их с файлом MAINTAINERS в дереве исходных текстов ядра (который описывает, кто за какую часть ядра отвечает), а также просматривает историю предыдущих изменений файла. На основе этого он магически генерирует список людей, которых следует оповестить о поправке, вместе с их адресами электронной почты.

Осталось только запустить свой любимый почтовый клиент и послать поправку всем адресатам, выданным get_maintainer.pl, верно? Э, не спешите. Почти все обычные почтовые клиенты вредят файлам исправлений, разрывая строки, где не надо, заменяя табуляцию на пробелы, съедая пробелы и делая прочие пакости.

Чтобы получить информацию об этих проблемах и узнать, как правильно сконфигурировать большинство почтовых клиентов, загляните в файл Documentation/email-clients.txt в дереве исходных кодов ядра. Он должен помочь вам, если вы намерены отсылать поправки через свой обычный почтовый клиент. Но мы пойдем другим путем...

Git сам умеет посылать поправки, созданные с помощью git format-patch, соответствующим разработчикам. Нужна лишь команда git send-email:

$ git send-email --to gregkh@suse.de --to wfp5p@virginia.edu \
 --cc devel@driverdev.osuosl.org \
 --cc linux-kernel@vger.kernel.org \
0001-Staging-comedi-fix-brace-coding-style-issue-in-ni_la.patch

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

Что дальше?

Итак, вы создали поправку и отослали ее; разработчик, кому она была адресована, должен через пару дней ответить по почте нечто приятное типа «спасибо за поправку, я ее применил» [“thanks for the patch, I have applied it”] либо комментарии по поводу того, что нужно доделать, чтобы поправка была принята. Не получив ответа за неделю, пошлите поправку снова. Не бойтесь, что это обозлит разработчика: настойчивость – ключ к привлечению внимания занятого куратора подсистемы ядра.

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

Личные инструменты
  • Купить электронную версию
  • Подписаться на бумажную версию