LXF129:kernelhack
(викификация, оформление) |
Текущая версия на 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) и не модифицировалось
- Это общественный вклад.
Смысл соглашения очень прост: это гарантия, что все участники знают о легальности внесения изменений. Каждый человек, к которому попадает поправка, по мере прохождения цепочки разработчик–куратор добавляет к ней свой ‘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 KroahHartman <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
Signedoffby: 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. Надеюсь, это означает, что каждый, прочитавший эту статью, в ближайшее время направит свою поправку к ядру, и, войдя во вкус, станет и дальше вносить свой вклад в крупнейшей программный проект в истории вычислительной техники.

