Журнал LinuxFormat - перейти на главную

LXF129:kernelhack

Материал из Linuxformat
(Различия между версиями)
Перейти к: навигация, поиск
(викификация, оформление)
 

Текущая версия на 14:27, 11 апреля 2011

Вскрываем ядро

Содержание

[править] ЯДРО ЖДЕТ ПОМОЩИ ОТ ТЕБЯ

А ТЫ записался в армию 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. Надеюсь, это означает, что каждый, прочитавший эту статью, в ближайшее время направит свою поправку к ядру, и, войдя во вкус, станет и дальше вносить свой вклад в крупнейшей программный проект в истории вычислительной техники.

Персональные инструменты
купить
подписаться
Яндекс.Метрика