Главная » Ядро Linux » Стиль написания исходного кода

1

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

Пока стиль  еще  не  выбран и  широко не  используется, не  так  важно, какой  именно стиль  выбрать.  К  счастью,  еще  очень  давно  Линус   представил на  рассмотрение стиль, который необходимо использовать, и при  написании большей части  кода  сейчас стараются придерживаться именно этого  стиля.  Подробное описание стиля  приведено  в файле   Documentation/CodingStyl e  со  свойственным Линусу  юмором.

Отступы

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

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

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

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

разработке ядра — это  размещать открывающую скобку  в первой строке, сразу  за соответствующим оператором. Закрывающая  скобка помещается в первой позиции  с новой строки, как  в следующем примере.

if (fox) {

dog();

cat();

}

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

if (fox) {

ant();

pig();

} else {

}

dog();

cat();

или  следующим образом.

do {

dog();

cat();

} while (fox);

Для  функций это  правило не  действует, потому  что  внутри  одной  функции тело другой  функции описывать нельзя.

unsigned long func(void)

{

/* .. */

}

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

if (foo)

bar();

Логика всего  этого  базируется на  K&R1.

Длинные строки

При  написании кода ядра  необходимо стараться, насколько это  возможно, чтобы длина   строки была  не  больше  80 символов. Это  позволяет строкам,  при  отображении  на терминале размером 80×24  символа,  вмещаться в одну   строу   терминала.

1 Брайа н У. Керниган, Денни с М. Ритчи , Язык программирования С, 2-е изд. Пер . с англ.  — М.: Издат. дом  "Вильяме", 2005.

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

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

static void get_pirate_parrot(const char *name, unsigned long disposition, unsigned long feather_quality)

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

int find_pirate_flag_by_color(const char *color, const char *name, int len)

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

Имена

В  именах нельзя использовать символы разных   регистров. Назвать переменную именем idx , или  даже  i — это  очень  хорошо, но  при  условии, что  будет  понятно назначение этой  переменной. Слишком хитрые  имена, такие  как  theLoopIndex , недопустимы. Так  называемая "венгерская запись"   (Hungarian notation),  когда  тип  переменной кодируется в ее имени, В данном случае — признак плохого  тона.  Это  С, а не Java  и  Unix, а не  Windows.

Тем  не  менее, глобальные переменные и функции должны иметь  наглядные имена.  Если  глобальной функции присвоить имя  atty() , то  это  может  привести к  путанице. Более   подходящим  будет  имя  g s t a c t i v e t t y ( ) .  Это  все-таки  Linux,  а не BSD.

Функции

Существует мнемоническое  правило:  функции  не  должны по  объему  кода  превышать двух экранов текста  и  иметь  больше   десяти локальных переменных.  Каждая функция должна  выполнять одно  действие,  но  делать  это  хорошо.  Не  вредно   разбить  функцию на  последовательность более  мелких   функций. Если  возникает беспокойство по  поводу  накладных расходов  за счет  вызова  функций, то можно использовать  подстановку тела — inline .

Комментарии

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

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

В ядре  используются комментарии в стиле  С, хотя  компилятор gcc поддерживает также  и комментарии в стиле  C++.  Обычно комментарии кода  ядра  должны быть похожи  на следующие (только на  английском языке, конечно).

/*

* get_ship_speed() возвращает текущее значение скорости

* пиратского корабля

* Необходима для вычисления координат корабля.

* Может переходить в состояние ожидания,

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

*/

Комментарии внутри  функций встречаются редко, и их нужно  использовать только  в специальных ситуациях, таких  как  документирование дефектов, или  для  важных замечаний.  Важные замечания часто  начинаются со строки "XXX:    ", а информация о дефектах—  со  строки  "FIXME:    ", как  в следующем примере.

/*

* FIXME: Считается, что dog == cat.

* В будущем это может быть не так

*/

У ядра  есть  возможность автоматической генерации документации. Она  основана на GNOME-doc, но  немного модифицирована и называется Kernel-doc. Для  создания документации в формате HTML необходимо выполнить следующую команду.

make htmldocs

Для  генерации документации в  формате postscript   команда должна  быть  следующей.

make psdocs

Документировать код  можно путем  введения комментариев в  специальном формате.

/**

* find_treasure нахождение сокровищ, помеченных на карте крестом

* @map карта сокровищ

* @time момент времени, когда были зарыты сокровища

*

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

*/

void find_treasure (int dog, int cat)

{

/* .. */

}

Для более подробной информации см.  файл Documentation/kernel-doc-nanoHOWTO.txt.

Использование директивы typede f

Разработчик и  ядра  не  любят  определять  новые   типы   с  помощью  оператор а typedef , и  причины этого  довольно трудно  объяснить. Разумное объяснение  может быть  следующим.

• Определение нового   типа  через  оператор  typede f  скрывает  истинный  вид структур   данных.

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

• Использование оператора typede f  — признак лени.

Чтобы   избежать насмешек,  лучше  не  использовать оператор typedef .

Конечно ,  существуют ситуации, в  которы х  полезн о  использоват ь  операто р typedef :  сокрытие  специфичных для  аппаратной  платформы деталей   реализации или  обеспечение совместимости при  изменении типа.  Нужно хорошо подумать, действительно ли  оператор  typede f  необходим  или  он  используется только   для  того, чтобы  уменьшить количество  символов при  наборе кода.

Использование того, что уже есть

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

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

Никаких директив ifde f в исходном коде

Использование  директив препроцессора  ifde f  в  исходном  коде   категорически не  рекомендуется.  Никогда  не  следует  делать  чего-нибудь вроде  следующего.

#ifdefconfig_foo foo();

#endif

Вместо этого, если макрос CONFIG_FOO не определен, необходимо определять функцию fоо(), какту,которая ничего не делает.

tifdefCONFIG_FOO

static int foo(void)

{

/* .. */

}

#else

static inline int foo(void) { }

#endif

После  этого  можно вызывать функцию  foo( )   без  всяких условий. Пусть  компилятор  поработает за вас.

Инициализация структур

Структуры необходимо инициализировать,  используя метки  полей. Это  позволяет    предотвратить некорректную  инициализацию  при  изменении  структур. Это также    позволяет выполнять инициализацию  не  всех  полей. К  сожалению, в стандарте  С99  принят довольно "страшненький" формат меток  полей, а в компиляторе gcc ранее  использовавшийся формат меток  полей  в стиле  GNU признан устаревшим. Следовательно, для  кода  ядра  необходимо использовать новый  формат,  согласно стандарту С99, каким бы  ужасным он  ни  был.

struct foo rny_foo = {

.а = INITIAL_A,

.Ь = INITIAL_B,

};

где  а и b — это  поля  структуры struc t   foo, а параметры INITIAL_A и  INITIAL_B — соответственно, их начальные значения. Если  поле  не указано при  инициализации, то  оно  устанавливается в свое  начальное значение,  согласно стандарту ANSI  С  (указателям присваивается значение NULL,  целочисленным полям — нулевое  значение, а нолям с  плавающей точкой—  значение 0.0).  Например, если  структура struc t   foo также  имеет  поле  in t   с, то  это  поле  в предыдущем примере будет  инициализировано  в значение 0.

Да, это  ужасно. Но  у нас  нет  другого  выбора.

Исправление ранее написанного кода

Если  в ваши  руки  попал  код, который даже  близко не  соответствует стилю  написания кода  ядра  Linux, то  все  равно  не  стоит  терять  надежды. Немного упорства, и утилита  inden t поможет сделать  все  как  надо.  Программа  inden t — отличная утилита GNU,  которая включена во многие поставки ОС  Linux  и предназначена для  форматирования  исходного кода  в соответствии с  заданными правилами.  Установки по умолчанию соответствуют стилю  форматирования GNU,  который выглядит не  очень красиво. Для  того  чтобы  утилита  выполняла форматирование в соответствии со  стилем  написания кода  ядра  Linux,  необходимо использовать следующие параметры.

indent -kr -i8 -ts8 -sob -180 -ss -bs -psl <файл>

Можно также  использовать  сценарий  scripts/Lindent ,  который  вызывает утилиту  inden t  с необходимыми параметрами.

Источник: Лав,  Роберт. Разработка ядра  Linux, 2-е  издание. : Пер.  с англ.  — М.  : ООО  «И.Д.  Вильяме» 2006. — 448 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии

1 комментарий

  1. remarkes says:

    в строке ошибка:
    indent -kr -i8 -ts8 -sob -180 -ss -bs -psl
    должно быть не 180 а l80 (“эль-восемьдесят”). Т.е.
    indent -kr -i8 -ts8 -sob -l80 -ss -bs -psl