Второе издание
Скачать 3.09 Mb.
|
Рис. 19.1. Обратный (big-endian) порядок следования байтов Номер байта Старший байт Младший байт Рис. 19.2. Прямей (little-endian) порядок следования байтов Рассмотрим, что эти типы кодирования обозначают на практике и как выглядит двоичное представление числа 1027, которое хранится в виде четырехбайтового це- лочисленного типа данных. 00000000 00000000 00000100 00000011 Внутренние представления этого числа в памяти при использовании прямого и обратного порядка байтов отличаются, как это показано в табл. 19.3. Таблица 19.3. Расположение данных в памяти для разных порядков следования байтов Адрес Обратный порядок Прямой порядок 0 00000000 00000011 1 00000000 00000100 2 00000100 00000000 3 00000011 00000000 Обратите внимание на то, что для аппаратной платформы с обратным порядком байтов самый старший байт записывается в самый минимальный адрес памяти. И наконец, еще один пример — фрагмент кода, который позволяет определить порядок байтов для той аппаратной платформы, на которой он выполняется. int х = 1; if (*(char *)&х == 1) /* прямой порядок */ else /* обратный порядок */ Этот пример работает как в ядре, так и в пространстве пользователя. 400 Глава 19 Номер байта 0 1 2 3 3 2 1 О История терминов big-endian и little-endian Термины big-endian и little-endian заимствованы из сатирического романа Джонатана Свифта "Путешествие Гулливера", который был издан в 1726 году. В этом романс наиболее важной политической проблемой народа лилипутов была пробле- ма, с какого конца следует разбивать яйцо: с тупого (big) или острого (little). Тех, кто предпочитал тупой конец называли "тупоконечниками" (big-endian), тех же, кто предпочитал острый конец, называли "остроконечниками" (little-endian). Аналогия между дебатами лилипутов и спорами о том, какой порядок байтов луч- ше, говорит о том, что это вопрос больше политический, чем технический. Порядок байтов в ядре Для каждой аппаратной платформы, которая поддерживается ядром Linux, в файле < a s m / b y t e o r d e r . h > определена одна из двух констант __BIG_ENDIAN или __LITTLE_ENDIAN, в соответствии с используемым порядком байтов. В этот заголовочный файл также включаются макросы из каталога i n c l u d e / l i n u x / b y t e o r d e r / , которые помогают конвертировать один порядок байтов в дру- гой. Ниже показаны наиболее часто используемые макросы. u23__cpu_to_be32(u32); /* преобразовать порядок байтов текущего процессора в порядок big-endian */ u32__cpu_to_le32(u32); /* преобразовать порядок байтов текущего процессора в порядок little-endian */ u32 be32_to_cpu(u32); /* преобразовать порядок байтов big-endian в порядок байтов текущего процессора */ u32__lе32_to_cpus(u32); /* преобразовать порядок байтов little-endian в порядок байтов текущего процессора */ Эти макросы выполняют преобразование одного порядка байтов в другой. В слу- чае когда порядки байтов, между которыми выполняется преобразование, одинако- вы (например, если выполняется преобразование в обратный порядоку байтов и процессор тоже использует такой же порядок), то эти макросы не делают ничего. В противном случае возвращается преобразованное значение. Таймер Никогда нельзя привязываться к какой-либо конкретной частоте генерации пре- рывания системного таймера и, соответственно, к тому, сколько раз в секунду изме- няется переменная j i f f i e s . Всегда необходимо использовать константу HZ, чтобы корректно определять интервалы времени. Это очень важно, потому что значение частоты системного таймера может отличаться не только для разных аппаратных платформ, но и для одной аппаратной платформы при использовании разных вер- сий ядра. Например, константа HZ для аппаратной платформы х86 сейчас равна 1000. Это значит, что прерывание таймера возникает 1000 раз в секунду, или каждую миллисе- кунду. Однако до серии ядер 2.6 для аппаратной платформы х86 значение константы HZ было равно 100. Для разных аппаратных платформ эти значения отличаются: для аппаратной платформы alpha константа HZ равна 1024, а для платформы ARM — 100. Переносимость 401 Никогда нельзя сравнивать значение переменной j i f f i e s с числом, таким как 1000, и думать, что это всегда будет означать одно и то же. Для получения интерва- лов времени необходимо всегда умножать или делить на константу HZ, как в следую- щем примере. HZ /* одна секунда */ (2*HZ) /* две секунды */ (HZ/2) /* полсекунды */ (HZ/100) /* 10 мс */ (2*Н2/100) /* 20 мс */ Константа HZ определена п файле Размер страницы памяти При работе со страницами памяти никогда нельзя привязываться к конкретному размеру страницы. Программисты, которые разрабатывают для аппаратной плат- формы х86, часто делают ошибку, считая, что размер страницы всегда равен 4 Кбай- та. Хотя это справедливо для платформы х86, для других аппаратных платформ раз- мер станицы может быть другим. Некоторые аппаратные платформы поддерживают несколько размеров страниц! В табл. 19.-1 приведен список размеров страниц памяти для всех поддерживаемых аппаратных платформ. Таблица 19.4. Размеры страниц памяти для разных аппаратных платформ Аппаратная платформа Значение PAGE_SHIFT Значение P A G E _ S I Z E alpha 13 8 Кбайт arm 12, 14, 15 4 Кбайт, 16 Кбайт, 32 Кбайт cris 13 8 Кбайт h8300 12 4 Кбайт i386 12 4 Кбайт ia64 12, 13, 14, 16 4 Кбайт, 8 Кбайт, 32 Кбайт, 64 Кбайт m68k 12, 13 4 Кбайт, 8 Кбайт m86knommu 12 4 Кбайт mips 12 4 Кбайт mips64 12 4 Кбайт 1 • parisc 12 4 Кбайт ррс 12 4 Кбайт ррс64 12 4 Кбайт S390 12 4 Кбайт sh 12 4 Кбайт spare 12,13 4 Кбайт, 8 Кбайт sparc64 13 8 Кбайт v850 12 4 Кбайт х86_64 12 4 Кбайт 402 Глава 19 При работе со страницами памяти необходимо использовать константу PAGE_SIZE, которая содержит размер страницы памяти в байтах. Значение макроса PAGE_SHIFT — это количество битов, на которое необходимо сдвинуть влево значение адреса, чтобы получить номер соответствующей страницы памяти. Например, для аппаратной платформы х86, для которой размер страницы равен 4 Кбайт, макрос PAGE_SIZE равен 4096, а макрос PAGE_SHIFT— 12. Эти значе- ния содержатся в заголовочном файле Порядок выполнения операций процессором Вспомните из материала главы 9, "Средства синхронизации в ядре", что для раз- личных аппаратных платформ процессоры в разной степени изменяют порядок вы- полнения программных инструкций. Для некоторых процессоров порядок выполне- ния операций строго соблюдается, запись данных в память и считывание данных из памяти выполняются в строго указанном в программе порядке. Другие процессоры имеют ослабленные требования к порядку выполнения операций считывания и за- писи данных и могут изменять порядок выполнения этих операций с целью опти- мизации. Если код зависит от порядка выполнения операций чтения-записи данных, то не- обходимо гарантировать, что даже процессор с самыми слабыми ограничениями на порядок выполнения чтения-записи будет выполнять эти операции в правильном по- рядке. Это делается с помощью соответствующих барьеров, таких как rmb() и wmb(). Более подробная информация приведена в главе 9, "Средства синхронизации в ядре". Многопроцессорность, преемптивность и верхняя память Может показаться неправильным включать поддержку симметричной многопро- цессорности, возможность вытеснения процессов в режиме ядра и работу с верхней памятью в вопросы переносимости. В конце концов, это не особенности аппарат- ной платформы, которые влияют на операционную систему, а функции ядра Linux, которые по многом не зависят от аппаратной платформы. Тем не менее для этих функций существуют важные конфигурационные параметры, которые необходимо учитывать при разработке кода. Программировать всегда необходимо под SMP, с поддержкой преемптивности и с использованием верхней памяти, чтобы код был безопасным всегда, при любых конфигурациях. Необходимо всегда соблюдать следу- ющие правила. • Всегда необходимо учитывать, что код может выполняться на SMP-системе и использовать соответствующие блокировки. • Всегда необходимо учитывать, что код может выполняться при включенной преемптивности ядра, поэтому необходимо всегда использовать необходимые блокировки и операции для управления преемптивностью. • Всегда необходимо учитывать, что код может выполняться на системе с под- держкой верхней памяти (непостоянно отображаемая память) и при необходи- мости использовать функцию kmap (). Переносимость 403 Пару слов о переносимости Если говорить коротко, то написание переносимого, ясного и красивого кода подразумевает следующие два момента. • Код необходимо разрабатывать с учетом самого общего сценария: следует предполагать, что все, что может случиться, обязательно случится, и принять на этот счет все возможные меры. • Всегда необходимо все подводить под наибольший общий знаменатель: нельзя полагаться на то, что будут доступны все возможности ядра, следует опираться только на минимум возможностей, которые доступны всем аппаратным плат- формам. Написание переносимого кода требует строгого учета многих факторов: размер машинного слова, размеры типов данных, выравнивание в памяти, порядок байтов, размер страницы, изменение порядка операций процессора и т.д. В большинстве случаев при программировании ядра следует гарантировать, что типы данных ис- пользуются правильно. Тем не менее время от времени все равно всплыпают про- блемы, связанные с особенностью той или другой аппаратной платформы. Важно понимать проблемы, связанные с переносимостью, и всегда писать четкий и пере- носимый код ядра. 404 Глава 19 20 Заплаты, разработка и сообщество О дно из самых больших преимуществ операционной системы Linux — это связанное с ней большое сообщество пользователей и разработчиков. Сообщество предоставляет множество глаз для проверки кода и множество пользо- вателей для тестирования и отправки сообщений об ошибках. Наконец, сообщество решает, какой код включать в основное ядро. Поэтому важно понимать, как это все происходит. Сообщество Если говорить о том, где физически существует сообщество разработчиков ядра Linux, то можно сослаться на список рассылки разработчиков ядра Linux (Linux Kernel Mail List, или, сокращенно, LKML). Список разработчиков ядра Linux— это то место, где происходит большинство дискуссий, дебатов и флеймов вокруг ядра Linux. Здесь обсуждаются новые возможности, и большая часть кода отправляется в этот список рассылки перед тем, как этот код для чего-нибудь начинает использоваться. В списке рассылки насчитывается до 300 сообщений в день — количество не для слабонерв- ных. Подписаться на этот список (или но крайней мере читать его обзор) рекомен- дуется всем, кто серьезно занимается разработкой ядра. Даже только наблюдая за работой специалистов, можно узнать достаточно МНОГО. Подписаться на данный список рассылки можно, отправив сообщение subscribe linux-kernel в виде обычного текста на адрес majordomo@vger.kernel.org. Больше информа- ции доступно по Интернет-адресу h t t p : / / v g e r . k e r n e l . o r g / , а список часто зада- ваемых вопросов (FAQ) — по адресу h t t p : //www.tux.org/lkml/. Много других WWW-сайтов и списков рассылки посвящены как ядру, так и вообще операционной системе Linux. Отличный ресурс для начинающих хакеров — h t t p : / / www.kernelnewbies.org/, сайт, который сможет удовлетворить желания всех, кто, стачивая зубы, грызет основы разработки ядра. Два других отличных источника ин- формации — это сайт http://www.lwn.net/, Linux Weekly News, на котором есть большая колонка новостей ядра, и сайт h t t p : / / w w w . k e r n e l t r a f f i c . o r g , Kernel Traffic, который содержит сводку сообщений из списка рассылки разработчиков ядра Linux с. комментариями. Стиль написания исходного кода Как и для любого большого программного проекта, для ядра Linux определен стиль написания исходного кода, который определяет форматирование и размеще- ние кода. Это сделано не потому, что стиль написания, который принят для Linux, лучше других (хотя очень может быть), и не потому, что все программисты пишут неразборчиво (хотя тоже бывает), а потому, что одинаковость стиля является важным моментом для обеспечения производительности разработки. Часто говорят, что стиль написания исходного кода не важен, потому что он не влияет на скомпилирован- ный объектный код. Однако для большого программного проекта, в котором задей- ствовано большое количество разработчиков, такого как ядро, важна слаженность стиля. Слаженность включает в себя одинаковость восприятия, что ведет к упроще- нию чтения, к избежанию путаницы и вселяет надежду на то, что и в будущем стиль останется одинаковым. К тому же, это приводит к увеличению количества разработ- чиков, которые смогут нормально читать ваш код, и увеличивает количество кода, который вы сможете нормально читать. Для проектов с открытым исходным кодом чем больше будет глаз, тем лучше. Пока стиль еще не выбран и широко не используется, не так важно, какой имен- но стиль выбрать. К счастью, еще очень давно Линус представил на рассмотрение стиль, который необходимо использовать, и при написании большей части кода сей- час стараются придерживаться именно этого стиля. Подробное описание стиля при- ведено в файле Documentation/CodingStyle со свойственным Линусу юмором. Отступы Для выравнивания текста и введения отступов используются символы табуляции. Размер одного символа табуляции при отображении соответствует восьми позициям. Это не означает, что для структурирования можно использовать восемь или четы- ре символа "пробел" либо что-нибудь еще. Это означает, что каждый уровень отсту- па равен одному символу табуляции от предыдущего и что при отображении длина символа табуляции равна восьми символам. По непонятным причинам, это правило почти всегда нарушается, несмотря на то что оно очень сильно влияет на читабель- ность. Восьмисимвольная табуляция позволяет очень легко визуально различать от- дельные блоки кода даже после нескольких часов работы. Если табуляция в восемь символов кажется очень большой, то не нужно делать так много вложений кода. Почему ваши функции имеют пять уровней вложенности? Необходимо исправлять код, а не отступы. Фигурные скобки Как располагать фигурные скобки, это личное дело каждого, и практически нет никаких принципиальных причин, по которым одно соглашение было бы лучше дру- гого, но какое-нибудь соглашение все-таки должно быть. Принятое соглашение при 406 Глава 20 1 Брайан У. Керниган, Деннис М. Ритчи, Язык программирования С, 2-е изд. Пер. с англ. — М.: Издат. дом "Вильяме", 2005. Заплаты, разработка и сообщество 407 разработке ядра — это размещать открывающую скобку в первой строке, сразу за со- ответствующим оператором. Закрывающая скобка помещается в первой позиции с новой строки, как в следующем примере. if (fox) { dog(); cat(); } В случае, когда за закрывающей скобкой продолжается то же самое выражение, то продолжение выражения записывается в той же строке, что и закрывающая скоб- ка, как показано ниже if (fox) { ant(); pig(); } else { dog(); cat(); } или следующим образом. do { dog(); cat(); } while (fox); Для функций это правило не действует, потому что внутри одной функции тело другой функции описывать нельзя. unsigned long func(void) { /* .. */ } И наконец, для выражений, в которых фигурные скобки не обязательны, эти скобки можно опустить. if (foo) bar(); Логика всего этого базируется на K&R 1 Длинные строки При написании кода ядра необходимо стараться, насколько это возможно, чтобы длина строки была не больше 80 символов. Это позволяет строкам, при отображе- нии на терминале размером 80x24 символа, вмещаться в одну строу терминала. Не существует стандартного правила, что делать, если длина строки кода обяза- тельно должна быть больше 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. Тем не менее, глобальные переменные и функции должны иметь наглядные име- на. Если глобальной функции присвоить имя a t t y ( ) , то это может привести к пу- танице. Более подходящим будет имя g s t a c t i v e t t y ( ) . Это все-таки Linux, а не BSD. Функции Существует мнемоническое правило: функции не должны по объему кода пре- вышать двух экранов текста и иметь больше десяти локальных переменных. Каждая функция должна выполнять одно действие, но делать это хорошо. Не вредно раз- бить функцию на последовательность более мелких функций. Если возникает бес- покойство по поводу накладных расходов за счет вызова функций, то можно исполь- зовать подстановку тела — i n l i n e . Комментарии Очень полезно использовать комментарии кода, но делать это нужно правильно. Обычно необходимо описывать, что делает код и для чего это делается. То, как реа- лизован алгоритм, описывать не нужно, это должно быть ясно из кода. Если так сде- 408 Глава 20 лать не получается, то, возможно, стоит пересмотреть то, что вы написали. Кроме того, комментарии не должны включать информацию о том, кто написал функцию, когда это было сделано, время модификации и др. Такую информацию логично раз- мещать в самом начале файла исходного кода. В ядре используются комментарии в стиле С, хотя компилятор 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-nano- HOWTO.txt. Заплаты, разработка и сообщество 409 Использование директивы typedef Разработчики ядра не любят определять новые типы с помощью оператора typedef, и причины этого довольно трудно объяснить. Разумное объяснение может быть следующим. • Определение нового типа через оператор t y p e d e f скрывает истинный вид структур данных. • Поскольку новый тип получается скрытым, то код более подвержен таким не- хорошим вещам, как передача структуры данных в функцию по значению, че- рез стек. • Использование оператора typedef — признак лени. Чтобы избежать насмешек, лучше не использовать оператор typedef. Конечно, существуют ситуации, в которых полезно использовать оператор typedef: сокрытие специфичных для аппаратной платформы деталей реализации или обеспечение совместимости при изменении типа. Нужно хорошо подумать, дей- ствительно ли оператор t y p e d e f необходим или он используется только для того, чтобы уменьшить количество символов при наборе кода. Использование того, что уже есть Не нужно изобретать паровоз. Ядро предоставляет функции работы со строками, подпрограммы для сжатия и декомпрессии данных и интерфейс работы со связан- ными списками — их необходимо использовать. Не нужно инкапсулировать стандартные интерфейсы в другие реализации обоб- щенных интерфейсов. Часто приходится сталкиваться с кодом, который переносит- ся с других операционных систем в систему Linux, при этом на основе существую- щих интерфейсов реализуется некоторая громоздкая функция, которая служит для связи нового кода с существующим. Такое не нравится никому, поэтому необходимо непосредственно использовать предоставляемые интерфейсы. Никаких директив i f d e f в исходном коде Использование директив препроцессора i f d e f в исходном коде категорически не рекомендуется. Никогда не следует делать чего-нибудь вроде следующего. #ifdef config_foo foo(); #endif Вместо этого, если макрос CONFIG_FOO не определен, необходимо определять функцию fоо(), как ту, которая ничего не делает. tifdef CONFIG_FOO static int foo(void) { /* .. */ } #else static inline int foo(void) { } #endif 410 Глава 20 После этого можно вызывать функцию foo() без всяких условий. Пусть компи- лятор поработает за вас. Инициализация структур Структуры необходимо инициализировать, используя метки полей. Это позво- ляет предотвратить некорректную инициализацию при изменении структур. Это также позволяет выполнять инициализацию не всех полей. К сожалению, в стан- дарте С99 принят довольно "страшненький" формат меток полей, а в компиляторе gcc ранее использовавшийся формат меток полей в стиле GNU признан устаревшим. Следовательно, для кода ядра необходимо использовать новый формат, согласно стандарту С99, каким бы ужасным он ни был. struct foo rny_foo = { .а = INITIAL_A, .Ь = INITIAL_B, }; где а и b — это поля структуры s t r u c t foo, а параметры INITIAL_A и INITIAL_B — соответственно, их начальные значения. Если поле не указано при инициализации, то оно устанавливается в свое начальное значение, согласно стандарту ANSI С (ука- зателям присваивается значение NULL, целочисленным полям — нулевое значение, а нолям с плавающей точкой— значение 0.0). Например, если структура s t r u c t foo также имеет поле i n t с, то это поле в предыдущем примере будет инициализирова- но в значение 0. Да, это ужасно. Но у нас нет другого выбора. Исправление ранее написанного кода Если в ваши руки попал код, который даже близко не соответствует стилю напи- сания кода ядра Linux, то все равно не стоит терять надежды. Немного упорства, и утилита i n d e n t поможет сделать все как надо. Программа i n d e n t — отличная утили- та GNU, которая включена во многие поставки ОС Linux и предназначена для фор- матирования исходного кода в соответствии с заданными правилами. Установки по умолчанию соответствуют стилю форматирования GNU, который выглядит не очень красиво. Для того чтобы утилита выполняла форматирование в соответствии со сти- лем написания кода ядра Linux, необходимо использовать следующие параметры. indent -kr -i8 -ts8 -sob -180 -ss -bs -psl <файл> Можно также использовать сценарий s c r i p t s / L i n d e n t , который вызывает ути- литу i n d e n t с необходимыми параметрами. Организация команды разработчиков Разработчики — это хакеры, которые занимаются развитием ядра Linux. Некоторые делают это за деньги, для некоторых это хобби, но практически все де- лают это с удовольствием. Разработчики ядра, которые внесли существенный вклад, перечислены в файле CREDITS, который находится в корневом каталоге дерева ис- ходных кодов ядра. Заплаты, разработка м сообщество 411 Для различных частей ядра выбираются ответственные разработчики (mainlainers), которые официально выполняют их поддержку. Ответственные разработчики — это один человек или группа людей, которые полностью отвечают за свою часть ядра. Каждая подсистема ядра, например сетевая подсистема, также имеет связанного с ней ответственного. Разработчики, которые отвечают за определенный драйвер или подсистему, обычно перечислены в файле MAINTAINERS. Этот файл тоже находится в корневом каталоге дерева исходных кодов. Среди ответственных разработчиков существует специальный человек, который отвечает за все ядро в целом (kernel maintainer). Исторически, Линус отвечает за разрабатываемую серию ядер (где наиболее интересно) и за первые выпуски ста- бильной версии ядра. Вскоре после того как ядро становится стабильным, Линус пе- редает бразды правления одному из ведущих разработчиков. Они продолжают под- держку стабильной серии ядра, а Линус начинает работу над новой разрабатываемой серией. Таким образом, серии ядер 2.0, 2.4 и 2.6 все еще активно поддерживаются. Несмотря на слухи, здесь нет никаких других, в том числе тайных, организаций. Отправка сообщений об ошибках Если вы обнаружили ошибку, то наилучшим решением будет исправить ее, сгене- рировать соответствующую заплату, оттестировать и отправить, как это будет расска- зано в следующих разделах. Конечно, можно и просто сообщить об ошибке, чтобы кто-нибудь исправил ее для вас. Наиболее важная часть сообщения об ошибке — это полное описание проблемы. Необходимо описать симптомы, системные сообщения и декодированное сообще- ние oops (если такое есть). Еще более важно, чтобы вы смогли пошагово описать, как устойчиво воспроизвести проблему, и кратко описать особенности вашего аппа- ратного обеспечения. Следующий этап — определение того, кому отправить сообщение об ошибке. В файле MAINTAINERS приведен список людей, которые отвечают за каждый драй- вер и подсистему. Эти люди должны получать все сообщения об ошибках, которые возникают в том коде, поддержку которого они выполняют. Если вы не смогли най- ти необходимого разработчика, то отправьте вопрос в список рассылки разработчи- ков ядра Linux по адресу l i n u x - k e r n e l @ v g e r . k e r n e l . o r g . Даже если вы нашли нужное ответственное лицо, то никогда не помешает отправить копию сообщения в список рассылки разработчиков. Больше информации об этом приведено в файлах REPORTING-BUGS и Documentation/oops-tracing.txt. Генерация заплат Все изменения исходного кода ядра Linux распространяются в виде заплат (patch). Заплаты представляют собой результат вывода утилиты GNU d i f f ( 1 ) в формате, который может подаваться на вход программы p a t c h (1). Наиболее просто сгене- рировать заплату можно в случае, когда имеется два дерева исходных кодов ядра: одно — стандартное, а другое — с вашими изменениями. Обычная схема имен состоит в том, что каталог, в котором находится стандартное ядро, называется 1 i n u x - x . у . z (каталог, в который разворачивается архив дерева исходного кода в формате tar), a 412 Глава 20 имя модифицированного ядра — linux. Для генерации заплаты на основе двух ката- логов с исходным кодом необходимо выполнить следующую команду из каталога, в котором находятся два рассмотренных дерева исходного кода. diff -urN linux-x.y.z/linux/ > my-patch Обычно это делается где-нибудь в домашнем каталоге, а не в каталоге / u s r / s r c / linux, поэтому нет необходимости иметь права пользователя root. Флаг -u указыва- ет, что необходимо использовать унифицированный формат вывода команды diff. Без этого флага внешний вид заплаты получается неудобочитаемым. Флаг -r указы- вает на необходимость рекурсивного анализа каталогов, а флаг -N указывает, что новые файлы, которые появились в измененном каталоге, должны быть включены в результат вывода команды diff. Если же необходимо получить только изменения одного файла, то можно выполнить следующую команду. diff -u linux-x.y.z/some/file_linux/some/file > my-patch Обратите внимание на то, что вычислять изменения необходимо всегда, находясь в одном текущем каталоге, а именно в том, где находятся оба дерева исходного кода. При этом получается заплата, которую легко могут использовать все, даже в случаях, когда имена каталогов исходного кода отличаются от тех, которые использовались при генерации заплаты. Для того чтобы применить заплату, которая сгенерирована таким образом, необходимо выполнить следующую команду из корневого каталога дерева исходного кода. patch -p1 < ../my-patch В этом примере имя файла, который содержит заплату, - my-patch, а находится он в родительском каталоге по отношению к каталогу, в котором хранится дерево исходного кода ядра. Флаг -p1 означает, что необходимо игнорировать (strip) имя первого каталога в путях всех файлов, в которые будут вноситься изменения. Это позволяет применить заплату независимо от того, какие имена каталогов кода ядра были на той машине, где создавалась заплата. Полезная утилита d i f f s t a t позволяет сгенерировать гистограмму изменений, к которым приведет применение заплаты (удаление и добавление строк). Для того чтобы получить эту информацию для какой-либо заплаты, необходимо выполнить следующую команду. diffstat -p1 my-patch Обычно полезно включить результат выполнения этой команды при отправле- нии заплаты п список рассылки lkml. Так как программа patch (1) игнорирует все строки до того момента, пока не будет обнаружен формат diff, то вначале заплаты можно включить короткое описание. Представление заплат Заплата должна быть сгенерирована так, как описано в предыдущем разделе. Если заплата касается определенного драйвера или подсистемы, то заплату нужно отправить соответствующему ответственному разработчику, одному из тех, которые перечислены в файле MAINTAINERS. Другой вариант — это отправить сообщение в список рассылки разработчиков ядра по адресу linux-kernel@vger.kernel.org. Заплаты, разработка и сообщество 413 Обычно тема (subject) письма, в котором содержится заплата, должна быть по- хожа на следующую " [PATCH] короткое описание. ". В теле письма должны быть описаны основные технические детали изменений, которые вносятся заплатой, а также обоснования необходимости этих изменений. Описание должно быть макси- мально конкретным. Также необходимо указать, на какую версию ядра рассчитана заплата. Большинство разработчиков ядра будут просматривать заплату прямо в теле пись- ма и при необходимости записывать все письмо в файл. Следовательно, лучше все- го будет вставить заплату прямо в тело письма, в самом конце сообщения. Будьте внимательны, потому что некоторые злобные почтовые клиенты вводят в сообще- ния дополнительное форматирование. Это испортит заплату и будет надоедать раз- работчикам. Если ваш почтовый клиент делает такие вещи, то необходимо поискать возможность включения текста без изменений ("Insert Inline") или что-нибудь ана- логичное. Нормально работает также присоединение (attachment) заплаты в виде обычного текста, без перекодировки. Если заплата большая или содержит несколько принципиальных изменений, то лучше разбить ее на логические части. Например, если вы предложили новый API и изменили несколько драйверов с целью его использования, то эти изменения мож- но разбить на две заплаты (новый API и изменения драйверов) и выслать их двумя письмами. Если в каком-либо случае необходимо вначале применить предыдущую за- плату, то это необходимо специально указать. После отправки наберитесь терпения и подождите ответа. Не нужно обижаться на негативный ответ — в конце концов это тоже ответ! Обдумайте проблему и вы- шлите обновленную версию заплаты. Если ответа нет, то попытайтесь разобраться, что было сделано не так, и решить проблему. Спросите у ответственного разработ- чика и в списке рассылки по поводу комментариев. Если повезет, то ваши изменения будут включены в новую версию ядра! Заключение Наиболее важными качествами любого хакера являются желание и умение рабо- тать — нужно искать себе проблемы и решать их. В этой книге приведено описание основных частей ядра, рассказано об интерфейсах, структурах данных, алгоритмах и принципах работы. Книга предоставляет вид ядра изнутри и делает это в практиче- ской форме. Она предназначена для того, чтобы удовлетворить ваше любопытство и стать отправной точкой в разработке ядра. Тем не менее, как уже было сказано, единственный способ начать разрабатывать ядро — это начать читать и писать исходный код. Операционная система Linux пре- доставляет возможность работать в сообществе, которое не только позволяет это делать, но и активно побуждает к указанным действиям. Если есть желание действо- вать — вперед! 414 Глава 20 |