<?xml version="1.0" encoding="windows-1251"?>
<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink">
 <stylesheet type="text/css">
                           section section title { page-break-before: auto; page-break-after: avoid }
  p &gt; code { font-size: 89% }
  image + p { page-break-before: avoid; margin-bottom: 1em; font-style: italic; text-align: center; }
  cite { font-style: normal; }
 </stylesheet>
 <description>
  <title-info>
   <genre>comp_osnet</genre>
   <genre>comp_programming</genre>
   <author>
    <first-name>Роберт</first-name>
    <last-name>Лав</last-name>
   </author>
   <book-title>Разработка ядра Linux</book-title>
   <annotation>
    <p>В книге детально рассмотрены основные подсистемы и функции ядер Linux серии 2.6, включая особенности построения, реализации и соответствующие программны интерфейсы. Рассмотренные вопросы включают: планирование выполнения процессов, управление временем и таймеры ядра, интерфейс системных вызовов, особенности адресации и управления памятью, страничный кэш, подсистему VFS, механизмы синхронизации, проблемы переносимости и особенности отладки. Автор книги является разработчиком основных подсистем ядра Linux. Ядро рассматривается как с теоретической, так и с прикладной точек зрения, что может привлечь читателей различными интересами и потребностями.</p>
    <p>Книга может быть рекомендована как начинающим, так и опытным разработчикам программного обеспечения, а также в качестве дополнительных учебных материалов.</p>
   </annotation>
   <date>2006</date>
   <coverpage>
    <image l:href="#img_0.jpeg"/></coverpage>
   <lang>ru</lang>
   <src-lang>en</src-lang>
   <translator>
    <first-name>Судаков</first-name>
    <middle-name>А.</middle-name>
    <last-name>А.</last-name>
   </translator>
  </title-info>
  <src-title-info>
   <genre>comp_osnet</genre>
   <genre>comp_programming</genre>
   <author>
    <first-name>Robert</first-name>
    <last-name>Love</last-name>
   </author>
   <book-title>Linux Kernel Development. Second Edition</book-title>
   <date>2005</date>
   <lang>en</lang>
  </src-title-info>
  <document-info>
   <author>
    <nickname>honorato bonafe</nickname>
   </author>
   <program-used>OOoFBTools-2.5 (ExportToFB21), FictionBook Editor Release 2.6.6</program-used>
   <date value="2015-01-10">10.01.2015</date>
   <id>OOoFBTools-2014-12-5-19-45-18-111</id>
   <version>1.0</version>
   <history>
    <p>1.0 djvu-&gt;fb2</p>
   </history>
  </document-info>
  <publish-info>
   <book-name>Разработка ядра Linux, 2-е издание</book-name>
   <publisher>Издательский дом "Вильямс"</publisher>
   <city>Москва</city>
   <year>2006</year>
   <isbn>5-8459-1085-4</isbn>
  </publish-info>
 </description>
 <body>
  <title>
   <p>Разработка ядра Linux</p>
   <p>Второе издание</p>
   <p>Роберт Лав</p>
  </title>
  <epigraph>
   <p>Посвящается Дорис (Doris) и Хелен (Helen)</p>
  </epigraph>
  <section>
   <title>
    <p>Предисловие</p>
   </title>
   <p>В связи с тем, что ядро и приложения операционной системы Linux используются все более широко, возрастает число разработчиков системного программного обеспечения, желающих заняться разработкой и поддержкой операционной системы Linux. Некоторые из этих инженеров руководствуются исключительно собственным интересом, некоторые работают в компаниях, которые занимаются операционной системой Linux, некоторые работают на производителей компьютерных аппаратных средств, некоторые заняты в проектах по разработке программного обеспечения на дому.</p>
   <p>Однако все они сталкиваются с общей проблемой: кривая затрат на изучение ядра становится все длиннее и круче. Система становится все более сложной и, кроме того, очень большой по объему. Годы проходят, и нынешние члены команды разработчиков ядра приобретают все более широкие и глубокие знания, что увеличивает разрыв между ними и разработчиками-новичками.</p>
   <p>Я уверен, что понимание основного кода ядра Linux уже сейчас является проблемой, приводящей к ухудшению качества ядра, и в будущем эта проблема станет еще более серьезной. Все, кому нравится операционная система Linux, несомненно, заинтересованы в увеличении числа разработчиков, которые смогут внести свой вклад в развитие ядра этой операционной системы.</p>
   <p>Один из возможных подходов к решению данной проблемы — ясность исходного кода: удобные интерфейсы, четкая структура, следование принципу "Делать мало, но делать хорошо" и т.д. Такое решение предложено Линусом Торвальдсом (Linus Torvalds).</p>
   <p>Подход, который предлагаю я, состоит в использовании большего числа комментариев внутри исходного кода, что поможет читателю понять, чего хотел достичь программист. (Процесс выявления расхождений между целью и реализацией известен как <emphasis>отладка</emphasis>. Этот процесс значительно затрудняется, если не известно, чего хотели достичь.)</p>
   <p>Однако комментарии все же не дают представления о том, для чего предназначено большинство подсистем и как разработчики приступали к их реализации.</p>
   <p>Именно печатное слово лучше всего подходит для стартовой точки такого понимания.</p>
   <p>Вклад Роберта Лава (Robert Love) состоит в предоставлении возможности, благодаря которой опытные разработчики смогут получить полную информацию о том, какие задачи должны выполнять различные подсистемы ядра и каким образом предполагается выполнение этих задач. Этой информации должно быть достаточно для многих людей: для любопытных, для разработчиков прикладного программного обеспечения, для тех, кто хочет ознакомиться с устройством ядра, и т.д.</p>
   <p>Кроме того, данная книга является ступенькой, которая может перенести начинающих разработчиков на новый уровень, где изменения в ядро вносятся для того, чтобы достичь определенной цели. Я хотел бы посоветовать начинающим разработчикам, чтобы они не боялись испачкать свои руки: наилучший способ понять какую- либо часть ядра — это внести в нее изменения. Внесение изменений повышает понимание разработчика до уровня, которого нельзя достичь простым чтением кода ядра.</p>
   <p>Серьезный разработчик ядра присоединится к спискам рассылки разработчиков и будет контактировать с другими коллегами. Это основной способ, позволяющий разработчикам учиться и быть на высоком уровне. Роберт очень хорошо осветил механизмы и культуру этой важной части жизни сообщества разработчиков ядра.</p>
   <p>Пользуйтесь книгой Роберта и учитесь по ней! Может быть, и вы решите сделать следующий шаг и вступить в сообщество разработчиков ядра, куда мы вас и приглашаем. Людей ценят по важности их дел, поэтому, помогая развитию операционной системы Linux, знайте, что ваша работа — небольшая, но непосредственная помощь десяткам или даже сотням миллионов людей.</p>
   <cite>
    <text-author>Эндрю Мортон (Andrew Morton)</text-author>
    <text-author>Open Source Development Labs</text-author>
   </cite>
  </section>
  <section>
   <title>
    <p>Введение</p>
   </title>
   <section>
    <p>Когда я сделал первую попытку превратить свой опыт работы с ядром Linux в текст книги, понял, что не знаю, как двигаться дальше. Не хотелось просто писать еще одну книгу о ядре операционной системы. Конечно, на эту тему <emphasis>не так уж и много</emphasis> книг, но все же я хотел сделать что-то такое, благодаря чему моя книга была бы особенной. Как достичь этой цели? Я не могу успокоиться, пока не сделаю что- нибудь особенное, лучшее в своем роде.</p>
    <p>Наконец я решил, что смогу предложить достаточно уникальный подход к данной теме. Моя работа — изучение и разработка ядра операционной системы. Мое увлечение — изучение и разработка ядра операционной системы. Моя любовь — ядро операционной системы. Конечно, за многие годы я успел собрать много интересных анекдотов и полезных советов. С моим опытом я смог бы написать книгу о том, как нужно разрабатывать программный код ядра и как этого делать <emphasis>не нужно</emphasis>. Прежде всего, эта книга об устройстве и практической реализации ядра операционной системы Linux. В ней информация представлена так, чтобы получить достаточно знаний для решения реальных практических задач и чтобы эти задачи решать правильно. Я человек прагматичный, и книга имеет практический уклон. Она должна быть полезной, интересной и легко читаться.</p>
    <p>Я надеюсь, что читатели, после прочтения этой книги, получат хорошее понимание тек правил (писанных и неписаных), которые действуют в ядре операционной системы. Я также надеюсь, что читатели сразу после прочтения этой книги смогут начать действовать и писать полезный, правильный и хороший код ядра. Конечно, эту книгу можно читать и просто ради интереса.</p>
    <p>Это то, что касалось еще первого издания книги. Однако время идет и снова приходится возвращаться к рассмотренным вопросам. В этом издании представлено несколько больше информации по сравнению с первым: материал серьезно пересмотрен и доработан, появились новые разделы и главы. С момента выхода первого издания в ядро были внесены изменения. Однако, что более важно, сообщество разработчиков ядра Linux приняло решение<a l:href="#n1" type="note">[1]</a> в ближайшем будущем не начинать разработку серии ядра 2.7. Было решено заняться стабилизацией серии ядра 2.6. Стабилизация включает в себя много моментов, тем не менее есть один важный, который касается данной книги, — книга, которая посвящена ядру серии 2.6, остается актуальной. Если изменения происходят не слишком быстро, то существует большой шанс, что "моментальный снимок" ядра останется актуальным и в будущем. В конце концов, книга сможет вырасти и стать канонической документацией по ядру. Я надеюсь, что именно такая книга и находится у вас в руках.</p>
    <p>Как бы там ни было, книга написана, и я надеюсь, что она вам понравится.</p>
   </section>
   <section>
    <title>
     <p>Итак…</p>
    </title>
    <p>Разработка программного кода ядра операционной системы не требует наличия гениальной, волшебной или густой бороды Unix-хакера. Хотя ядро операционной системы и имеет некоторые свои особенности, оно незначительно отличается от любого большого программного продукта. Так же как и в случае любой сложной программы, здесь есть, что изучать, но в программировании ядра не намного больше священных или непонятных вещей, чем в создании любой другой программы.</p>
    <p>Очень важно, чтобы вы читали программный код. Доступность открытого исходного кода операционной системы Linux — это подарок, который встречается очень редко. Однако недостаточно <emphasis>только</emphasis> читать исходный код. Необходимо взяться за дело серьезно и изменять этот программный код. Находите ошибки и исправляйте их! Улучшайте драйверы для своего аппаратного обеспечения! Находите слабые места и закрывайте их! У вас все получится, если вы будете сами писать программный код.</p>
   </section>
   <section>
    <title>
     <p>Версия ядра</p>
    </title>
    <p>Эта книга посвящена ядрам Linux серии 2.6 и базируется на версии ядра 2.6.10. Ядро — это "движущийся объект", и никакая книга не в состоянии передать динамику во все моменты времени. Тем не менее базовые внутренние структуры ядра уже сформировались, и основные усилия по представлению материала были направлены на то, чтобы этот материал можно было использовать и в будущем.</p>
   </section>
   <section>
    <title>
     <p>Читательская аудитория</p>
    </title>
    <p>Эта книга предназначена для разработчиков программного обеспечения, которые заинтересованы в понимании ядра операционной системы Linux. Тем не менее это не построчные комментарии исходного кода ядра. Это также не руководство по разработке драйверов и не справочник по программному интерфейсу (API) ядра (кстати, формализованного API ядра Linux никогда не было). Целью книги является предоставление достаточной информации об устройстве и реализации ядра для того, чтобы подготовленный программист смог начать разработку программного кода. Разработка ядра может быть увлекательным и полезным занятием, и я хочу ознакомить читателя с этой сферой деятельности по возможности быстро. В книге обсуждаются как вопросы теории, так и практические приложения, она обращена к людям, которые интересуются и тем, и другим. Я всегда придерживался мнения, что для понимания практических приложений необходима теория, тем не менее я считаю, что эта книга не сильно углубляется в оба этих направления. Я надеюсь, что, независимо от мотиваций необходимости понимания ядра операционной системы Linux, эта книга сможет объяснить особенности устройства и реализации в достаточной степени.</p>
    <p>Таким образом, данная книга освещает как использование основных подсистем ядра, так и особенности их устройства и реализации. Я думаю, что эти вопросы важны и достойны обсуждения. Хороший пример — глава 7, "Обработка нижних половин и отложенные действия", посвященная обработчикам нижних половин (bottom half).</p>
    <p>В этой главе рассказывается о принципах работы и об особенностях реализации механизмов обработки нижних половин (эта часть может быть интересна разработчикам основных механизмов ядра), а также о том, как на практике использовать экспортируемый интерфейс ядра для реализации собственных обработчиков bottom half (это может быть интересно для разработчиков драйверов устройств). На самом деле мне кажется, что обе эти стороны обсуждения будут интересны для всех групп разработчиков. Разработчик основных механизмов ядра, который, конечно, должен понимать принципы работы внутренних частей ядра, должен также понимать и то, как интерфейсы ядра будут использоваться на практике. В то же самое время разработчик драйверов устройств получит большую пользу от хорошего понимания того, что стоит за этим интерфейсом.</p>
    <p>Все это сродни изучению программного интерфейса некоторой библиотеки наряду с изучением того, как эта библиотека реализована. На первый взгляд, разработчик прикладных программ должен понимать лишь интерфейс (API). И действительно, интерфейсы часто предлагают рассматривать в виде черного ящика. Разработчик библиотеки, наоборот, обычно интересуется лишь принципом работы и реализации функций библиотеки. Я уверен, что обе группы разработчиков должны потратить некоторое время на изучение другой стороны предмета. Разработчик программ, который хорошо понимает операционную систему, сможет значительно лучше эту операционную систему использовать. Аналогично разработчик библиотеки должен иметь хотя бы малое представление о том, что происходит в реальной жизни, и, в частности, о тех программах, в которых будет использоваться его библиотека. Поэтому я старался коснуться как устройства, так и использования подсистем ядра не только в связи с тем, что эта книга может быть полезна для одной или другой группы разработчиков, а в надежде, что <emphasis>весь материал</emphasis> книги будет полезен для всех разработчиков.</p>
    <p>Предполагается, что читатель знаком с языком программирования С и операционной системой Linux. Некоторые знания принципов построения операционных систем также желательны. Я старался объяснять все понятия, однако в случае проблем в списке литературы можно найти несколько отличных книг, которые посвящены основам построения операционных систем.</p>
    <p>Эта книга будет полезна для студентов, изучающих основы построения операционных систем, в качестве <emphasis>прикладного</emphasis> пособия и вводного материала по соответствующей теории. Книга пригодна как для расширенных специальных курсов, так и для общих специальных курсов, причем в последнем случае без дополнительных материалов. Я прошу потенциальных учебных инструкторов связаться со мной; я буду очень рад оказать помощь.</p>
   </section>
   <section>
    <title>
     <p>Интернет-ресурс</p>
    </title>
    <p>Автор поддерживает Интернет-сайт <code>http://tech9.net/rml/kernel_book/</code>, содержащий информацию о данной книге, включая ошибки, расширенные и исправленные разделы, а также информацию о будущих изданиях. Всем читателям рекомендуется посетить этот сайт.</p>
   </section>
   <section>
    <title>
     <p>Благодарности ко второму изданию</p>
    </title>
    <p>Как и большинство авторов, я писал эту книгу, не сидя в пещере (что само по себе хорошо, потому что в пещерах могут водиться медведи), и, следовательно, многие люди оказали мне поддержку в создании рукописи своим сердцем и умом. Поскольку невозможно привести полный список этих людей, я хочу поблагодарить всех своих друзей и коллег за помощь, поддержку и конструктивную критику.</p>
    <p>В первую очередь, я хотел бы высказать благодарность моему редактору Скотту Мейерсу (Scott Meyers) за руководство, благодаря которому второе издание книги превратилось из идеи в конечный продукт. Мне снова было очень приятно работать с Джорджем Недеффом (Georg Nedeff), производственным редактором, который во всем обеспечивал порядок. Особая благодарность литературному редактору Марго Кэтс (Margo Catts). Мы можем только желать, чтобы наше владение ядром было так же совершенно, как ее владение печатным словом.</p>
    <p>Отдельное спасибо техническим редакторам этого издания Адаму Белею (Adam Belay), Мартину Пулу (Martin Pool) и Крису Ривере (Chris Rivera). Их знания и исправления помогли сделать эту книгу неизмеримо лучше. Если, несмотря на их неоценимые усилия, все же остались ошибки, то это вина автора. Такое же большое спасибо Заку Брауну (Zak Brown), который приложил огромные усилия к техническому редактированию первого издания.</p>
    <p>Многие разработчики ядра отвечали на вопросы, предоставляли поддержку или просто писали программный код, интересный настолько, что по нему можно было бы написать отдельную книгу. Среди них Андреа Аркангели (Andrea Arcangely), Алан Кокс (Alan Сох), Грег Кроах-Хартман (Greg Kroah-Hartman), Даниэл Филлипс (Daniel Phillips), Дэвид Миллер (David Miller), Патрик Мочел (Patrick Mochel), Эндрю Мортон (Andrew Morton), Звене Мвейкамбо (Zwane Mwaikambo), Ник Пиггин (Nick Piggin) и Линус Торвальдс (Linus Torvalds). Особое спасибо тайному сообществу ядра (хотя никакого тайного сообщества нет).</p>
    <p>Я хочу выразить свою любовь и признательность многим людям. Среди них Пол Амичи (Paul Amichi), Кейт Бэрбег (Keith Barbag), Дейв Эггерс (Dave Eggers), Ричард Эриксон (Richard Erickson), Нат Фридман {Nat Friedman), Дастин Холл (Dustin Hall), Джойс Хокинс (Joyce Hawkins), Мигуэль де Иказа (Miguel de Icaza), Джимми Крел (Jimmy Krehl), Дорис Лав (Doris Love), Джонатан Лав (Jonathan Love), Патрик ЛеКлер (Patrick LeClair), Линда Лав (Linda Love), Рэнди О'Дауд (Randy O'Dowd), Сальваторэ Рибаудо (Salvatore Ribaudo) и его чудесная мама, Крис Ривера (Chris Rivera), Джой Шау (Joey Shaw), Джэрэми ВанДорен (Jeremy VanDoren) и его семья, Стив Вейсберг (Steve Weisberg) и Хелен Винснант (Helen Whinsnant).</p>
    <p>И в заключение, спасибо за все моим родителям.</p>
    <p>Желаю большого хакерского счастья!</p>
    <cite>
     <text-author>Роберт Лав,</text-author>
     <text-author>г. Кембридж, штат, Массачусетс.</text-author>
    </cite>
   </section>
  </section>
  <section>
   <title>
    <p>Об авторе</p>
   </title>
   <p><strong>Роберт Лав</strong> (Robert Love) использует операционную систему Linux с первых дней ее существования. Он является страстным активистом сообществ разработчиков ядра и GNOME. Сейчас Роберт работает главным инженером по разработке ядра группы разработчиков Ximian Desktop компании Novell. До этого он работал инженером по разработке ядра компании Monta Vista Software.</p>
   <p>Проекты по разработке ядра, которыми занимался автор, включают планировщик выполнения процессов, преемптивное (вытесняемое) ядро (preemptive kernel), уровень событий ядра, улучшение поддержки виртуальной памяти (VM), улучшение поддержки многопроцессорного оборудования. Роберт является автором утилит <code>schedutils</code> и менеджера томов GNOME. Роберт Лав читает лекции и пишет статьи по основам построения ядра операционной системы и получает приглашения редактировать статьи в издании <emphasis>Linux Journal</emphasis>.</p>
   <p>Автор получил степень бакалавра по математике и вычислительной технике в университете штата Флорида. Хотя Роберт и родился в южной Флориде, своим домом он считает Кембридж, штат Массачусетс. Роберт увлекается футболом, фотографией и любит готовить.</p>
  </section>
  <section>
   <title>
    <p>От издательства</p>
   </title>
   <section>
    <p>Вы, читатель этой книги, и есть главный ее критик. Мы ценим ваше мнение и хотим знать, что было сделано нами правильно, что можно было сделать лучше и что еще вы хотели бы увидеть изданным нами. Нам интересно услышать и любые другие замечания, которые вам хотелось бы высказать в наш адрес.</p>
    <p>Мы ждем ваших комментариев и надеемся на них. Вы можете прислать нам бумажное или электронное письмо либо просто посетить наш Web-сервер и оставить свои замечания там. Одним словом, любым удобным для вас способом дайте нам знать, нравится ли вам эта книга, а также выскажите свое мнение о том, как сделать наши книги более интересными для вас.</p>
    <p>Посылая письмо или сообщение, не забудьте указать название книги и ее авторов, а также ваш обратный адрес. Мы внимательно ознакомимся с вашим мнением и обязательно учтем его при отборе и подготовке к изданию последующих книг. Наши координаты:</p>
    <p>E-mail: <code>info@williamspublishing.com</code></p>
    <p>WWW: <code>http://www.williamspublishing.com</code></p>
    <p>Информация для писем из:</p>
    <p>России: 115419, Москва, а/я 783</p>
    <p>Украины: 03150, Киев, а/я 152</p>
   </section>
   <section>
    <title>
     <p>Для читателей</p>
    </title>
    <p>Более подробную информацию об этой и других книгах издательства Sams Publishing можно получить на Интернет-сайте <code>www.nowellpress.com</code>. Для поиска информации о книгах введите в поисковое поле код ISBN (без соединительных черточек) или название книги.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 1</p>
    <p>Введение в ядро Linux</p>
   </title>
   <section>
    <p>Даже после трех десятилетий использования операционная система (ОС) Unix все еще считается одной из самых мощных и элегантных среди всех существующих операционных систем. Со времени создания операционной системы Unix в 1969 году, это детище Денниса Ритчи (Dennis Ritchie) и Кена Томпсона (Ken Thompson) стало легендарным творением, системой, принцип работы которой выдержал испытание временем и имя которой оказалось почти незапятнанным.</p>
    <p>Операционная система Unix выросла из Multics — многопользовательской операционной системы, проект по созданию которой потерпел неудачу в корпорации Bell Laboratories. По прекращении проекта Multics, сотрудники центра Bell Laboratories Computer Sciences Research Center прекратили работу и так и не создали дееспособной диалоговой операционной системы. Летом 1969 года программисты корпорации Bell Labs разработали проект файловой системы, которая в конце концов была включена в операционную систему Unix. Томпсон осуществил реализацию операционной системы для реально не используемой платформы PDP-7. В 1971 году операционная система Unix была перенесена на платформу PDP-11, а в 1973 году переписана с использованием языка программирования С, что было беспрецедентным шагом в то время, но этот шаг стал основой для будущей переносимости. Первая версия операционной системы Unix, которая использовалась вне стен Bell Labs, называлась Unix System версии 6, ее обычно называют V6.</p>
    <p>Другие компании перенесли операционную систему Unix на новые типы машин. Версии, полученные в результате переноса, содержали улучшения, которые позже привели к появлению нескольких разновидностей этой операционной системы, В 1977 году корпорация Bell Labs выпустила комбинацию этих вариантов в виде одной операционной системы Unix System III, а в 1982 году корпорация AT&amp;T представила версию System V<a l:href="#n2" type="note">[2]</a>.</p>
    <p>Простота устройства операционной системы Unix, а также тот факт, что эта система распространялась вместе со своим исходным кодом, привели к тому, что дальнейшие разработки начали проводиться в других организациях. Наиболее важным среди таких разработчиков был Калифорнийский университет в городе Беркли (University of California at Berkeley).</p>
    <p>Варианты операционной системы Unix из Беркли именовались Berkeley Software Distributions (BSD). Первая версия операционной системы Unix, разработанная в Беркли в 1981 году, называлась 3BSD. Следом за ней появились выпуски серии 4BSD: 4.0BSD, 4.1BSD, 4.2BSD и 4.3BSD. В этих версиях операционной системы Unix была добавлена виртуальная память, замещение страниц по требованию (demand paging) и стек протоколов TCP/IP. Последней официальной версией ОС Unix из Беркли была 4.4BSD, выпущенная в 1993 году, которая содержала переписанную систему управления виртуальной памятью. Сейчас разработка линии BSD продолжается в операционных системах Darwin, Dragonfly BSD, FreeBSD, NetBSD и OpenBSD.</p>
    <p>В 1980–1990-х годах многие компании, разработчики рабочих станций и серверов, предложили свои коммерческие версии операционной системы Unix. Эти операционные системы обычно базировались на реализациях AT&amp;T или Беркли и поддерживали дополнительные профессиональные возможности, которые обеспечивала соответствующая аппаратная платформа. Среди таких систем были Tru64 компании Digital, HP-UX компании Hewlett Packard, AIX компании IBM, DYNIX/ptx компании Sequent, IRIX компании SGI, Solaris компании Sun.</p>
    <p>Первоначальное элегантное устройство операционной системы Unix в соединении с многолетними нововведениями и улучшениями, которые за ними последовали, сделали систему Unix мощной, устойчивой и стабильной. Очень небольшое количество характеристик ОС Unix ответственны за ее устойчивость. Во-первых, операционная система Unix проста: в то время как в некоторых операционных системах реализованы тысячи системных вызовов и эти системы имеют недостаточно ясное назначение, Unix-подобные операционные системы обычно имеют только несколько сотен системных вызовов и достаточно четкий дизайн. Во-вторых, в операционной системе Unix <emphasis>все представляется в виде файлов</emphasis><a l:href="#n3" type="note">[3]</a>. Такая особенность позволяет упростить работу с данными и устройствами, а также обеспечить это посредством простых системных вызовов: <code>open()</code>, <code>read()</code>, <code>write()</code>, <code>ioctl()</code> и <code>close()</code>. В-третьих, ядро и системные утилиты операционной системы Unix написаны на языке программирования С — это свойство делает Unix удивительно переносимой и доступной для широкого круга разработчиков операционной системой.</p>
    <p>Для ОС Unix характерно очень малое время создания нового процесса и уникальный системный вызов <code>fork()</code>. И наконец, операционная система Unix предоставляет простые и в то же время устойчивые средства межпроцессного взаимодействия, которые, в сочетании с быстрым созданием процессов, позволяют создавать простые утилиты, которые <emphasis>умеют выполнять всего одну функцию, но делают это хорошо</emphasis>, и могут быть связаны вместе для выполнения более сложных задач.</p>
    <p>Сегодня Unix — современная операционная система, которая поддерживает многозадачность, многопоточность, виртуальную память, замещение страниц по требованию, библиотеки совместного использования, загружаемые по требованию, и сеть TCP/IP. Многие варианты операционной системы Unix поддерживают масштабирование до сотен процессоров, в то время как другие варианты ОС Unix работают на миниатюрных устройствах в качестве встраиваемых систем. Хотя разработка Unix больше не является исследовательским проектом, все же продолжаются разработки (с целью получить дополнительные преимущества) с использованием возможностей операционной системы Unix, которая при этом остается практичной операционной системой общего назначения.</p>
    <p>Операционная система Unix обязана своим успехом простоте и элегантности построения. В основе ее сегодняшней мощности лежат давние идеи Денниса Ритчи, Кена Томпсона и других разработчиков, обеспечившие возможность операционной системе Unix бескомпромиссно развиваться.</p>
   </section>
   <section>
    <title>
     <p>Потом пришел Линус: введение в Linux</p>
    </title>
    <p>Операционная система Linux была разработана Линусом Торвальдсом (Linus Torvalds) в 1991 году как операционная система для компьютеров, работающих на новом в то время микропроцессоре Intel 80386. Тогда Линус Торвальдс был студентом университета в Хельсинки и был крайне возмущен отсутствием мощной и в то же время свободно доступной Unix-подобной операционной системы. Операционная система DOS, продукт корпорации Microsoft, была для Торвальдса полезна только лишь, чтобы поиграть в игрушку "Принц Персии", и не для чего больше. Линус пользовался операционной системой Minix, недорогой Unix-подобной операционной системой, которая была создана в качестве учебного пособия. В этой операционной системе ему не нравилось отсутствие возможности легко вносить и распространять изменения исходного кода (это запрещалось лицензией ОС Minix), а также технические решения, которые использовал автор ОС Minix.</p>
    <p>Поставленный перед такой проблемой, Линус решил написать свою операционную систему. Начал он с написания простого эмулятора терминала, который он подключал к большим Unix-системам в университете. Его эмулятор терминала постепенно рос, развивался и улучшался. Постепенно у Линуса появилась еще не совсем зрелая, но полноценная Unix-система. В 1991 году он опубликовал в Интернет ее первую версию.</p>
    <p>По некоторым неясным причинам, использование операционной системы Linux и количество ее пользователей начали стремительно расти. Более важным для успеха Linux стало то, что эта операционная система привлекла многих разработчиков, которые начали изменять, исправлять и улучшать код. Благодаря соответствующему лицензионному соглашению, ОС Linux быстро стала совместным проектом, который разрабатывается многими людьми.</p>
    <p>Сейчас Linux — это развитая операционная система, работающая на аппаратных платформах AMD x86-64, ARM, Compaq Alpha, CRIS, DEC VAX, H8/300, Hitachi SuperH, HP PA-RISC, IBM S/390, Intel IA-64, MIPS, Motorola 68000, PowerPC, SPARC, UltraSPARC и v850. Она работает в различных системах, как размером с часы, так и на больших супер-компьютерных кластерах. Сегодня коммерческий интерес к операционной системе Linux достаточно высок. Как новые корпорации, ориентирующиеся исключительно на Linux (Monta Vista или Red Hat), так и старые (IBM, Novell) предлагают решения на основе этой ОС для встраиваемых систем, десктопов и серверов.</p>
    <p>Операционная система Linux является клоном Unix, по ОС Linux— это не Unix. Хотя в ОС Linux позаимствовано много идей от Unix, в Linux реализован API ОС Unix (как это определено в стандарте POSIX и спецификации Single Unix Specification), все же система Linux не является производной от исходного кода Unix, как это имеет место для других Unix-систем, Там, где это желательно, были сделаны отклонения от пути, по которому шли другие разработчики, однако это не подрывает основные принципы построения операционной системы Unix и не нарушает программные интерфейсы.</p>
    <p>Одна из наиболее интересных особенностей операционной системы Linux — то, что это не коммерческий продукт; наоборот, это совместный проект, который выполняется через всемирную сеть Интернет. Конечно, Линус остается создателем Linux и занимается <emphasis>поддержкой</emphasis> ядра, но работа продолжается группой мало связанных между собой разработчиков. Фактически кто угодно может внести свой вклад в операционную систему Linux. Ядро Linux, так же как и большая часть операционной системы, является <emphasis>свободно распространяемым</emphasis> программным обеспечением и имеет <emphasis>открытый исходный код</emphasis><a l:href="#n4" type="note">[4]</a>.</p>
    <p>В частности, ядро Linux выпускается под лицензией GNU General Public License (GPL) версии 2.0. В результате каждый имеет право загружать исходный код и вносить в него любые изменения. Единственная оговорка — любое распространение внесенных вами изменений должно производиться на тех же условиях, которыми пользовались вы при получении исходного кода, включая доступность самого исходного программного кода<a l:href="#n5" type="note">[5]</a>.</p>
    <p>Операционная система Linux предоставляет много возможностей для многих людей. Основными частями системы являются ядро, библиотека функций языка С, компилятор, набор инструментов, основные системные утилиты, такие как программа для входа в систему (login) и обработчик команд пользователя (shell). В операционную систему Linux может быть включена современная реализация системы X Windows, включая полно-функциональную среду офисных приложений (desktop environment), такую как, например, GNOME. Для ОС Linux существуют тысячи свободных и коммерческих программ. В этой книге под понятием <emphasis>Linux</emphasis>, в основном, имеется в виду <emphasis>ядро Linux</emphasis>. Там, где это может привести к неопределенностям, будет указано, что имеется в виду под понятием Linux — вся система или только ядро. Строго говоря, термин Linux относится только к ядру.</p>
   </section>
   <section>
    <title>
     <p>Обзор операционных систем и ядер</p>
    </title>
    <p>Из-за неуклонного роста возможностей и не очень качественного построения некоторых современных операционных систем, понятие операционной системы стало несколько неопределенным. Многие пользователи считают, что то, что они видят на экране, — и есть операционная система. Обычно, и в этой книге тоже, под <emphasis>операционной системой</emphasis> понимается часть компьютерной системы, которая отвечает за основные функции использования и администрирования. Это включает в себя ядро и драйверы устройств, системный загрузчик (boot loader), командный процессор и другие интерфейсы пользователя, а также базовую файловую систему и системные утилиты. В общем, только <emphasis>необходимые</emphasis> компоненты. Термин <emphasis>система</emphasis> обозначает операционную систему и все пользовательские программы, которые работают под ее управлением.</p>
    <p>Конечно, основной темой этой книги будет <emphasis>ядро</emphasis> операционной системы. Интерфейс пользователя — это внешняя часть операционной системы, а ядро — внутренняя. В своей основе ядро — это программное обеспечение, которое предоставляет базовые функции для всех остальных частей операционной системы, занимается управлением аппаратурой и распределяет системные ресурсы. Ядро часто называют <emphasis>основной частью</emphasis> (core) или <emphasis>контроллером</emphasis> операционной системы. Типичные компоненты ядра — обработчики прерываний, которые обслуживают запросы на прерывания, планировщик, который распределяет процессорное время между многими процессами, система управления памятью, которая управляет адресным пространством процессов, и системные службы, такие как сетевая подсистема и подсистема межпроцессного взаимодействия. В современных системах с устройствами управления защищенной памятью ядро обычно занимает привилегированное положение по отношению к пользовательским программам. Это включает доступ ко всем областям защищенной памяти и полный доступ к аппаратному обеспечению. Состояние системы, в котором находится ядро, и область памяти, в которой находится ядро, вместе называются <emphasis>пространством ядра</emphasis> (или режимом ядра, kernel-space). Соответственно, пользовательские программы выполняются в <emphasis>пространствах задач</emphasis> (пользовательский режим, режим задач, user-space). Пользовательским программам доступно лишь некоторое подмножество машинных ресурсов, они не могут выполнять некоторые системные функции, напрямую обращаться к аппаратуре и делать другие недозволенные вещи. При выполнении программного кода ядра система находится в пространстве (режиме) ядра, в отличие от нормального выполнения пользовательских программ, которое происходит в режиме задачи.</p>
    <p>Прикладные программы, работающие в системе, взаимодействуют с ядром с помощью интерфейса <emphasis>системных вызовов</emphasis> (system call) (рис. 1.1). Прикладная программа обычно вызывает функции различных библиотек, например <emphasis>библиотеки функций</emphasis> языка С, которые, в свою очередь, обращаются к интерфейсу системных вызовов для того, чтобы отдать приказ ядру выполнить определенные действия от их имени. Некоторые библиотечные вызовы предоставляют функции, для которых отсутствует системный вызов, и поэтому обращение к ядру — это только один этап в более сложной функции. Давайте рассмотрим всем известную функцию <code>printf()</code>. Эта функции обеспечивает форматирование и буферизацию данных и лишь после этого один раз обращается к системному вызову <code>write()</code> для вывода данных на консоль. Некоторые библиотечные функции соответствуют функциям ядра один к одному. Например, библиотечная функция <code>open()</code> не делает ничего, кроме выполнения системного вызова <code>open()</code>. В то же время некоторые библиотечные функции, как, например, <code>strcpy()</code>, надо полагать, вообще не используют обращения к ядру. Когда прикладная программа выполняет системный вызов, то говорят, что <emphasis>ядро выполняет работу от имени прикладной программы</emphasis>. Более того, говорят, что прикладная программа <emphasis>выполняет системный вызов в пространстве ядра</emphasis>, а ядро выполняется в <emphasis>контексте процесса</emphasis>. Такой тип взаимодействия, когда прикладная программа <emphasis>входит</emphasis> в ядро через интерфейс системных вызовов, является фундаментальным способом выполнения задач.</p>
    <image l:href="#img_1.jpeg"/>
    <p><strong>Рис. 1.1</strong>. Взаимодействие между прикладными программами, ядром и аппаратным обеспечением</p>
    <p>В функции ядра входит также управление системным аппаратным обеспечением. Практически все платформы, включая те, на которых работает операционная система Linux, используют <emphasis>прерывания</emphasis> (interrupt). Когда аппаратному устройству необходимо как-то взаимодействовать с системой, оно генерирует прерывание, которое прерывает работу ядра в асинхронном режиме<a l:href="#n6" type="note">[6]</a>.</p>
    <p>Обычно каждому типу прерываний соответствует номер. Ядро использует номер прерывания для выполнения специального обработчика прерывания (interrupt handler), который обрабатывает прерывание и отправляет на него ответ. Например, при вводе символа с клавиатуры, контроллер клавиатуры генерирует прерывание, чтобы дать знать системе, что в буфере клавиатуры есть новые данные. Ядро определяет номер прерывания, которое пришло в систему и выполняет соответствующий обработчик прерывания. Обработчик прерывания обрабатывает данные, поступившие с клавиатуры, и даст знать контроллеру клавиатуры, что ядро готово для приема новых данных. Для обеспечения синхронизации выполнения ядро обычно может запрещать прерывания: или все прерывания, или только прерывание с определенным номером. Во многих операционных системах обработчики прерываний не выполняются в контексте процессов. Они выполняются в специальном <emphasis>контексте прерывания</emphasis> (interrupt context), который не связан ни с одним процессом. Этот специальный контекст существует то только для того, чтобы дать обработчику прерывания возможность быстро отреагировать на прерывание и закончить работу.</p>
    <p>Контексты выполнения заданий полностью определяют всю широту возможных действий ядра. Фактически, можно заключить, что в операционной системе Linux процессор в любой момент времени выполняет один из трех типов действий.</p>
    <p>• Работа от имени определенного процесса в режиме ядра в контексте процесса.</p>
    <p>• Работа по обработке прерывания в режиме ядра в контексте прерывания, не связанном с процессами.</p>
    <p>• Выполнение кода пользовательской программы в режиме задачи.</p>
   </section>
   <section>
    <title>
     <p>Ядро Linux в сравнении с классическими ядрами Unix</p>
    </title>
    <p>Благодаря общему происхождению и одинаковому API, современные ядра Unix имеют некоторые общие характерные черты. За небольшими исключениями ядра Unix представляют собой монолитные статические бинарные файлы. Это значит, что они существуют в виде больших исполняемых образов, которые выполняются один раз и используют одну копию адресного пространства. Для работы операционной системы Unix обычно требуется система с контроллером управления страничной адресацией памяти (memory management unit); это аппаратное обеспечение позволяет обеспечить защиту памяти в системе и предоставить каждому процессу уникальное виртуальное адресное пространство. В списке литературы приведены мои любимые книги по устройству классических ядер операционной системы Unix.</p>
    <cite>
     <subtitle>Сравнение решений на основе монолитного ядра и микроядра</subtitle>
     <p>Операционные системы, в соответствии с особенностями построения, можно разделить на две большие группы: с монолитным ядром и с микроядром. (Есть еще третий тип — экзоядро, которое пока еще используется, в основном, только в исследовательских операционных системах, но уже начинает пробивать дорогу в большой мир.)</p>
     <p>Монолитное ядро является самым простым, и до 1980-х годов все ядра строились именно таким образом. Монолитное ядро реализовано в виде одного большого процесса, который выполняется в одном адресном пространстве, Такие ядра обычно хранятся на диске в виде одного большого статического бинарного файла. Все службы ядра существуют и выполняются в одном большом адресном пространстве ядра. Взаимодействия в ядре выполняются очень просто, потому что все, что выполняется в режиме ядра, — выполняется в одном адресном пространстве. Ядро может вызывать функции непосредственно, как это делает пользовательское приложение. Сторонники такой модели обычно указывают на простоту и высокую производительность монолитных ядер.</p>
     <p>Микроядра не реализуются в виде одного большого процесса. Все функции ядра разделяются на несколько процессов, которые обычно называют серверами. В идеале, в привилегированном режиме работают только те серверы, которым абсолютно необходим привилегированный режим. Остальные серверы работают в пространстве пользователя. Все серверы, тем не менее, поддерживаются независимыми друг от друга и выполняются каждый в своем адресном пространстве. Следовательно, прямой вызов функций, как в случае монолитного ядра, невозможен. Все взаимодействия внутри микроядра выполняются с помощью передачи сообщений. Механизм межпроцессного взаимодействия (Inter Process Communication, IPC) встраивается в систему, и различные серверы взаимодействуют между собой и обращаются к "службам" друг друга путем отправки сообщений через механизм IPC. Разделение серверов позволяет предотвратить возможность выхода из строя одного сервера при выходе из строя другого.</p>
     <p>Кроме того, модульность системы позволяет одному серверу вытеснять из памяти другого. Поскольку механизм IPC требует больше накладных расходов, чем обычный вызов функции, и при этом может потребоваться переключение контекста из пространства пользователя в пространство ядра и наоборот, то передача сообщений приводит к падению производительности по сравнению с монолитными ядрами, в которых используются обычные вызовы функций.</p>
     <p>В современных операционных системах с микроядром, большинство серверов выполняется в пространстве ядра, чтобы избавиться от накладных расходов, связанных с переключением контекста, кроме того, это дает потенциальную возможность прямого вызова функций. Ядро операционной системы Windows NT, а также ядро Mach (на котором базируется часть операционной системы Mac OS X) — это примеры микроядер. В последних версиях как Windows NT, так и Mac OS X все серверы выполняются только в пространстве ядра, что является отходом от первоначальной концепции микроядра.</p>
     <p>Ядро ОС Linux монолитное, т.е. оно выполняется в одном адресном пространстве, в режиме ядра. Тем не менее ядро Linux позаимствовало некоторые хорошие свойства микроядерной модели: в нем используется преемптивное ядро, поддерживаются потоки пространства ядра и возможность динамической загрузки в ядро внешних бинарных файлов (модулей ядра). Ядро Linux не использует никаких функций микроядерной модели, которые приводят к снижению производительности: все выполняется в режиме ядра с непосредственным вызовом функций, вместо передачи сообщений. Следовательно, операционная система Linux — модульная, многопоточная, а выполнение самого ядра можно планировать.</p>
     <p>Прагматизм снова победил.</p>
    </cite>
    <p>По мере того как Линус и другие разработчики вносили свой вклад в ядро Linux, они принимали решения о том, как развивать ОС Linux без пренебрежения корнями, связанными с Unix (и, что более важно, без пренебрежения API ОС Unix). Поскольку операционная система Linux не базируется на какой-либо версии ОС Unix, Линус и компания имели возможность найти и выбрать наилучшее решение для любой проблемы и даже со временем изобрести новые решения! Ниже приводится анализ характеристик ядра Linux, которые отличают его от других разновидностей Unix.</p>
    <p>• Ядро Linux поддерживает динамическую загрузку модулей ядра. Хотя ядро Linux и является монолитным, оно дополнительно поддерживает динамическую загрузку и выгрузку исполняемого кода ядра по необходимости.</p>
    <p>• Ядро Linux поддерживает симметричную многопроцессорную обработку (SMP). Хотя большинство коммерческих вариантов операционной системы Unix сейчас поддерживает SMP, большинство традиционных реализаций ОС Unix такой поддержки не имеет.</p>
    <p>• Ядро Linux является преемптивным. В отличие от традиционных вариантов ОС Unix, ядро Linux в состоянии вытеснить выполняющееся задание, даже если это задание работает в режиме ядра. Среди коммерческих реализаций ОС Unix преемптивное ядро имеют только операционные системы Solaris и IRIX.</p>
    <p>• В ядре Linux используется интересный подход для поддержки многопоточности (threads): потоки ни чем не отличаются от обычных процессов. С точки зрения ядра все процессы одинаковы, просто некоторые из них имеют общие ресурсы.</p>
    <p>• В ядре Linux отсутствуют некоторые функции ОС Unix, которые считаются плохо реализованными, как, например, поддержка интерфейса STREAMS, или отвечают "глупым" стандартам.</p>
    <p>• Ядро Linux является полностью открытым во всех смыслах этого слова. Набор функций, реализованных в ядре Linux, — это результат свободной и открытой модели разработки операционной системы Linux. Если какая-либо функция ядра считается маловажной или некачественной, то разработчики ядра не обязаны ее реализовать. В противоположность этому, внесение изменений при разработке ядра Linux занимает "элитарную" позицию: изменения должны решать определенную практическую задачу, должны быть логичными и иметь понятную четкую реализацию. Следовательно, функции некоторых современных вариантов ОС Unix, такие как память ядра со страничной реализацией, не были реализованы. Несмотря на имеющиеся различия, Linux является операционной системой со строгим наследованием традиций ОС Unix.</p>
   </section>
   <section>
    <title>
     <p>Версии ядра Linux</p>
    </title>
    <p>Ядро Linux поставляется в двух вариантах: стабильном (stable) и разрабатываемом (development). Версии стабильного ядра - это выпуски продукции промышленного уровня, которая готова для широкого использования. Новые стабильные версии ядра обычно выпускаются для исправления ошибок и для предоставления новых драйверов устройств. Разрабатываемые версии ядра, наоборот, подвержены быстрым изменениям. По мере того как разработчики экспериментируют с новыми решениями, часто вносятся радикальные изменения в ядро.</p>
    <p>Ядра Linux стабильных и разрабатываемых версий можно отличить друг от друга с помощью простой схемы присваивания имен (рис. 1.2.). Три числа, которые разделяются точкой, определяют версию ядра. Первое число — значение старшей (major) версии, второе — значение младшей (minor), третье число — значение редакции (выпуска, revision). Значение младшей версии также определяет, является ли ядро стабильным или разрабатываемым; если это значение четное, то ядро стабильное, а если нечетное, то разрабатываемое. Так, например, версия 2.6.0 определяет стабильное ядро. Ядро имеет старшую версию 2, младшую версию 6 и редакцию 0. Первые два числа также определяют "серию ядер", в данном случае серия ядер — 2.6.</p>
    <image l:href="#img_2.jpeg"/>
    <p><strong>Рис. 1.2</strong>. Соглашение о присваивании имен ядрам</p>
    <p>Разработка ядра соответствует различным фазам. Вначале разработчики ядра работают над новыми функциями, что напоминает хаос. Через определенное время ядро оказывается сформировавшимся, и в конце концов объявляется замораживание функций.</p>
    <p>Начиная с этого момента никакие новые функции не могут быть добавлены в ядро. Однако работа над существующими функциями может быть продолжена. После того как ядро становится почти стабильным, осуществляется замораживание кода. В этом случае допускаются только исправления ошибок. Вскоре после этого (можно надеяться) ядро выпускается в виде первой, новой, стабильной версии. Например, при стабилизации серии ядер 2.5 получается серия 2.6.</p>
    <cite>
     <subtitle>Все это неправда</subtitle>
     <p>По крайней мере — не совсем. Приведенное только что описание процесса разработки ядра технически правильное. Раньше процесс происходил именно так, как описано. Тем не менее летом 2004 года на ежегодном саммите для приглашенных разработчиков ядра Linux было принято решение продолжить разработку серии 2.6 ядра Linux и в ближайшем будущем не переходить на серию разрабатываемого ядра 2.7. Такое решение было принято потому, что ядро 2.6 получилось хорошим; оно, в основном, стабильно и на горизонте нет никаких новых функций, которые требуют серьезного вторжения в ядро.</p>
     <p>Кроме того, и, возможно, это главное — существующая система поддержки, которая обеспечивается Линусом Торвальдсом и Эндрю Мортоном, работает чрезвычайно хорошо. Разработчики ядра уверены, что процесс разработки может продолжаться таким образом, что серия ядер 2.6 будет оставаться стабильной и в ней будут появляться новые возможности. Время рассудит, но уже сейчас результаты выглядят хорошо.</p>
    </cite>
    <p>Эта книга базируется на ядрах стабильной серии 2.6.</p>
   </section>
   <section>
    <title>
     <p>Сообщество разработчиков ядра Linux</p>
    </title>
    <p>Когда вы начинаете разрабатывать код ядра Linux, вы становитесь частью глобального сообщества разработчиков ядра Linux. Главный форум этого сообщества — <emphasis>список рассылки разработчиков ядра Linux</emphasis> (linux-kernel mailing list). Информация по поводу подписки на этот форум доступна по адресу <code>http://vger.kernel.org</code>. Следует заметить, что это достаточно перегруженный сообщения список рассылки (количество сообщений порядка 300 в день) и что другие читатели этого списка (разработчики ядра, включая Линуса) не очень склонны заниматься ерундой. Однако этот список рассылки может оказать неоценимую помощь в процессе разработки; здесь вы сможете найти тестологов, получить экспертную оценку и задать вопросы.</p>
    <p>В последних главах приведен обзор процесса разработки ядра и более полное описание того, как успешно принимать участие в деятельности сообщества разработчиков ядра.</p>
   </section>
   <section>
    <title>
     <p>Перед тем как начать</p>
    </title>
    <p>Эта книга посвящена ядру Linux: как оно работает, почему оно работает и чему следует уделить внимание. Далее будут описаны принципы работы и реализация основных подсистем ядра, а также интерфейсы и программная семантика. Эта книга касается практических вопросов, и в ней используется подход на основании золотой серединки указанных выше направлений. Такой интересный подход в сочетании с анекдотами из личной практики автора и советами по хакерским приемам позволяет быть уверенным в том, что книга станет хорошим стартом.</p>
    <p>Я надеюсь, что у читателей есть доступ к системе Linux и дереву исходного кода ядра. В идеале предполагается, что читатель— это пользователь операционной системы Linux, который уже "копался" в исходном программном коде, но все же нуждается в некоторой помощи для того, чтобы все связать воедино. В принципе, читатель может и не быть пользователем Linux, но хочет разобраться в устройстве ядра из чистого любопытства. Тем не менее, для того чтобы самому научиться писать программы — исходный код незаменим. Исходный программный код <emphasis>свободно</emphasis> доступен — пользуйтесь им!</p>
    <cite>
     <text-author>Удачи!</text-author>
    </cite>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 2</p>
    <p>Начальные сведения о ядре Linux</p>
   </title>
   <section>
    <p>В этой главе будут рассмотрены основные вопросы, связанные с ядром Linux: где получить исходный код, как его компилировать и как инсталлировать новое ядро. После этого рассмотрим некоторые допущения, связанные с ядром Linux, отличия между ядром и пользовательскими программам, а также общие методы, которые используются в ядре.</p>
    <p>Ядро имеет интересные особенности, которые отличают его от других программ, но нет таких вещей, в которых нельзя разобраться. Давайте этим займемся.</p>
   </section>
   <section>
    <title>
     <p>Получение исходного кода ядра</p>
    </title>
    <section>
     <p>Исходный программный код последней версии ядра всегда доступен как в виде полного архива в формате tar (tarball), так и виде инкрементной заплаты по адресу <code>http://www.kernel.org</code>.</p>
     <p>Если нет необходимости по той или другой причине работать со старыми версиями ядра, то всегда нужно использовать самую последнюю версию. Архив <code>kernel.org</code> — это то место, где можно найти как само ядро, так и заплаты к нему от ведущих разработчиков.</p>
    </section>
    <section>
     <title>
      <p>Инсталляция исходного кода ядра</p>
     </title>
     <p>Архив исходного кода ядра в формате tar распространяется в сжатых форматах GNU zip (gzip) и bzip2. Формат bzip2 наиболее предпочтителен, так как обеспечивает больший коэффициент сжатия по сравнению с форматом gzip. Архив ядра в формате bzip2 имеет имя <code>linux-x.y.z.tar.bz2</code>, где <code>x</code>, <code>y</code>, <code>z</code> — это номер соответствующей версии исходного кода ядра. После загрузки исходного кода его можно декомпрессировать очень просто. Если tar-архив сжат с помощью GNU zip, то необходимо выполнить следующую команду.</p>
     <p><code>$ tar xvzf linux-x.y.z.tar.gz</code></p>
     <p>Если сжатие выполнено с помощью bzip2, то команда должна иметь следующий вид.</p>
     <p><code>$ tar xvjf linux-x.y.z.tar.bz2</code></p>
     <p>Обе эти команды позволяют декомпрессировать и развернуть дерево исходных кодов ядра в каталог с именем <code>linux-x.y.z</code>.</p>
     <cite>
      <subtitle>Где лучше инсталлировать и изменять исходный код</subtitle>
      <p>Исходный код ядра обычно инсталлируется в каталог <code>/usr/src/linux</code>. Заметим, что это дерево исходного кода нельзя использовать для разработок. Версия ядра, с которой была скомпилирована ваша <emphasis>библиотека С</emphasis>, часто связывается с этим деревом каталогов. Кроме того, чтобы вносить изменения в ядро, не обязательно иметь права пользователя root, вместо этого лучше работать в вашем домашнем каталоге и использовать права пользователя root только для инсталляции ядра. Даже при инсталляции нового ядра каталог <code>/usr/src/linux</code> лучше оставлять без изменений.</p>
     </cite>
    </section>
    <section>
     <title>
      <p>Использование заплат</p>
     </title>
     <p>В сообществе разработчиков ядра Linux заплаты (patch) — это основной <emphasis>язык общения</emphasis>. Вы будете распространять ваши изменения исходного кода ядра в виде заплат и получать изменения кода от других разработчиков тоже в виде заплат. При данном рассмотрении наиболее важными являются <emphasis>инкрементные заплаты</emphasis> (incremental patch), которые позволяют перейти от одной версии ядра к другой. Вместо того чтобы загружать большой архив ядра, можно просто применить инкрементную заплату и перейти от имеющейся версии к следующей. Это позволяет сэкономить время и пропускную способность каналов связи. Для того чтобы применить инкрементную заплату, находясь в каталоге дерева исходных кодов ядра, нужно просто выполнить следующую команду.</p>
     <p><code>$ patch -p1 &lt; ../patch-x.y.z</code></p>
     <p>Обычно заплата для перехода на некоторую версию ядра должна применяться к предыдущей версии ядра.</p>
     <p>В следующих главах использование заплат рассматривается более подробно.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Дерево исходных кодов ядра</p>
    </title>
    <p>Дерево исходных кодов ядра содержит ряд каталогов, большинство из которых также содержит подкаталоги. Каталоги, которые находятся в корне дерева исходных кодов, и их описание приведены в табл. 2.1.</p>
    <empty-line/>
    <p><strong>Таблица 2.1</strong>. Каталоги в корне дерева исходных кодов ядра</p>
    <table>
     <tr align="left">
      <th align="left" valign="top">Каталог</th>
      <th align="left" valign="top">Описание</th>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>arch</code></td>
      <td align="left" valign="top">Специфичный для аппаратной платформы исходный код</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>crypto</code></td>
      <td align="left" valign="top">Криптографический API</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>Documentation</code></td>
      <td align="left" valign="top">Документация исходного кода ядра</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>drivers</code></td>
      <td align="left" valign="top">Драйверы устройств</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>fs</code></td>
      <td align="left" valign="top">Подсистема VFS и отдельные файловые системы</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>include</code></td>
      <td align="left" valign="top">Заголовочные файлы ядра</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>init</code></td>
      <td align="left" valign="top">Загрузка и инициализация ядра</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>ipc</code></td>
      <td align="left" valign="top">Код межпроцессного взаимодействия</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>kernel</code></td>
      <td align="left" valign="top">Основные подсистемы, такие как планировщик</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>lib</code></td>
      <td align="left" valign="top">Вспомогательные подпрограммы</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>mm</code></td>
      <td align="left" valign="top">Подсистема управления памятью и поддержка виртуальной памяти</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>net</code></td>
      <td align="left" valign="top">Сетевая подсистема</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>scripts</code></td>
      <td align="left" valign="top">Сценарии компиляции ядра</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>security</code></td>
      <td align="left" valign="top">Модуль безопасности Linux</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>sound</code></td>
      <td align="left" valign="top">Звуковая подсистема</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>usr</code></td>
      <td align="left" valign="top">Начальный код пространства пользователя (initramfs)</td>
     </tr>
    </table>
    <p>Некоторые файлы, которые находятся в корне дерева исходных кодов, также заслуживают внимания. Файл <code>COPYING</code> — это лицензия ядра (GNU GPL v2). Файл <code>CREDITS</code> — это список разработчиков, которые внесли большой вклад в разработку ядра. Файл <code>MAINTAINERS</code> — список людей, которые занимаются поддержкой подсистем и драйверов ядра. И наконец, <code>Makefile</code> — это основной сборочный файл ядра.</p>
   </section>
   <section>
    <title>
     <p>Сборка ядра</p>
    </title>
    <section>
     <p>Сборка ядра достаточно проста. Это может показаться удивительным, но она даже более проста, чем компиляция и инсталляция других системных компонентов, как, например библиотеки <code>glibc</code>. В ядрах серии 2.6 встроена новая система конфигурации и компиляции, которая позволяет сделать эту задачу еще проще и является долгожданным улучшением по сравнению с серией ядер 2.4.</p>
     <p>Так как доступен исходный код ядра Linux, то, это означает, что есть возможность сконфигурировать ядро перед компиляцией. Есть возможность скомпилировать поддержку только необходимых драйверов и функций. Конфигурация ядра— необходимый этап перед тем, как его компилировать. Поскольку в ядре бесчисленное количество функций и вариантов поддерживаемого аппаратного обеспечения, возможностей по конфигурации, мягко говоря, <emphasis>много</emphasis>. Конфигурация управляется с помощью опций конфигурации в виде <code>CONFIG_FEATURE</code>. Например, поддержка симметричной многопроцессорной обработки (Symmetric multiprocessing, SMP) устанавливается с помощью опции <code>CONFIG_SMP</code>. Если этот параметр установлен, то поддержка функций SMP включена. Если этот параметр не установлен, то функции поддержки SMP отключены. Все конфигурационные параметры хранятся в файле <code>.config</code> в корневом каталоге дерева исходного кода ядра и устанавливаются одной из конфигурационных программ, например, с помощью команды <code>make xconfig</code>. Конфигурационные параметры используются как для определения того, какие файлы должны быть скомпилированы во время сборки ядра, так и для управления процессом компиляции через директивы препроцессора.</p>
     <p>Конфигурационные переменные бывают двух видов: логические (<emphasis>boolean</emphasis>) и переменные с тремя состояниями (<emphasis>tristate</emphasis>). Логические переменные могут принимать значения <code><emphasis>yes</emphasis></code> и <code><emphasis>no</emphasis></code>. Такие переменные конфигурации ядра, как <code>CONFIG_PREEMPT</code>, обычно являются логическими. Конфигурационная переменная с тремя состояниями может принимать значения <code><emphasis>yes</emphasis></code>, <code><emphasis>no</emphasis></code> и <code><emphasis>module</emphasis></code>. Значение <code><emphasis>module</emphasis></code> отвечает конфигурационному параметру, который установлен, но соответствующий код должен компилироваться как модуль (т.е. как отдельный объект, который загружается динамически). Драйверы устройств обычно представляются конфигурационными переменными с тремя состояниями.</p>
     <p>Конфигурационные параметры могут иметь целочисленный, или строковый, тип. Эти параметры не контролируют процесс сборки, а позволяют указать значения, которые встраиваются в исходный код ядра с помощью препроцессора. Например, с помощью конфигурационного параметра можно указать размер статически выделенного массива.</p>
     <p>Ядра, которые включаются в поставки ОС Linux такими производителями, как Novell и Redhat, компилируются как часть дистрибутива. В таких ядрах обычно имеется большой набор различных функций и практически полный набор всех драйверов устройств в виде загружаемых модулей. Это позволяет получить хорошее базовое ядро и поддержку широкого диапазона оборудования. К сожалению, как разработчикам ядра, вам потребуется компилировать свои ядра и самим разбираться, какие модули включать, а какие нет.</p>
     <p>В ядре поддерживается несколько инструментов, которые позволяют выполнять конфигурацию. Наиболее простой инструмент — это текстовая утилита командной строки:</p>
     <p><code>make config</code></p>
     <p>Эта утилита просматривает все параметры один за другим и интерактивно запрашивает у пользователя, какое значение соответствующего параметра установить — <code><emphasis>yes</emphasis></code>, <code><emphasis>no</emphasis></code> или <code><emphasis>module</emphasis></code> (для переменной с тремя состояниями). Эта операция требует <emphasis>длительного</emphasis> времени, и если у вас не почасовая оплата, то лучше использовать утилиту на основе интерфейса <emphasis>ncurses</emphasis>:</p>
     <p><code>make menuconfig</code></p>
     <p>или графическую утилиту на основе системы <emphasis>X11</emphasis>:</p>
     <p><code>make xconfig</code></p>
     <p>или еще более удобную графическую утилиту, основанную на библиотеке <emphasis>gtk+</emphasis>:</p>
     <p><code>make gconfig</code></p>
     <p>Эти утилиты позволяют разделить все параметры по категориям, таким как <strong>Processor Features</strong> (Свойства процессора) и <strong>Network Devices</strong> (Сетевые устройства). Пользователи могут перемещаться по категориям и, конечно, изменять значения конфигурационных параметров. Команда</p>
     <p><code>$ make defconfig</code></p>
     <p>позволяет создать конфигурационный файл, который будет содержать параметры, используемые по умолчанию для текущей аппаратной платформы. Хотя эти параметры и достаточно общие (ходят слухи, что для аппаратной платформы i386 используется конфигурация Линуса), они являются хорошей стартовой точкой, если вы никогда перед этим не занимались конфигурацией ядра. Чтобы все сделать быстро, необходимо выполнить эту команду, а потом проверить, включена ли поддержка всех нужных аппаратных устройств.</p>
     <p>Конфигурационные параметры содержатся в корне дерева каталогов исходного кода ядра в файле с именем <code>.config</code>. Для вас может показаться более простым, так же как и для большинства разработчиков, непосредственно редактировать этот конфигурационный файл. Достаточно легко проводить поиск в этом файле и изменять значение конфигурационных параметров. После внесения изменений в конфигурационный файл или при использовании существующего конфигурационного файла для нового дерева каталогов исходного кода ядра, необходимо активизировать и обновить конфигурацию с помощью команды:</p>
     <p><code>make oldconfig</code></p>
     <p>Кстати, перед сборкой ядра эту команду также необходимо выполнить. После того как конфигурация ядра выполнена, можно выполнить сборку с помощью команды:</p>
     <p><code>make</code></p>
     <p>В отличие от предыдущих серий ядер, в версии 2.6 больше нет необходимости выполнять команду <code>make dep</code> перед сборкой ядра, так как создание дерева зависимостей выполняется автоматически. Также не нужно указывать цель сборки, например <emphasis>bzImage</emphasis>, как это было необходимо для более ранних версий. Правило, записанное в файле с именем <code>Makefile</code>, которое используется по умолчанию, в состоянии обработать все!</p>
    </section>
    <section>
     <title>
      <p>Уменьшение количества выводимых сообщений</p>
     </title>
     <p>Для того чтобы уменьшить шум, связанный с сообщениями, которые выдаются во время сборки, но в то же время видеть предупреждения и сообщения об ошибках, можно использовать такую хитрость, как перенаправление стандартного вывода команды <code>make(1)</code>:</p>
     <p><code>make &gt; "имя_некоторого_файла"</code></p>
     <p>Если вдруг окажется необходимым просмотреть выводимые сообщения, можно воспользоваться соответствующим файлом. Но обычно, если предупреждения или сообщения об ошибках <emphasis>выводятся</emphasis> на экран, в этом нет необходимости.</p>
     <p>На самом деле я выполняю следующую команду</p>
     <p><code>make &gt; /dev/null</code></p>
     <p>что позволяет совсем избавиться от ненужных сообщений.</p>
    </section>
    <section>
     <title>
      <p>Параллельная сборка</p>
     </title>
     <p>Программа <code>make(1)</code> предоставляет возможность разбить процесс сборки на несколько заданий. Каждое из этих заданий выполняется отдельно от остальных и параллельно с остальными, существенно ускоряя процесс сборки на многопроцессорных системах. Это также позволяет более оптимально использовать процессор, Поскольку время компиляции большого дерева исходного кода также включает время ожидания завершения ввода-вывода (время, в течение которого процесс ждет завершения операций ввода-вывода).</p>
     <p>По умолчанию утилита <code>make(1)</code> запускает только одну задачу, поскольку часто файлы сборки содержат некорректную информацию о зависимостях. При неправильной информации о зависимостях несколько заданий могут начать "наступать друг другу на ноги", что приведет к ошибкам компиляции. Конечно же, в файле сборки ядра таких ошибок нет. Для компиляции ядра с использованием параллельной сборки необходимо выполнить следующую команду.</p>
     <p><code>$ make -jn</code></p>
     <p>где <emphasis>n</emphasis> — количество заданий, которые необходимо запустить.</p>
     <p>Обычно запускается один или два процесса на процессор. Например, на двухпроцессорной машине можно использовать следующий запуск.</p>
     <p><code>$ make -j4</code></p>
     <p>Используя такие отличные утилиты, как <code>distcc(1)</code> и <code>ccache(1)</code>, можно еще более существенно уменьшить время компиляции ядра.</p>
    </section>
    <section>
     <title>
      <p>Инсталляция ядра</p>
     </title>
     <p>После того как ядро собрано, его необходимо инсталлировать. Процесс инсталляции существенно зависит от платформы и типа системного загрузчика. Для того чтобы узнать, в какой каталог должен быть скопирован образ ядра и как установить его для загрузки, необходимо обратиться к руководству по используемому системному загрузчику. На случай если новое ядро будет иметь проблемы с работоспособностью, всегда следует сохранить одну или две копии старых ядер, которые гарантированно работоспособны!</p>
     <p>Например, для платформы x86, при использовании системного загрузчика grub можно скопировать загружаемый образ ядра из файла <code>arch/i386/boot/bzImage</code> в каталог <code>/boot</code> и отредактировать файл <code>/etc/grub/grub.conf</code> для указания записи, которая соответствует новому ядру. В системах, где для загрузки используется загрузчик LILO, необходимо соответственно отредактировать файл <code>/etc/lilo.conf</code> и запустить утилиту <code>lilo(8)</code>.</p>
     <p>Инсталляция модулей ядра автоматизирована и не зависит от аппаратной платформы. Просто нужно запустить следующую команду с правами пользователя root.</p>
     <p><code>$ make modules_install</code></p>
     <p>В процессе компиляции в корневом каталоге дерева исходного кода ядра также создается файл <code>System.map</code>. В этом файле содержится таблица соответствия символов ядра их начальным адресам в памяти. Эта таблица используется при отладке для перевода адресов памяти в имена функций и переменных.</p>
    </section>
   </section>
   <section>
    <title>
     <p>"Зверек другого рода"</p>
    </title>
    <section>
     <p>Ядро имеет некоторые отличия в сравнении с обычными пользовательскими приложениями, эти отличия хотя и не обязательно приводят к серьезным усложнениям при программировании, но все же создают специфические проблемы при разработке ядра.</p>
     <p>Эти отличия делают ядро <emphasis>зверьком другого рода</emphasis>. Некоторые из старых правил при этом остаются в силе, а некоторые правила являются полностью новыми. Хотя часть различий очевидна (все знают, что ядро может делать все, что пожелает), другие различия не так очевидны. Наиболее важные отличия описаны ниже.</p>
     <p>• Ядро не имеет доступа к библиотеке функций языка С.</p>
     <p>• Ядро программируется с использованием компилятора GNU С.</p>
     <p>• В ядре нет такой защиты памяти, как в режиме пользователя.</p>
     <p>• В ядре нельзя легко использовать вычисления с плавающей точкой.</p>
     <p>• Ядро использует стек небольшого фиксированного размера.</p>
     <p>• Поскольку в ядре используются асинхронные прерывания, ядро является преемптивным и в ядре имеется поддержка SMP, то в ядре необходимо учитывать наличие параллелизма и использовать синхронизацию.</p>
     <p>• Переносимость очень важна.</p>
     <p>Давайте рассмотрим более детально все эти проблемы, так как все разработчики ядра должны постоянно помнить о них.</p>
    </section>
    <section>
     <title>
      <p>Отсутствие библиотеки <code>libc</code></p>
     </title>
     <p>В отличие от обычных пользовательских приложений, ядро не компонуется со стандартной библиотекой функций языка С (и ни с какой другой библиотекой такого же типа). Для этого есть несколько причин, включая некоторые ситуации с дилеммой о курице и яйце, однако первопричина — скорость выполнения и объем кода. Полная библиотека функций языка С, и даже только самая необходимая ее часть, очень большая и неэффективная для ядра.</p>
     <p>При этом не нужно расстраиваться, так как многие из функций библиотеки языка С реализованы в ядре. Например, обычные функции работы со строками описаны в файле <code>lib/string.с</code>. Необходимо лишь подключить заголовочный файл <code>&lt;linux/string.h&gt;</code> и пользоваться этими функциями.</p>
     <cite>
      <subtitle>Заголовочные файлы</subtitle>
      <p>Заметим, что упомянутые заголовочные файлы и заголовочные файлы, которые будут упоминаться далее в этой книге, принадлежат дереву исходного кода ядра. В файлах исходного кода ядра нельзя подключать заголовочные файлы извне этого дерева каталогов, так же как и нельзя использовать внешние библиотеки,</p>
     </cite>
     <p>Отсутствует наиболее известная функция <code>printf()</code>. Ядро не имеет доступа к функции <code>printf()</code>, однако ему доступна функция <code>printk()</code>. Функция <code>printk()</code> копирует форматированную строку в буфер системных сообщений ядра (kernel log buffer), который обычно читается с помощью программы <code>syslog</code>. Использование этой функции аналогично использованию <code>printf()</code>:</p>
     <p><code>printk("Hello world! Строка: %s и целое число: %d\n",</code></p>
     <p><code> a_string, an_integer);</code></p>
     <p>Одно важное отличие между <code>printf()</code> и <code>printk()</code> состоит в том, что в функции <code>printk()</code> можно использовать флаг уровня вывода. Этот флаг используется программой <code>syslog</code> для того, чтобы определить, нужно ли показывать сообщение ядра. Вот пример использования уровня вывода:</p>
     <p><code>printk(KERN_ERR "Это была ошибка !\n");</code></p>
     <p>Функция <code>printk()</code> будет использоваться на протяжении всей книги. В следующих главах приведено больше информации о функции <code>printk()</code>.</p>
    </section>
    <section>
     <title>
      <p>Компилятор GNU С</p>
     </title>
     <p>Как и все "уважающие себя" ядра Unix, ядро Linux написано на языке С. Может быть, это покажется неожиданным, но ядро Linux написано не на чистом языке С в стандарте ANSI С. Наоборот, где это возможно, разработчики ядра используют различные расширения языка, которые доступны с помощью средств компиляции gcc (GNU Compiler Collection — коллекция компиляторов GNU, в которой содержится компилятор С, используемый для компиляции ядра).</p>
     <p>Разработчики ядра используют как расширения языка С ISO C99<a l:href="#n7" type="note">[7]</a> так и расширения GNU С. Эти изменения связывают ядро Linux с компилятором gcc, хотя современные компиляторы, такие как Intel С, имеют достаточную поддержку возможностей компилятора gcc для того, чтобы ими тоже можно было компилировать ядро Linux. В ядре не используются какие-либо особенные расширения стандарта C99, и кроме того, поскольку стандарт C99 является официальной редакцией языка С, эти расширения редко приводят к возникновению ошибок в других частях кода. Более интересные и, возможно, менее знакомые отклонения от стандарта языка ANSI С связаны с расширениями GNU С. Давайте рассмотрим некоторые наиболее интересные расширения, которые могут встретиться в программном коде ядра.</p>
     <subtitle>Функции с подстановкой тела</subtitle>
     <p>Компилятор GNU С поддерживает функции с подстановкой тела (inline functions). Исполняемый код функции с подстановкой тела, как следует из названия, вставляется во все места программы, где указан вызов функции. Это позволяет избежать дополнительных затрат на вызов функции и возврат из функции (сохранение и восстановление регистров) и потенциально позволяет повысить уровень оптимизации, так как компилятор может оптимизировать код вызывающей и вызываемой функций вместе. Обратной стороной такой подстановки (ничто в этой жизни не дается даром) является увеличение объема кода, увеличение используемой памяти и уменьшение эффективности использования процессорного кэша инструкций. Разработчики ядра используют функции с подстановкой тела для небольших функций, критичных ко времени выполнения. Использовать подстановку тела для больших функций, особенно когда они вызываются больше одного раза или не слишком критичны ко времени выполнения, не рекомендуется.</p>
     <p>Функции с подстановкой тела объявляются с помощью ключевых слов <code>static</code> и <code>inline</code> в декларации функции. Например,</p>
     <p><code>static inline void dog(unsigned long tail_size);</code></p>
     <p>Декларация функции должна быть описана перед любым ее вызовом, иначе подстановка тела не будет произведена. Стандартный прием — это размещение функций с подстановкой тела в заголовочных файлах. Поскольку функция объявляется как статическая (<code>static</code>), экземпляр функции без подстановки тела не создается. Если функция с подстановкой тела используется только в одном файле, то она может быть размещена в верхней части этого файла.</p>
     <p>В ядре использованию функций с подстановкой тела следует отдавать преимущество по сравнению с использованием сложных макросов.</p>
     <subtitle>Встроенный ассемблер</subtitle>
     <p>Компилятор gcc С позволяет встраивать инструкции языка ассемблера в обычные функции языка С. Эта возможность, конечно, должна использоваться только в тех частях ядра, которые уникальны для определенной аппаратной платформы.</p>
     <p>Для встраивания ассемблерного кода используется директива компилятора <code>asm()</code>.</p>
     <p>Ядро Linux написано на смеси языков ассемблера и С. Язык ассемблера используется в низкоуровневых подсистемах и на участках кода, где нужна большая скорость выполнения. Большая часть коду ядра написана на языке программирования С.</p>
     <subtitle>Аннотация ветвлений</subtitle>
     <p>Компилятор gnu С имеет встроенные директивы, позволяющие оптимизировать различные ветви условных операторов, которые наиболее или наименее вероятны. Компилятор использует эти директивы для соответственной оптимизации кода. В ядре эти директивы заключаются в макросы <code>likely()</code> и <code>unlikely()</code>, которые легко использовать. Например, если используется оператор <code>if</code> следующего вида:</p>
     <p><code>if (foo) {</code></p>
     <p><code> /* ... */</code></p>
     <p><code>}</code></p>
     <p>то для того, чтобы отметить этот путь выполнения как маловероятный, необходимо указать:</p>
     <p><code>/* предполагается, что значение переменной foo равно нулю ...*/</code></p>
     <p><code>if (unlikely(foo)) {</code></p>
     <p><code> /* ... */</code></p>
     <p><code>}</code></p>
     <p>И наоборот, чтобы отметить этот путь выполнения как наиболее вероятный</p>
     <p><code>/* предполагается, что значение переменной foo не равно нулю ...*/</code></p>
     <p><code>if (likely(foo)) {</code></p>
     <p><code> /* ... * /</code></p>
     <p><code>}</code></p>
     <p>Эти директивы необходимо использовать только в случае, когда направление ветвления с большой вероятностью известно априори или когда необходима оптимизация какой-либо части кода за счет другой части. Важно помнить, что эти директивы дают увеличение производительности, когда направление ветвления предсказано правильно, однако приводят к потере производительности при неправильном предсказании. Наиболее часто директивы <code>unlikely()</code> и <code>likely()</code> используются для проверки ошибок.</p>
    </section>
    <section>
     <title>
      <p>Отсутствие защиты памяти</p>
     </title>
     <p>Когда прикладная программа предпринимает незаконную попытку обращения к памяти, ядро может перехватить эту ошибку и аварийно завершить соответствующий процесс. Если ядро предпринимает попытку некорректного обращения к памяти, то результаты могут быть менее контролируемы. Нарушение правил доступа к памяти в режиме ядра приводит к ошибке <emphasis>oops</emphasis>, которая является наиболее часто встречающейся ошибкой ядра. Не стоит говорить, что нельзя обращаться к запрещенным областям памяти, разыменовывать указатели со значением <code>NULL</code> и так далее, однако в ядре ставки значительно выше!</p>
     <p>Кроме того, память ядра не использует замещение страниц. Поэтому каждый байт памяти, который использован в ядре, — это еще один байт доступной физической памяти. Это необходимо помнить всякий раз, когда добавляются новые <code>функции ядра</code>.</p>
    </section>
    <section>
     <title>
      <p>Нельзя просто использовать вычисления с плавающей точкой</p>
     </title>
     <p>Когда пользовательская программа использует вычисления с плавающей точкой, ядро управляет переходом из режима работы с целыми числами в режим работы с плавающей точкой. Операции, которые ядро должно выполнить для использования инструкций работы с плавающей точкой, зависят от аппаратной платформы.</p>
     <p>В отличие от режима задачи, в режиме ядра нет такой роскоши, как прямое использование вычислений с плавающей точкой. Активизация режима вычислений с плавающей точкой в режиме ядра требует сохранения и восстановления регистров устройства поддержки вычислений с плавающей точкой вручную, кроме прочих рутинных операций. Если коротко, то можно посоветовать: <emphasis>не нужно этого делать</emphasis>; никаких вычислений с плавающей точкой в режиме ядра.</p>
    </section>
    <section>
     <title>
      <p>Маленький стек фиксированного размера</p>
     </title>
     <p>Пользовательские программы могут "отдохнуть" вместе со своими тоннами статически выделяемых переменных в стеке, включая структуры большого размера и многоэлементные массивы. Такое поведение является законным в режиме задачи, так как область стека пользовательских программ может динамически увеличиваться в размере (разработчики, которые писали программы под старые и не очень интеллектуальные операционные системы, как, например, DOS, могут вспомнить то время, когда даже стек пользовательских программ имел фиксированный размер).</p>
     <p>Стек, доступный в режиме ядра, не является ни большим, ни динамически изменяемым, он мал по объему и имеет фиксированный размер. Размер стека зависит от аппаратной платформы. Для платформы x86 размер стека может быть сконфигурирован на этапе компиляции и быть равным 4 или 8 Кбайт. Исторически так сложилось, что размер стека ядра равен двум страницам памяти, что соответствует 8 Кбайт для 32-разрядных аппаратных платформ и 16 Кбайт — для 64-разрядных. Этот размер фиксирован. Каждый процесс получает свою область стека.</p>
     <p>Более подробное обсуждение использования стека в режиме ядра смотрите в следующих главах.</p>
    </section>
    <section>
     <title>
      <p>Синхронизация и параллелизм</p>
     </title>
     <p>Ядро подвержено состояниям конкуренции за ресурсы (race condition). В отличие от однопоточной пользовательской программы, ряд свойств ядра позволяет осуществлять параллельные обращения к ресурсам общего доступа, и поэтому требуется выполнять синхронизацию для предотвращения состояний конкуренции за ресурсы. В частности, возможны следующие ситуации.</p>
     <p>• Ядро Linux поддерживает многопроцессорную обработку. Поэтому, без соответствующей защиты, код ядра может выполняться на одном, двух или большем количестве процессоров и при этом одновременно обращаться к одному ресурсу.</p>
     <p>• Прерывания возникают асинхронно по отношению к исполняемому коду. Поэтому, без соответствующей защиты, прерывания могут возникнуть во время обращения к ресурсу общего доступа, и обработчик прерывания может тоже обратиться к этому же ресурсу.</p>
     <p>• Ядро Linux является преемптивным. Поэтому, без соответствующей защиты, исполняемый код ядра может быть вытеснен в пользу другого кода ядра, который тоже может обращаться к некоторому общему ресурсу.</p>
     <p>Стандартное решение для предотвращения состояния конкуренции за ресурсы (состояния гонок) — это использование спин-блокировок и семафоров.</p>
     <p>Более полное обсуждение вопросов синхронизации и параллелизма приведено в следующих главах.</p>
    </section>
    <section>
     <title>
      <p>Переносимость — это важно</p>
     </title>
     <p>При разработке пользовательских программ переносимость не всегда является целью, однако операционная система Linux является переносимой и должна оставаться такой. Это означает, что платформо-независимый код, написанный на языке С, должен компилироваться без ошибок и правильно выполняться на большом количестве систем.</p>
     <p>Несколько правил, такие как не создавать зависимости от порядка следования байтов, обеспечивать возможность использования кода для 64-битовых систем, не привязываться к размеру страницы памяти или машинного слова и другие — имеют большое значение. Эти вопросы более подробно освещаются в одной из следующих глав.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Резюме</p>
    </title>
    <p>Да, ядро— это действительно нечто иное: отсутствует защита памяти, нет проверенной библиотеки функций языка С, маленький стек, большое дерево исходного кода. Ядро Linux играет по своим правилам и занимается серьезными вещами. Тем не менее, ядро — это всего лишь программа; оно, по сути, не сильно отличается от других обычных программ. Не нужно его бояться.</p>
    <p>Понимание того, что ядро не так уж страшно, как кажется, может стать первым шагом к пониманию того, что все <emphasis>имеет свой смысл</emphasis>. Однако чтобы достичь этой утопии, необходимо стараться, читать исходный код, изменять его и не падать духом.</p>
    <p>Вводный материал, который был представлен в первой главе, и базовые моменты, которые описаны в текущей, надеюсь, станут хорошим фундаментом для тех знаний, которые будут получены при прочтении всей книги. В следующих разделах будут рассмотрены конкретные подсистемы ядра и принципы их работы.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 3</p>
    <p>Управление процессами</p>
   </title>
   <section>
    <p>Процесс — одно из самых важных абстрактных понятий в Unix-подобных операционных системах<a l:href="#n8" type="note">[8]</a>. По сути, процесс — это программа, т.е. объектный код, хранящийся на каком-либо носителе информации и находящийся в состоянии исполнения. Однако процесс — это не только исполняемый программный код, который для операционной системы Unix часто называется <emphasis>text section</emphasis> (<emphasis>сегмент текста</emphasis> или <emphasis>сегмент кода</emphasis>). Процессы также включают в себя <emphasis>сегмент данных</emphasis> (<emphasis>data section</emphasis>), содержащий глобальные переменные; набор ресурсов, таких как открытые файлы и ожидающие на обработку сигналы; адресное пространство и один или более <emphasis>потоков выполнения</emphasis>. Процесс — это живой результат выполнения программного кода.</p>
    <p>Потоки выполнения, которые часто для сокращения называют просто потоками (<emphasis>thread</emphasis>), представляют собой объекты, выполняющие определенные операции внутри процесса. Каждый поток включает в себя уникальный счетчик команд (program counter), стек выполнения и набор регистров процессора. Ядро планирует выполнение отдельных потоков, а не процессов. В традиционных Unix-подобных операционных системах каждый процесс содержал только один поток. Однако в современных системах многопоточные программы используются очень широко. Как будет показано далее, в операционной системе Linux используется уникальная реализация потоков — между процессами и потоками нет никакой разницы. Поток в операционной системе Linux — это специальный тип процесса.</p>
    <p>В современных операционных системах процессы предусматривают наличие двух виртуальных ресурсов: виртуального процессора и виртуальной памяти. Виртуальный процессор создает для процесса иллюзию, что этот процесс монопольно использует всю компьютерную систему, за исключением, может быть, только того, что физическим процессором совместно пользуются десятки других процессов. В главе 4, "Планирование выполнения процессов", эта виртуализация обсуждается более подробно. Виртуальная память предоставляет процессу иллюзию того, что он один располагает всей памятью компьютерной системы. Виртуальной памяти посвящена глава 11, "Управление памятью". Потоки <emphasis>совместно</emphasis> используют одну и ту же виртуальную память, хотя каждый поток получает свой виртуальный процессор.</p>
    <p>Следует подчеркнуть, что сама по себе программа процессом не является; процесс — это <emphasis>выполняющаяся</emphasis> программа плюс набор соответствующих ресурсов. Конечно, может существовать два процесса, которые исполняют <emphasis>одну и ту же</emphasis> программу. В действительности может даже существовать два или больше процессов, которые совместно используют одни и те же ресурсы, такие как открытые файлы, или адресное пространство. Процесс начинает свое существование с момента создания, что впрочем не удивительно. В операционной системе Linux такое создание выполняется с помощью системного вызова <code>fork()</code> (буквально, ветвление или вилка), который создает новый процесс путем полного копирования уже существующего. Процесс, который вызвал системную функцию <code>fork()</code>, называется <emphasis>порождающим</emphasis> (<emphasis>родительским</emphasis>, <emphasis>parent</emphasis>), новый процесс именуют <emphasis>порожденным</emphasis> (<emphasis>дочерний</emphasis>, <emphasis>child</emphasis>). Родительский процесс после этого продолжает выполнение, а порожденный процесс начинает выполняться с места возврата из системного вызова. Часто после разветвления в одном из процессов желательно выполнить какую-нибудь другую программу. Семейство функций <code>exec*()</code> позволяет создать новое адресное пространство и загрузить в него новую программу. В современных ядрах Linux функция <code>fork()</code> реализована через системный вызов <code>clone()</code>, который будет рассмотрен в следующем разделе.</p>
    <p>Выход из программы осуществляется с помощью системного вызова <code>exit()</code>. Эта функция завершает процесс и освобождает все занятые им ресурсы. Родительский процесс может запросить о состоянии порожденных им процессов с помощью системного вызова <code>wait4()</code><a l:href="#n9" type="note">[9]</a>, который заставляет один процесс ожидать завершения другого. Когда процесс завершается, он переходит в специальное состояние <emphasis>зомби</emphasis> (<emphasis>zombie</emphasis>), которое используется для представления завершенного процесса до того момента, пока порождающий его процесс не вызовет системную функцию <code>wait()</code> или <code>waitpid()</code>.</p>
    <p>Иное название для процесса — <emphasis>задание или задача</emphasis> (task). О процессах в ядре операционной системы Linux говорят как о задачах. В этой книге оба понятия взаимозаменяемы, хотя по возможности для представления работающей программы в ядре будет использоваться термин <emphasis>задача</emphasis>, а для представления в режиме пользователя — термин <emphasis>процесс</emphasis>.</p>
   </section>
   <section>
    <title>
     <p>Дескриптор процесса и структура task structure</p>
    </title>
    <section>
     <p>Ядро хранит информацию о всех процессах в двухсвязном списке, который называется <emphasis>task list</emphasis><a l:href="#n10" type="note">[10]</a> (<emphasis>список задач</emphasis>). Каждый элемент этого списка является <emphasis>дескриптором процесса</emphasis> и имеет тип структуры <code>struct task_struct</code>, которая описана в файле <code>include/linux/sched.h</code>. Дескриптор процесса содержит всю информацию об определенном процессе.</p>
     <p>Структура <code>task_struct</code> — достаточно большая структура данных размером порядка 1,7 Кбайт на 32-разрядной машине. Однако этот размер не такой уж большой, учитывая, что в данной структуре содержится вся информация о процессе, которая необходима ядру. Дескриптор процесса содержит данные, которые описывают выполняющуюся программу, — открытые файлы, адресное пространство процесса, ожидающие на обработку сигналы, состояние процесса и многое другое (рис. 3.1).</p>
     <image l:href="#img_3.jpeg"/>
     <p><strong>Рис. 3.1</strong>. Дескриптор процесса и список задач</p>
    </section>
    <section>
     <title>
      <p>Выделение дескриптора процесса</p>
     </title>
     <p>Память для структуры <code>task_struct</code> выделяется с помощью подсистемы выделения памяти, которая называется <emphasis>слябовый распределитель</emphasis> (<emphasis>slab allocator</emphasis>), для возможности повторного использования объектов и раскрашивания кэша (cache coloring) (см. главу 11, "Управление памятью"). В ядрах до серии 2.6 структура <code>task_struct</code> хранилась в конце стека ядра каждого процесса. Это позволяет для аппаратных платформ, у которых достаточно мало регистров процессора (как, например, платформа x86), вычислять местоположение дескриптора процесса, только зная значение регистра <emphasis>указателя стека</emphasis> (<emphasis>stack pointer</emphasis>), без использования дополнительных регистров для хранения самого адреса этого местоположения. Так как теперь дескриптор процесса создается с помощью слябового распределителя, была введена новая структура <code>thread_info</code>, которая хранится в области дна стека (для платформ, у которых стек растет в сторону уменьшения значения адреса памяти) или в области вершины стека (для платформ, у которых стек растет в сторону увеличения значения адреса памяти)<a l:href="#n11" type="note">[11]</a> (рис. 3.2.).</p>
     <empty-line/>
     <image l:href="#img_4.jpeg"/>
     <p><strong>Рис 3.2</strong>. Дескриптор процесса и стек ядра</p>
     <p>Структура <code>struct thread_info</code> для платформы x86 определена в файле <code>&lt;asm/thread_info.h&gt;</code> в следующем виде.</p>
     <p><code>struct thread_info {</code></p>
     <p><code> struct task_struct   *task;</code></p>
     <p><code> struct exec_domain   *exec_domain;</code></p>
     <p><code> unsigned long        flags;</code></p>
     <p><code> unsigned long        status;</code></p>
     <p><code> __u32                cpu;</code></p>
     <p><code> __s32                preempt_count;</code></p>
     <p><code> mm_segment_t         addr_limit;</code></p>
     <p><code> struct restart_block restart_block;</code></p>
     <p><code> unsigned             long previous_esp;</code></p>
     <p><code> __u8                 supervisor_stack[0];</code></p>
     <p><code>};</code></p>
     <p>Для каждой задачи ее структура <code>thread_info</code> хранится в конце стека ядра этой задачи. Элемент структуры <code>thread_info</code> с именем <code>task</code> является указателем на структуру <code>task_struct</code> этой задачи.</p>
    </section>
    <section>
     <title>
      <p>Хранение дескриптора процесса</p>
     </title>
     <p>Система идентифицирует процессы с помощью уникального значения, которое называется <emphasis>идентификатором процесса</emphasis> (<emphasis>process identification</emphasis>, <emphasis>PID</emphasis>). Идентификатор <code>PID</code> — это целое число, представленное с помощью скрытого типа <code>pid_t</code><a l:href="#n12" type="note">[12]</a> , который обычно соответствует знаковому целому— <code>int</code>.</p>
     <p>Однако, для обратной совместимости со старыми версиями ОС Unix и Linux максимальное значение этого параметра по умолчанию составляет всего лишь 32768 (что соответствует типу данных <code>short int</code>). Ядро хранит значение данного параметра в поле <code>pid</code> дескриптора процесса.</p>
     <p>Это максимальное значение является важным, потому что оно определяет максимальное количество процессов, которые одновременно могут существовать в системе. Хотя значения 32768 и достаточно для офисного компьютера, для больших серверов может потребоваться значительно больше процессов. Чем меньше это значение, тем скорее нумерация процессов будет начинаться сначала, что приводит к нарушению полезного свойства: больший номер процесса соответствует процессу, который запустился позже. Если есть желание нарушить в системе обратную совместимость со старыми приложениями, то администратор может увеличить это максимальное значение во время работы системы с помощью записи его в файл <code>/proc/sys/kernel/pid_max</code>.</p>
     <p>Обычно в ядре на задачи ссылаются непосредственно с помощью указателя на их структуры <code>task_struct</code>. И действительно, большая часть кода ядра, работающего с процессами, работает прямо со структурами <code>task_struct</code>. Следовательно, очень полезной возможностью было бы быстро находить дескриптор процесса, который выполняется в данный момент, что и делается с помощью макроса current. Этот макрос должен быть отдельно реализован для всех поддерживаемых аппаратных платформ. Для одних платформ указатель на структуру <code>task_struct</code> процесса, выполняющегося в данный момент, хранится в регистре процессора, что обеспечивает более эффективный доступ. Для других платформ, у которых доступно меньше регистров процессора, чтобы зря не тратить регистры, используется тот факт, что структура <code>thread_info</code> хранится в стеке ядра. При этом вычисляется положение структуры <code>thread_info</code>, а вслед за этим и адрес структуры <code>task_struct</code> процесса.</p>
     <p>Для платформы x86 значение параметра <code>current</code> вычисляется путем маскирования 13 младших бит указателя стека для получения адреса структуры <code>thread_info</code>. Это может быть сделано с помощью функции <code>current_thread_info()</code>. Соответствующий код на языке ассемблера показан ниже.</p>
     <p><code>movl $-8192, %eax</code></p>
     <p><code>andl %esp, %eax</code></p>
     <p>Окончательно значение параметра <code>current</code> получается путем разыменования значения поля <code>task</code> полученной структуры <code>thread_info</code>:</p>
     <p><code>current_thread_info()-&gt;task;</code></p>
     <p>Для контраста можно сравнить такой подход с используемым на платформе PowerPC (современный процессор на основе RISC-архитектуры фирмы IBM), для которого значение переменной <code>current</code> хранится в регистре процессора <code>r2</code>. На платформе PPC такой подход можно использовать, так как, в отличие от платформы x86, здесь регистры процессора доступны в изобилии. Так как доступ к дескриптору процесса — это очень частая и важная операция, разработчики ядра для платформы PPC сочли правильным пожертвовать одним регистром для этой цели.</p>
    </section>
    <section>
     <title>
      <p>Состояние процесса</p>
     </title>
     <p>Поле <code>state</code> дескриптора процесса описывает текущее состояние процесса (рис. 3.3). Каждый процесс в системе гарантированно находится в одном из пяти различных состояний.</p>
     <image l:href="#img_5.jpeg"/>
     <p><strong>Рис. 3.3</strong>. Диаграмма состояний процесса</p>
     <p>Эти состояния представляются значением одного из пяти возможных флагов, описанных ниже.</p>
     <p>• <code><strong>TASK_RUNNING</strong></code> — процесс готов к выполнению (runnable). Иными словами, либо процесс выполняется в данный момент, либо находится в одной из очередей процессов, ожидающих на выполнение (эти очереди, <code>runqueue</code>, обсуждаются в главе 4. "Планирование выполнения процессов").</p>
     <p>• <code><strong>TASK_INTERRUPTIBLE</strong></code> — процесс приостановлен (находится в состоянии ожидания, <emphasis>sleeping</emphasis>), т.е. заблокирован в ожидании выполнения некоторого условия. Когда это условие выполнится, ядро переведет процесс в состояние <code>TASK_RUNNING</code>. Процесс также возобновляет выполнение (wake up) преждевременно при получении им сигнала.</p>
     <p>• <code><strong>TASK_UNINTERRUPTIBLE</strong></code> — аналогично <code>TASK_INTERRUPTIBLE</code>, за исключением того, что процесс не возобновляет выполнение при получении сигнала. Используется в случае, когда процесс должен ожидать беспрерывно или когда ожидается, что некоторое событие может возникать достаточно часто. Так как задача в этом состоянии не отвечает на сигналы, <code>TASK_UNINTERRUPTIBLE</code> используется менее часто, чем <code>TASK_INTERRUPTIBLE</code><a l:href="#n13" type="note">[13]</a>.</p>
     <p>• <code><strong>TASK_ZOMBIE</strong></code> — процесс завершен, однако порождающий его процесс еще не вызвал системный вызов <code>wait4()</code>. Дескриптор такого процесса должен оставаться доступным на случай, если родительскому процессу потребуется доступ к этому дескриптору. Когда родительский процесс вызывает функцию <code>wait4()</code>, то такой дескриптор освобождается.</p>
     <p>• <code><strong>TASK_STOPPED</strong></code> — выполнение процесса остановлено. Задача не выполняется и не имеет право выполняться. Такое может случиться, если задача получает какой-либо из сигналов <code>SIGSTOP</code>, <code>SIGTSTP</code>, <code>SIGTTIN</code> или <code>SIGTTOU</code>, а также если сигнал приходит в тот момент, когда процесс находится в состоянии отладки.</p>
    </section>
    <section>
     <title>
      <p>Манипулирование текущим состоянием процесса</p>
     </title>
     <p>Исполняемому коду ядра часто необходимо изменять состояние процесса. Наиболее предпочтительно для этого использовать функцию</p>
     <p><code>set_task state(task, state);</code></p>
     <p><code>/* установить задание 'task' в состояние 'state' */</code></p>
     <p>которая устанавливает указанное состояние для указанной задачи. Если применимо, то эта функция также пытается применить <emphasis>барьер памяти</emphasis> (<emphasis>memory barrier</emphasis>), чтобы гарантировать доступность установленного состояния для всех процессоров (необходимо только для SMP-систем). В других случаях это эквивалентно выражению:</p>
     <p><code>task-&gt;state = state;</code></p>
     <p>Вызов <code>set_current_state(state)</code> является синонимом к вызову <code>set_task_state(current, state)</code>.</p>
    </section>
    <section>
     <title>
      <p>Контекст процесса</p>
     </title>
     <p>Одна из наиболее важных частей процесса— это исполняемый программный код. Этот код считывается из <emphasis>выполняемого файла</emphasis> (<emphasis>executable</emphasis>) и выполняется в адресном пространстве процесса. Обычно выполнение программы осуществляется в <emphasis>пространстве пользователя</emphasis>. Когда программа выполняет системный вызов (см. главу 5, "Системные вызовы") или возникает исключительная ситуация, то программа входит в <emphasis>пространство ядра</emphasis>.</p>
     <p>С этого момента говорят, что ядро "выполняется от имени процесса" и делает это в <emphasis>контексте процесса</emphasis>. В контексте процесса макрос <code>current</code> является действительным<a l:href="#n14" type="note">[14]</a>. При выходе из режима ядра процесс продолжает выполнение в пространстве пользователя, если в это время не появляется готовый к выполнению более приоритетный процесс. В таком случае активизируется планировщик, который выбирает для выполнения более приоритетный процесс.</p>
     <p>Системные вызовы и обработчики исключительных ситуаций являются строго определенными интерфейсами ядра. Процесс может начать выполнение в пространстве ядра только посредством одного из этих интерфейсов — любые обращения к ядру возможны только через эти интерфейсы.</p>
    </section>
    <section>
     <title>
      <p>Дерево семейства процессов</p>
     </title>
     <p>В операционной системе Linux существует четкая иерархия процессов. Все процессы являются потомками процесса <code>init</code>, значение идентификатора <code>PID</code> для которого равно 1. Ядро запускает процесс <code>init</code> на последнем шаге процедуры загрузки системы. Процесс <code>init</code>, в свою очередь, читает системные файлы <emphasis>сценариев начальной загрузки</emphasis> (<emphasis>initscripts</emphasis>) и выполняет другие программы, что в конце концов завершает процедуру загрузки системы.</p>
     <p>Каждый процесс в системе имеет всего один порождающий процесс. Кроме того, каждый процесс может иметь один или более порожденных процессов. Процессы, которые порождены одним и тем же родительским процессом, называются <emphasis>родственными</emphasis> (<emphasis>siblings</emphasis>). Информация о взаимосвязи между процессами хранится в дескрипторе процесса. Каждая структура <code>task_struct</code> содержит указатель на структуру <code>task_struct</code> родительского процесса, который называется parent, эта структура также имеет список порожденных процессов, который называется <code>children</code>. Следовательно, если известен текущий процесс (<code>current</code>), то для него можно определить дескриптор родительского процесса с помощью выражения:</p>
     <p><code>struct task_struct *task = current-&gt;parent;</code></p>
     <p>Аналогично можно выполнить цикл по процессам, порожденным от текущего процесса, с помощью кода:</p>
     <p><code>struct task_struct *task;</code></p>
     <p><code>struct list_head *list;</code></p>
     <empty-line/>
     <p><code>list_for_each(list, &amp;current-&gt;children) {</code></p>
     <p><code> task = list_entry(list, struct task_struct, sibling);</code></p>
     <p><code> /* переменная task теперь указывает на один из процессов,</code></p>
     <p><code>    порожденных текущим процессом */</code></p>
     <p><code>}</code></p>
     <p>Дескриптор процесса <code>init</code> — это статически выделенная структура данных с именем <code>init_task</code>. Хороший пример использования связей между всеми процессами — это приведенный ниже код, который всегда выполняется успешно.</p>
     <p><code>struct task_struct *task;</code></p>
     <empty-line/>
     <p><code>for (task = current; task != $init_task; task = task-&gt;parent)</code></p>
     <p><code> ;</code></p>
     <p><code>/* переменная task теперь указывает на процесс init */</code></p>
     <p>Конечно, проходя по иерархии процессов, можно перейти от одного процесса системы к другому. Иногда, однако, желательно выполнить цикл по <emphasis>всем</emphasis> процессам системы. Такая задача решается очень просто, так как список задач — это двухсвязный список. Для того чтобы получить указатель на следующее задание из этого списка, имея действительный указатель на дескриптор какого-либо процесса, можно использовать показанный ниже код:</p>
     <p><code>list_entry(task-&gt;tasks.next, struct task_struct, tasks);</code></p>
     <p>Получение указателя на предыдущее задание работает аналогично.</p>
     <p><code>list_entry(task-&gt;tasks.prev, struct task_struct, tasks);</code></p>
     <p>Дна указанных выше выражения доступны также в виде макросов <code>next_task(task)</code> (получить следующую задачу), <code>prev_task(task)</code> (получить предыдущую задачу). Наконец, макрос <code>for_each_process(task)</code> позволяет выполнить цикл по всему списку задач. На каждом шаге цикла переменная <code>task</code> указывает на следующую задачу из списка:</p>
     <p><code>struct task_struct *task;</code></p>
     <empty-line/>
     <p><code>for_each_process(task) {</code></p>
     <p><code> /* просто печатается имя команды и идентификатор PID</code></p>
     <p><code>    для каждой задачи */</code></p>
     <p><code> printk("%s[%d]\n", task-&gt;comm, task-&gt;pid);</code></p>
     <p><code>}</code></p>
     <p>Следует заметить, что организация цикла по всем задачам системы, в которой выполняется много процессов, может быть достаточно дорогостоящей операцией. Для применения такого кода должны быть веские причины (и отсутствовать другие альтернативы).</p>
    </section>
   </section>
   <section>
    <title>
     <p>Создание нового процесса</p>
    </title>
    <section>
     <p>В операционной системе Unix создание процессов происходит уникальным образом. В большинстве операционных систем для создания процессов используется метод <emphasis>порождения</emphasis> процессов (<emphasis>spawn</emphasis>). При этом создается новый процесс в новом адресном пространстве, в которое считывается исполняемый файл, и после этого начинается исполнение процесса. В ОС Unix используется другой подход, а именно разбиение указанных выше операций на две функции: <code>fork()</code> и <code>exec()</code><a l:href="#n15" type="note">[15]</a>.</p>
     <p>В начале с помощью функции <code>fork()</code> создается порожденный процесс, который является копией текущего задания. Порожденный процесс отличается от родительского только значением идентификатора <code>PID</code> (который является уникальным в системе), значением параметра <code>PPID</code> (идентификатор <code>PID</code> родительского процесса, который устанавливается в значение <code>PID</code> порождающего процесса), некоторыми ресурсами, такими как ожидающие на обработку сигналы (которые не наследуются), а также статистикой использования ресурсов. Вторая функция — <code>exec()</code> — загружает исполняемый файл в адресное пространство процесса и начинает исполнять его. Комбинация функций <code>fork()</code> и <code>exec()</code> аналогична той одной функции создания процесса, которую предоставляет большинство операционных систем.</p>
    </section>
    <section>
     <title>
      <p>Копирование при записи</p>
     </title>
     <p>Традиционно при выполнении функции <code>fork()</code> делался дубликат всех ресурсов родительского процесса и передавался порожденному. Такой подход достаточно наивный и неэффективный. В операционной системе Linux вызов <code>fork()</code> реализован с использованием механизма <emphasis>копирования при записи</emphasis> (<emphasis>copy-on-write</emphasis>) страниц памяти. Технология копирования при записи (copy-on-write, COW) позволяет отложить или вообще предотвратить копирование данных. Вместо создания дубликата адресного пространства процесса родительский и порожденный процессы могут совместно использовать одну и ту же копию адресного пространства. Однако при этом данные помечаются особым образом, и если вдруг один из процессов начинает изменять данные, то создается дубликат данных, и каждый процесс получает уникальную копию данных. Следовательно, дубликаты ресурсов создаются только тогда, когда в эти ресурсы осуществляется запись, а до того момента они используются совместно в режиме только для чтения (read-only). Такая техника позволяет задержать копирование каждой страницы памяти до того момента, пока в эту страницу памяти не будет осуществляться запись. В случае, если в страницы памяти никогда не делается запись, как, например, при вызове функции <code>exec()</code> сразу после вызова <code>fork()</code>, то эти страницы никогда и не копируются. Единственные накладные расходы, которые вносит вызов функции <code>fork()</code>, — это копирование таблиц страниц родительского процесса и создание дескриптора порожденного процесса. Данная оптимизация предотвращает ненужное копирование большого количества данных (размер адресного пространства часто может быть более 10 Мбайт), так как процесс после разветвления в большинстве случаев сразу же начинает выполнять новый исполняемый образ. Эта оптимизация очень важна, потому чти идеология операционной системы Unix предусматривает быстрое выполнение процессов.</p>
     <subtitle>Функция <code>fork()</code></subtitle>
     <p>В операционной системе Linux функция <code>fork()</code> реализована через системный вызов <code>clone()</code>. Этот системный вызов может принимать в качестве аргументов набор флагов, определяющих, какие ресурсы должны быть общими (если вообще должны) у родительского и порожденного процессов. Далее в разделе "Реализация потоков в ядре Linux" об этих флагах рассказано более подробно. Библиотечные вызовы <code>fork()</code>, <code>vfork()</code> и <code>__clone()</code> вызывают системную функцию <code>clone()</code> с соответствующими флагами. В свою очередь системный вызов <code>clone()</code> вызывает функцию ядра <code>do_fork()</code>.</p>
     <p>Основную массу работы по разветвлению процесса выполняет функция <code>do_fork()</code>, которая определена в файле <code>kernel/fork.c</code>. Эта функция, в свою очередь, вызывает функцию <code>copy_process()</code> и запускает новый процесс на выполнение. Ниже описана та интересная работа, которую выполняет функция <code>copy_process()</code>.</p>
     <p>• Вызывается функция <code>dup_task_struct()</code>, которая создает стек ядра, структуры <code>thread_info</code> и <code>task_struct</code> для нового процесса, причем все значения указанных структур данных идентичны для порождающего и порожденного процессов. На этом этапе дескрипторы родительского и порожденного процессов идентичны.</p>
     <p>• Проверяется, не произойдет ли при создании нового процесса переполнение лимита на количество процессов для данного пользователя.</p>
     <p>• Теперь необходимо сделать порожденный процесс отличным от родительского. При этом различные поля дескриптора порожденного процесса очищаются или устанавливаются в начальные значения. Большое количество данных дескриптора процесса является совместно используемым.</p>
     <p>• Далее состояние порожденного процесса устанавливается в значение <code>TASK_UNINTERRUPTIBLE</code>, чтобы гарантировать, что порожденный процесс не будет выполняться.</p>
     <p>• Из функции <code>copy_process()</code> вызывается функция <code>copy_flags()</code>, которая обновляет значение поля <code>flags</code> структуры <code>task struct</code>. При этом сбрасывается флаг <code>PF_SUPERPRIV</code>, который определяет, имеет ли процесс права суперпользователя. Флаг <code>PF_FORKNOEXEC</code>, который указывает на то, что процесс не вызвал функцию <code>exec()</code>, — устанавливается.</p>
     <p>• Вызывается функция <code>get_pid()</code>, которая назначает новое значение идентификатора <code>PID</code> для новой задачи.</p>
     <p>• В зависимости от значений флагов, переданных в функцию <code>clone()</code>, осуществляется копирование или совместное использование открытых файлов, информации о файловой системе, обработчиков сигналов, адресного пространства процесса и пространства имен (<emphasis>namespace</emphasis>). Обычно эти ресурсы совместно используются потоками одного процесса. В противном случае они будут уникальными и будут копироваться на этом этапе.</p>
     <p>• Происходит разделение оставшейся части кванта времени между родительским и порожденным процессами (это более подробно обсуждается в главе 4, "Планирование выполнения процессов").</p>
     <p>• Наконец, происходит окончательная зачистка структур данных и возвращается указатель на новый порожденный процесс.</p>
     <p>Далее происходит возврат в функцию <code>do_fork()</code>. Если возврат из функции <code>copy_process()</code> происходит успешно, то новый порожденный процесс возобновляет выполнение. Порожденный процесс намеренно запускается на выполнение раньше родительского<a l:href="#n16" type="note">[16]</a>.</p>
     <p>В обычной ситуации, когда порожденный процесс сразу же вызывает функцию <code>exec()</code>, это позволяет избежать накладных расходов, связанных с тем, что если родительский процесс начинает выполняться первым, то он будет ожидать возможности записи в адресное пространство посредством механизма копирования при записи.</p>
     <subtitle>Функция <code>vfork()</code></subtitle>
     <p>Системный вызов <code>vfork()</code> позволяет получить тот же эффект, что и системный вызов <code>fork()</code>, за исключением того, что записи таблиц страниц родительского процесса не копируются. Вместо этого порожденный процесс запускается как отдельный поток в адресном пространстве родительского процесса и родительский процесс блокируется до того момента, пока порожденный процесс не вызовет функцию <code>exec()</code> или не завершится. Порожденному процессу запрещена запись в адресное пространство. Такая оптимизация была желанной в старые времена 3BSD, когда реализация системного вызова <code>fork()</code> не базировалась на технике копирования страниц памяти при записи. Сегодня, при использовании техники копирования страниц памяти при записи и запуске порожденного процесса перед родительским, единственное преимущество вызова <code>vfork()</code> — это отсутствие копирования таблиц страниц родительского процесса. Если когда-нибудь в операционной системе Linux будет реализовано копирование полей таблиц страниц при записи<a l:href="#n17" type="note">[17]</a>, то вообще не останется никаких преимуществ. Поскольку семантика функции <code>vfork()</code> достаточно ненадежна (что, например, будет, если вызов <code>exec()</code> завершится неудачно?), то было бы здорово, если бы системный вызов <code>vfork()</code> умер медленной и мучительной смертью. Вполне можно реализовать системный вызов <code>vfork()</code> через обычный вызов <code>fork()</code>, что действительно имело место в ядрах Linux до версии 2.2.</p>
     <p>Сейчас системный вызов <code>vfork()</code> реализован через специальный флаг в системном вызове <code>clone()</code>, как показано ниже.</p>
     <p>• При выполнении функции <code>copy_process()</code> поле <code>vfork_done</code> структуры <code>task_struct</code> устанавливается в значение <code>NULL</code>.</p>
     <p>• При выполнении функции <code>do_fvork()</code>, если соответствующий флаг установлен, поле <code>vfork_done</code> устанавливается в ненулевое значение (начинает указывать на определенный адрес).</p>
     <p>• После того как порожденный процесс в первый раз запущен, родительский процесс, вместо того чтобы возвратиться из функции <code>copy_process()</code> к выполнению, начинает ожидать, пока порожденный процесс не подаст ему сигнал через указатель <code>vfork_done</code>.</p>
     <p>• При выполнении порожденным процессом функции <code>mm_release()</code> (которая вызывается, когда задание заканчивает работу со своим адресным пространством), если значение поля <code>vfork_done</code> не равно <code>NULL</code>, родительский процесс получает указанный выше сигнал.</p>
     <p>• При возврате в функцию <code>do_fork()</code> родительский процесс возобновляет выполнение и выходит из этой функции.</p>
     <p>Если все прошло так, как запланировано, то теперь порожденный процесс выполняется в новом адресном пространстве, а родительский процесс — в первоначальном адресном пространстве. Накладные расходы меньше, но реализация не очень привлекательна.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Реализация потоков в ядре Linux</p>
    </title>
    <section>
     <p>Многопоточность — это популярная сегодня программная абстракция. Она обеспечивает выполнение нескольких потоков в совместно используемом адресном пространстве памяти. Потоки также могут совместно использовать открытые файлы и другие ресурсы. Многопоточность используется для <emphasis>параллельного программирования</emphasis> (<emphasis>concurrent programming</emphasis>), что на многопроцессорных системах обеспечивает истинный <emphasis>параллелизм</emphasis>.</p>
     <p>Реализация потоков в операционной системе Linux уникальна. Для ядра Linux не существует отдельной концепции потоков. В ядре Linux потоки реализованы так же, как и обычные процессы. В ОС Linux нет никакой особенной семантики для планирования выполнения потоков или каких-либо особенных структур данных для представления потоков. Поток— это просто процесс, который использует некоторые ресурсы совместно с другими процессами. Каждый поток имеет структуру <code>task_struct</code> и представляется для ядра обычным процессом (который совместно использует ресурсы, такие как адресное пространство, с другими процессами).</p>
     <p>В этом смысле Linux отличается от других операционных систем, таких как Microsoft Windows или Sun Solaris, которые имеют <emphasis>явные</emphasis> средства поддержки потоков в ядре (в этих системах иногда потоки называются <emphasis>процессами с быстрым переключением контекста</emphasis>, <emphasis>lightweight process</emphasis>). Название "процесс с быстрым переключением контекста" показывает разницу между философией Linux и других операционных систем. Для остальных операционных систем потоки— это абстракция, которая обеспечивает облегченные, более быстрые для исполнения сущности, чем обычные тяжелые процессы. Для операционной системы Linux потоки — это просто способ совместного использования ресурсов несколькими процессами (которые и так имеют достаточно малое время переключения контекста)<a l:href="#n18" type="note">[18]</a>.</p>
     <p>Допустим, у нас есть процесс, состоящий из четырех потоков. В операционных системах с явной поддержкой потоков должен существовать дескриптор процесса, который далее указывает на четыре потока. Дескриптор процесса описывает совместно используемые ресурсы, такие как адресное пространство и открытые файлы. Потоки описываются ресурсами, которые принадлежат только им. В ОС Linux, наоборот, существует просто четыре процесса и, соответственно, четыре обычные структуры <code>task_struct</code>. Четыре процесса построены так, чтобы совместно использовать определенные ресурсы.</p>
     <p>Потоки создаются так же, как и обычные задания, за исключением того, что в системный вызов <code>clone()</code> передаются флаги с указанием, какие ресурсы должны использоваться совместно:</p>
     <p><code>clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);</code></p>
     <p>Результат выполнения показанного кода будет таким же, как и при выполнении обычного вызова <code>fork()</code>, за исключением того, что адресное пространство, ресурсы файловой системы, дескрипторы файлов и обработчики сигналов останутся общими. Другими словами, новая задача, так же как и родительский процесс, — обычные потоки. В отличие от этого, обычный вызов <code>fork()</code> может быть реализован следующим образом:</p>
     <p><code>clone(SIGCHLD, 0);</code></p>
     <p>а вызов <code>vfork()</code> в таком виде:</p>
     <p><code>clone(CLONE_VFORK | CLONE_VM | SIGCHLD, 0);</code></p>
     <p>Флаги, которые передаются в системный вызов <code>clone()</code> , помогают указать особенности поведения нового процесса и детализировать, какие ресурсы должны быть общими для родительского и порожденного процессов. В табл. 3.1 приведены флаги системного вызова <code>clone()</code> и их эффект.</p>
     <empty-line/>
     <p><strong>Таблица 3.1</strong>. Флаги системного вызова clone()</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Флаг</th>
       <th align="left" valign="top">Описание</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_FILES</code></td>
       <td align="left" valign="top">Родительский и порожденный процессы совместно используют открытые файлы</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_FS</code></td>
       <td align="left" valign="top">Родительский и порожденный процессы совместно используют информацию о файловой системе</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_IDLETASK</code></td>
       <td align="left" valign="top">Установить значение PID в нуль (используется только для холостых (idle) задач)</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_NEWNS</code></td>
       <td align="left" valign="top">Создать новое пространство имен для порожденной задачи</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_PARENT</code></td>
       <td align="left" valign="top">Родительский процесс вызывающего процесса становится родительским и для порожденного</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_PTRACE</code></td>
       <td align="left" valign="top">Продолжить трассировку и для порожденного процесса</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_SETTID</code></td>
       <td align="left" valign="top">Возвратить значение идентификатора TID в пространство пользователя</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_SETTLS</code></td>
       <td align="left" valign="top">Для порожденного процесса создать новую область локальных данных потока (thread local storage, TLS)</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_SIGHAND</code></td>
       <td align="left" valign="top">У порожденного и родительского процессов будут общие обработчики сигналов</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_SYSVSEM</code></td>
       <td align="left" valign="top">У родительского и порожденного процессов будет общая семантика обработки флага <code>SEM_UNDO</code> для семафоров System V</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_THREAD</code></td>
       <td align="left" valign="top">Родительский и порожденный процессы будут принадлежать одной группе потоков</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_VFORK</code></td>
       <td align="left" valign="top">Использовать <code>vfork()</code>: родительский процесс будет находиться а приостановленном состоянии, пока порожденный процесс не возобновит его работу</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_UNTRACED</code></td>
       <td align="left" valign="top">Запретить родительскому процессу использование флага <code>CLONE_PTRACE</code> для порожденного процесса</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_STOP</code></td>
       <td align="left" valign="top">Запустить процесс в состоянии <code>TASK_STOPPED</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_CHILD_CLEARTID</code></td>
       <td align="left" valign="top">Очистить идентификатор TID для порожденного процесса</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_CHILD_SETTID</code></td>
       <td align="left" valign="top">Установить идентификатор TID для порожденного процесса</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_PARENT_SETTID</code></td>
       <td align="left" valign="top">Установить идентификатор TID для родительского процесса</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>CLONE_VM</code></td>
       <td align="left" valign="top">У порожденного и родительского процессов будет общее адресное пространство</td>
      </tr>
     </table>
    </section>
    <section>
     <title>
      <p>Потоки в пространстве ядра</p>
     </title>
     <p>Часто в ядре полезно выполнить некоторые операции в фоновом режиме. В ядре такая возможность реализована с помощью <emphasis>потоков пространства ядра</emphasis> (<emphasis>kernel thread</emphasis>) — обычных процессов, которые выполняются исключительно в пространстве ядра. Наиболее существенным отличием между потоками пространства ядра и обычными процессами является то, что потоки в пространстве ядра не имеют адресного пространства (значение указателя <code>mm</code> для них равно <code>NULL</code>). Эти потоки работают только в пространстве ядра, и их контекст не переключается в пространство пользователя. Тем не менее потоки в пространстве ядра планируются и вытесняются так же, как и обычные процессы.</p>
     <p>В ядре Linux потоки пространства ядра выполняют определенные задания, наиболее часто используемые, — это <emphasis>pdfush</emphasis> и <emphasis>ksoftirq</emphasis>. Эти потоки создаются при загрузке системы другими потоками пространства ядра. В действительности поток в пространстве ядра может быть создан только другим потоком, работающим в пространстве ядра. Интерфейс для запуска нового потока в пространстве ядра из уже существующего потока следующий:</p>
     <p><code>int kernel_thread(int (*fn)(void*), void* arg, unsigned long flags);</code></p>
     <p>Новая задача создается с помощью обычного системного вызова <code>clone()</code> с соответствующими значениями флагов, указанными в параметре flags. При возврате из системного вызова родительский поток режима ядра завершается и возвращает указатель на структуру <code>task_struct</code> порожденного процесса. Порожденный процесс выполняет функцию, адрес которой указан в параметре <code>fn</code>, в качестве аргумента этой функции передается параметр <code>arg</code>. Для указания обычных флагов потоков пространства ядра существует флаг <code>CLONE_KERNEL</code>, который объединяет в себе флаги <code>CLONE_FS</code>, <code>CLONE_FILES</code> и <code>CLONE_SIGHAND</code>, так как большинство потоков пространства ядра должны указывать эти флаги в параметре <code>flags</code>.</p>
     <p>Чаще всего поток пространства ядра продолжает выполнять свою функцию вечно (или, по крайней мере, до перегрузки системы, но когда она произойдет в случае ОС Linux- неизвестно). Функция потока обычно содержит замкнутый цикл, в котором поток пространства ядра по необходимости возобновляет выполнение, исполняет свои обязанности и снова переходит в приостановленное состояние.</p>
     <p>В следующих главах более детально будут рассмотрены конкретные примеры потоков пространства ядра.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Завершение процесса</p>
    </title>
    <section>
     <p>Как это ни грустно, но любой процесс в конечном итоге должен завершиться. Когда процесс завершается, ядро должно освободить ресурсы, занятые процессом, и оповестить процесс, который является родительским для завершившегося, о том, что его порожденный процесс, к сожалению, "умер".</p>
     <p>Обычно уничтожение процесса происходит тогда, когда процесс вызывает системный вызов <code>exit()</code> явно или неявно при выходе из главной функции программы (компилятор языка С помещает вызов функции <code>exit()</code> после возврата из функции <code>main()</code>). Процесс также может быть завершен непроизвольно. Это происходит, когда процесс получает сигнал или возникает исключительная ситуация, которую процесс не может обработать или проигнорировать. Независимо от того, каким образом процесс завершается, основную массу работы выполняет функция <code>do_exit(),</code> а именно указанные далее операции.</p>
     <p>• Устанавливается флаг <code>PF_EXITING</code> в поле <code>flags</code> структуры <code>task struct</code>.</p>
     <p>• Вызывается функция <code>del_timer_sync()</code>, чтобы удалить все таймеры ядра. После выхода из этой функции гарантируется, что нет никаких ожидающих таймеров и никакой обработчик таймера не выполняется.</p>
     <p>• Если включена возможность учета системных ресурсов, занятых процессами (BSD process accounting), то вызывается функция <code>acct_process()</code> для записи информации об учете ресурсов, которые использовались процессом.</p>
     <p>• Вызывается функция <code>__exit_mm()</code> для освобождения структуры <code>mm_struct</code>, занятой процессом. Если эта структура не используется больше ни одним процессом (другими словами, не является разделяемой), то она освобождается совсем.</p>
     <p>• Вызывается функция <code>exit_sem()</code>. Если процесс находится в очереди ожидания на освобождение семафора подсистемы IPC, то в этой функции процесс удаляется из этой очереди.</p>
     <p>• Вызываются функции <code>__exit_files()</code>, <code>__exit_fs()</code>, <code>exit_namespace()</code> и <code>exit_signals()</code> для уменьшения счетчика ссылок на объекты, которые отвечают файловым дескрипторам, данным по файловой системе, пространству имен и обработчикам сигналов соответственно. Если счетчик ссылок какого- либо объекта достигает значения, равного нулю, то соответствующий объект больше не используется никаким процессом и удаляется.</p>
     <p>• Устанавливается код завершения задания, который хранится в поле <code>exit_code</code> структуры <code>task struct</code>. Значение этого кода передается как аргумент функции <code>exit()</code> или задается тем механизмом ядра, из-за которого процесс завершается.</p>
     <p>• Вызывается функция <code>exit_notify()</code>, которая отправляет сигналы родительскому процессу завершающегося задания и назначает новый родительский процесс (reparent) для всех порожденных завершающимся заданием процессов, этим процессом становится или какой-либо один поток из группы потоков завершающегося процесса, или процесс <code>init</code>. Состояние завершающегося процесса устанавливается в значение <code>TASK_ZOMBIE</code>.</p>
     <p>• Вызывается функция <code>schedule()</code> для переключения на новый процесс (см. главу 4, "Планирование выполнения процессов"). Поскольку процесс в состоянии <code>TASK_ZOMBIE</code> никогда не планируется на выполнение, этот код является последним, который выполняется завершающимся процессом.</p>
     <p>Исходный код функции <code>do_exit()</code> описан в файле <code>kernel/exit.c</code>.</p>
     <p>К этому моменту освобождены все объекты, занятые задачей (если они используются только этой задачей). Задача больше не может выполняться (действительно, у нее больше нет адресного пространства, в котором она может выполняться), а кроме того, состояние задачи — <code>TASK_ZOMBIE</code> Единственные области памяти, которые теперь занимает процесс, — это стек режима ядра и слябовый объект, соответственно содержащие структуры <code>thread_info</code> и <code>task_struct</code>.</p>
     <p>Задание завершено настолько, насколько остается возможность передать необходимую информацию родительскому процессу.</p>
    </section>
    <section>
     <title>
      <p>Удаление дескриптора процесса</p>
     </title>
     <p>После возврата из функции <code>do_exit()</code> дескриптор завершенного процесса все еще существует в системе, но процесс находится в состоянии <code>TASK_ZOMBIE</code> и не может выполняться. Как уже рассказывалось выше, это позволяет системе получить информацию о порожденном процессе после его завершения. Следовательно, завершение процесса и удаление его дескриптора происходят в разные моменты времени. После того как родительский процесс получил информацию о завершенном порожденном процессе, структура <code>task_struct</code> порожденного процесса освобождается.</p>
     <p>Семейство функций <code>wait()</code> реализовано через единственный (и достаточно сложный) системный вызов <code>wait4()</code>. Стандартное поведение этой функции — приостановить выполнение вызывающей задачи до тех пор, пока один из ее порожденных процессов не завершится. При этом возвращается идентификатор <code>PID</code> завершенного порожденного процесса. В дополнение к этому, в данную функцию передается указатель на область памяти, которая после возврата из функции будет содержать код завершения завершившегося порожденного процесса.</p>
     <p>Когда приходит время окончательно освободить дескриптор процесса, вызывается функция <code>release_task()</code>, которая выполняет указанные ниже операции.</p>
     <p>• Вызывается функция <code>free_uid()</code> для декремента счетчика ссылок на информацию о пользователе процесса. В системе Linux поддерживается кэш с информацией о каждом пользователе, в частности сколько процессов и открытых файлов имеет пользователь. Если счетчик ссылок достигает значения нуль, то пользователь больше не имеет запущенных процессов и открытых файлов, в результате кэш уничтожается.</p>
     <p>• Вызывается функция <code>unhash_process()</code> для удаления процесса из хеш-таблицы идентификаторов процессов <code>pidhash</code> и удаления задачи из списка задач.</p>
     <p>• Если задача была в состоянии трассировки (<emphasis>ptrace</emphasis>), то родительским для нее снова назначается первоначальный родительский процесс и задача удаляется из списка задач, которые находятся в состоянии трассировки (<emphasis>ptrace</emphasis>) данным процессом.</p>
     <p>• В конце концов вызывается функция <code>put_task_struct()</code> для освобождения страниц памяти, содержащих стек ядра процесса и структуру <code>thread_info</code>, a также освобождается слябовый кэш, содержащий структуру <code>task_struct</code>.</p>
     <p>На данном этапе дескриптор процесса, а также все ресурсы, которые принадлежали только этому процессу, освобождены.</p>
    </section>
    <section>
     <title>
      <p>Дилемма "беспризорного" процесса</p>
     </title>
     <p>Если родительский процесс завершается до того, как завершаются вес его потомки, то должен существовать какой-нибудь механизм назначения нового родительского процесса для порожденных, иначе процессы, у которых нет родительского, навсегда останутся в состоянии зомби, что будет зря расходовать системную память. Решение этой проблемы было указано выше: новым родительским процессом становится или какой-либо один поток из группы потоков завершившегося родительского процесса, или процесс <code>init</code>. При выполнении функции <code>do_exit()</code> вызывается функция <code>notify_parent()</code>, которая в свою очередь вызывает <code>forget_original_parent()</code> для осуществления переназначения родительского процесса (reparent), как показано ниже.</p>
     <p><code>struct task_struct *p, *reaper = father;</code></p>
     <p><code>struct list_head *list;</code></p>
     <empty-line/>
     <p><code>if (father-&gt;exit_signal != -1)</code></p>
     <p><code> reaper = prev_thread(reaper);</code></p>
     <p><code>else</code></p>
     <p><code> reaper = child_reaper;</code></p>
     <empty-line/>
     <p><code>if (reaper == father)</code></p>
     <p><code> reaper = child_reaper;</code></p>
     <p>Этот программный код присваивает переменной reaper указатель на другое задание в группе потоков данного процесса. Если в этой группе потоков нет другого задания, то переменной <code>reaper</code> присваивается значение переменной <code>child_reaper,</code> которая содержит указатель на процесс <code>init</code>. Теперь, когда найден подходящий родительский процесс, нужно найти все порожденные процессы и установить для них полученное значение родительского процесса, как показано ниже.</p>
     <p><code>list_for_each(list, &amp;father-&gt;children) {</code></p>
     <p><code> p = list_entry(list, struct task_struct, sibling);</code></p>
     <p><code> reparent_thread(p, reaper, child_reaper);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>list_for_each(list, &amp;father-&gt;ptrace_children) {</code></p>
     <p><code> p = list_entry(list, struct task_struct, ptrace_list);</code></p>
     <p><code> reparent_thread(p, reaper, child_reaper);</code></p>
     <p><code>}</code></p>
     <p>В этом программном коде организован цикл по двум спискам: по списку порожденных процессов <emphasis>child list</emphasis> и по списку порожденных процессов, находящихся в состоянии трассировки другими процессами <emphasis>ptraced child list</emphasis>. Основная причина, по которой используется именно два списка, достаточно интересна (эта новая особенность появилась в ядрах серии 2.6). Когда задача находится в состоянии <emphasis>ptrace</emphasis>, для нее временно назначается родительским тот процесс, который осуществляет отладку (debugging). Когда завершается истинный родительский процесс для такого задания, то для такой дочерней задачи также нужно осуществить переназначение родительского процесса. В ядрах более ранних версий это приводило к необходимости <emphasis>организации цикла по всем заданиям системы</emphasis> для поиска порожденных процессов. Решение проблемы, как было указано выше, — это поддержка отдельного списка для порожденных процессов, которые находятся в состоянии трассировки, что уменьшает число операций поиска: происходит переход от поиска порожденных процессов по всему списку задач к поиску только по двум спискам с достаточно малым числом элементов.</p>
     <p>Когда для процессов переназначение родительского процесса прошло успешно, больше нет риска, что какой-либо процесс навсегда останется в состоянии зомби. Процесс <code>init</code> периодически вызывает функцию <code>wait()</code> для всех своих порожденных процессов и, соответственно, удаляет все зомби-процессы, назначенные ему.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Резюме</p>
    </title>
    <p>В этой главе рассмотрена важная абстракция операционной системы — процесс. Здесь описаны общие свойства процессов, их назначение, а также представлено сравнение процессов и потоков. Кроме того, описывается, как операционная система Linux хранит и представляет информацию, которая относится к процессам (структуры <code>task_struct</code> и <code>thread_info</code>), как создаются процессы (вызовы <code>clone()</code> и <code>fork()</code>), каким образом новые исполняемые образы загружаются в адресное пространство (семейство вызовов <code>exec()</code>), иерархия процессов, каким образом родительский процесс собирает информацию о своих потомках (семейство функций <code>wait()</code>) и как в конце концов процесс завершается (непроизвольно или с помощью вызова <code>exit()</code>).</p>
    <p>Процесс — это фундаментальная и ключевая абстракция, которая является основой всех современных операционных систем и, в конце концов, причиной, по которой вообще существуют операционные системы (чтобы выполнять программы).</p>
    <p>В следующей главе рассказывается о планировании выполнения процессов — изящной и интересной функции ядра, благодаря которой ядро принимает решение, какие процессы должны выполняться, в какое время и в каком порядке.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 4</p>
    <p>Планирование выполнения процессов</p>
   </title>
   <section>
    <p>В предыдущей главе были рассмотрены процессы — абстракция операционной системы, связанная с активным программным кодом. В этой главе представлен планировщик процессов — код, который позволяет процессам выполняться.</p>
    <p>Планировщик (<emphasis>scheduler</emphasis>) — это компонент ядра, который выбирает из всех процессов системы тот, который должен выполняться следующим. Таким образом, планировщик (или, как еще его называют, <emphasis>планировщик выполнения процессов</emphasis>) можно рассматривать как программный код, распределяющий конечные ресурсы процессорного времени между теми процессами операционной системы, которые могут выполняться. Планировщик является основой <emphasis>многозадачных</emphasis> (<emphasis>multitasking</emphasis>) операционных систем, таких как ОС Linux. Принимая решение о том, какой процесс должен выполняться следующим, планировщик несет ответственность за наилучшее использование ресурсов системы и создает впечатление того, что несколько процессов выполняются одновременно.</p>
    <p>Идея, лежащая в основе планирования выполнения процессов, достаточно проста. При наличии готовых к выполнению процессов, для того чтобы лучше использовать процессорное время, необходимо, чтобы всегда выполнялся какой-нибудь процесс. Если в системе процессов больше, чем процессоров, то некоторые процессы будут выполняться не во все моменты времени. Эти процессы <emphasis>готовы к выполнению</emphasis> (<emphasis>runnable</emphasis>). Исходя из информации о наборе готовых к выполнению процессов, выбор того процесса, который должен выполняться в следующий момент времени, и есть то фундаментальное решение, которое принимает планировщик.</p>
    <p>Многозадачные операционные системы— это те, которые могут выполнять попеременно или одновременно несколько процессов. На однопроцессорной машине такие системы создает иллюзию того, что несколько процессов выполняются одновременно. На многопроцессорной машине они позволяют процессам действительно выполняться параллельно на нескольких процессорах. На машинах любого типа эти системы позволяют процессам выполняться в фоновом режиме и не занимать процессорное время, если нет соответствующей работы. Такие задания, хотя и находятся в памяти, но <emphasis>не готовы к выполнению</emphasis>. Вместо этого данные процессы используют ядро, чтобы <emphasis>блокироваться</emphasis> до тех пор, пока не произойдет некоторое событие (ввод с клавиатуры, приход данных по сети, наступление некоторого момента времени в будущем и т.д.). Следовательно, ОС Linux может содержать 100 процессов в памяти, но только один из них будет в исполняемом состоянии.</p>
    <p>Многозадачные (multitasking) операционные системы бывают двух видов: системы с <emphasis>кооперативной</emphasis> (<emphasis>cooperative</emphasis>) <emphasis>многозадачностью</emphasis> и системы с <emphasis>вытесняющей</emphasis> (<emphasis>preemptive</emphasis>, <emphasis>преемптивной</emphasis>) <emphasis>многозадачностью</emphasis>. Операционная система Linux, так же как и большинство вариантов ОС Unix и других современных операционных систем, обеспечивает вытесняющую многозадачность. В системе с вытесняющей многозадачностью решение о том, когда один процесс должен прекратить выполнение, а другой возобновить его, принимает планировщик. Событие, заключающееся в принудительном замораживании выполняющегося процесса, называется <emphasis>вытеснением</emphasis> (<emphasis>preemption</emphasis>) этого процесса. Период времени, в течение которого процесс выполняется перед тем, как будет вытеснен, известен заранее. Этот период называется <emphasis>квантом времени</emphasis> (<emphasis>timeslice</emphasis>) процесса. В действительности квант времени соответствует той <emphasis>части</emphasis> процессорного времени, которая выделяется процессу. С помощью управления величинами квантов времени процессов планировщик принимает также и глобальное решение о планировании работы всей системы. При этом, кроме всего прочего, предотвращается возможность монопольного использования ресурсов всей системы одним процессом. Как будет показано далее, величины квантов времени в операционной системе Linux рассчитываются динамически, что позволяет получить некоторые интересные преимущества.</p>
    <p>В противоположность рассмотренному выше типу многозадачности, в системах с <emphasis>кооперативной многозадачностью</emphasis> процесс продолжает выполняться до тех пор, пока он добровольно не примет решение о прекращении выполнения. Событие, связанное с произвольным замораживанием выполняющегося процесса, называется <emphasis>передачей управления</emphasis> (<emphasis>yielding</emphasis>). У такого подхода очень много недостатков: планировщик не может принимать глобальные решения относительно того, сколько процессы должны выполняться; процесс может монополизировать процессор на большее время, чем это необходимо пользователю; "зависший" процесс, который никогда не передает управление системе, потенциально может привести к неработоспособности системы. К счастью, большинство операционных систем, разработанных за последнее десятилетие, предоставляют режим вытесняющей многозадачности. Наиболее известным исключением является операционная система Mac OS версии 9 и более ранних версий. Конечно, операционная система Unix имеет вытесняющую многозадачность с момента своего создания.</p>
    <p>При разработке ядер ОС Linux серии 2.5, планировщик ядра был полностью реконструирован. Новый тип планировщика часто называется <emphasis>O(1)-планировщиком</emphasis> (<emphasis>O(1) scheduler</emphasis>) в связи с соответствующим масштабированием времени выполнения алгоритма планирования<a l:href="#n19" type="note">[19]</a>. Этот планировщик позволяет преодолеть недостатки предыдущих версий планировщика ядра Linux и обеспечить расширенную функциональность, а также более высокие характеристики производительности. В этой главе будут рассмотрены основы работы планировщиков, как эти основы использованы в О(1)-планировщике, а также цели создания O(1)-планировщика, его устройство, практическая реализация, алгоритмы работы и соответствующие системные вызовы.</p>
   </section>
   <section>
    <title>
     <p>Стратегия планирования</p>
    </title>
    <section>
     <p>Стратегия (policy) планирования— это характеристики поведения планировщика, которые определяют, что и когда должно выполняться. Стратегия планирования определяет глобальный характер поведения системы и отвечает за оптимальное использование процессорного времени. Таким образом, это понятие очень важное.</p>
    </section>
    <section>
     <title>
      <p>Процессы, ограниченные скоростью ввода-вывода и скоростью процессора</p>
     </title>
     <p>Процессы можно классифицировать как те, которые <emphasis>ограничены скоростью ввода-вывода</emphasis> (<emphasis>I/O-bound</emphasis>), и те, которые <emphasis>ограничены скоростью процессора</emphasis> (<emphasis>processor-bound</emphasis>). К первому типу относятся процессы, которые большую часть своего времени выполнения тратят на отправку запросов на ввод-вывод информации и на ожидание ответов на эти запросы. Следовательно, такие процессы часто готовы к выполнению, но могут выполняться только в течение короткого периода времени, так как в конце концов они блокируются в ожидании выполнения ввода-вывода (имеются в виду не только дисковые операции ввода-вывода, но и любой другой тип ввода-вывода информации, как, например, работа с клавиатурой).</p>
     <p>Процессы, ограниченные скоростью процессора, наоборот, большую часть времени исполняют программный код. Такие процессы обычно выполняются до того момента, пока они не будут вытеснены, так как эти процессы не блокируются в ожидании на запросы ввода-вывода. Поскольку такие процессы не влияют на скорость ввода-вывода, то для обеспечения нормальной скорости реакции системы не требуется, чтобы они выполнялись часто. Стратегия планирования процессов, ограниченных скоростью процессора, поэтому предполагает, что такие процессы должны выполняться реже, но более продолжительный период времени. Конечно, оба эти класса процессов взаимно не исключают друг друга. Пример процесса, ограниченного скоростью процессора, — это выполнение бесконечного цикла.</p>
     <p>Указанные классификации не являются взаимно исключающими. Процессы могут сочетать в себе оба типа поведения: сервер системы X Windows — это процесс, который одновременно интенсивно загружает процессор и интенсивно выполняет операции ввода-вывода. Некоторые процессы могут быть ограничены скоростью ввода-вывода, но время от времени начинают выполнять интенсивную процессорную работу. Хороший пример — текстовый процессор, который обычно ожидает нажатия клавиш, но время от времени может сильно загружать процессор, выполняя проверку орфографии.</p>
     <p>Стратегия планирования операционной системы должна стремиться к удовлетворению двух несовместных условий: обеспечение высокой скорости реакции процессов (малого времени задержки, low latency) и высокой производительности (throughput). Для удовлетворения этим требованиям часто в планировщиках применяются сложные алгоритмы определения наиболее подходящего для выполнения процесса, которые дополнительно гарантируют, что все процессы, имеющие более низкий приоритет, также будут выполняться. В Unix-подобных операционных системах стратегия планирования направлена на то, чтобы процессы, ограниченные скоростью ввода-вывода, имели больший приоритет. Использование более высокого приоритета для процессов, ограниченных скоростью ввода-вывода, приводит к увеличению скорости реакции процессов, так как интерактивные программы обычно ограничиваются скоростью ввода-вывода. В операционной системе Linux для обеспечения хорошей скорости реакции интерактивных программ применяется оптимизация по времени оклика (обеспечение малого времени задержки), т.е. процессы, ограниченные скоростью ввода-вывода, имеют более высокий приоритет. Как будет видно далее, это реализуется таким образом, чтобы не пренебрегать и процессами, ограниченными скоростью процессора.</p>
    </section>
    <section>
     <title>
      <p>Приоритет процесса</p>
     </title>
     <p>Наиболее широко распространенным типом алгоритмов планирования является планирование <emphasis>с управлением по приоритетам</emphasis> (<emphasis>priority-based</emphasis>). Идея состоит в том, чтобы расположить процессы по порядку в соответствии с их важностью и необходимостью использования процессорного времени. Процессы с более высоким приоритетом будут выполняться раньше тех, которые имеют более низкий приоритет, в то время как процессы с одинаковым приоритетом планируются на выполнение циклически (по кругу, round-robin), т.е. периодически один за другим. В некоторых операционных системах, включая Linux, процессы с более высоким приоритетом получают также и более длительный квант времени. Процесс, который готов к выполнению, у которого еще не закончился квант времени и который имеет наибольший приоритет, будет выполняться всегда. Как операционная система, так и пользователь могут устанавливать значение приоритета процесса и таким образом влиять на работу планировщика системы.</p>
     <p>В операционной системе Linux используется планировщик <emphasis>с динамическим управлением по приоритетам</emphasis> (dynamic priority-based), который основан на такой же идее. Основной принцип состоит в том, что вначале устанавливается некоторое начальное значение приоритета, а затем планировщик может динамически уменьшать или увеличивать это значение приоритета для выполнения своих задач. Например, ясно, что процесс, который тратит много времени на выполнение операций ввода-вывода, ограничен скоростью ввода-вывода. В операционной системе Linux такие процессы получают более высокое значение динамического приоритета. С другой стороны, процесс, который постоянно полностью использует свое значение кванта времени, — это процесс, ограниченный скоростью процессора. Такие процессы получают меньшее значение динамического приоритета.</p>
     <p>В ядре Linux используется два различных диапазона приоритетов. Первый — это параметр nice, который может принимать значения в диапазоне от -20 до 19, по умолчанию значение этого параметра равно 0. Большее значение параметра <emphasis>nice</emphasis> соответствует меньшему значению приоритета — необходимо быть более тактичным к другим процессам системы (<emphasis>nice</emphasis> — англ. тактичный, хороший). Процессы с меньшим значением параметра <emphasis>nice</emphasis> (большим значением приоритета) выполняются раньше процессов с большим значением <emphasis>nice</emphasis> (меньшим приоритетом). Значение параметра nice позволяет также определить, насколько продолжительный квант времени получит процесс. Процесс со значением параметра <emphasis>nice</emphasis> равным -20 получит квант времени самой большой длительности, в то время как процесс со значением параметра nice равным 19 получит наименьшее значение кванта времени. Использование параметра <emphasis>nice</emphasis> — это стандартный способ указания приоритетов процессов для всех Unix-подобных операционных систем.</p>
     <p>Второй диапазон значений приоритетов— это приоритеты реального времени (real-time priority), которые будут рассмотрены ниже. По умолчанию диапазон значений этого параметра лежит от 0 до 99. Все процессы реального времени имеют более высокий приоритет по сравнению с обычными процессами. В операционной системе Linux приоритеты реального времени реализованы в соответствии со стандартом POSIX. В большинстве современных Unix-систем они реализованы по аналогичной схеме.</p>
    </section>
    <section>
     <title>
      <p>Квант времени</p>
     </title>
     <p>Квант времени (timeslice<a l:href="#n20" type="note">[20]</a>) — это численное значение, которое характеризует, как долго может выполняться задание до того момента, пока оно не будет вытеснено. Стратегия планирования должна устанавливать значение кванта времени, используемое по умолчанию, что является непростой задачей. Слишком большое значение кванта времени приведет к ухудшению интерактивной производительности системы— больше не будет впечатления, что процессы выполняются параллельно. Слишком малое значение кванта времени приведет к возрастанию накладных расходов на переключение между процессами, так как больший процент системного времени будет уходить на переключение с одного процесса с малым квантом времени на другой процесс с малым квантом времени. Более того, снова возникают противоречивые требования к процессам, ограниченным скоростью ввода-вывода и скоростью процессора. Процессам, ограниченным скоростью ввода-вывода, не требуется продолжительный квант времени, в то время как для процессов, ограниченных скоростью процессора, настоятельно требуется продолжительный квант времени, например, чтобы поддерживать кэши процессора в загруженном состоянии.</p>
     <p>На основе этих аргументов можно сделать вывод, что любое большое значение кванта времени приведет к ухудшению интерактивной производительности. При реализации большинства операционных систем такой вывод принимается близко к сердцу и значение кванта времени, используемое по умолчанию, достаточно мало, например равно 20 мс. Однако в операционной системе Linux используется то преимущество, что процесс с самым высоким приоритетом всегда выполняется. Планировщик ядра Linux поднимает значение приоритета для интерактивных задач, что позволяет им выполняться более часто. Поэтому в ОС Linux планировщик использует достаточно большое значение кванта времени (рис 4.1). Более того, планировщик ядра Linux динамически определяет значение кванта времени процессов в зависимости от их приоритетов. Это позволяет процессам с более высоким приоритетом, которые считаются более важными, выполняться более часто и в течение большего периода времени. Использование динамического определения величины кванта времени и приоритетов позволяет обеспечить большую устойчивость и производительность планировщика.</p>
     <image l:href="#img_6.jpeg"/>
     <p><strong>Рис. 4.1</strong>. Вычисление кванта времени процесса</p>
     <p>Следует заметить, что процесс не обязательно должен использовать весь свой квант времени за один раз. Например, процесс, у которого квант времени равен 100 мс, не обязательно должен беспрерывно выполняться в течение 100 мс, рискуя потерять всю оставшуюся неистраченную часть кванта времени. Процесс может выполняться в течение пяти периодов длительностью по 20 мс каждый.</p>
     <p>Таким образом, интерактивные задачи также получают преимущество от использования продолжительного кванта времени, если даже вся продолжительность кванта времени не будет использована сразу, гарантируется, что такие процессы будут готовы к выполнению по возможности долго.</p>
     <p>Когда истекает квант времени процесса, считается, что процесс потерял право выполняться. Процесс, у которого нет кванта времени, не имеет права выполняться до того момента, пока все другие процессы не используют свой квант времени. Когда это случится, то у всех процессов будет значение оставшегося кванта времени, равное нулю. В этот момент значения квантов времени для всех процессов пересчитываются. В планировщике ОС Linux используется интересный алгоритм для обработки ситуации, когда все процессы использовали свой квант времени. Этот алгоритм будет рассмотрен далее.</p>
    </section>
    <section>
     <title>
      <p>Вытеснение процесса</p>
     </title>
     <p>Как уже упоминалось, операционная система Linux использует <emphasis>вытесняющую</emphasis> многозадачность. Когда процесс переходит в состояние <code>TASK_RUNNING</code>, ядро проверяет значение приоритета этого процесса. Если это значение больше, чем приоритет процесса, который выполняется в данный момент, то активизируется планировщик, чтобы запустить новый процесс на выполнение (имеется в виду тот процесс, который только что стал готовым к выполнению). Дополнительно, когда квант времени процесса становится равным нулю, он вытесняется и планировщик готов к выбору нового процесса.</p>
    </section>
    <section>
     <title>
      <p>Стратегия планирования в действии</p>
     </title>
     <p>Рассмотрим систему с двумя готовыми к выполнению заданиями: программой для редактирования текстов и видеокодером. Программа для редактирования текстов ограничена скоростью ввода-вывода, потому что она тратит почти все свое время на ожидание ввода символов с клавиатуры пользователем (не имеет значение, с какой скоростью пользователь печатает, это не <emphasis>те</emphasis> скорости). Несмотря ни на что, при нажатии клавиши пользователь хочет, чтобы текстовый редактор отреагировал <emphasis>сразу же</emphasis>. В противоположность этому видеокодер ограничен скоростью процессора. Если не считать, что он время от времени считывает необработанные данные с диска и записывает результирующий видеоформат на диск, то кодер большую часть времени выполняет программу видеокодека для обработки данных, что легко загружает процессор на все 100%. Для этой программы нет строгих ограничений на время выполнения: пользователю не важно, запустится она на полсекунды раньше или на полсекунды позже. Конечно, чем раньше она завершит работу, тем лучше.</p>
     <p>В такой системе планировщик установит для текстового редактора больший приоритет и выделит более продолжительный квант времени, чем для видеокодера, так как текстовый редактор — интерактивная программа. Для текстового редактора продолжительности кванта времени хватит с избытком. Более того, поскольку текстовый редактор имеет больший приоритет, он может вытеснить процесс видеокодера при необходимости. Это гарантирует, что программа текстового редактора будет немедленно реагировать на нажатия клавиш. Однако это не причинит никакого вреда и видеокодеру, так как программа текстового редактора работает с перерывами, и во время перерывов видеокодер может монопольно использовать систему. Все это позволяет оптимизировать производительность для обоих приложений.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Алгоритм планирования</p>
    </title>
    <section>
     <p>В предыдущих разделах была рассмотрена в самых общих чертах теория работы планировщика процессов в операционной системе Linux. Теперь, когда мы разобрались с основами, можно более глубоко погрузиться в то, как именно работает планировщик ОС Linux.</p>
     <p>Программный код планировщика операционной системы Linux содержится в файле <code>kernel/sched.c</code>. Алгоритм планирования и соответствующий программный код были существенно переработаны в те времена, когда началась разработка ядер серии 2.5. Следовательно, программный код планировщика является полностью новым и отличается от планировщиков предыдущих версий. Новый планировщик разрабатывался для того, чтобы удовлетворять указанным ниже требованиям.</p>
     <p>• Должен быть реализован полноценный <emphasis>O(1)</emphasis>-планировщик. Любой алгоритм, использованный в новом планировщике, должен завершать свою работу за постоянный период времени, независимо от числа выполняющихся процессов и других обстоятельств.</p>
     <p>• Должна обеспечиваться хорошая масштабируемость для SMP-систем. Каждый процессор должен иметь свои индивидуальные элементы блокировок и свою индивидуальную очередь выполнения.</p>
     <p>• Должна быть реализована улучшенная SMP-привязка (SMP affinity). Задания, для выполнения на каждом процессоре многопроцессорной системы, должны быть распределены правильным образом, и, по возможности, выполнение этих задач должно продолжаться на одном и том же процессоре. Осуществлять миграцию заданий с одного процессора на другой необходимо только для уменьшения дисбаланса между размерами очередей выполнения всех процессоров.</p>
     <p>• Должна быть обеспечена хорошая производительность для интерактивных приложений. Даже при значительной загрузке система должна иметь хорошую реакцию и немедленно планировать выполнение интерактивных задач.</p>
     <p>• Должна быть обеспечена равнодоступность ресурсов (fairness). Ни один процесс не должен ощущать нехватку квантов времени за допустимый период. Кроме того, ни один процесс не должен получить недопустимо большое значение кванта времени.</p>
     <p>• Должна быть обеспечена оптимизация для наиболее распространенного случая, когда число готовых к выполнению процессов равно 1–2, но в то же время должно обеспечиваться хорошее масштабирование и на случай большого числа процессоров, на которых выполняется большое число процессов.</p>
     <p>Новый планировщик позволяет удовлетворить всем этим требованиям.</p>
    </section>
    <section>
     <title>
      <p>Очереди выполнения</p>
     </title>
     <p>Основная структура данных планировщика — это <emphasis>очередь выполнения</emphasis> (<emphasis>runqueue</emphasis>). Очередь выполнения определена в файле <code>kernel/sched.c</code><a l:href="#n21" type="note">[21]</a> в виде структуры <code>struct runqueue</code>. Она представляет собой список готовых к выполнению процессов для данного процессора.</p>
     <p>Для каждого процессора определяется своя очередь выполнения. Каждый готовый к выполнению процесс может находиться в одной и только в одной очереди выполнения. Кроме этого, очередь выполнения содержит информацию, необходимую для планирования выполнения на данном процессоре. Следовательно, очередь выполнения — это действительно основная структура данных планировщика для каждого процессора. Рассмотрим нашу структуру, описание которой приведено ниже. Комментарии объясняют назначения каждого поля.</p>
     <p><code>struct runqueue {</code></p>
     <p><code> spinlock_t lock; /* спин-блокировка для зашиты этой очереди выполнения */</code></p>
     <p><code> unsigned long nr_running; /* количество задач, готовых к выполнению */</code></p>
     <p><code> unsigned long nr_switches; /* количество переключений контекста */</code></p>
     <p><code> unsigned long expired timestamp; /* время последнего обмена массивами */</code></p>
     <p><code> unsigned long nr_uninterruptible; /* количество заданий в состоянии</code></p>
     <p><code>                                      непрерываемого ожидания */</code></p>
     <p><code> unsigned long long timestamp last tick; /* последняя метка времени</code></p>
     <p><code>                                            планировщика */</code></p>
     <p><code> struct task_struct *curr; /* текущее задание, выполняемое на данном</code></p>
     <p><code>                              процессоре */</code></p>
     <p><code> struct task_struct *idle; /* холостая задача данного процессора */</code></p>
     <p><code> struct mm_struct *prev_mm; /* поле mm_struct последнего выполняемого</code></p>
     <p><code>                               задания */</code></p>
     <p><code> struct prio_array *active; /* указатель на активный массив приоритетов */</code></p>
     <p><code> struct prio_array *expired; /* указатель на истекший</code></p>
     <p><code>                                массив приоритетов */</code></p>
     <p><code> struct prio_array arrays[2]; /* массивы приоритетов */</code></p>
     <p><code> struct task_struct *migration_thread; /* миграционный поток для</code></p>
     <p><code>                                          данного процессора */</code></p>
     <p><code> struct list_head migration_queue; /* миграционная очередь для</code></p>
     <p><code>                                      данного процессора */</code></p>
     <p><code> atomic_t nr_iowait; /* количество заданий, ожидающих на ввод-вывод */</code></p>
     <p><code>};</code></p>
     <p>Поскольку очередь выполнения — это основная структура данных планировщика, существует группа макросов, которые используются для доступа к определенным очередям выполнения. Макрос <code>cpu_rq(processor)</code> возвращает указатель на очередь выполнения, связанную с процессором, имеющим заданный номер. Аналогично макрос <code>this_rq()</code> возвращает указатель на очередь, связанную с текущим процессором. И наконец, макрос <code>task_rq(task)</code> возвращает указатель на очередь, в которой находится соответствующее задание.</p>
     <p>Перед тем как производить манипуляции с очередью выполнения, ее необходимо заблокировать (блокировки подробно рассматриваются в главе 8, "Введение в синхронизацию выполнения кода ядра"). Так как очередь выполнения уникальна для каждого процессора, процессору редко необходимо блокировать очередь выполнения другого процессора (однако, как будет показано далее, такое все же иногда случается). Блокировка очереди выполнения позволяет запретить внесение любых изменений в структуру данных очереди, пока процессор, удерживающий блокировку, выполняет операции чтения или записи полей этой структуры. Наиболее часто встречается ситуация, когда необходимо заблокировать очередь выполнения, в которой выполняется текущее задание. В этом случае используются функции <code>task_rq_lock()</code> и <code>task_rq_unlock()</code>, как показано ниже.</p>
     <p><code>struct runqueue *rq;</code></p>
     <p><code>unsigned long flags;</code></p>
     <empty-line/>
     <p><code>rq = task_rq_lock(task, &amp;flags);</code></p>
     <p><code>/* здесь можно производить манипуляции с очередью выполнения */</code></p>
     <p><code>task_rq_unlock(rq, flags);</code></p>
     <p>Альтернативными функциями выступают функция <code>this_rq_lock()</code>, которая позволяет заблокировать текущую очередь выполнения, и функция <code>rq_unlock(struct runqueue *rq)</code>, позволяющая разблокировать указанную в аргументе очередь.</p>
     <p>Для предотвращения взаимных блокировок код, который захватывает блокировки нескольких очередей, всегда должен захватывать блокировки в одном и том же порядке, а именно в порядке увеличения значения адреса памяти очереди (в главе 8, "Введение в синхронизацию выполнения кода ядра", приведено полное описание). Пример, как это осуществить, показан ниже.</p>
     <p><code>/* для того, чтобы заблокировать ... */</code></p>
     <p><code>if (rq1 &lt; rq2) (</code></p>
     <p><code> spin_lock(&amp;rq1-&gt;lock);</code></p>
     <p><code> spin_lock(&amp;rq2-&gt;lock);</code></p>
     <p><code>} else (</code></p>
     <p><code> spin_lock(&amp;rq2-&gt;lock);</code></p>
     <p><code> spin_lock(&amp;rq1-&gt;lock);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>/* здесь можно манипулировать обеими очередями ... */</code></p>
     <empty-line/>
     <p><code>/* для того, чтобы разблокировать ... */</code></p>
     <p><code>spin_unlock(&amp;rq1-&gt;lock);</code></p>
     <p><code>spin_unlock(&amp;rq2-&gt;lock);</code></p>
     <p>С помощью функций <code>double_rq_lock()</code> и <code>double_rq_unlock()</code> указанные шаги можно выполнить автоматически. При этом получаем следующее.</p>
     <p><code>double_rq_lock(rq1, rq2);</code></p>
     <p><code>/* здесь можно манипулировать обеими очередями ...*/</code></p>
     <p><code>double_rq_unlock(rq1, rq2);</code></p>
     <p>Рассмотрим небольшой пример, который показывает, почему важен порядок захвата блокировок. Вопрос взаимных блокировок обсуждается в главах 8, "Введение в синхронизацию выполнения кода ядра" и 9, "Средства синхронизации в ядре". Эта проблема касается не только очередей выполнения: вложенные блокировки должны всегда захватываться в одном и том же порядке. Спин-блокировки используются для предотвращения манипуляций с очередями выполнения несколькими задачами одновременно. Принцип работы этих блокировок аналогичен ключу, с помощью которого открывают дверь. Первое задание, которое подошло к двери, захватывает ключ, входит в дверь и закрывает ее с другой стороны. Если другое задание подходит к двери и определяет, что дверь закрыта (потому что за дверью находится первое задание), то оно должно остановиться и подождать, пока первое задание на выйдет и не возвратит ключ. Ожидание называется <emphasis>спиннингом</emphasis> (<emphasis>вращением</emphasis>, <emphasis>spinning</emphasis>), так как на самом деле задание постоянно выполняет цикл, периодически проверяя, не возвращен ли ключ. Теперь рассмотрим, что будет, если одно задание пытается сначала заблокировать первую очередь выполнения, а затем вторую, в то время как другое задание пытается сначала заблокировать вторую очередь, а затем — первую. Допустим, что первое задание успешно захватило блокировку первой очереди, в то время как второе задание захватило блокировку второй очереди. После этого первое задание пытается заблокировать вторую очередь, а второе задание — первую. Ни одно из заданий никогда не добьется успеха, так как другое задание уже захватило эту блокировку. Оба задания будут ожидать друг друга вечно. Так же как в тупике дороги создается блокировка движения, так и неправильный порядок захвата блокировок приводит к тому, что задания начинают ожидать друг друга вечно, и тоже возникает тупиковая ситуация, которая еще называется взаимоблокировкой. Если оба задания захватывают блокировки в одном и том же порядке, то рассмотренной ситуации произойти не может. В главах 8 и 9 представлено полное описание блокировок.</p>
    </section>
    <section>
     <title>
      <p>Массивы приоритетов</p>
     </title>
     <p>Каждая очередь выполнения содержит два <emphasis>массива приоритетов</emphasis> (<emphasis>priority arrays</emphasis>): активный и истекший. Массивы приоритетов определены в файле <code>kernel/sched.c</code> в виде описания <code>struct prio_array</code>. Массивы приоритетов — это структуры данных, которые обеспечивают O(1)-планирование. Каждый массив приоритетов содержит для каждого значения приоритета одну очередь процессов, готовых к выполнению. Массив приоритетов также содержит <emphasis>битовую маску приоритетов</emphasis> (<emphasis>priority bitmap</emphasis>), используемую для эффективного поиска готового к выполнению задания, у которого значение приоритета является наибольшим в системе.</p>
     <p><code> struct prio_array {</code></p>
     <p><code> int nr_active;                     /* количество заданий */</code></p>
     <p><code> unsigned long bitmap[BITMAP_SIZE]; /* битовая маска приоритетов */</code></p>
     <p><code> struct list_head queue[MAX_PRIO];  /* очереди приоритетов */</code></p>
     <p><code>};</code></p>
     <p>Константа <code>MAX_PRIO</code> — это количество уровней приоритета в системе. По умолчанию значение этой константы равно 140. Таким образом, для каждого значения приоритета выделена одна структура <code>struct list_head</code>. Константа <code>BITMAP_SIZE</code> — это размер массива переменных, каждый элемент которого имеет тип <code>unsigned long</code>. Каждый бит этого массива соответствует одному действительному значению приоритета. В случае 140 уровней приоритетов и при использовании 32-разрядных машинных слов, значение константы <code>BITMAP_SIZE</code> равно 5. Таким образом, поле <code>bitmap</code> — это массив из пяти элементов, который имеет длину 160 бит.</p>
     <p>Все массивы приоритетов содержат поле <code>bitmap</code>, каждый бит этого поля соответствует одному значению приоритета в системе. В самом начале значения всех битов равны 0. Когда задача с определенным приоритетом становится готовой к выполнению (то есть значение статуса этой задачи становится равным <code>TASK_RUNNING</code>), соответствующий этому приоритету бит поля <code>bitmap</code> устанавливается в значение 1. Например, если задача с приоритетом, равным 7, готова к выполнению, то устанавливается бит номер 7. Нахождение задания с самым высоким приоритетом в системе сводится только лишь к нахождению самого первого установленного бита в битовой маске. Так как количество приоритетов неизменно, то время, которое необходимо затратить на эту операцию поиска, постоянно и не зависит от количества процессов, выполняющихся в системе. Более того, для каждой поддерживаемой аппаратной платформы в ОС Linux должен быть реализован быстрый алгоритм <emphasis>поиска первого установленного бита</emphasis> (<emphasis>find first set</emphasis>) для проведения быстрого поиска в битовой маске. Эта функция называется s<code>ched_find_first_bit()</code>. Для многих аппаратных платформ существует машинная инструкция нахождения первого установленного бита в заданном машинном слове<a l:href="#n22" type="note">[22]</a>. Для таких систем нахождение первого установленного бита является тривиальной операций и сводится к выполнению этой инструкции несколько раз.</p>
     <p>Каждый массив приоритетов также содержит массив очередей, представленных структурами <code>struct list_head</code>. Этот массив называется queue. Каждому значению приоритета соответствует своя очередь. Очереди реализованы в виде связанных списков, и каждому значению приоритета соответствует список всех процессов системы, готовых к выполнению, имеющих это значение приоритета и находящихся в очереди выполнения данного процессора. Нахождение задания, которое будет выполняться следующим, является простой задачей и сводится к выбору следующего элемента из списка. Все задания с одинаковым приоритетом планируются на выполнение циклически.</p>
     <p>Массив приоритетов также содержит счетчик <code>nr_active</code>, значение которого соответствует количеству готовых к выполнению заданий в данном массиве приоритетов.</p>
    </section>
    <section>
     <title>
      <p>Пересчет квантов времени</p>
     </title>
     <p>Во многих операционных системах (включая и более старые версии ОС Linux) используется прямой метод для пересчета значения кванта времени каждого задания, когда все эти значения достигают нуля.</p>
     <p>Обычно это реализуется с помощью цикла по всем задачам в системе, например, следующим образом.</p>
     <p><code>for (каждого задания в системе) (</code></p>
     <p><code> пересчитать значение приоритета</code></p>
     <p><code> пересчитать значение кванта времени</code></p>
     <p><code>}</code></p>
     <p>Значение приоритета и другие атрибуты задачи используются для определения нового значения кванта времени. Такой подход имеет некоторые проблемы.</p>
     <p>• Пересчет потенциально может занять много времени. Хуже того, время такого расчета масштабируется как <emphasis>О(n)</emphasis>, где <emphasis>n</emphasis> — количество задач в системе.</p>
     <p>• Во время пересчета должен быть использован какой-нибудь тип блокировки для защиты списка задач и отдельных дескрипторов процессов. В результате получается высокий уровень конфликтов при захвате блокировок.</p>
     <p>• Отсутствие определенности в случайно возникающих пересчетах значений квантов времени является проблемой для программ реального времени.</p>
     <p>• Откровенно говоря, это просто нехорошо (что является вполне оправданной причиной для каких-либо усовершенствований ядра Linux).</p>
     <p>Новый планировщик ОС Linux позволяет избежать использования цикла пересчета приоритетов. Вместо этого в нем применяется <emphasis>два</emphasis> массива приоритетов для каждого процессора: <emphasis>активный</emphasis> (<emphasis>active</emphasis>) и <emphasis>истекший</emphasis> (<emphasis>expired</emphasis>). Активный массив приоритетов содержит очередь, в которую включены все задания соответствующей очереди выполнения, для которых еще не иссяк квант времени. Истекший массив приоритетов содержит все задания соответствующей очереди, которые израсходовали свой квант времени. Когда значение кванта времени для какого-либо задания становится равным нулю, то перед тем, как поместить это задание в истекший массив приоритетов, для него вычисляется новое значение кванта времени. Пересчет значений кванта времени для всех процессов проводится с помощью перестановки активного и истекшего массивов местами. Так как на массивы ссылаются с помощью указателей, то переключение между ними будет выполняться так же быстро, как и перестановка двух указателей местами. Показанный ниже код выполняется в функции <code>schedule()</code>.</p>
     <p><code>struct prio_array array = rq-&gt;active;</code></p>
     <empty-line/>
     <p><code>if (!array-&gt;nr_active) {</code></p>
     <p><code> rq-&gt;active = rq-&gt;expired;</code></p>
     <p><code> rq-&gt;expired = array;</code></p>
     <p><code>}</code></p>
     <p>Упомянутая перестановка и есть ключевым, моментом O(1)-планировщика. Вместо того чтобы все время пересчитывать значение приоритета и кванта времени для каждого процесса, O(1)-планировщик выполняет простую двухшаговую перестановку массивов. Такая реализация позволяет решить указанные выше проблемы.</p>
     <subtitle>Функция <code>schedule()</code></subtitle>
     <p>Все действия по выбору следующего задания на исполнение и переключение на выполнение этого задания реализованы в виде функции <code>schedule()</code>. Эта функция вызывается явно кодом ядра при переходе в приостановленное состояние (sleep), a также в случае когда какое-либо задание вытесняется. Функция <code>schedule()</code> выполняется независимо каждым процессором. Следовательно, каждый процессор самостоятельно принимает решение о том, какой процесс выполнять следующим.</p>
     <p>Функция <code>schedule()</code> достаточно проста, учитывая характер тех действий, которые она выполняет. Следующий код позволяет определить задачу с наивысшим приоритетом.</p>
     <p><code>struct task_struct *prev, *next;</code></p>
     <p><code>struct list_head *queue;</code></p>
     <p><code>struct prio_array *array;</code></p>
     <p><code>int idx;</code></p>
     <empty-line/>
     <p><code>prev = current;</code></p>
     <p><code>array = rq-&gt;active;</code></p>
     <p><code>idx = sched_find_first_bit(array-&gt;bitmap);</code></p>
     <p><code>queue = array-&gt;queue + idx;</code></p>
     <p><code>next = list_entry(queue-&gt;next, struct task struct, run_list);</code></p>
     <p>Вначале осуществляется поиск в битовой маске активного массива приоритетов для нахождения номера самого первого установленного бита. Этот бит соответствует готовой к выполнению задаче с наивысшим приоритетом. Далее планировщик выбирает первое задание из списка заданий, которое соответствует найденному значению приоритета. Это и есть задача с наивысшим значением приоритета в системе, и эту задачу планировщик будет запускать на выполнение. Все рассмотренные операции показаны на рис. 4.2.</p>
     <image l:href="#img_7.jpeg"/>
     <p><strong>Рис. 4.2</strong>. Алгоритм работы О(1)-планировщика операционной системы Linux</p>
     <p>Если полученные значения переменных <code>prev</code> и <code>next</code> не равны друг другу, то для выполнения выбирается новое задание (<code>next</code>). При этом для переключения с задания, на которое указывает переменная <code>prev</code>, на задание, соответствующее переменной next, вызывается функция <code>context_switch()</code>, зависящая от аппаратной платформы. Переключение контекста будет рассмотрено в одном из следующих разделов.</p>
     <p>В рассмотренном коде следует обратить внимание на два важных момента. Во- первых, он очень простой и, следовательно, очень быстрый. Во-вторых, количество процессов в системе не влияет на время выполнения этого кода. Для нахождения наиболее подходящего для выполнения процесса не используются циклы. В действительности никакие факторы не влияют на время, за которое функция <code>schedule()</code> осуществляет поиск наиболее подходящего для выполнения задания. Время выполнения этой операции всегда постоянно.</p>
    </section>
    <section>
     <title>
      <p>Вычисление приоритетов и квантов времени</p>
     </title>
     <p>В начале этой главы было рассмотрено, как приоритет и квант времени используются для того, чтобы влиять на те решения, которые принимает планировщик. Кроме того, были рассмотрены процессы, ограниченные скоростью ввода-вывода и скоростью процессора, а также было описано, почему желательно поднимать приоритет интерактивных задач. Теперь давайте рассмотрим код, который реализует эти соображения.</p>
     <p>Процессы имеют начальное значение приоритета, которое называется nice. Это значение может лежать в диапазоне от -20 до 19, по умолчанию используется значение 0. Значение 19 соответствует наиболее низкому приоритету, а значение -20 — наиболее высокому. Значение параметра <emphasis>nice</emphasis> хранится в поле <code>static_prio</code> структуры <code>task_struct</code> процесса. Это значение называется статическим приоритетом, потому что оно не изменяется планировщиком и остается таким, каким его указал пользователь. Планировщик свои решения основывает на динамическом приоритете, которое хранится в поле <code>prio</code>. Динамический приоритет вычисляется как функция статического приоритета и интерактивности задания.</p>
     <p>Функция <code>effective_prio()</code> возвращает значение динамического приоритета задачи. Эта функция исходит из значения параметра <emphasis>nice</emphasis> для данной задачи и вычисляет для этого значения надбавку или штраф в диапазоне от -5 до 5, в зависимости от интерактивности задачи. Например, задание с высокой интерактивностью, которое имеет значение параметра <emphasis>nice</emphasis>, равное 10, может иметь динамический приоритет, равный 5. И наоборот, программа со значением параметра nice, равным 10, которая достаточно активно использует процессор, может иметь динамический приоритет, равный 12. Задачи, которые обладают умеренной интерактивностью, не получают ни надбавки, ни штрафа, и их динамический приоритет совпадает со значением параметра <emphasis>nice</emphasis>.</p>
     <p>Конечно, планировщик по волшебству не может определить, какой процесс является интерактивным. Для определения необходима некоторая эвристика, которая отражает, является ли процесс ограниченным скоростью ввода-вывода или скоростью процессора. Наиболее выразительный показатель — сколько времени задача находится в приостановленном состоянии (sleep). Если задача проводит большую часть времени в приостановленном состоянии, то она ограничена вводом-выводом. Если задача больше времени находится в состоянии готовности к выполнению, чем в приостановленном состоянии, то эта задача не интерактивна. В экстремальных случаях, если задача большую часть времени находится в приостановленном состоянии, то она полностью ограничена скоростью ввода-вывода; если задача все время готова к выполнению, то она ограничена скоростью процессора.</p>
     <p>Для реализации такой эвристики в ядре Linux предусмотрен изменяемый показатель того, как соотносится время, которое процесс проводит в приостановленном состоянии, со временем, которое процесс проводит в состоянии готовности к выполнению. Значение этого показателя хранится в поле <code>sleep</code>_avg структуры <code>task_struct</code>. Диапазон значений этого показателя лежит от нуля до значения <code>MAXSLEEP_AVG</code>, которое по умолчанию равно 10 мс. Когда задача становится готовой к выполнению после приостановленного состояния, значение поля <code>sleep_avg</code> увеличивается на значение времени, которое процесс провел в приостановленном состоянии, пока значение <code>sleep_avg</code> не достигнет <code>MAXSLEEP_AVG</code>. Когда задача выполняется, то в течение каждого импульса таймера (timer tick) значение этой переменной уменьшается, пока оно не достигнет значения 0.</p>
     <p>Такой показатель является, на удивление, надежным. Он рассчитывается не только на основании того, как долго задача находится в приостановленном состоянии, но и на основании того, насколько мало задача выполняется. Таким образом, задача, которая проводит много времени в приостановленном состоянии и в то же время постоянно использует свой квант времени, не получит большой прибавки к приоритету: показатель работает не только для поощрения интерактивных задач, но и для наказания задач, ограниченных скоростью процессора. Этот показатель также устойчив по отношению к злоупотреблениям. Задача, которая получает повышенное значение приоритета и большое значение кванта времени, быстро утратит свою надбавку к приоритету, если она постоянно выполняется и сильно загружает процессор. В конце концов, такой показатель обеспечивает малое время реакции. Только что созданный интерактивный процесс быстро достигнет высокого значения поля <code>sleep_avg</code>. Несмотря на все сказанное, надбавка и штраф применяются к значению параметра <emphasis>nice</emphasis>, так что пользователь также может влиять на работу системного планировщика путем изменения значения параметра <emphasis>nice</emphasis> процесса.</p>
     <p>Расчет значения кванта времени, наоборот, более прост, так как значение динамического приоритета уже базируется на значении параметра <emphasis>nice</emphasis> и на интерактивности (эти показатели планировщик учитывает как наиболее важные). Поэтому продолжительность кванта времени может быть просто выражена через значение динамического приоритета. Когда создается новый процесс, порожденный и родительский процессы делят пополам оставшуюся часть кванта времени родительского процесса. Такой подход обеспечивает равнодоступность ресурсов и предотвращает возможность получения бесконечного значения кванта времени путем постоянного создания порожденных процессов. Однако после того, как квант времени задачи иссякает, это значение пересчитывается на основании динамического приоритета задачи. Функция <code>task_timeslice()</code> возвращает новое значение кванта времени для данного задания. Расчет просто сводится к масштабированию значения приоритета в диапазон значений квантов времени. Чем больше значение приоритета задачи, тем большей продолжительности квант времени получит задание в текущем цикле выполнения. Максимальное значение кванта времени равно <code>MAX_TIMESLICE</code>, которое по умолчанию равно 200 мс. Даже задания с самым низким приоритетом получают квант времени продолжительностью <code>MIN_TIMESLICE</code>, что соответствует 10 мс. Задачи с приоритетом, используемым по умолчанию (значение параметра <emphasis>nice</emphasis>, равно 0 и отсутствует надбавка и штраф за интерактивность), получают квант времени продолжительностью 100 мс, как показано в табл. 4.1.</p>
     <empty-line/>
     <p><strong>Таблица 4.1</strong>. Продолжительности квантов времени планировщика</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Тип задания</th>
       <th align="left" valign="top">Значение параметра <emphasis>nice</emphasis></th>
       <th align="left" valign="top">Продолжительность кванта времени</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top">Вновь созданное</td>
       <td align="left" valign="top">То же, что и у родительского процесса</td>
       <td align="left" valign="top">Половина от родительского процесса</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">Минимальный приоритет</td>
       <td align="left" valign="top">+19</td>
       <td align="left" valign="top">5 мс (MIN_TIMESLICE)</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">Приоритет по умолчанию</td>
       <td align="left" valign="top">0</td>
       <td align="left" valign="top">100 мс (DEF_TIMESLICE)</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">Максимальный приоритет</td>
       <td align="left" valign="top">-20</td>
       <td align="left" valign="top">800 мс (MAX_TIMESLICE)</td>
      </tr>
     </table>
     <p>Для интерактивных задач планировщик оказывает дополнительную услугу: если задание достаточно интерактивно, то при исчерпании своего кванта времени оно будет помещено не в истекший массив приоритетов, а обратно в активный массив приоритетов. Следует вспомнить, что пересчет значений квантов времени производится путем перестановки активного и истекшего массивов приоритетов: активный массив становится истекшим, а истекший — активным. Такая процедура обеспечивает пересчет значений квантов времени, который масштабируется по времени как <emphasis>O(1)</emphasis>. С другой стороны, это может привести к тому, что интерактивное задание станет готовым к выполнению, но не получит возможности выполняться, так как оно "застряло" в истекшем массиве. Помещение интерактивных заданий снова в активный массив позволяет избежать такой проблемы. Следует заметить, что это задание не будет выполняться сразу же, а будет запланировано на выполнение по кругу вместе с другими заданиями, которые имеют такой же приоритет. Данную логику реализует функция <code>scheduler_tick()</code>, которая вызывается обработчиком прерываний таймера (обсуждается в главе 10, "Таймеры и управление временем"), как показано ниже.</p>
     <p><code>struct task_struct *task = current;</code></p>
     <p><code>struct runqueue *rq = this_rq();</code></p>
     <empty-line/>
     <p><code>if (!--task-&gt;time_slice) {</code></p>
     <p><code> if (!TASK_INTERACTIVE(task) || EXPIRED_STARVING(rq))</code></p>
     <p><code>  enqueue_task(task, rq-&gt;expired);</code></p>
     <p><code> else</code></p>
     <p><code>  enqueue_task(task, rq-&gt;active);</code></p>
     <p><code>}</code></p>
     <p>Показанный код уменьшает значение кванта времени процесса и проверяет, не стало ли это значение равным нулю. Если стало, то задание является истекшим и его необходимо поместить в один из массивов. Для этого код вначале проверяет интерактивность задания с помощью макроса <code>TASK_INTERACTIVE()</code>. Этот макрос на основании значения параметра nice рассчитывает, является ли задание "достаточно интерактивным". Чем меньше значение <emphasis>nice</emphasis> (чем выше приоритет), тем менее интерактивным должно быть задание. Задание со значением параметра <emphasis>nice</emphasis>, равным 19, никогда не может быть достаточно интерактивным для помещения обратно в активный массив. Наоборот, задание со значением <emphasis>nice</emphasis>, равным -20, должно очень сильно использовать процессор, чтобы его не поместили в активный массив. Задача со значением <emphasis>nice</emphasis>, используемым по умолчанию, т.е. равным нулю, должна быть достаточно интерактивной, чтобы быть помещенной обратно в активный массив, но это также отрабатывается достаточно четко. Следующий макрос, <code>EXPIRED_STARVING()</code>, проверяет, нет ли в истекшем массиве процессов, особенно <emphasis>нуждающихся</emphasis> в выполнении (<emphasis>starving</emphasis>), когда массивы не переключались в течение достаточно долгого времени. Если массивы давно не переключались, то обратное помещение задачи в активный массив еще больше задержит переключение, что приведет к тому, что задачи в истекшем массиве еще больше будут нуждаться в выполнении. Если это не так, то задача может быть помещена обратно в активный массив. В других случаях задача помещается в истекший массив, что встречается наиболее часто.</p>
    </section>
    <section>
     <title>
      <p>Переход в приостановленное состояние и возврат к выполнению</p>
     </title>
     <p>Приостановленное состояние задачи (состояние ожидания, заблокированное состояние, <emphasis>sleeping</emphasis>, <emphasis>blocked</emphasis>) представляет собой специальное состояние задачи, в котором задание не выполняется. Это является очень важным, так как в противном случае планировщик выбирал бы на выполнение задания, которые не "хотят" выполняться, или, хуже того, состояние ожидания должно было бы быть реализовано в виде цикла, занимающего время процессора. Задачи могут переходить в приостановленное состояние по нескольким причинам, но в любом случае— в ожидании наступления некоторого события. Событием может быть ожидание наступления некоторого момента времени, ожидание следующей порции данных при файловом вводе-выводе или другое событие в аппаратном обеспечении. Задача также может переходить в приостановленное состояние непроизвольным образом, когда она пытается захватить семафор в режиме ядра (эта ситуация рассмотрена в главе 9, "Средства синхронизации в ядре"). Обычная причина перехода в приостановленное состояние — это выполнение операций файлового ввода-вывода, например задание вызывает функцию <code>read()</code> для файла, который необходимо считать с диска. Еще один пример— задача может ожидать на ввод данных с клавиатуры. В любом случае ядро ведет себя одинаково: задача помечает себя как находящуюся в приостановленном состоянии, помещает себя в очередь ожидания (wail queue), удаляет себя из очереди выполнения и вызывает функцию <code>schedule()</code> для выбора нового процесса на выполнение. Возврат к выполнению (wake up) происходит в обратном порядке: задача помечает себя как готовую к выполнению, удаляет себя из очереди ожидания и помещает себя в очередь выполнения.</p>
     <p>Как указывалось в предыдущей главе, с приостановленным состоянием связаны два значения поля состояния процесса: <code>TASK_INTERRUPTIBLE</code> и <code>TASK_UNINTERRUPTIBLE</code>. Они отличаются только тем, что в состоянии <code>TASK_UNINTERRUPTIBLE</code> задача игнорирует сигналы, в то время как задачи в состоянии <code>TASK_INTERRUPTIBLE</code> возвращаются к выполнению преждевременно и обрабатывают пришедший сигнал. Оба типа задач, находящихся в приостановленном состоянии, помещаются в очередь ожидания, ожидают наступления некоторого события и не готовы к выполнению.</p>
     <p>Приостановленное состояние обрабатывается с помощью очередей ожидания (wait queue). Очередь ожидания — это просто список процессов, которые ожидают наступления некоторого события. Очереди ожидания в ядре представляются с помощью типа данных <code>wait_queue_head_t</code>. Они могут быть созданы статически с помощью макроса <code>DECLARE_WAIT_QUEUE_HEAD()</code> или выделены динамически с последующей инициализацией с помощью функции <code>init_waitqueue_head()</code>. Процессы помещают себя в очередь ожидания и устанавливают себя в приостановленное состояние. Когда происходит событие, связанное с очередью ожидания, процессы, находящиеся в этой очереди, возвращаются к выполнению. Важно реализовать переход в приостановленное состояние и возврат к выполнению правильно, так чтобы избежать конкуренции за ресурсы (race).</p>
     <p>Существуют простые интерфейсы для перехода в приостановленное состояние, и они широко используются. Однако использование этих интерфейсов может привести к состояниям конкуренции: возможен переход в приостановленное состояние <emphasis>после</emphasis> того, как соответствующее событие уже произошло. В таком случае задача может находиться в приостановленном состоянии неопределенное время. Поэтому рекомендуется следующий метод для перехода в приостановленное состояние в режиме ядра.</p>
     <p><code>/* пусть q — это очередь ожидания (созданная в другом месте) ,</code></p>
     <p><code>где мы хотим находиться в приостановленном состоянии */</code></p>
     <p><code>DECLARE_WAITQUEUE(wait, current);</code></p>
     <empty-line/>
     <p><code>add_wait_queue(q, &amp;wait);</code></p>
     <p><code>set_current_state(TASK_INTERRUPTIBLE); /* или TASK_UNINTERRUPTIBLE */</code></p>
     <p><code>/* переменная condition характеризует наступление события,</code></p>
     <p><code>   которого мы ожидаем */</code></p>
     <p><code>while (!condition)</code></p>
     <p><code> schedule();</code></p>
     <p><code>set_current_state(TASK_RUNNING);</code></p>
     <p><code>remove_wait_queue(q, &amp;wait);</code></p>
     <p>Опишем шаги, которые должна проделать задача для того, чтобы поместить себя в очередь ожидания.</p>
     <p>• Создать элемент очереди ожидания с помощью макроса <code>DECLARE_WAITQUEUE()</code>.</p>
     <p>• Добавить себя в очередь ожидания с помощью функции <code>add_wait_queue()</code>. С помощью этой очереди ожидания процесс будет возвращен в состояние готовности к выполнению, когда условие, на выполнение которого ожидает процесс, будет выполнено. Конечно, для этого где-то в другом месте должен быть код, который вызывает функцию <code>wake_up()</code> для данной очереди, когда произойдет соответствующее событие.</p>
     <p>• Изменить состояние процесса в значение <code>TASK_INTERRUPTIBLE</code> или <code>TASK_UNINTERRUPTIBLE</code>.</p>
     <p>• Проверить, не выполнилось ли ожидаемое условие. Если выполнилось, то больше нет необходимости переходить в приостановленное состояние. Если нет, то вызвать функцию <code>schedule()</code>.</p>
     <p>• Когда задача становится готовой к выполнению, она снова проверяет выполнение ожидаемого условия. Если условие выполнено, то производится выход из цикла. Если нет, то снова вызывается функция <code>schedule()</code> и повторяется проверка условия.</p>
     <p>• Когда условие выполнено, задача может установить свое состояние в значение <code>TASK_RUNNING</code> и удалить себя из очереди ожидания с помощью функции <code>remove_wait_queue()</code>.</p>
     <p>Если условие выполнится перед тем, как задача переходит в приостановленное состояние, то цикл прервется и задача не перейдет в приостановленное состояние по ошибке. Следует заметить, что во время выполнения тела цикла код ядра часто может выполнять и другие задачи. Например, перед выполнением функции <code>schedule()</code> может возникнуть необходимость освободить некоторые блокировки и захватить их снова после возврата из этой функции; если процессу был доставлен сигнал, то необходимо возвратить значение <code>-ERESTARTSYS</code>; может возникнуть необходимость отреагировать на некоторые другие события.</p>
     <p>Возврат к выполнению (wake up) производится с помощью функции <code>wake_up()</code>, которая возвращает все задачи, ожидающие в данной очереди, в состояние готовности к выполнению. Вначале вызывается функция <code>try_to_wake_up()</code>, которая устанавливает поле состояния задачи в значение <code>TASK_RUNNING</code>, далее вызывается функция <code>activate_task()</code> для добавления задачи в очередь выполнения и устанавливается флаг <code>need_resched</code> в ненулевое значение, если приоритет задачи, которая возвращается к выполнению, больше приоритета текущей задачи. Код, который отвечает за наступление некоторого события, обычно вызывает функцию <code>wake_up()</code> после того, как это событие произошло. Например, после того как данные прочитаны с жесткого диска, подсистема VFS вызывает функцию <code>wake_up()</code> для очереди ожидания, которая содержит все процессы, ожидающие поступления данных.</p>
     <p>Важным может быть замечание о том, что переход в приостановленное состояние часто сопровождается ложными переходами к выполнению. Это возникает потому, что переход задачи в состояние выполнения не означает, что событие, которого ожидала задача, уже наступило: поэтому переход в приостановленное состояние должен всегда выполняться в цикле, который гарантирует, что условие, на которое ожидает задача, действительно выполнилось (рис. 4.3).</p>
     <image l:href="#img_8.jpeg"/>
     <p><strong>Рис. 4.3</strong>. Переход в приостановленное состояние (sleeping) и возврат к выполнению (wake up)</p>
    </section>
    <section>
     <title>
      <p>Балансировка нагрузки</p>
     </title>
     <p>Как уже рассказывалось ранее, планировщик операционной системы Linux реализует отдельные очереди выполнения и блокировки для каждого процессора в симметричной многопроцессорной системе. Это означает, что каждый процессор поддерживает свой список процессов и выполняет алгоритм планирования только для заданий из этого списка. Система планирования, таким образом, является уникальной для каждого процессора. Тогда каким же образом планировщик обеспечивает какую-либо глобальную стратегию планирования для многопроцессорных систем? Что будет, если нарушится балансировка очередей выполнения, скажем, в очереди выполнения одного процессора будет находиться пять процессов, а в очереди другого — всего один? Решение этой проблемы выполняется системой балансировки нагрузки, которая работает с целью гарантировать, что все очереди выполнения будут сбалансированными. Система балансировки нагрузки сравнивает очередь выполнения текущего процессора с другими очередями выполнения в системе.</p>
     <p>Если обнаруживается дисбаланс, то процессы из самой загруженной очереди выполнения <emphasis>выталкиваются</emphasis> в текущую очередь, В идеальном случае каждая очередь выполнения будет иметь одинаковое количество процессов. Такая ситуация, конечно, является высоким идеалом, к которому система балансировки может только приблизиться.</p>
     <p>Система балансировки нагрузки реализована в файле <code>kernel/sched.c</code> в виде функции <code>load_balance()</code>. Эта функция вызывается в двух случаях. Она вызывается функцией <code>schedule()</code>, когда текущая очередь выполнения пуста. Она также вызывается по таймеру с периодом в 1 мс, когда система не загружена, и каждые 200 мс в другом случае. В однопроцессорной системе функция <code>load_balance()</code> не вызывается никогда, в действительности она даже не компилируется в исполняемый образ ядра, питому что в системе только одна очередь выполнения и никакой балансировки не нужно.</p>
     <p>Функция балансировки нагрузки вызывается при заблокированной очереди выполнения текущего процессора, прерывания при этом также запрещены, чтобы защитить очередь выполнения от конкурирующего доступа. В том случае, когда функция <code>load_balance()</code> вызывается из функции <code>schedule()</code>, цель ее вызова вполне ясна, потому что текущая очередь выполнения пуста и нахождение процессов в других очередях с последующим их проталкиванием в текущую очередь позволяет получить преимущества. Когда система балансировки нагрузки активизируется посредством таймера, то ее задача может быть не так очевидна. В данном случае это необходимо для устранения любого дисбаланса между очередями выполнения, чтобы поддерживать их в почти одинаковом состоянии, как показано на рис. 4.4.</p>
     <image l:href="#img_9.jpeg"/>
     <p><strong>Рис. 4.4</strong>. Система балансировки нагрузки</p>
     <p>Функция <code>load_balance()</code> и связанные с ней функции сравнительно большие и сложные, хотя шаги, которые они предпринимают, достаточно ясны.</p>
     <p>• Функция <code>load_balance()</code> вызывает функцию <code>find_busiest_queue()</code> для определения наиболее загруженной очереди выполнения. Другими словами — очередь с наибольшим количеством процессов в ней. Если нет очереди выполнения, количество процессов в которой на 25% больше, чем в дайной очереди, то функция <code>find_busiest_queue()</code> возвращает значение <code>NULL</code> и происходит возврат из функции <code>load_balance()</code>. В другом случае возвращается указатель на самую загруженную очередь.</p>
     <p>• Функция <code>load_balance()</code> принимает решение о том, из какого массива приоритетов самой загруженной очереди будут проталкиваться процессы. Истекший массив является более предпочтительным, так как содержащиеся в нем задачи не выполнялись достаточно долгое время и, скорее всего, не находятся в кэше процессора (т.е. не активны в кэше, not "cache hot"). Если истекший массив приоритетов пуст, то ничего не остается, как использовать активный массив.</p>
     <p>• Функция <code>load_balance()</code> находит непустой список заданий, соответствующий самому высокому приоритету (с самым маленьким номером), так как важно более равномерно распределять задания с высоким приоритетом, чем с низким.</p>
     <p>• Каждое задание с данным приоритетом анализируется для определения задания, которое не выполняется, не запрещено для миграции из-за процессорной привязки и не активно в кэше. Если найдена задача, которая удовлетворяет этому критерию, то вызывается функция <code>pull_task()</code> для проталкивания этой задачи из наиболее загруженной очереди в данную очередь.</p>
     <p>• Пока очереди выполнения остаются разбалансированными, предыдущие два шага повторяются и необходимое количество заданий проталкивается из самой загруженной очереди выполнения в данную очередь выполнения. В конце концов, когда дисбаланс устранен, очередь выполнения разблокируется и происходит возврат из функции <code>load_balance()</code>.</p>
     <p>Далее показана функция <code>load_balance()</code>, немного упрощенная, но содержащая все важные детали.</p>
     <p><code>static int load_balance(int this_cpu, runqueue_t *this_rq,</code></p>
     <p><code> struct sched_domain *sd, enum idle_type idle) {</code></p>
     <p><code> struct sched_group *group;</code></p>
     <p><code> runqueue_t *busiest;</code></p>
     <p><code> unsigned long imbalance;</code></p>
     <p><code> int nr_moved;</code></p>
     <empty-line/>
     <p><code> spin_lock(&amp;this_rq-&gt;lock);</code></p>
     <empty-line/>
     <p><code> group = find_busiest_group(sd, this_cpu, &amp;imbalance, idle);</code></p>
     <p><code> if (!group)</code></p>
     <p><code>  goto out_balanced;</code></p>
     <p><code> busiest = find_busiest_queue(group);</code></p>
     <p><code> if (!busiest)</code></p>
     <p><code>  goto out_balanced;</code></p>
     <empty-line/>
     <p><code> nr_moved = 0;</code></p>
     <p><code> if (busiest-&gt;nr_running &gt; 1) {</code></p>
     <p><code>  double_lock_balance(this_rq, busiest);</code></p>
     <p><code>  nr_moved = move_tasks(this_rq, this_cpu, busiest,</code></p>
     <p><code>   imbalance, sd, idle);</code></p>
     <p><code>  spin_unlock(&amp;busiest-&gt;lock);</code></p>
     <p><code> }</code></p>
     <p><code> spin_unlock(&amp;this_rq-&gt;lock);</code></p>
     <empty-line/>
     <p><code> if (!nr_moved) {</code></p>
     <p><code>  sd-&gt;nr_balance_failed++;</code></p>
     <empty-line/>
     <p><code>  if (unlikely(sd-&gt;nr_balance_failed &gt; sd-&gt;cache_nice_tries+2)) {</code></p>
     <p><code>   int wake = 0;</code></p>
     <empty-line/>
     <p><code>   spin_lock(&amp;busiest-&gt;lock);</code></p>
     <p><code>   if (!busiest-&gt;active_balance) {</code></p>
     <p><code>    busiest-&gt;active_balance = 1;</code></p>
     <p><code>    busiest-&gt;push_cpu = this_cpu;</code></p>
     <p><code>    wake = 1;</code></p>
     <p><code>   }</code></p>
     <p><code>   spin_unlock(&amp;busiest-&gt;lock);</code></p>
     <p><code>   if (wake)</code></p>
     <p><code>    wake_up_process(busiest-&gt;migration_thread);</code></p>
     <p><code>   sd-&gt;nr_balance_failed = sd-&gt;cache_nice_tries;</code></p>
     <p><code>  }</code></p>
     <p><code> } else</code></p>
     <p><code>  sd-&gt;nr_balance_failed = 0;</code></p>
     <p><code> sd-&gt;balance_interval = sd-&gt;min_interval;</code></p>
     <p><code> return nr_moved;</code></p>
     <p><code>out_balanced:</code></p>
     <p><code> spin_unlock(&amp;this_rq-&gt;lock);</code></p>
     <empty-line/>
     <p><code> if (sd-&gt;balance_interval &lt; sd-&gt;max_interval)</code></p>
     <p><code>  sd-&gt;balance_interval *= 2;</code></p>
     <empty-line/>
     <p><code> return 0;</code></p>
     <p><code>}</code></p>
    </section>
   </section>
   <section>
    <title>
     <p>Вытеснение и переключение контекста</p>
    </title>
    <section>
     <p>Переключение контекста — это переключение от одной, готовой к выполнению задачи к другой. Это переключение производится с помощью функции <code>context_switch()</code>, определенной в файле <code>kernel/sched.c</code>. Данная функция вызывается функцией <code>schedule()</code>, когда новый процесс выбирается для выполнения. При этом выполняются следующие шаги.</p>
     <p>• Вызывается функция <code>switch_mm()</code>, которая определена в файле <code>include/asm/mmu_context.h</code> и предназначена для переключения от виртуальной памяти старого процесса к виртуальной памяти нового процесса.</p>
     <p>• Вызывается функция <code>switch_to()</code>, определенная в файле <code>include/asm/system.h</code>, для переключения от состояния процессора предыдущего процесса к состоянию процессора нового процесса. Эта процедура включает восстановление информации стека ядра и регистров процессора.</p>
     <p>Ядро должно иметь информацию о том, когда вызывать функцию <code>schedule()</code>. Если эта функция будет вызываться только тогда, когда программный код вызывает ее явно, то пользовательские программы могут выполняться неопределенное время. Поэтому ядро поддерживает флаг <code>need_resched</code> для того, чтобы сигнализировать, необходимо ли вызывать функцию <code>schedule()</code> (табл. 4.2). Этот флаг устанавливается функцией <code>scheduler_tick()</code>, когда процесс истрачивает свой квант времени, и функцией <code>try_to_wake_up()</code>, когда процесс с приоритетом более высоким, чем у текущего процесса, возвращается к выполнению. Ядро проверяет значение этого флага, и если он установлен, то вызывается функция <code>schedule()</code> для переключения на новый процесс. Этот флаг является сообщением ядру о том, что планировщик должен быть активизирован по возможности раньше, потому что другой процесс должен начать выполнение.</p>
     <empty-line/>
     <p><strong>Таблица 4.2</strong>. Функции для управления флагом need_resched</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Функция</th>
       <th align="left" valign="top">Назначение</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>set_tsk_need_resched(task)</code></td>
       <td align="left" valign="top">Установить флаг <code>need_resched</code> для данного процесса</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>clear_tsk_need_resched(task)</code></td>
       <td align="left" valign="top">Очистить флаг <code>need_resched</code> для данного процесса</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>need_resched()</code></td>
       <td align="left" valign="top">Проверить значение флага <code>need_resched</code> для данного процесса. Возвращается значение <code>true</code>, если этот флаг установлен, и <code>false</code>, если не установлен</td>
      </tr>
     </table>
     <p>Во время переключения в пространство пользователи или при возврате из прерывания, значение флага <code>need_resched</code> проверяется. Если он установлен, то ядро активизирует планировщик перед тем, как продолжить работу.</p>
     <p>Этот флаг не является глобальной переменной, так как обращение к дескриптору процесса получается более быстрым, чем обращение к глобальным данным (из-за скорости обращения к переменной <code>current</code> и потому, что соответствующие данные могут находиться в кэше). Исторически, этот флаг был глобальным в ядрах до серии 2.2. В ядрах серий 2.2 и 2.4 этот флаг принадлежал структуре <code>task_struct</code> и имел тип <code>int</code>. В серии ядер 2.6 этот флаг перемещен в один определенный бит специальной переменной флагов структуры <code>thread_info</code>. Легко видеть, что разработчики ядра никогда не могут быть всем довольны.</p>
    </section>
    <section>
     <title>
      <p>Вытеснение пространства пользователя</p>
     </title>
     <p>Вытеснение пространства пользователя (user preemption) происходит в тот момент, когда ядро собирается возвратить управление режиму пользователя, при этом устанавливается флаг <code>need_resched</code> и, соответственно, активизируется планировщик. Когда ядро возвращает управление в пространство пользователя, то оно находится в безопасном и "спокойном" состоянии. Другими словами, если продолжение выполнения текущего задания является безопасным, то безопасным будет также и выбор нового задания для выполнения. Поэтому когда ядро готовится возвратить управление в режим пользователя или при возврате из прерывания или после системного вызова, происходит проверка флага <code>need_resched</code>. Если этот флаг установлен, то активизируется планировщик и выбирает новый, более подходящий процесс для исполнения. Как процедура возврата из прерывания, так и процедура возврата из системного вызова являются зависимыми от аппаратной платформы и обычно реализуются на языке ассемблера в файле <code>entry.S</code> (этот файл, кроме кода входа в режим ядра, также содержит и код выхода из режима ядра). Если коротко, то вытеснение пространства пользователя может произойти в следующих случаях.</p>
     <p>• При возврате в пространство пользователя из системного вызова.</p>
     <p>• При возврате в пространство пользователя из обработчика прерывания.</p>
    </section>
    <section>
     <title>
      <p>Вытеснение пространства ядра</p>
     </title>
     <p>Ядро операционной системы Linux, в отличие от ядер большинства вариантов ОС Unix, является полностью преемптивным (вытесняемым, preemptible). В непреемптивных ядрах код ядра выполняется до завершения. Иными словами, планировщик не может осуществить планирование для выполнения другого задания, пока какое-либо задание выполняется в пространстве ядра — код ядра планируется на выполнение кооперативно, а не посредством вытеснения. Код ядра выполняется до тех пор, пока он не завершится (возвратит управление в пространство пользователя) или пока явно не заблокируется. С появлением серии ядер 2.6, ядро Linux стало преемптивным: теперь есть возможность вытеснить задание в любой момент, конечно, пока ядро находится в состоянии, когда безопасно производить перепланирование выполнения.</p>
     <p>В таком случае когда же безопасно производить перепланирование? Ядро способно вытеснить задание, работающее в пространстве ядра, когда это задание не удерживает блокировку. Иными словами, блокировки используются в качестве маркеров тех областей, в которые задание не может быть вытеснено. Ядро рассчитано на многопроцессорность (SMP-safe), поэтому если блокировка не удерживается, то код ядра является реентерабельным и его вытеснять безопасно.</p>
     <p>Первое изменение, внесенное для поддержки вытеснения пространства ядра, — это введение счетчика преемптивности <code>preempt_count</code> в структуру <code>thread_info</code> каждого процесса. Значение этого счетчика вначале равно нулю и увеличивается на единицу при каждом захвате блокировки, а также уменьшается на единицу при каждом освобождении блокировки. Когда значение счетчика равно нулю— ядро является вытесняемым. При возврате из обработчика прерывания, если возврат выполняется в пространство ядра, ядро проверяет значения переменных <code>need_resched</code> и <code>preempt_count</code>. Если флаг <code>need_resched</code> установлен и значение счетчика preempt_count равно нулю, значит, более важное задание готово к выполнению и выполнять вытеснение безопасно. Далее активизируется планировщик. Если значение счетчика <code>preempt_count</code> не равно нулю, значит, удерживается захваченная блокировка и выполнять вытеснение не безопасно. В таком случае возврат из обработчика прерывания происходит в текущее выполняющееся задание. Когда освобождаются все блокировки, удерживаемые текущим заданием, значение счетчика <code>preempt_count</code> становится равным нулю. При этом код, осуществляющий освобождение блокировки, проверяет, не установлен ли флаг <code>need_resched</code>. Если установлен, то активизируется планировщик. Иногда коду ядра необходимо иметь возможность запрещать или разрешать вытеснение в режиме ядра, что будет рассмотрено в главе 9.</p>
     <p>Вытеснение пространства ядра также может произойти явно, когда задача блокируется в режиме ядра или явно вызывается функция <code>schedule()</code>. Такая форма преемптивности ядра всегда поддерживалась, так как в этом случае нет необходимости в дополнительной логике, которая бы давала возможность убедиться, что вытеснение проводить безопасно. Предполагается, что если код явно вызывает функцию <code>schedule()</code>, то точно известно, что перепланирование производить безопасно.</p>
     <p>Вытеснение пространства ядра может произойти в следующих случаях.</p>
     <p>• При возврате из обработчика прерывания в пространство ядра.</p>
     <p>• Когда код ядра снова становится преемптивным.</p>
     <p>• Если задача, работающая в режиме ядра, явно вызывает функцию <code>schedule()</code>.</p>
     <p>• Если задача, работающая в режиме ядра, переходит в приостановленное состояние, т.е. блокируется (что приводит к вызову функции <code>schedule()</code>).</p>
    </section>
   </section>
   <section>
    <title>
     <p>Режим реального времени</p>
    </title>
    <p>Операционная система Linux обеспечивает две стратегии планирования в режиме реального времени (real-lime): <code>SCHED_FIFO</code> и <code>SCHED_RR</code>. Стратегия планирования <code>SCHED_OTHER</code> является обычной стратегией планирования, т.е. стратегий планирования не в режиме реального времени. Стратегия <code>SCHED_FIFO</code> обеспечивает простой алгоритм планирования по идеологии "первым вошел — первым обслужен" (first-in first-out, FIFO) без квантов времени. Готовое к выполнению задание со стратегией планирования <code>SCHED_FIFO</code> всегда будет планироваться на выполнение перед всеми заданиями со стратегией планирования <code>SCHED_OTHER</code>. Когда задание со стратегией <code>SCHED_FIFO</code> становится готовым к выполнению, то оно будет продолжать выполняться до тех пор, пока не заблокируется или пока явно не отдаст управление. Две или более задач с одинаковым приоритетом, имеющие стратегию планирования <code>SCHED_FIFO</code>, будут планироваться на выполнение по круговому алгоритму (round-robin). Если задание, имеющее стратегию планирования <code>SCHED_FIFO</code>, является готовым к выполнению, то все задачи с более низким приоритетом не могут выполняться до тех пор, пока это задание не завершится.</p>
    <p>Стратегия <code>SCHED_RR</code> аналогична стратегии <code>SCHED_FIFO</code>, за исключением того, что процесс может выполняться только до тех пор, пока не израсходует предопределенный ему квант времени. Таким образом, стратегия <code>SCHED_RR</code> — это стратегия <code>SCHED_FIFO</code> с квантами времени, т.е. круговой алгоритм планирования (round-robin) реального времени. Когда истекает квант времени процесса со стратегией планирования SCHED_RR, то другие процессы с таким же приоритетом планируются по круговому алгоритму. Квант времени используется только для того, чтобы перепланировать выполнение заданий с таким же приоритетом. Так же как в случае стратегии <code>SCHED_FIFO</code>, процесс с более высоким приоритетом сразу же вытесняет процессы с более низким приоритетом, а процесс с более низким приоритетом никогда не сможет вытеснить процесс со стратегией планирования <code>SCHED_RR</code>, даже если у последнего истек квант времени.</p>
    <p>Обе стратегии планирования реального времени используют статические приоритеты. Ядро не занимается расчетом значений динамических приоритетов для задач реального времени. Это означает, что процесс, работающий в режиме реального времени, всегда сможет вытеснить процесс с более низким значением приоритета.</p>
    <p>Стратегии планирования реального времени в операционной системе Linux обеспечивают так называемый мягкий режим реального времени (soft real-time). Мягкий режим реального времени обозначает, что ядро пытается планировать выполнение пользовательских программ в границах допустимых временных сроков, но не всегда гарантирует выполнение этой задачи. В противоположность этому операционные системы с жестким режимом реального времени (hard real-time) всегда гарантируют выполнение всех требований по планированию выполнения процессов в заданных пределах. Операционная система Linux не может гарантировать возможности планирования задач реального времени. Тем не менее стратегия планирования ОС Linux гарантирует, что задачи реального времени будут выполняться всякий раз, когда они готовы к выполнению. Хотя в ОС Linux и отсутствуют средства, гарантирующие работу в жестком режиме реального времени, тем не менее производительность планировщика ОС Linux в режиме реального времени достаточно хорошая. Ядро серии 2.6 в состоянии удовлетворить очень жестким временным требованиям.</p>
    <p>Приоритеты реального времени лежат в диапазоне от 1 до <code>MAX_RT_PRIO</code> минус 1, По умолчанию значение константы <code>MAX_RT_PRIO</code> равно 100, поэтому диапазон значений приоритетов реального времени по умолчанию составляет от 1 до 99. Это пространство приоритетов объединяется с пространством значений параметра nice для стратегии планирования <code>SCHED_OTHER</code>, которое соответствует диапазону приоритетов от значения <code>MAX_RT_PRIO</code> до значения (<code>MAX_RT_PRIO</code>+40). По умолчанию это означает, что диапазон значений параметра nice от -20 до +19 взаимно однозначно отображается в диапазон значений приоритетов от 100 до 139.</p>
   </section>
   <section>
    <title>
     <p>Системные вызовы для управления планировщиком</p>
    </title>
    <section>
     <p>Операционная система Linux предоставляет семейство системных вызовов для управления параметрами планировщика. Эти системные вызовы позволяют манипулировать приоритетом процесса, стратегией планирования и процессорной привязкой, а также предоставляют механизм, с помощью которого можно явно <emphasis>передать</emphasis> процессор (<emphasis>yield</emphasis>) в использование другим заданиям.</p>
     <p>Существуют различные книги, а также дружественные страницы системного руководства (man pages), которые предоставляют информацию об этих системных вызовах (реализованных в библиотеке С без особых интерфейсных оболочек, а прямым вызовом системной функции). В табл. 4.3 приведен список этих функций с кратким описанием. О том, как системные вызовы реализованы в ядре, рассказывается в главе 5, "Системные вызовы".</p>
     <empty-line/>
     <p><strong>Таблица 4.3</strong>. Системные вызовы для управления планировщиком</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Системный вызов</th>
       <th align="left" valign="top">Описание</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>nice()</code></td>
       <td align="left" valign="top">Установить значение параметра <code>nice</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>sched_setscheduler()</code></td>
       <td align="left" valign="top">Установить стратегию планирования</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>sched_getscheduler()</code></td>
       <td align="left" valign="top">Получить стратегию планирования</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>sched_setparam()</code></td>
       <td align="left" valign="top">Установить значение приоритета реального времени</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>sched_getparam()</code></td>
       <td align="left" valign="top">Получить значение приоритета реального времени</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>sched_get_priority_max()</code></td>
       <td align="left" valign="top">Получить максимальное значение приоритета реального времени</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>sched_get_priority_min()</code></td>
       <td align="left" valign="top">Получить минимальное значение приоритета реального времени</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>sched_rr_get_interval()</code></td>
       <td align="left" valign="top">Получить продолжительность кванта времени</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>sched_setaffinity()</code></td>
       <td align="left" valign="top">Установить процессорную привязку</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>sched_getaffinity()</code></td>
       <td align="left" valign="top">Получить процессорную привязку</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>sched_yield()</code></td>
       <td align="left" valign="top">Временно передать процессор другим заданиям</td>
      </tr>
     </table>
    </section>
    <section>
     <title>
      <p>Системные вызовы, связанные с управлением стратегией и приоритетом</p>
     </title>
     <p>Системные вызовы <code>sched_setscheduler()</code> и <code>sched_getcheduler()</code> позволяют соответственно установить и получить значение стратегии планирования и приоритета реального времени для указанного процесса. Реализация этих функций, так же как и для большинства остальных системных вызовов, включает большое количество разнообразных проверок, инициализаций и очистку значений аргументов. Полезная работа включает в себя только чтение или запись полей <code>policy</code> и <code>rt_priority</code> структуры <code>task_struct</code> указанного процесса.</p>
     <p>Системные вызовы <code>sched_setparam()</code> и <code>sched_getparam()</code> позволяют установить и получить значение приоритета реального времени для указанного процесса. Последняя функция просто возвращает значение поля <code>rt_priority</code>, инкапсулированное в специальную структуру <code>sched_param</code>. Вызовы <code>sched_get_priority_max()</code> и <code>sched_get_priority_min()</code> возвращают соответственно максимальное и минимальное значение приоритета реального времени для указанной стратегии планирования. Максимальное значение приоритета для стратегий планирования реального времени равно (<code>MAX_USER_RT_PRIO-1</code>), а минимальное значение — 1.</p>
     <p>Для обычных задач функция <code>nice()</code> увеличивает значение статического приоритета вызывающего процесса на указанную в аргументе величину. Только пользователь root может указывать отрицательные значения, т.е. уменьшать значение параметра nice и соответственно увеличивать приоритет. Функция <code>nice()</code> вызывает функцию ядра <code>set_user_nice()</code>, которая устанавливает значение полей <code>static_prio</code> и <code>prio</code> структуры <code>task_struct</code>.</p>
    </section>
    <section>
     <title>
      <p>Системные вызовы управления процессорной привязкой</p>
     </title>
     <p>Планировщик ОС Linux может обеспечивать жесткую процессорную привязку (processor affinity). Хотя планировщик пытается обеспечивать мягкую или естественную привязку путем удержания процессов на одном и том же процессоре, он также позволяет пользователям сказать: "Эти задания должны выполняться только на указанных процессорах независимо ни от чего". Значение жесткой привязки хранится в виде битовой маски в поле <code>cpus_allowed</code> структуры <code>task_struct</code>. Эта битовая маска содержит один бит для каждого возможного процессора в системе. По умолчанию все биты установлены в значение 1, и поэтому процесс потенциально может выполняться на всех процессорах в системе. Пользователь с помощью функции <code>sched_setaffinity()</code> может указать другую битовую маску с любой комбинацией установленных битов. Аналогично функция <code>sched_getaffinity()</code> возвращает текущее значение битовой маски <code>cpus_allowed</code>.</p>
     <p>Ядро обеспечивает жесткую привязку очень простым способом. Во-первых, только что созданный процесс наследует маску привязки от родительского процесса. Поскольку родительский процесс выполняется на дозволенном процессоре, то и порожденный процесс также будет выполняться на дозволенном процессоре. Во-вторых, когда привязка процесса изменяется, ядро использует <emphasis>миграционные потоки</emphasis> (<emphasis>migration threads</emphasis>) для проталкивания задания на дозволенный процессор. Следовательно, процесс всегда выполняется только на том процессоре, которому соответствует установленный бит в поле <code>cpus_allowed</code> дескриптора процесса.</p>
    </section>
    <section>
     <title>
      <p>Передача процессорного времени</p>
     </title>
     <p>Операционная система Linux предоставляет системный вызов <code>sched_yield()</code> как механизм, благодаря которому процесс может явно передать процессор под управление другим ожидающим процессам. Этот вызов работает путем удаления процесса из активного массива приоритетов (где он в данный момент находится, потому что процесс выполняется) с последующим помещением этого процесса в истекший массив. Получаемый аффект состоит не только в том, что процесс вытесняется и становится последним в списке заданий с соответствующим приоритетом, а также в том, что помещение процесса в истекший массив гарантирует, что этот процесс не будет выполняться некоторое время. Так как задачи реального времени никогда не могут быть помещены в истекший массив, они составляют специальный случай. Поэтому они только перемещаются в конец списка заданий с таким же значением приоритета (и не помещаются в истекший массив). В более ранних версиях ОС. Linux семантика вызова <code>sched_yield()</code> была несколько иной. В лучшем случае задание только лишь перемещалось в конец списка заданий с данным приоритетом. Сегодня для пользовательских программ и даже для потоков пространства ядра должна быть полная уверенность в том, что действительно необходимо отказаться от использования процессора, перед тем как ввязывать функцию <code>sched_yield()</code>.</p>
     <p>В коде ядра, для удобства, можно вызывать функцию <code>yield()</code>, которая проверяет, что состояние задачи равно <code>TASK_RUNNING</code>, а после этого вызывает функцию <code>sched_yield()</code>. Пользовательские программы должны использовать системный вызов <code>sched_yield()</code>.</p>
    </section>
   </section>
   <section>
    <title>
     <p>В завершение о планировщике</p>
    </title>
    <p>Планировщик выполнения процессов является важной частью ядра, так как выполнение процессов (по крайней мере, для большинства из нас) — это основное использование компьютера. Тем не менее, удовлетворение всем требованиям, которые предъявляются к планировщику — не тривиальная задача. Большое количество готовых к выполнению процессов, требования масштабируемости, компромисс между производительностью и временем реакции, а также требования для различных типов загрузки системы приводят к тому, что тяжело найти алгоритм, который подходит для всех случаев. Несмотря на это, новый планировщик процессов ядра Linux приближается к тому, чтобы удовлетворить всем этим требованиям и обеспечить оптимальное решение для всех случаев, включая отличную масштабируемость и привлекательную реализацию.</p>
    <p>Проблемы, которые остались, включают возможность точной настройки (или даже полную замену) алгоритма оценки степени интерактивности задания, который приносит много пользы, когда работает правильно, и приносит много неудобств, когда выполняет предсказания неверно. Работа над альтернативными реализациями продолжается. Когда-нибудь мы увидим новую реализацию в основном ядре.</p>
    <p>Улучшение поведения планировщика для NUMA систем (систем с неоднородным доступом к памяти) становится все более актуальной задачей, так как количество машин на основе NUMA-платформ возрастает. Поддержка <emphasis>доменов планирования</emphasis> (<emphasis>scheduler domain</emphasis>) — абстракция, которая позволяет описать топологию процессов; она была включена в ядро 2.6 в одной из первых версий.</p>
    <p>Эта глава посвящена теории планирования процессов, а также алгоритмам и специфической реализации планировщика ядра Linux. В следующей главе будет рассмотрен основной интерфейс, который предоставляется ядром для выполняющихся процессов, — системные вызовы.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 5</p>
    <p>Системные вызовы</p>
   </title>
   <section>
    <p>Ядро операционной системы предоставляет набор интерфейсов, благодаря которым процессы, работающие в пространстве пользователя, могут взаимодействовать с системой. Эти интерфейсы предоставляют пользовательским программам доступ к аппаратному обеспечению и другим ресурсам операционной системы. Интерфейсы работают как посыльные между прикладными программами и ядром, при этом пользовательские программы выдвигают различные запросы, а ядро выполняет их (или приказывает убираться подальше). Тот факт, что такие интерфейсы существуют, а также то, что прикладные программы не имеют права непосредственно делать все, что им заблагорассудится, является ключевым моментом для обеспечения стабильности системы, а также позволяет избежать крупных беспорядков.</p>
    <p>Системные вызовы являются прослойкой между аппаратурой и процессами, работающими в пространстве пользователя. Эта прослойка служит для трех главных целей. Во-первых, она обеспечивает абстрактный интерфейс между аппаратурой и пространством пользователя. Например, при записи или чтении данных из файла прикладным программам нет дела до типа жесткого диска, до среды, носителя информации, и даже до типа файловой системы, на которой находится файл. Во-вторых, системные вызовы гарантируют безопасность и стабильность системы. Так как ядро работает посредником между ресурсами системы и пространством пользователя, оно может принимать решение о предоставлении доступа в соответствии с правами пользователей и другими критериями. Например, это позволяет предотвратить возможность неправильного использования аппаратных ресурсов программами, воровство каких-либо ресурсов у других программ, а также возможность нанесения вреда системе. И наконец, один общий слой между пространством пользователя и остальной системой позволяет осуществить виртуальное представление процессов, как обсуждается в главе 3, "Управление процессами".</p>
    <p>Если бы приложения имели свободный доступ ко всем ресурсам системы без помощи ядра, то было бы почти невозможно реализовать многозадачность и виртуальную память. В операционной системе Linux системные вызовы являются единственным средством, благодаря которому пользовательские программы могут связываться с ядром; они являются единственной законной точкой входа в ядро. Другие интерфейсы ядра, такие как файлы устройств или файлы на файловой системе <code>/proc</code>, в конечном счете сводятся к обращению через системные вызовы.</p>
    <p>Интересно, что в ОС Linux реализовано значительно меньше системных вызовов, чем во многих других операционных системах<a l:href="#n23" type="note">[23]</a>.</p>
    <p>В этой главе рассказывается о роли и реализации системных вызовов в операционной системе Linux.</p>
   </section>
   <section>
    <title>
     <p>API, POSIX и библиотека С</p>
    </title>
    <p>Обычно прикладные программы не разрабатываются с непосредственным использованием системных вызовов, при этом используются программные интерфейсы приложений (Application Programing Interface, API). Это является важным, так как в таком случае нет необходимости в корреляции между интерфейсами, которые используют приложения, и интерфейсами, которые предоставляет ядро. Различные API определяют набор программных интерфейсов, которые используются приложениями. Эти интерфейсы могут быть реализованы с помощью одного системного вызова, нескольких системных вызовов, а также вообще без использования системных вызовов. В действительности, может существовать один и тот же программный интерфейс приложений для различных операционных систем, в то время как реализация этих API может для разных ОС существенно отличаться.</p>
    <p>Один из наиболее популярных программных интерфейсов приложений в мире Unix-подобных систем базируется на стандарте POSIX. Технически стандарт POSIX включает в себя набор стандартов IEEE<a l:href="#n24" type="note">[24]</a>, целью которого является обеспечение переносимого стандарта операционной системы, приблизительно базирующегося на ОС Unix. ОС Linux соответствует стандарту POSIX.</p>
    <p>Стандарт POSIX является хорошим примером соотношения между интерфейсом API и системными вызовами. Для большинства Unix-подобных операционных систем вызовы интерфейса API, определенные в стандарте POSIX, сильно коррелируют с системными вызовами. Конечно, стандарт POSIX создавался для того, чтобы сделать те интерфейсы, которые предоставляли ранние версии ОС Unix, похожими между собой. С другой стороны, некоторые операционные системы, далекие от OS Unix, такие как Windows NT, предоставляют библиотеки, совместимые со стандартом POSIX.</p>
    <p>Частично интерфейс к системным вызовам в операционной системе Linux, так же как и в большинстве Unix-систем, обеспечивается библиотекой функций на языке С. Библиотека С реализует главный программный интерфейс приложений для Unix-систем, что включает стандартную библиотеку языка программирования С и интерфейс системных вызовов. Библиотека С используется всеми программами, написанными на языке программирования С, а также, в связи со свойствами языка С, может быть легко использована для программ, написанных на других языках программирования.</p>
    <image l:href="#img_10.jpeg"/>
    <p><strong>Рис. 5.1</strong>. Взаимоотношения между приложением, библиотекой С и ядром на примере вызова функции <code>printf()</code></p>
    <p>Дополнительно библиотека функций С также представляет большую часть API-стандарта POSIX.</p>
    <p>С точки зрения прикладного программиста, системные вызовы не существенны: все, с чем работает программист, — это интерфейс API. С другой стороны, ядро имеет отношение только к системным вызовам: все, что делают библиотечные вызовы и пользовательские программы с системными вызовами, — это для ядра не существенно. Тем не менее с точки зрения ядра все-таки важно помнить о потенциальных возможностях использования системного вызова для того, чтобы по возможности поддерживать универсальность и гибкость системных вызовов.</p>
    <p>Общий девиз для интерфейсов ОС Unix — это "предоставлять механизм, а не стратегию". Другими словами, системные вызовы существуют для того, чтобы обеспечить определенную функцию в наиболее абстрактном смысле. А то, каким образом используется эта функция, ядра не касается.</p>
   </section>
   <section>
    <title>
     <p>Вызовы syscall</p>
    </title>
    <section>
     <p>Системные вызовы (часто называемые <emphasis>syscall</emphasis> в ОС Linux) обычно реализуются в виде вызова функции. Для них могут быть определены один или более аргументов (inputs), которые могут приводить к тем или иным побочным эффектам<a l:href="#n25" type="note">[25]</a>, например к записи данных в файл или к копированию некоторых данных в область памяти, на которую указывает переданный указатель. Системные вызовы также имеют возвращаемое значение типа <code>long</code><a l:href="#n26" type="note">[26]</a>, которое указывает на успешность выполнения операции или на возникшие ошибки. Обычно, но не всегда, возвращение отрицательного значения указывает на то, что произошла ошибка. Возвращение нулевого значения обычно (но не всегда) указывает на успешность выполнения операции. Системные вызовы ОС Unix в случае ошибки записывают специальный код ошибки в глобальную переменную <code>errno</code>. Значение этой переменной может быть переведено в удобочитаемую форму с помощью библиотечной функции <code>perror()</code>.</p>
     <p>Системные вызовы, конечно, имеют определенное поведение. Например, системный вызов <code>getpid()</code> определен для того, чтобы возвращать целочисленное значение, равное значению идентификатора <code>PID</code> текущего процесса. Реализация этой функции в ядре очень проста.</p>
     <p><code>asmlinkage long sys_getpid(void) {</code></p>
     <p><code> return current-&gt;tgid;</code></p>
     <p><code>)</code></p>
     <p>Следует заметить, что в определении ничего не говорится о способе реализации. Ядро должно обеспечить необходимую функциональность системного вызова, но реализация может быть абсолютно свободной, главное, чтобы результат был правильный. Конечно, рассматриваемый системный вызов в действительности является таким же простым, как и показано, и существует не так уж много различных вариантов для его реализации (на самом деле более простого метода не существует)<a l:href="#n27" type="note">[27]</a>.</p>
     <p>Даже из такого примера можно сделать пару наблюдений, которые касаются системных вызовов. Во-первых, следует обратить внимание на модификатор <code>asmlinkage</code> в объявлении функции. Это волшебное слово дает компилятору информацию о том, что обращение к аргументам этой функции должно производиться только через стек. Для всех системных вызовов использование этого модификатора является обязательным. Во-вторых, следует обратить внимание, что системный вызов <code>getpid()</code> объявлен в ядре, как <code>sys_getpid()</code>. Это соглашение о присваивании имен используется для всех системных вызовов операционной системы Linux: системный вызов <code>bar()</code> должен быть реализован с помощью функции <code>sys_bar()</code>.</p>
    </section>
    <section>
     <title>
      <p>Номера системных вызовов</p>
     </title>
     <p>Каждому системному вызову операционной системы Linux присваивается <emphasis>номер системного вызова</emphasis> (<emphasis>syscall number</emphasis>). Этот уникальный номер используется для обращения к определенному системному вызову. Когда процесс выполняет системный вызов из пространства пользователя, процесс не обращается к системному вызову по имени.</p>
     <p>Номер системного вызова является важным атрибутом. Однажды назначенный номер не должен меняться никогда, иначе это нарушит работу уже скомпилированных прикладных программ. Если системный вызов удаляется, то соответствующий номер не может использоваться повторно. В операционной системе Linux предусмотрен так называемый "не реализованный" ("not implemented") системный вызов — функция <code>sys_ni_syscall()</code>, которая не делает ничего, кроме того, что возвращает значение, равное <code>-ENOSYS</code>, — код ошибки, соответствующий неправильному системному вызову. Эта функция служит для "затыкания дыр" в случае такого редкого событии, как удаление системного вызова.</p>
     <p>Ядро поддерживает список зарегистрированных системных вызовов в таблице системных вызовов. Эта таблица хранится в памяти, на которую указывает переменная <code>sys_call_table</code>. Данная таблица зависит от аппаратной платформы и обычно определяется в файле <code>entry.S</code>. В таблице системных вызовов каждому уникальному номеру системного вызова назначается существующая функция <code>syscall</code>.</p>
    </section>
    <section>
     <title>
      <p>Производительность системных вызовов</p>
     </title>
     <p>Системные вызовы в операционной системе Linux работают быстрее, чем во многих других операционных системах. Это отчасти связано с невероятно малым временем переключения контекста. Переход в режим ядра и выход из него являются хорошо отлаженным процессом и простым делом. Другой фактор — это простота как механизма обработки системных вызовов, так и самих системных вызовов.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Обработка системных вызовов</p>
    </title>
    <section>
     <p>Приложения пользователя не могут непосредственно выполнять код ядра. Они не могут просто вызвать функцию, которая существует в пространстве ядра, так как ядро находится в защищенной области памяти. Если программы смогут непосредственно читать и писать в адресное пространство ядра, то безопасность системы "вылетит в трубу".</p>
     <p>Пользовательские программы должны каким-либо образом сигнализировать ядру о том, что им необходимо выполнить системный вызов и что система должна переключиться в режим ядра, где системный вызов должен быть выполнен с помощью ядра, работающего от имени приложения.</p>
     <p>Таким механизмом, который может подать сигнал ядру, является программное прерывание: создается исключительная ситуация (exception) и система переключается в режим ядра для выполнения обработчика этой исключительной ситуации. Обработчик исключительной ситуации в данном случае и является обработчиком системного вызова (system call handler). Для аппаратной платформы x86 это программное прерывание определено как машинная инструкция <code>int $0x80</code>. Она приводит в действие механизм переключения в режим ядра и выполнение вектора исключительной ситуации с номером 128, который является обработчиком системных вызовов. Обработчик системных вызовов— это функция с очень подходящим именем <code>system_call()</code>. Данная функция зависима от аппаратной платформы и определена в файле <code>entry.S</code><a l:href="#n28" type="note">[28]</a>. В новых процессорах появилась такая новая функция, как <emphasis>sysenter</emphasis>. Эта функция обеспечивает более быстрый и специализированный способ входа в ядро для выполнения системного вызова, чем использование инструкции программного прерывания — <code>int</code>. Поддержка такой функции была быстро добавлена в ядро. Независимо от того, каким образом выполняется системный вызов, основным является то, что пространство пользователя вызывает исключительную ситуацию, или прерывание, чтобы вызвать переход в ядро.</p>
    </section>
    <section>
     <title>
      <p>Определение необходимого системного вызова</p>
     </title>
     <p>Простой переход в пространство ядра сам по себе не является достаточным, потому что существует много системных вызовов, каждый из которых осуществляет переход в режим ядра одинаковым образом. Поэтому ядру должен передаваться номер системного вызова.</p>
     <p>Для аппаратной платформы x86 номер системного вызова сохраняется в регистре процессора <code>eax</code> перед тем, как вызывается программное прерывание. Обработчик системных вызовов после этого считывает это значение из регистра <code>eax</code>. Для других аппаратных платформ выполняется нечто аналогичное.</p>
     <p>Функция <code>system_call()</code> проверяет правильность переданного номера системного вызова путем сравнения его со значением постоянной <code>NR_syscalls</code>. Если значение номера больше или равно значению <code>NR_syscalls</code>, то функция возвращает значение <code>-ENOSYS</code>. В противном случае вызывается соответствующий системный вызов следующим образом:</p>
     <p><code>call *sys_call_table(,%eax,4)</code></p>
     <p>Так как каждый элемент таблицы системных вызовов имеет длину 32 бит (4 байт), то ядро умножает данный номер системного вызова на 4 для получения нужной позиции в таблице системных вызовов (рис. 5.2).</p>
     <image l:href="#img_11.jpeg"/>
     <p><strong>Рис. 5.2</strong>. Запуск обработчика системных вызовов и выполнение системного вызова</p>
    </section>
    <section>
     <title>
      <p>Передача параметров</p>
     </title>
     <p>В дополнение к номеру вызова, большинство системных вызовов требует передачи им одного или нескольких параметров. Во время перехвата исключительной ситуации пространство пользователя должно каким-либо образом передать ядру эти параметры. Самый простой способ осуществить такую передачу — это сделать по аналогии с передачей номера системной функции: параметры хранятся в регистрах процессора. Для аппаратной платформы x86 регистры <code>ebx</code>, <code>ecx</code>, <code>edx</code>, <code>esi</code>, <code>edi</code> содержат соответственно первые пять аргументов. В случае редких ситуаций с шестью или более аргументами, используется один регистр, который содержит указатель на память пространства пользователя, где хранятся все параметры.</p>
     <p>Возвращаемое значение также передается в пространство пользователя через регистр. Для аппаратной платформа x86 оно хранится в регистре <code>eax</code>.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Реализация системных вызовов</p>
    </title>
    <section>
     <p>Реализация системного вызова в ОС Linux не связана с поведением обработчика системных вызовов. Добавление нового системного вызова в операционной системе Linux является сравнительно простым делом. Тяжелая работа связана с разработкой и реализацией самого системного вызова. Регистрация его в ядре проста. Давайте рассмотрим шаги, которые необходимо предпринять, чтобы написать новый системный вызов в операционной системе Linux.</p>
     <p>Первый шаг в реализации системного вызова — это определение его назначения, т.е. что он должен делать. Каждый системный вызов должен иметь только одно назначение. Мультиплексные системные вызовы (один системный вызов, который выполняет большой набор различных операций, в зависимости от значения флага, передаваемого в качестве аргумента) в операционной системе Linux использовать не рекомендуется. Для примера того, как <emphasis>не надо делать</emphasis>, можно обратиться к системной функции <code>ioctl()</code>.</p>
     <p>Какие должны быть аргументы, возвращаемые значения и коды ошибок для новой системной функции? Системная функция должна иметь понятный и простой интерфейс, по возможности с меньшим количеством аргументов. Семантика и поведение системных функций — это очень важные вещи, они не должны меняться, потому что от них будет зависеть работа прикладных программ.</p>
     <p>Важным является разработка интерфейса с прицелом на будущее. Не ограничены ли возможности функции без необходимости? Разрабатываемый системный вызов должен быть максимально общим. Не нужно полагать, что завтра он будет использоваться так же, как сегодня. <emphasis>Назначение</emphasis> системного вызова должно оставаться постоянным, но его <emphasis>использование</emphasis> может меняться. Является ли системный вызов переносимым? Не нужно делать допущений о возможном размере машинного слова или порядка следования байтов. В главе 19, "Переносимость", рассматриваются соответствующие вопросы. Нужно удостовериться, что никакие неверные допущения не будут мешать использованию системного вызова в будущем. Помните девиз Unix: "Обеспечивать механизм, а не стратегию".</p>
     <p>При разработке системного вызова важно помнить, что переносимость и устойчивость необходимы не только сегодня, но и будут необходимы в будущем. Основные системные вызовы ОС Unix выдержали это испытание временем. Большинство из них такие же полезные и применимые сегодня, как и почти тридцать лет назад!</p>
    </section>
    <section>
     <title>
      <p>Проверка параметров</p>
     </title>
     <p>Системные вызовы должны тщательно проверять все свои параметры для того, чтобы убедиться, что их значения адекватны и законны. Системные вызовы выполняются в пространстве ядра, и если пользователь может передать неправильные значения ядру, то стабильность и безопасность системы могут пострадать.</p>
     <p>Например, системные вызовы для файлового ввода-вывода данных должны проверить, является ли значение файлового дескриптора допустимым. Функции, связанные с управлением процессами, должны проверить, является ли значение переданного идентификатора <code>PID</code> допустимым. Каждый параметр должен проверяться не только на предмет допустимости и законности, но и на предмет правильности значения.</p>
     <p>Одна из наиболее важных проверок — это проверка указателей, которые передает пользователь. Представьте, что процесс может передать любой указатель, даже тот, который указывает на область памяти, не имеющей прав чтения! Процесс может таким обманом заставить ядро скопировать данные, к которым процесс не имеет доступа, например данные, принадлежащие другому процессу. Перед тем как следовать указателю, переданному из пространства пользователя, система должна убедиться в следующем.</p>
     <p>• Указатель указывает на область памяти в пространстве пользователя. Нельзя, чтобы процесс заставил ядро обратиться к памяти ядра от имени процесса.</p>
     <p>• Указатель указывает на область памяти в адресном пространстве текущего процесса. Нельзя позволять, чтобы процесс заставил ядро читать данные других процессов.</p>
     <p>• Для операций чтения есть права на чтение области памяти. Для операций записи есть права на запись области памяти. Нельзя, чтобы процессы смогли обойти ограничения на чтение и запись.</p>
     <p>Ядро предоставляет две функции для выполнения необходимых проверок при копировании данных в пространство пользователя и из него. Следует помнить, что ядро никогда не должно слепо следовать за указателем в пространстве пользователя! Одна из этих двух функций должна использоваться всегда.</p>
     <p>Для записи в пространство пользователя предоставляется функция <code>copy_to_user()</code>. Она принимает три параметра: адрес памяти назначения в пространстве пользователя; адрес памяти источника в пространстве ядра; и размер данных, которые необходимо скопировать, в байтах.</p>
     <p>Для чтения из пространства пользователя используется функция <code>copy_from_user()</code>, которая аналогична функции <code>copy_to_user()</code>. Эта функция считывает данные, на которые указывает второй параметр, в область памяти, на которую указывает первый параметр, количество данных — третий параметр.</p>
     <p>Обе эти функции возвращают количество байтов, которые они не смогли скопировать в случае ошибки. При успешном выполнении операции возвращается нуль. В случае такой ошибки стандартным является возвращение системным вызовом значения <code>-EFAULT</code>.</p>
     <p>Давайте рассмотрим пример системного вызова, который использует функции <code>copy_from_user()</code> и <code>copy_to_user()</code>. Системный вызов <code>silly_copy()</code> является до крайности бесполезным. Он просто копирует данные из своего первого параметра во второй. Это очень не эффективно, так как используется дополнительное промежуточное копирование в пространство ядра безо всякой причины. Но зато это позволяет проиллюстрировать суть дела.</p>
     <p><code>/*</code></p>
     <p><code>* Системный вызов silly copy — крайне бесполезная функция,</code></p>
     <p><code>* которая копирует len байтов из области памяти,</code></p>
     <p><code>* на которую указывает параметр src, в область памяти,</code></p>
     <p><code>* на которую указывает параметр dst, с использованием ядра</code></p>
     <p><code>* безо всякой на то причины. Но это хороший пример!</code></p>
     <p><code>*/</code></p>
     <p><code>asmlinkage long sys_silly_copy(unsigned long *src,</code></p>
     <p><code> unsigned long *dst, unsigned long len) {</code></p>
     <p><code> unsigned long buf;</code></p>
     <p><code> /* возвращаем ошибку, если размер машинного слова в ядре</code></p>
     <p><code>    не совпадает с размером данных, переданных пользователем */</code></p>
     <p><code> if (len != sizeof(buf))</code></p>
     <p><code>  return -EINVAL;</code></p>
     <p><code> /* копируем из src, который является адресом в пространстве</code></p>
     <p><code>    пользователя, в buf */</code></p>
     <p><code> if (copy_from_user(&amp;buf, src, len))</code></p>
     <p><code>  return -EFAULT;</code></p>
     <p><code> /* копируем из buf в dst, который тоже является адресом</code></p>
     <p><code>    в пространстве пользователя */</code></p>
     <p><code> if (copy_to_user(dst, &amp;buf, len))</code></p>
     <p><code>  return -EFAULT;</code></p>
     <p><code> /* возвращаем количество скопированных данных */</code></p>
     <p><code> return len;</code></p>
     <p><code>}</code></p>
     <p>Следует заметить, что обе функции, <code>copy_from_user()</code> и <code>copy_to_user()</code>, могут блокироваться. Это возникает, например, если страница памяти, содержащая данные пользователя, не находится в физической памяти, а в данный момент вытеснена на диск. В таком случае процесс будет находиться в приостановленном состоянии до тек пор, пока обработчик прерываний из-за отсутствия страниц (page fault handler) не возвратит страницу памяти в оперативную память из файла подкачки на диске.</p>
     <p>Последняя проверка — это проверка на соответствие правам доступа. В старых версиях ядра Linux стандартом было использование функции <code>suser()</code> для системных вызовов, которые требуют прав пользователя <strong>root</strong>. Эта функция просто проверяла, запущен ли процесс от пользователя <strong>root</strong>. Сейчас эту функцию убрали и заменили более мелко структурированным набором системных "возможностей использования" (capabilities). В новых системах предоставляется возможность проверять специфические права доступа к специфическим ресурсам. Функция <code>capable()</code> с допустимым значением флага, определяющего тип прав, возвращает ненулевое значение, если пользователь обладает указанным правом, и нуль— в противном случае. Например, вызов <code>capable</code> (<code>CAP_SYS_NICE</code>) проверяет, имеет ли вызывающий процесс возможность модифицировать значение параметра <emphasis>nice</emphasis> других процессов. По умолчанию суперпользователь владеет всеми правами, а пользователь, не являющийся пользователем root, не имеет никаких дополнительных прав. Следующий пример системного вызова, который демонстрирует использование возможностей использования, тоже является практически бесполезным.</p>
     <p><code>asmlinkage long sys_am_i_popular(void) {</code></p>
     <p><code> /* Проверить, имеет пи право процесс использовать</code></p>
     <p><code>    возможность CAP_SYS_NICE */</code></p>
     <p><code> if (!capable(CAP_SYS_NICE))</code></p>
     <p><code>  return -EPERM;</code></p>
     <p><code> /* Возвратить нуль, чтобы обозначить успешное завершение */</code></p>
     <p><code> return 0;</code></p>
     <p><code>}</code></p>
     <p>Список всех "возможностей использования" и прав, которые за ними закреплены, содержится в файле <code>&lt;linux/capability.h&gt;</code>.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Контекст системного вызова</p>
    </title>
    <section>
     <p>Как уже обсуждалось в главе 3, "Управление процессами", при выполнении системного вызова ядро работает в контексте процесса. Указатель <code>current</code> указывает на текущее задание, которое и есть процессом, выполняющим системный вызов.</p>
     <p>В контексте процесса ядро может переходит в приостановленное состояние (например, если системный вызов блокируется при вызове функции или явно вызывает функцию <code>schedule()</code>), а также является полностью вытесняемым. Эти два момента важны. Возможность переходить в приостановленное состояние означает, что системный вызов может использовать большую часть функциональных возможностей ядра. Как будет видно из главы 6, "Прерывания и обработка прерываний", наличие возможности переходить в приостановленное состояние значительно упрощает программирование ядра<a l:href="#n29" type="note">[29]</a>. Тот факт, что контекст процесса является вытесняемым, подразумевает, что, как и в пространстве пользователя, текущее задание может быть вытеснено другим заданием. Так как новое задание может выполнить тот же системный вызов, необходимо убедиться, что системные вызовы являются реентерабельными. Это очень похоже на требования, выдвигаемые для симметричной мультипроцессорной обработки. Способы защиты, которые обеспечивают реентерабельность, описаны в главе 8, "Введение в синхронизацию выполнения кода ядра", и в главе 9, "Средства синхронизации в ядре".</p>
     <p>После завершение системного вызова управление передается обратно в функцию <code>system_call()</code>, которая в конце концов производит переключение в пространство пользователя, и далее выполнение пользовательского процесса продолжается.</p>
    </section>
    <section>
     <title>
      <p>Окончательные шаги регистрации системного вызова</p>
     </title>
     <p>После того как системный вызов написан, процедура его регистрации в качестве официального системного вызова тривиальна и состоит в следующем.</p>
     <p>• Добавляется запись в конец таблицы системных вызовов. Это необходимо сделать для всех аппаратных платформ, которые поддерживают этот системный вызов (для большинства системных вызовов — это все возможные платформы). Положение системного вызова в таблице — это номер системного вызова, начиная с нуля. Например, десятая запись таблицы соответствует системному вызову с номером девять.</p>
     <p>• Для всех поддерживаемых аппаратных платформ номер системной функции должен быть определен в файле <code>include/linux/unistd.h</code>.</p>
     <p>• Системный вызов должен быть вкомпилирован в образ ядра (в противоположность компиляции в качестве загружаемого модуля<a l:href="#n30" type="note">[30]</a>). Это просто соответствует размещению кода в каком-нибудь важном файле каталога <code>kernel/</code>.</p>
     <p>Давайте более детально рассмотрим эти шаги на примере функции системного вызова, <code>foo()</code>. Вначале функция <code>sys_fоо()</code> должна быть добавлена в таблицу системных вызовов. Для большинства аппаратных платформ таблица системных вызовов размещается в файле <code>entry.S</code> и выглядит примерно следующим образом.</p>
     <p><code>ENTRY (sys_call_table)</code></p>
     <p><code> .long sys_restart_syscall / * 0 * /</code></p>
     <p><code> .long sys_exit</code></p>
     <p><code> .long sys_fork</code></p>
     <p><code> .long sys_read</code></p>
     <p><code> .long sys_write</code></p>
     <p><code> .long sys_open /* 5 */</code></p>
     <p><code>...</code></p>
     <p><code> .long sys_timer_delete</code></p>
     <p><code> .long sys_clock_settime</code></p>
     <p><code> .long sys_clock_gettime /* 280 */</code></p>
     <p><code> .long sys_clock_getres</code></p>
     <p><code> .long sys_clock_nanosleep</code></p>
     <p>Необходимо добавить новый системный вызов в конец этого списка:</p>
     <p><code>.long sys_foo</code></p>
     <p>Нашему системному вызову будет назначен следующий свободный номер, 283, хотя мы этого явно и не указывали. Для каждой аппаратной платформы, которую мы будем поддерживать, системный вызов должен быть добавлен в таблицу системных вызовов соответствующей аппаратной платформы (нет необходимости получать номер системного вызова для каждой платформы). Обычно необходимо сделать системный вызов доступным для всех аппаратных платформ. Следует обратить внимание на договоренность указывать комментарии с номером системного вызова через каждые пять записей, что позволяет быстро найти, какой номер какому системному вызову соответствует.</p>
     <p>Далее необходимо добавить номер системного вызова в заголовочный файл <code>include/asm/unistd.h</code>, который сейчас выглядит примерно так.</p>
     <p><code>/*</code></p>
     <p><code>* This file contains the system call numbers.</code></p>
     <p><code>*/</code></p>
     <p><code>#define __NR_restart_syscall 0</code></p>
     <p><code>#define __NR_exit 1</code></p>
     <p><code>#define __NR_fork 2</code></p>
     <p><code>#define __NR_read 3</code></p>
     <p><code>#define __NR_write 4</code></p>
     <p><code>#define __NR_open 5</code></p>
     <p><code>...</code></p>
     <p><code>#define __NR_mq_unlink       278</code></p>
     <p><code>#define __NR_mq_timedsend    279</code></p>
     <p><code>#define __NR_mq_timedreceive 280</code></p>
     <p><code>#define __NR_mq_notify       281</code></p>
     <p><code>#define __NR_mq_getsetattr   282</code></p>
     <p>В конец файла добавляется следующая строка.</p>
     <p><code>#define __NR_foo 283</code></p>
     <p>В конце концов необходимо реализовать сам системный вызов <code>foo()</code>. Так как системный вызов должен быть вкомпилорован в образ ядра во всех конфигурациях, мы его поместим в файл <code>kernel/sys.c</code>. Код необходимо размещать в наиболее подходящем файле. Например, если функция относится к планированию выполнения процессов, то ее необходимо помещать в файл <code>sched.c</code>.</p>
     <p><code>/*</code></p>
     <p><code>* sys_foo - всеми любимый системный вызов.</code></p>
     <p><code>*</code></p>
     <p><code>* Возвращает размер стека ядра процесса</code></p>
     <p><code>*/</code></p>
     <p><code>asmlinkage long sys_foo(void) {</code></p>
     <p><code> return THREAD_SIZE;</code></p>
     <p><code>}</code></p>
     <p>Это все! Загрузите новое ядро. Теперь из пространства пользователя можно вызвать системную функцию <code>foo()</code>.</p>
    </section>
    <section>
     <title>
      <p>Доступ к системным вызовам из пространства пользователя</p>
     </title>
     <p>В большинстве случаев системные вызовы поддерживаются библиотекой функций языка С. Пользовательские приложения могут получать прототипы функций из стандартных заголовочных файлов и компоновать программы с библиотекой С для использования вашего системного вызова (или библиотечной функции, которая вызывает ваш системный вызов). Однако если вы только что написали системный вызов, то маловероятно, что библиотека <code>glibc</code> уже его поддерживает!</p>
     <p>К счастью, ОС Linux предоставляет набор макросов-оболочек для доступа к системным вызовам. Они позволяют установить содержимое регистров и выполнить машинную инструкцию <code>int $0x80</code>. Эти макросы имеют имя <code>syscall<emphasis>n</emphasis>()</code>, где <code><emphasis>n</emphasis></code> — число от нуля до шести. Это число соответствует числу параметров, которые должны передаваться в системный вызов, так как макросу необходима информация о том, сколько ожидается параметров, и соответственно, нужно записать эти параметры в регистры процессора. Например, рассмотрим системный вызов <code>open()</code>, который определен следующим образом.</p>
     <p><code>long open(const char *filename, int flags, int mode)</code></p>
     <p>Макрос для вызова этой системной функции будет выглядеть так.</p>
     <p><code>#define NR_open 5</code></p>
     <p><code>_syscall3(long, NR_open, const char*, filename, int, flags, int, mode);</code></p>
     <p>После этого приложение может просто вызывать функцию <code>open()</code>.</p>
     <p>Каждый макрос принимает <code>2 + 2*n</code> параметров. Первый параметр соответствует типу возвращаемого значения системного вызова. Второй параметр — имя системного вызова. После этого следуют тип и имя каждого параметра в том же порядке, что и у системного вызова. Постоянная <code>NR_open</code>, которая определена в файле <code>&lt;asm/unistd.h&gt;</code>, — это номер системного вызова. В функцию на языке программирования С такой вызов превращается с помощью вставок на языке ассемблера, которые выполняют рассмотренные в предыдущем разделе шаги. Значения аргументов помещаются в соответствующие регистры, и выполняется программное прерывание, которое перехватывается в режиме ядра. Вставка данного макроса в приложение — это все, что необходимо для выполнения системного вызова <code>open()</code>.</p>
     <p>Напишем макрос, который позволяет вызвать нашу замечательную системную функцию, и соответствующий код, который позволяет этот вызов протестировать.</p>
     <p><code>#define __NR_foo 283</code></p>
     <p><code>__syscall0()(long, foo)</code></p>
     <empty-line/>
     <p><code>int main() {</code></p>
     <p><code> long stack_size;</code></p>
     <empty-line/>
     <p><code>stack_size = foo();</code></p>
     <p><code> printf("Размер стека ядра равен %ld\n" , stack_size);</code></p>
     <p><code> return 0;</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>Почему не нужно создавать системные вызовы</p>
     </title>
     <p>Новый системный вызов легко реализовать, тем не менее это необходимо делать только тогда, когда ничего другого не остается. Часто, для того чтобы обеспечить новый системный вызов, существуют более подходящие варианты. Давайте рассмотрим некоторые "за" и "против" и возможные варианты.</p>
     <p>Для создания нового интерфейса в виде системного вызова могут быть следующие "за".</p>
     <p>• Системные вызовы просто реализовать и легко использовать.</p>
     <p>• Производительность системных вызовов в операционной системе Linux очень высока.</p>
     <p>Возможные "против".</p>
     <p>• Необходимо получить номер системного вызова, который должен быть официально назначен в период работы над разрабатываемыми сериями ядер.</p>
     <p>• После того как системный вызов включен в стабильную серию ядра, он становится "высеченным в камне". Интерфейс не должен меняться, чтобы не нарушить совместимости с прикладными пользовательскими программами.</p>
     <p>• Для каждой аппаратной платформы необходимо регистрировать отдельный системный вызов и осуществлять его поддержку.</p>
     <p>• Для простого обмена информацией системный вызов — это "стрельба из пушки по воробьям".</p>
     <p>Возможные варианты.</p>
     <p>• Реализовать файл устройства и использовать функции <code>read()</code> и <code>write()</code> для этого устройства, а также использовать функцию <code>ioctl()</code> для манипуляции специфическими параметрами или для получения специфической информации.</p>
     <p>• Некоторые интерфейсы, например семафоры, могут быть представлены через дескрипторы файлов. Управлять этими устройствами также можно по аналогии с файлами.</p>
     <p>• Добавить информационный файл в соответствующем месте файловой системы <code>sysfs</code>.</p>
     <p>Для большого числа интерфейсов, системные вызовы — это правильный выбор. В операционной системе Linux пытаются избегать простого добавления системного вызова для поддержки каждой новой, вдруг появляющейся абстракции. В результате получился удивительно четкий уровень системных вызовов, который принес очень мало разочарований и привел к малому числу не рекомендованных к использованию и устаревших (deprecated) интерфейсов (т.е. таких, которые больше не используются или не поддерживаются).</p>
     <p>Малая частота добавления новых системных вызовов свидетельствует о том, что Linux — это стабильная операционная система с полным набором функций. Очень немного системных вызовов было добавлено во время разработки серий ядер 2.3 и 2.5. Большая часть из новых системных вызовов предназначена для улучшения производительности.</p>
    </section>
   </section>
   <section>
    <title>
     <p>В заключение о системных вызовах</p>
    </title>
    <p>В этой главе было рассмотрено, что такое системные вызовы и как они соотносятся с вызовами библиотечных функций и интерфейсом прикладных программ (API). После этого было описано, как системные вызовы реализованы в ядре Linux, а также была представлена последовательность событий для выполнения системного вызова: программное прерывание ядра, передача номера системного вызова и аргументов системного вызова, выполнение соответствующей функции системного вызова и возврат результатов работы в пространство пользователя.</p>
    <p>Далее было рассказано, как добавить новый системный вызов, и был приведен простой пример использования системного вызова из пространства пользователя. Весь процесс является достаточно простым! Из простоты создания системного вызова следует, что основная работа по добавлению нового системного вызова сводится к реализации функции системного вызова. В оставшейся части книги рассмотрены основные принципы, а также интерфейсы, которые необходимо использовать при создании хорошо работающих, оптимальных и безопасных системных вызовов.</p>
    <p>В конце главы были рассмотрены "за" и "против" относительно реализации системных вызовов и представлен краткий список возможных вариантов добавления новых системных вызовов.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 6</p>
    <p>Прерывания и обработка прерываний</p>
   </title>
   <section>
    <p>Управление аппаратными устройствами, которые подключены к вычислительной машине, — это одна из самых ответственных функций ядра. Частью этой работы является необходимость взаимодействия с отдельными устройствами машины. Поскольку процессоры обычно работают во много раз быстрее, чем аппаратура, с которой они должны взаимодействовать, то для ядра получается неэффективным отправлять запросы и тратить время, ожидая ответы от потенциально более медленного оборудования. Учитывая небольшую скорость отклика оборудования, ядро должно иметь возможность оставлять на время работу с оборудованием и выполнять другие действия, пока аппаратное устройство не закончит обработку запроса. Одно из возможных решений этой проблемы — периодический <emphasis>опрос</emphasis> оборудования (<emphasis>polling</emphasis>). Ядро периодически может проверять состояние аппаратного устройства системы и соответственным образом реагировать. Однако такой подход вносит дополнительные накладные расходы, потому что, независимо от того, готов ответ от аппаратного устройства или оно еще выполняет запрос, все равно осуществляется постоянный систематический опрос состояния устройства через постоянные интервалы времени. Лучшим решением является обеспечение механизма, который позволяет подавать ядру сигнал о необходимости уделить внимание оборудованию. Такой механизм называется прерыванием (interrupt).</p>
   </section>
   <section>
    <title>
     <p>Прерывания</p>
    </title>
    <p>Прерывания позволяют аппаратным устройствам взаимодействовать с процессором. Например, при наборе на клавиатуре контроллер клавиатуры (или другое устройство, которое обслуживает клавиатуру) генерирует прерывание, чтобы объявить операционной системе о том, что произошли нажатия клавиш. Прерывания — это специальные электрические сигналы, которые аппаратные устройства посылают процессору. Процессор получает прерывание и дает сигнал операционной системе о том, что ОС может обработать новые данные. Аппаратные устройства генерируют прерывания асинхронно по отношению к тактовому генератору процессора — прерывания могут возникать непредсказуемо, в любой момент времени. Следовательно, работа ядра может быть прервана в любой момент для того, чтобы обработать прерывания.</p>
    <p>Физически прерывания производятся электрическими сигналами, которые создаются устройствами и направляются на входные контакты микросхемы контроллера прерываний. Контроллер прерываний в свою очередь отправляет сигнал процессору. Процессор выполняет детектирование сигнала и прерывает выполнение работы для того, чтобы обработать прерывание. После этого процессор извещает операционную систему о том, что произошло прерывание и операционная система может соответствующим образом это прерывание обработать.</p>
    <p>Различные устройства связаны со своими прерываниями с помощью уникальных числовых значений, соответствующих каждому прерыванию. Отсюда следует, что прерывания, поступившие от клавиатуры, отличаются от прерываний, поступивших от жесткого диска. Это позволяет операционной системе различать прерывания и иметь информацию о том, какое аппаратное устройство произвело данное прерывание. Поэтому операционная система может обслуживать каждое прерывание с помощью своего уникального обработчика.</p>
    <p>Идентификаторы, соответствующие прерываниям, часто называются линиями запросов на прерывание (interrupt request lines, IRQ lines). Обычно это некоторые числа. Например, для платформы PC значение IRQ, равное 0, — это прерывание таймера, a IRQ, равное 1, — прерывание клавиатуры. Однако не все номера прерываний жестко определены. Прерывания, связанные с устройствами шины PCI, например, назначаются динамически. Другие платформы, которые не поддерживают стандарт PCI, имеют аналогичные функции динамического назначения номеров прерываний. Основная идея состоит в том, что определенные прерывания связаны с определенными устройствами, и у ядра есть вся эта информация. Аппаратное обеспечение, чтобы привлечь внимание ядра, генерирует прерывание вроде <emphasis>«Эй! Было новое нажатие клавиши! Его необходимо обработать!»</emphasis>.</p>
    <cite>
     <subtitle>Исключительные ситуации</subtitle>
     <p>Исключительные ситуации (exceptions) часто рассматриваются вместе с прерываниями. В отличие от прерываний, они возникают синхронно с тактовым генератором процессора. И действительно, их часто называют синхронными прерываниями. Исключительные ситуации генерируются процессором при выполнении машинных инструкций как реакция на ошибку программы (например, деление на нуль) или как реакция на аварийную ситуацию, которая может быть обработана ядром (например, прерывание из-за отсутствия страницы, page fault). Так как большинство аппаратных платформ обрабатывают исключительные ситуации аналогично обработке прерываний, то инфраструктуры ядра, для обоих видов обработки, также аналогичны. Большая часть материала, посвященная обработке прерываний (асинхронных, которые генерируются аппаратными устройствами), также относится и к исключительным ситуациям (синхронным, которые генерируются самим процессором).</p>
     <p>С одним типом исключительной ситуации мы уже встречались в предыдущей главе. Там было рассказано, как для аппаратной платформы x86 реализованы системные вызовы на основе программных прерываний. При этом генерируется исключительная ситуация, которая заставляет переключиться в режим ядра и в конечном итоге приводит к выполнению определенного обработчика системного вызова. Прерывания работают аналогичным образом, за исключением того, что прерывания генерируются не программным, а аппаратным обеспечением.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>Обработчики прерываний</p>
    </title>
    <section>
     <p>Функция, которую выполняет ядро в ответ на определенное прерывание, называется <emphasis>обработчиком прерывания</emphasis> (<emphasis>interrupt handler</emphasis>) или <emphasis>подпрограммой обслуживания прерывания</emphasis> (<emphasis>interrupt service routine</emphasis>). Каждому устройству, которое генерирует прерывания, соответствует свой обработчик прерывания. Например, одна функция обрабатывает прерывание от системного таймера, а другая — прерывания, сгенерированные клавиатурой. Обработчик прерывания для какого-либо устройства является частью драйвера этого устройства — кода ядра, который управляет устройством.</p>
     <p>В операционной системе Linux обработчики прерываний — это обычные функции, написанные на языке программирования С. Они должны соответствовать определенному прототипу, чтобы ядро могло стандартным образом принимать информацию об обработчике, а в остальном— это обычные функции. Единственное, что отличает обработчики прерываний от других функций ядра, — это то, что они вызываются ядром в ответ на прерывание и выполняются в специальном контексте, именуемом <emphasis>контекстом прерывания</emphasis> (<emphasis>interrupt context</emphasis>), который будет рассмотрен далее.</p>
     <p>Так как прерывание может возникнуть в любой момент времени, то, соответственно, и обработчик прерывания может быть вызван в любой момент времени. Крайне важно, чтобы обработчик прерывания выполнялся очень быстро и возобновлял управление прерванного кода по возможности быстро. Поэтому, хотя для аппаратного обеспечения и важно, чтобы прерывание обслуживалось немедленно, для остальной системы важно, чтобы обработчик прерывания выполнялся в течение максимально короткого промежутка времени. Минимально возможная работа, которую должен сделать обработчик прерывания, — это отправить подтверждение устройству, что прерывание получено. Однако обычно обработчики прерываний должны выполнить большее количество работы. Например, рассмотрим обработчик прерывания сетевого устройства. Вместе с отправкой подтверждения аппаратному обеспечению, обработчик прерывания должен скопировать сетевые пакеты из аппаратного устройства в память системы, обработать их, отправить соответствующему стеку протоколов или соответствующей программе. Очевидно, что для этого требуется много работы.</p>
    </section>
    <section>
     <title>
      <p>Верхняя и нижняя половины</p>
     </title>
     <p>Ясно, что два указанных требования о том, что обработчик прерывания должен выполняться быстро и, в дополнение к этому, выполнять много работы, являются противоречивыми. В связи с конфликтными требованиями, обработчик прерываний разбивается на две части, или половины. Обработчик прерывания является <emphasis>верхней половиной</emphasis> (<emphasis>top half</emphasis>) — он выполняется сразу после приема прерывания и выполняет работу, критичную к задержкам во времени, такую как отправка подтверждения о получении прерывания или сброс аппаратного устройства. Работа, которую можно выполнить позже, откладывается до выполнения <emphasis>нижней</emphasis> (или основной) <emphasis>половины</emphasis> (<emphasis>bottom half</emphasis>). Нижняя половина обрабатывается позже, в более удобное время, когда все прерывания разрешены. Достаточно часто нижняя половина выполняется сразу же после возврата из обработчика прерывания.</p>
     <p>Операционная система предоставляет различные механизмы для реализации обработки нижних половин, которые обсуждаются в главе 7, "Обработка нижних половин и отложенные действия".</p>
     <p>Рассмотрим пример разделения обработчика прерывания на верхнюю и нижнюю половины на основе старой доброй сетевой платы. Когда сетевой интерфейсный адаптер получает входящие из сети пакеты, он должен уведомить ядро о том, что доступны новые данные. Это необходимо сделать немедленно, чтобы получить оптимальную пропускную способность и время задержки при передаче информации по сети. Поэтому немедленно генерируется прерывание: <emphasis>«Эй, ядро! Есть свежие пакеты!»</emphasis>. Ядро отвечает выполнением зарегистрированного обработчика прерывания от сетевого адаптера.</p>
     <p>Обработчик прерывания выполняется, аппаратному обеспечению направляется подтверждение, пакеты копируются в основную память, и после этого сетевой адаптер готов к получению новых пакетов. Эта задача является важной, критичной ко времени выполнения и специфической для каждого типа аппаратного обеспечения. Остальная часть обработки сетевых пакетов выполняется позже — нижней половиной обработчика прерывания. В этой главе мы рассмотрим обработку верхних половин, а в следующей — нижних.</p>
    </section>
    <section>
     <title>
      <p>Регистрация обработчика прерывания</p>
     </title>
     <p>Ответственность за обработчики прерываний лежит на драйверах устройств, которые управляют определенным типом аппаратного обеспечения. С каждым устройством связан драйвер, и если устройство использует прерывания (а большинство использует), то драйвер должен выполнить регистрацию обработчика прерывания.</p>
     <p>Драйвер может зарегистрировать обработчик прерывания для обработки заданной линии с помощью следующей функции.</p>
     <p><code>/* request_irq: выделить заданную линию прерывания */</code></p>
     <p><code>int request_irq(unsigned int irq,</code></p>
     <p><code> irqreturn_t (*handler)(int, void*, struct pt_regs*),</code></p>
     <p><code> unsigned long irqflags, const char* devname, void *dev_id);</code></p>
     <p>Первый параметр, <code>irq</code>, указывает назначаемый номер прерывания. Для некоторых устройств, таких как, например, обычные устройства персонального компьютера, таймер и клавиатура, это значение, как правило, жестко закреплено. Для большинства других устройств это значение определяется путем проверки (probing) или другим динамическим способом.</p>
     <p>Второй параметр, <code>handler</code>, — это указатель на функцию обработчика прерывания, которая обслуживает данное прерывание. Эта функция вызывается, когда в операционную систему приходит прерывание. Следует обратить внимание на специфический прототип функции-обработчика. Она принимает три параметра и возвращает значение типа <code>irqreturn_t</code>. Ниже в этой главе мы более подробно обсудим эту функцию.</p>
     <p>Третий параметр, <code>irqflags</code>, может быть равным нулю или содержать битовую маску с одним или несколькими следующими флагами.</p>
     <p>• <code>SA_INTERRUPT</code>. Этот флаг указывает, что данный обработчик прерывания — это <emphasis>быстрый обработчик прерывания</emphasis>. Исторически так сложилось, что операционная система Linux различает быстрые и медленные обработчики прерываний. Предполагается, что быстрые обработчики выполняются быстро, но потенциально очень часто, поэтому поведение обработчика прерывания изменяется, чтобы обеспечить максимально возможную скорость выполнения. Сегодня существует только одно отличие: при выполнении быстрого обработчика прерываний запрещаются <emphasis>все</emphasis> прерывания на локальном процессоре. Это позволяет быстрому обработчику завершится быстро, и другие прерывания никак этому не пометают. По умолчанию (если этот флаг не установлен) разрешены все прерывания, кроме тех, которые маскированы на всех процессорах и обработчики которых в данный момент выполняются. Для всех прерываний, кроме прерываний таймера, нет необходимости устанавливать этот флаг.</p>
     <p>• <code>SA_SAMPLE_RANDOM</code>. Этот флаг указывает, что прерывания, сгенерированные данным устройством, должны вносить вклад в пул энтропии ядра. Пул энтропии ядра обеспечивает генерацию истинно случайных чисел на основе различных случайных событий. Если этот флаг указан, то моменты времени, когда приходят прерывания, будут введены в пул энтропии. Этот флаг <strong>нельзя</strong> устанавливать, если устройство генерирует прерывания в предсказуемые моменты времени (как, например, системный таймер) или на устройство может повлиять внешний злоумышленник (как, например, сетевое устройство). С другой стороны, большинство устройств генерируют прерывания в непредсказуемые моменты времени и поэтому являются хорошим источником энтропии. Для более подробного описания пула энтропии ядра см.. приложение Б, "Генератор случайных чисел ядра".</p>
     <p>• <code>SA_SHIRQ</code>. Этот флаг указывает, что номер прерывания может совместно использоваться несколькими обработчиками прерываний (shared). Каждый обработчик, который регистрируется на одну и ту же линию прерывания, должен указывать этот флаг. В противном случае для каждой линии может существовать только один обработчик прерывания. Более подробная информация о совместно используемых обработчиках прерываний приведена в следующем разделе.</p>
     <p>Четвертый параметр, <code>devname</code>, — это ASCII-строка, которая описывает, какое устройство связано с прерыванием. Например, для прерывания клавиатуры персонального компьютера это значение равно <code>"keyboard"</code>. Текстовые имена устройств применяются для взаимодействия с пользователями с помощью интерфейсов <code>/proc/irq</code> и <code>/proc/interrupts</code>, которые вскоре будут рассмотрены.</p>
     <p>Пятый параметр, <code>dev_id</code>, в основном, применяется для совместно используемых линий запросов на прерывания. Когда обработчик прерывания освобождается (описано ниже), параметр <code>dev_id</code> обеспечивает уникальный идентификатор (cookie), который позволяет удалять только необходимый обработчик линии прерывания. Без этого параметра было бы невозможно ядру определить, какой обработчик данной линии прерывания следует удалить. Если линия запроса на прерывание не является совместно используемой, то можно в качестве этого параметра указывать <code>NULL</code>, если же номер прерывания является совместно используемым, то необходимо указывать уникальный идентификатор (cookie) (если устройство не подключено к тине ISA, то, скорее всего, оно поддерживает совместно используемые номера прерываний).</p>
     <p>Этот параметр также передается обработчику прерывания при каждом вызове. Обычная практика — это передача указателя на структуру устройства (контекст устройства), так как этот параметр является уникальным, и, кроме того, в обработчике прерывания может быть полезным иметь указатель на эту структуру.</p>
     <p>В случае успеха функция <code>request_irq()</code> возвращает нуль. Возврат ненулевого значения указывает на то, что произошла ошибка и указанный обработчик прерывания не был зарегистрирован. Наиболее часто встречающийся код ошибки — это значение <code>-EBUSY</code>, что указывает на то, что данная линия запроса на прерывание уже занята (и или при текущем вызове, или при первом вызове не был указан флаг <code>SA_SHIRQ</code>).</p>
     <p>Следует обратить внимание, что функция <code>request_irq()</code> может переходить в состояние ожидания (sleep) и, соответственно, не может вызываться из контекста прерывания, или в других ситуациях, когда код не может блокироваться. Распространенной ошибкой является мнение, что функцию <code>request_irq()</code> можно безопасно вызывать в случаях, когда нельзя переходить в состояние ожидания. Это происходит отчасти от того, что действительно сразу непонятно, почему функция <code>request_irq()</code> должна чего-то ожидать. Дело в том. что при регистрации происходит добавление информации о линии прерывания в каталоге <code>/proc/irq</code>. Функция <code>proc_mkdir()</code> используется для создания новых элементов на файловой системе <code>procfs</code>. Эта функция вызывает функцию <code>proc_create()</code> для создания новых элементов файловой системы <code>procfs</code>, которая в свою очередь вызывает функцию <code>kmalloc()</code> для выделения памяти. Как будет показано в главе 11, "Управление памятью", функция <code>kmalloc()</code> может переходить в состояние ожидания. Вот так вот!</p>
     <p>Для регистрации линии прерывания и инсталляции обработчика в коде драйвера можно использовать следующий вызов.</p>
     <p><code>if (request_irq(irqn, my_interrupt, SA_SHIRQ, "my_device", dev)) {</code></p>
     <p><code> printk(KERN_ERR "my_device: cannot register IRQ %d\n", irqn);</code></p>
     <p><code> return -EIO;</code></p>
     <p><code>}</code></p>
     <p>В этом примере параметр <code>irqn</code> — это запрошенный номер линии запроса на прерывание, параметр <code>my_interrupt</code> — это обработчик этой линии прерывания, линия запроса на прерывание может быть совместно используемой, имя устройства — <code>"my_device"</code>, <code>dev</code> — значение параметра <code>dev_id</code>. В случае ошибки код печатает сообщение, что произошла ошибка, и возвращается из выполняющейся функции. Если функция регистрации возвращает нулевое значение, то обработчик прерывания инсталлирован успешно. С этого момента обработчик прерывания будет вызываться в ответ на приходящие прерывания. Важно произвести инициализацию оборудования и регистрацию обработчика прерывания в правильной последовательности, чтобы предотвратить возможность вызова обработчика до того момента, пока оборудование не инициализировано.</p>
    </section>
    <section>
     <title>
      <p>Освобождение обработчика прерывания</p>
     </title>
     <p>Для освобождения линии прерывания необходимо вызвать функцию</p>
     <p><code>void free_irq(unsigned int irq, void *dev_id);</code></p>
     <p>Если указанная линия не является совместно используемой, то эта функция удаляет обработчик и запрещает линию прерывания. Если линия запроса на прерывание является совместно используемой, то удаляется обработчик, соответствующий параметру <code>dev_id</code>. Линия запроса на прерывание также запрещается, когда удаляется последний обработчик. Теперь понятно, почему важно передавать уникальное значение параметра <code>dev_id</code>. При использовании совместно используемых прерываний требуется уникальный идентификатор для того, чтобы отличать друг от друга различные обработчики, связанные с одним номером прерывания, и позволить функции <code>free_irq()</code> удалять правильный обработчик. В любом случае, если параметр <code>dev_id</code> не равен значению <code>NULL</code>, то он должен соответствовать тому обработчику, который удаляется.</p>
     <p>Вызов функции <code>free_irq()</code> должен производиться из контекста процесса.</p>
     <empty-line/>
     <p><strong>Таблица 6.1</strong>. Список функций управления регистрацией прерываний</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Функция</th>
       <th align="left" valign="top">Описание</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>request_irq()</code></td>
       <td align="left" valign="top">Зарегистрировать заданный обработчик прерывания для заданной линии прерывания</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>free_irq()</code></td>
       <td align="left" valign="top">Освободить указанный обработчик прерывания. Если с линией прерывания больше не связан ни один обработчик, то запретить указанную линию прерывания</td>
      </tr>
     </table>
    </section>
   </section>
   <section>
    <title>
     <p>Написание обработчика прерывания</p>
    </title>
    <section>
     <p>Следующее описание является типичным для обработчика прерывания.</p>
     <p><code>static irqreturn_t intr_handler(int irq, void *dev_id,</code></p>
     <p><code> struct pt_regs *regs);</code></p>
     <p>Заметим, что оно должно соответствовать аргументу, который передается в функцию <code>request_irq()</code>. Первый параметр, <code>irq</code>, — это численное значение номера прерывания, которое обслуживается обработчиком. Сейчас этот параметр практически не используется, кроме разве что при печати сообщений. Для версий ядра, меньших 2.0, не было параметра <code>dev_id</code>, поэтому параметр <code>irq</code> использовался, чтобы различать устройства, которые обслуживаются одним драйвером, и поэтому используют один и тот же обработчик прерываний (как пример можно рассмотреть компьютер с несколькими контроллерами жесткого диска одного типа).</p>
     <p>Второй параметр, <code>dev_id</code>, — это указатель, равный значению, которое было передано в функцию <code>request_irq()</code> при регистрации обработчика прерывания. Если значение этого параметра является уникальным, что необходимо для поддержки совместно используемых прерываний, то его можно использовать как идентификатор для того, чтобы отличать друг от друга различные устройства, которые потенциально могут использовать один обработчик. В связи с тем, что структура (контекст) устройства (device structure) является как уникальной, так и, возможно, полезной при использовании в обработчике, обычно в качестве параметра <code>dev_id</code> передают указатель на эту структуру.</p>
     <p>Последний параметр, <code>regs</code>, — это указатель на структуру, содержащую значения регистров процессора и состояние процессора, которые были сохранены перед началом обслуживания прерывания. Этот параметр используется редко, в основном для отладки. Сейчас разработчики начинают склоняться к мысли, что этот параметр нужно убрать. В существующих обработчиках прерываний он используется мало, и если его убрать, то не будет больших разочарований.</p>
     <p>Возвращаемое значение обработчиков прерываний имеет специальный тип <code>irqreturn_t</code>. Обработчик может возвращать два специальных значения: <code>IRQ_NONE</code> или <code>IRQ_HANDLED</code>. Первое значение возвращается, если обработчик прерывания обнаружил, что устройство, которое он обслуживает, не является источником прерывания. Второе значение возвращается, если обработчик вызван правильно и устройство, которое он обслуживает, является источником прерывания. Кроме этого, может быть использован макрос <code>IRQ_RETVAL(x)</code>. Если значение параметра <code>x</code> не равно нулю, то макрос возвращает значение <code>IRQ_HANDLED</code>, иначе возвращается значение, равное <code>IRQ_NONE</code>. Эти специальные значения позволяют дать ядру информацию о том, генерирует ли устройство паразитные (необрабатываемые) прерывания. Если все обработчики прерывания, которые обслуживают данную линию, возвращают значение <code>IRQ_NONE</code>, то ядро может обнаружить проблему. Заметим, что этот странный тип возвращаемого значения, <code>irqreturn_t</code>, просто соответствует типу <code>int</code>. Подстановка типа используется для того, чтобы обеспечить совместимость с более ранними версиями ядра, у которых не было подобной функции. До серии ядер 2.6 обработчик прерывания имел возвращаемое значение типа <code>void</code>. В коде новых драйверов можно применить переопределение типа <code>typedef irqreturn_t</code> в тип <code>void</code> и драйверы могут работать с ядрами серии 2.4 без дальнейшей модификации.</p>
     <p>Обработчик прерываний может помечаться как <code>static</code>, так как он никогда не вызывается непосредственно в других файлах кода.</p>
     <p>Роль обработчика прерывания зависит только от устройства и тех причин, по которым это устройство генерирует прерывания. Минимально, обработчик прерывания должен отправить устройству подтверждение о том, что прерывание получено. Дли более сложных устройств необходимо дополнительно отправить и принять данные, а также выполнить другую более сложную работу. Как уже упоминалось, сложная работа должна по возможности выполняться обработчиком нижней половины прерывания, которые будут рассмотрены в следующей главе.</p>
     <cite>
      <subtitle>Реентерабельность и обработчики прерываний</subtitle>
      <p>Обработчики прерываний в операционной системе Linux не обязаны быть реентерабельными. Когда выполняется некоторый обработчик прерывания, соответствующая линия запроса на прерывание маскируется на всех процессорах, что предотвращает возможность приема запроса на прерывание с этой пинии. Обычно все остальные прерывания в этот момент разрешены, поэтому другие прерывания могут обслуживаться, тогда как текущая линия всегда является запрещенной. Следовательно, никакой обработчик прерываний никогда не вызывается параллельно самому себе для обработки вложенных запросов на прерывание. Это позволяет значительно упростить написание обработчиков прерываний.</p>
     </cite>
    </section>
    <section>
     <title>
      <p>Совместно используемые обработчики</p>
     </title>
     <p>Совместно используемые (shared) обработчики выполняются практически так же, как и не совместно используемые. Существует, однако, три главных отличия.</p>
     <p>• Флаг <code>SA_SHIRQ</code> должен быть установлен в параметре <code>flags</code> при вызове функции <code>request_irq()</code>.</p>
     <p>• Аргумент <code>dev_id</code> этой же функции должен быть уникальным для каждого зарегистрированного обработчика. Достаточным является передача указателя на структуру, которая описывает устройство. Обычно так и поступают, поскольку структура контекста устройства является уникальной для каждого устройства, и, кроме того, данные этой структуры потенциально могут быть полезными при выполнении обработчика. Для совместно используемого обработчика нельзя присваивать параметру <code>dev_id</code> значение <code>NULL</code>!</p>
     <p>• Обработчик прерывания должен иметь возможность распознать, сгенерировано ли прерывание тем устройством, которое обслуживается этим обработчиком. Для этого требуется как поддержка аппаратного обеспечения, так и наличие соответствующей логики в обработчике прерывания. Если аппаратное устройство не имеет необходимых функций, то не будет никакой возможности в обработчике прерывания определить, какое из устройств на совместно используемой линии является источником прерывания.</p>
     <p>Все драйверы, которые рассчитаны на совместно используемую линию прерывания, должны удовлетворять указанным выше требованиям. Если хотя бы одно из устройств, которые совместно используют линию прерывания, не делает это корректно, то все остальные устройства также не смогут совместно использовать линию. Если функция <code>request_irq()</code> вызывается с указанием флага <code>SH_SHIRQ</code>, то этот вызов будет успешным только в том случае, если для данной линии прерывания еще нет зарегистрированных обработчиков или все обработчики для данной линии зарегистрированы с указанием флага <code>SH_SHIRQ</code>. Заметим, что для ядер серии 2.6, в отличие от более ранних серий, можно "смешивать" совместно используемые обработчики с различными значениями флага <code>SA_INTERRUPT</code>.</p>
     <p>Когда ядро получает прерывание, то оно последовательно вызывает все обработчики, зарегистрированные для данной линии. Поэтому важно, чтобы обработчик прерывания был в состоянии определить, какое устройство является источником этого прерывания. Обработчик должен быстро завершиться, если соответствующее ему устройство не генерировало это прерывание. Такое условие требует, чтобы аппаратное устройство имело регистр состояния (status register) или другой аналогичный механизм, которым обработчик может воспользоваться для проверки. На самом деле большинство устройств действительно имеют данную функцию.</p>
    </section>
    <section>
     <title>
      <p>Настоящий обработчик прерывания</p>
     </title>
     <p>Давайте рассмотрим настоящий обработчик прерывания, который используется в драйвере устройства RTC (real-time clock, часы реального времени), находящегося в файле <code>drivers/char/rtc.c</code>. Устройство RTC есть во многих вычислительных системах, включая персональные компьютеры (PC). Это отдельное от системного таймера устройство, которое используется для установки системных часов, для подачи сигналов таймера (alarm) или для реализации генераторов периодических сигналов (periodic timer). Установка системных часов обычно производится путем записи значений в специальный регистр или диапазон адресов (номеров портов) ввода-вывода (I/O range). Подача сигналов таймера или генератор периодических сигналов обычно реализуются через прерывания. Прерывание эквивалентно некоторому сигналу таймера: оно генерируется, когда истекает период времени сигнального таймера. При загрузке драйвера устройства RTC вызывается функция <code>rtc_init()</code> для инициализации драйвера. Одна из ее обязанностей — это регистрация обработчика прерывания. Делается это следующим образом.</p>
     <p><code>if (request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL) {</code></p>
     <p><code> printk(KERN_ERR "rtc: cannot register IRQ %d\n" , rtc_irq);</code></p>
     <p><code> return -EIO;</code></p>
     <p><code>}</code></p>
     <p>Из данного примера видно, что номер линии прерывания — это константа <code>RTC_IRQ</code>, значение которой определяется отдельно для каждой аппаратной платформы с помощью препроцессора. Например, для персональных компьютеров это значение всегда соответствует <code>IRQ 8</code>. Второй параметр— это обработчик прерывания, <code>rtc_interrupt</code>, при выполнении которого запрещены все прерывания в связи с указанием флага <code>SA_INTERRUPT</code>. Из четвертого параметра можно заключить, что драйвер будет иметь имя <code>"rtc"</code>. Так как наше устройство не может использовать линию прерывания совместно с другими устройствами и обработчик прерывания не используется для каких-либо других целей, в качестве параметра <code>dev_id</code> передается значение <code>NULL</code>.</p>
     <p>И наконец, собственно сам обработчик прерывания.</p>
     <p><code>/*</code></p>
     <p><code>* Очень маленький обработчик прерывания. Он выполняется с</code></p>
     <p><code>* установленным флагом SA_INTERRUPT, однако существует</code></p>
     <p><code>* возможность конфликта с выполнением функции set_rtc_mmss()</code></p>
     <p><code>* (обработчик прерывания rtc и обработчик прерывания системного</code></p>
     <p><code>* таймера могут выполняться одновременно на двух разных</code></p>
     <p><code>* процессорах). Следовательно, необходимо сериализировать доступ</code></p>
     <p><code>* к микросхеме с помощью спин-блокировки rtc_lock, что должно</code></p>
     <p><code>* быть сделано для всех аппаратных платформ в коде работы с</code></p>
     <p><code>* таймером. (Тело функции set_rtc_mmss() ищите в файлах</code></p>
     <p><code>* ./arch/XXXX/kernel/time.c)</code></p>
     <p><code>*/</code></p>
     <p><code>static irqreturn_t rtc_interrupt(int irq, void *dev_id,</code></p>
     <p><code> struct pt_regs *regs) {</code></p>
     <p><code> /*</code></p>
     <p><code> * Прерывание может оказаться прерыванием таймера, прерыванием</code></p>
     <p><code> * завершения обновления или периодическим прерыванием.</code></p>
     <p><code> * Состояние (причина) прерывания хранится в самом</code></p>
     <p><code> * младшем байте, а общее количество прерывание — в оставшейся</code></p>
     <p><code> * части переменной rtc_irq_data</code></p>
     <p><code> */</code></p>
     <p><code> spin_lock(&amp;rtc_lock);</code></p>
     <empty-line/>
     <p><code> rtc_irq_data += 0x100;</code></p>
     <p><code> rtc_irq_data &amp;= ~0xff;</code></p>
     <p><code> rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) &amp; 0xF0);</code></p>
     <empty-line/>
     <p><code> if (rtc_status &amp; RTC_TIMER_ON)</code></p>
     <p><code>  mod_timer(&amp;rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100);</code></p>
     <p><code> spin_unlock(&amp;rtc_lock);</code></p>
     <empty-line/>
     <p><code> /*</code></p>
     <p><code> * Теперь выполним остальные действия</code></p>
     <p><code> */</code></p>
     <p><code> spin_lock(&amp;rtc_task_lock);</code></p>
     <p><code> if (rtc_callback)</code></p>
     <p><code>  rtc_callback-&gt;func(rtc_callback-&gt;private_data);</code></p>
     <p><code> spin_unlock(&amp;rtc_task_lock);</code></p>
     <p><code> wake_up_interruptible(&amp;rtc_wait);</code></p>
     <empty-line/>
     <p><code> kill_fasync(&amp;rtc_async_queue, SIGIO, POLL_IN);</code></p>
     <empty-line/>
     <p><code> return IRQ_HANDLED;</code></p>
     <p><code>}</code></p>
     <p>Эта функция вызывается всякий раз, когда система получает прерывание от устройства RTC. Прежде всего, следует обратить внимание на вызовы функций работы со спин-блокировками: первая группа вызовов гарантирует, что к переменной <code>rtc_irq_data</code> не будет конкурентных обращений другими процессами на SMP-машине, а вторая — защищает в аналогичной ситуации параметры структуры <code>rtc_callback</code>. Блокировки обсуждаются в главе 9, "Средства синхронизации в ядре".</p>
     <p>Переменная <code>rtc_irq_data</code> содержит информацию об устройстве RTC и обновляется с помощью функции <code>mod_timer()</code>. О таймерах рассказывается в главе 10, "Таймеры и управление временем".</p>
     <p>Последняя часть кода, окруженная спин-блокировками, выполняет функцию обратного вызова (callback), которая может быть установлена извне. Драйвер RTC позволяет устанавливать функцию обратного вызова, которая может быть зарегистрирована пользователем и будет исполняться при каждом прерывании, приходящем от устройства RTC.</p>
     <p>В конце функция обработки прерывания возвращает значение <code>IRQ_HANDLED</code>, чтобы указать, что прерывание от данного устройства обработано правильно. Так как этот обработчик прерывания не поддерживает совместное использование линий прерывания и не существует механизма, посредством которого обработчик прерываний RTC может обнаружить вложенные запросы на прерывание, то этот обработчик всегда возвращает значение <code>IRQ_HANDLED</code>.</p>
    </section>
    <section>
     <title>
      <p>Контекст прерывания</p>
     </title>
     <p>При выполнении обработчика прерывания или обработчика нижней половины, ядро находится в <emphasis>контексте прерывания</emphasis>. Вспомним, что контекст процесса — это режим, в котором работает ядро, выполняя работу от имени процесса, например выполнение системного вызова или потока пространства ядра. В контексте процесса макрос <code>current</code> возвращает указатель на соответствующее задание. Более того, поскольку в контексте процесса процесс связан с ядром, то контекст процесса может переходить в состояние ожидания или использовать функции планировщика каким- либо другим способом..</p>
     <p>В противоположность только что рассмотренному, контекст прерывания не связан ни с одним процессом. Макрос <code>current</code> в контексте прерывания является незаконным (хотя он и указывает на процесс, выполнение которого было прервано). Так как нет процесса, то контекст прерывания не может переходить в состояние ожидания (sleep) — действительно, каким образом можно перепланировать его выполнение? Поэтому некоторые функции ядра не могут быть вызваны из контекста прерывания. Если функция может переводить процесс в состояние ожидания, то ее нельзя вызывать в обработчике прерывания, что ограничивает набор функций, которые можно использовать в обработчиках прерываний.</p>
     <p>Контекст прерывания является критичным ко времени исполнения, так как обработчик прерывания прерывает выполнение некоторого программного кода. Код же самого обработчика должен быть простой и быстрый. Использование циклов проверки состояния чего-либо (busy loop) крайне нежелательно. Это очень важный момент. Всегда следует помнить, что обработчик прерывания прерывает работу некоторого кода (возможно, даже обработчика другой линии запроса на прерывание!). В связи со своей асинхронной природой обработчики прерываний должны быть как можно более быстрыми и простыми. Максимально возможную часть работы необходимо изъять из обработчика прерывания и переложить на обработчик нижней половины, который выполняется в более подходящее время.</p>
     <p>Возможность установить стек контекста прерывания является конфигурируемой. Исторически, обработчик прерывания не имеет своего стека. Вместо этого он должен был использовать стек ядра прерванного процесса<a l:href="#n31" type="note">[31]</a>. Стек ядра имеет размер две страницы памяти, что обычно соответствует 8 Кбайт для 32-разрядных аппаратных платформ и 16 Кбайт для 64-разрядных платформ. Так как в таком случае обработчики прерываний совместно используют стек, то они должны быть очень экономными в отношении того, что они в этом стеке выделяют. Конечно, стек ядра изначально является ограниченным, поэтому любой код ядра должен принимать это во внимание.</p>
     <p>В ранних версиях ядер серии 2.6 была введена возможность ограничить размер стека ядра от двух до одной страницы памяти, что равно 4 Кбайт на 32-разрядных аппаратных платформах. Это уменьшает затраты памяти, потому что раньше каждый процесс требовал две страницы памяти ядра, которая не может быть вытеснена на диск. Чтобы иметь возможность работать со стеком уменьшенного размера, каждому обработчику прерывания выделяется свой стек, отдельный для каждого процессора. Этот стек называется <emphasis>стеком прерывания</emphasis>. Хотя общий размер стека прерывания и равен половине от первоначально размера совместно используемого стека, тем не менее в результате выходит, что суммарный размер стека получается большим, потому что на каждый стек прерывания выделяется целая страница памяти.</p>
     <p>Обработчик прерывания не должен зависеть от того, какие настройки стека используются и чему равен размер стека ядра. Всегда необходимо использовать минимально возможное количество памяти в стеке.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Реализация системы обработки прерываний</p>
    </title>
    <p>Возможно, не вызовет удивления, что реализация системы обработки прерываний в операционной системе Linux очень сильно зависит от аппаратной платформы. Она зависит от типа процессора, типа контроллера прерываний, особенностей аппаратной платформы и устройства самой вычислительной машины.</p>
    <p>На рис. 6.1 показана диаграмма пути, который проходит запрос на прерывание в аппаратном обеспечении и в ядре.</p>
    <image l:href="#img_12.jpeg"/>
    <p><strong>Рис. 6.1</strong>. Прохождение запроса на прерывание в аппаратном обеспечении и в ядре</p>
    <p>Устройство инициирует прерывание путем отправки электрического сигнала контроллеру прерывания по аппаратной шине. Если соответствующая линия запроса на прерывание не запрещена (линия может быть в данный момент времени замаскирована), то контроллер прерываний отправляет прерывание процессору. Для большинства аппаратных платформ это осуществляется путем подачи сигнала на специальный вывод процессора. Если прерывания не запрещены в процессоре (может случиться, что они запрещены), то процессор немедленно прекращает ту работу, которую он выполнял, запрещает систему прерываний, осуществляет переход на специальный предопределенный адрес памяти и начинает выполнять программный код, который находится по этому адресу. Этот предопределенный адрес памяти устанавливается ядром и является точкой входа в обработчики прерываний.</p>
    <p>Прохождение прерывания в ядре начинается из жестко определенной точки входа, так же как и в случае системных вызовов. Для каждой линии прерывания существует своя уникальная точка, куда переходит процессор. Именно этим способом ядро получает информацию о номере IRQ приходящего прерывания. В точке входа сначала в стеке ядра сохраняется значение номера прерывания и значения всех регистров процессора (которые соответствуют прерванному заданию). После этого ядро вызывает функцию <code>do_IRQ()</code>. Далее, начиная с этого момента, почти весь код обработки прерываний написан на языке программирования С, хотя несмотря на это код все же остается зависимым от аппаратной платформы.</p>
    <p>Функция <code>do_IRQ()</code> определена следующим образом.</p>
    <p><code>unsigned int do_IRQ(struct pt_regs regs);</code></p>
    <p>Так как соглашение о вызовах функций в языке С предусматривает сохранение аргументов функций в вершине стека, то структура <code>pt_regs</code> содержит первоначальные значения всех регистров процессора, которые были сохранены ассемблерной подпрограммой в точке входа. Так как значение номера прерывания также сохраняется, то функция <code>do_IRQ()</code> может это значение восстановить. Для аппаратной платформы x86 код будет следующим.</p>
    <p><code>int irq = regs.orig_eax &amp; 0xff;</code></p>
    <p>После вычисления значения номера линии прерывания, функция <code>do_IRQ()</code> отправляет уведомление о получении прерывания и запрещает доставку прерываний с данной линии. Для обычных машин платформы PC, эти действия выполняются с помощью функции <code>mask_and_ack_8295A()</code>, которую вызывает функция <code>do_IRQ()</code>. Далее функция <code>do_IRQ()</code> выполняет проверку, что для данной линии прерывания зарегистрирован правильный обработчик прерывания, что этот обработчик разрешен и что он не выполняется в данный момент. Если все эти условия выполнены, то вызывается функция <code>handle_IRQ_event()</code>, которая выполняет установленные для данной линии обработчики прерывания. Для аппаратной платформы x86 функция <code>handle_IRQ_event()</code> имеет следующий вид.</p>
    <p><code>int handle_IRQ_event(unsigned int irq, struct pt_regs *regs,</code></p>
    <p><code> struct irqaction *action) {</code></p>
    <p><code> int status = 1;</code></p>
    <p><code> if (!(action-&gt;flags &amp; SA_INTERRUPT))</code></p>
    <p><code>  local_irq_enable();</code></p>
    <p><code> do {</code></p>
    <p><code>  status != action-&gt;flags;</code></p>
    <p><code>  action-&gt;chandler(irq, action-&gt;dev_id, regs);</code></p>
    <p><code>  action = action-&gt;next;</code></p>
    <p><code> } while (action);</code></p>
    <p><code> if (status &amp; SA_SAMPLE_RANDOM)</code></p>
    <p><code>  add_interrupt_randomness(irq);</code></p>
    <p><code> local_irq_disable();</code></p>
    <p><code> return status;</code></p>
    <p><code>}</code></p>
    <p>Так как процессор запретил прерывания, они снова разрешаются, если не указан флаг <code>SA_INTERRUPT</code> при регистрации обработчика. Вспомним, что флаг <code>SA_INTERRUPT</code> указывает, что обработчик должен выполняться при всех запрещенных прерываниях. Далее в цикле вызываются все потенциальные обработчики прерываний. Если эта линия не является совместно используемой, то цикл заканчивается после первой итерации. В противном случае вызываются все обработчики. После этого вызывается функция <code>add_interrupt_randomness()</code>, если при регистрации указан флаг <code>SA_SAMPLE_RANDOM</code>. Данная функция использует временные характеристики прерывания, чтобы сгенерировать значение энтропии для генератора случайных чисел. В приложении Б, "Генератор случайных чисел ядра", приведена более подробная информация о генераторе случайных чисел ядра.</p>
    <p>В конце прерывания снова запрещаются (для функции <code>do_IRQ()</code> требуется, чтобы прерывания были запрещены). Функция <code>do_IRQ()</code> производит очистку стека и возврат к первоначальной точке входа, откуда осуществляется переход к функции <code>ret_from_intr()</code>.</p>
    <p>Функция <code>ret_from_intr()</code>, так же как и код входа, написана на языке ассемблера. Эта функция проверяет, есть ли ожидающий запрос на перепланирование выполнения процессов (следует вспомнить главу 4, "Планирование выполнения процессов", и флаг <code>need_resched</code>). Если есть запрос на перепланирование и ядро должно передать управление в пространство пользователя (т.е. прерывание прервало работу пользовательского процесса), то вызывается функция <code>schedule()</code>. Если возврат производится в пространство ядра (т.е. прерывание прервало работу кода ядра), то функция <code>schedule()</code> вызывается, только если значение счетчика <code>preempt_count</code> равно нулю (в противном случае небезопасно производить вытеснение кода ядра), После возврата из функции <code>schedule()</code> или если нет никакой ожидающей работы, восстанавливаются первоначальные значения регистров процессора и ядро продолжает работу там, где оно было прервано.</p>
    <p>Для платформы x86, подпрограммы, написанные на языке ассемблера, находятся в файле <code>arch/i386/kernel/entry.S</code>, а соответствующие функции на языке С — в файле <code>arch/i386/kernel/irq.с</code>. Для других поддерживаемых аппаратных платформ имеются аналогичные файлы.</p>
    <subtitle>Интерфейс <code>/proc/interrupts</code></subtitle>
    <p>Файловая система <emphasis>procfs</emphasis> — это виртуальная файловая система, которая существует только в памяти ядра и обычно монтируется на каталог <code>/proc</code>. Чтение или запись файлов на файловой системе procfs приводит к вызовам функций ядра, которые имитируют чтение или запись обычных файлов. Важный пример — это файл <code>/proc/interrupts</code>, который содержит статистику, связанную с прерываниями в системе, Ниже приведен пример вывода из этого файла на однопроцессорном персональном компьютере.</p>
    <p><code>CPU0</code></p>
    <p><code>  0: 3602371 XT-PIC timer</code></p>
    <p><code>  1: 3048    XT-PIC i8042</code></p>
    <p><code>  2: 0       XT-PIC cascade</code></p>
    <p><code>  4: 2689466 XT-PIC uhci-hcd, eth0</code></p>
    <p><code>  5: 0       XT-PIC EMU10K1</code></p>
    <p><code> 12: 85077   XT-PIC uhci-hcd</code></p>
    <p><code> 15: 24571   XT-PIC aic7xxx</code></p>
    <p><code>NMI: 0</code></p>
    <p><code>LOC: 3602236</code></p>
    <p><code>ERR: 0</code></p>
    <p>Первая колонка содержит названия линий прерывания. В показанной системе присутствуют линии прерываний с номерами 0–2, 4, 5, 12 и 15. Линии, для которых не инсталлирован обработчик, не показываются. Вторая колонка — это количество запросов на прерывания с данным номером. В действительности такая колонка является отдельной для каждого процессора, но в данной машине только один процессор.</p>
    <p>Как легко видеть, обработчик прерываний таймера получил <code>3.602.371</code><a l:href="#n32" type="note">[32]</a> запрос на прерывание, в то время как обработчик прерываний звукового адаптера (<code>EMU10K1</code>) не получил ни одного прерывания (это говорит о том, что он не использовался с того момента, как машина была загружена). Третья колонка— это контроллер прерываний, который обслуживает данное прерывание. Значение <code>XT-PIC</code> соответствует программируемому контроллеру прерываний PC (PC programmable interrupt controller). Для систем с устройством I/О APIC для большинства прерываний в качестве контроллера прерываний будет указано значение <code>IO-APIC-level</code> или <code>IO-APIC-edge</code>. И наконец, последняя колонка — это устройство, которое связано с прерыванием. Имя устройства указывается в параметре <code>dev_name</code> при вызове функции <code>request_irq()</code>, как обсуждалось ранее. Если прерывание используется совместно, как в случае прерывания номер 4 в этом примере, то перечисляются все устройства, зарегистрированные на данной линии прерывания.</p>
    <p>Для любопытствующих, код, связанный с файловой системой <code>procfs</code>, находится в файле <code>fs/proc</code>. Функция, которая обеспечивает работу интерфейса <code>/proc/interrupts</code>, называется <code>show_interrupts()</code> и является зависимой от аппаратной платформы.</p>
   </section>
   <section>
    <title>
     <p>Управление прерываниями</p>
    </title>
    <section>
     <p>В ядре Linux реализовано семейство интерфейсов для управления состояниями прерываний в машине. Эти интерфейсы позволяют запрещать прерывания для текущего процессора или маскировать линию прерывания для всей машины. Эти функции очень сильно зависят от аппаратной платформы и находятся в файлах <code>&lt;asm/system.h&gt;</code> и <code>&lt;asm/irq.h&gt;</code>. В табл. 6.2 приведен полный список этих интерфейсов.</p>
     <p>Причины, по которым необходимо управлять системой обработки прерываний, в основном, сводятся к необходимости обеспечения синхронизации. Путем запрещения прерываний можно гарантировать, что обработчик прерывания не вытеснит текущий исполняемый код. Более того, запрещение прерываний также запрещает и вытеснение кода ядра. Однако ни запрещение доставки прерываний, ни запрещение преемптивности ядра не дают никакой защиты от конкурентного обращения других процессоров. Так как операционная система Linux поддерживает многопроцессорные системы, в большинстве случаев код ядра должен захватить некоторую блокировку, чтобы предотвратить доступ другого процессора к совместно используемым данным. Эти блокировки обычно захватываются в комбинации с запрещением прерываний на текущем процессоре. Блокировка предоставляет защиту от доступа другого процессора, а запрещение прерываний обеспечивает защиту от конкурентного доступа из возможного обработчика прерывания. В главах 8 и 9 обсуждаются различные аспекты проблем синхронизации и решения этих проблем.</p>
     <p>Тем не менее понимание интерфейсов ядра для управления прерываниями является важным.</p>
    </section>
    <section>
     <title>
      <p>Запрещение и разрешение прерываний</p>
     </title>
     <p>Для локального запрещения прерываний на текущем процессоре (и <emphasis>только</emphasis> на текущем процессоре) и последующего разрешения можно использовать следующий код.</p>
     <p><code>local_irq_disable();</code></p>
     <p><code>/* прерывания запрещены ... */</code></p>
     <p><code>local_irq_enable();</code></p>
     <p>Эти функции обычно реализуются в виде одной инструкции на языке ассемблера (что, конечно, зависит от аппаратной платформы). Для платформы x86 функция <code>local_irq_disable()</code> — это просто машинная инструкция <code>cli</code>, а функция <code>local_irq_enable()</code> — просто инструкция <code>sti</code>. Для хакеров, не знакомых с платформой x86, <code>sti</code> и <code>cli</code> — это ассемблерные вызовы, которые соответственно позволяют установить (<emphasis><strong>s</strong>e<strong>t</strong></emphasis>) или очистить (<emphasis><strong>cl</strong>ear</emphasis>) флаг разрешения прерываний (<emphasis>allow <strong>i</strong>nterrupt flag</emphasis>). Другими словами, они разрешают или запрещают доставку прерываний на вызвавшем их процессоре.</p>
     <p>Функция <code>local_irq_disable()</code> является опасной в случае, когда <emphasis>перед</emphasis> ее вызовом прерывания уже были запрещены. При этом соответствующий ей вызов функции <code>local_irq_enable()</code> разрешит прерывания независимо от того, были они запрещены первоначально (до вызова <code>local_irq_disable()</code>) или нет. Для того чтобы избежать такой ситуации, необходим механизм, который позволяет восстанавливать состояние системы обработки прерывании в первоначальное значение. Это требование имеет общий характер, потому что некоторый участок кода ядра в одном случае может выполняться при разрешенных прерываниях, а в другом случае— при запрещенных, в зависимости от последовательности вызовов функций. Например, пусть показанный фрагмент кода является частью функции. Эта функция вызывается двумя другими функциями, и в первом случае перед вызовом прерывания запрещаются, а во втором — нет. Так как при увеличении объема кода ядра становится сложно отслеживать все возможные варианты вызова функции, значительно безопаснее сохранять состояние системы прерываний перед тем, как запрещать прерывания. Вместо разрешения прерываний просто восстанавливается первоначальное состояние системы обработки прерываний следующим образом.</p>
     <p><code>unsigned long flags;</code></p>
     <empty-line/>
     <p><code>local_irq_save(flags);</code></p>
     <p><code>/* прерывания запрещены . . */</code></p>
     <p><code>local_irq_restore(flags);</code></p>
     <p><code>/* состояние системы прерываний восстановлено</code></p>
     <p><code>   в первоначальное значение ... */</code></p>
     <p>Нужно заметить, что эти функции являются макросами, поэтому передача параметра <code>flags</code> выглядит как передача по значению. Этот параметр содержит зависящие от аппаратной платформы данные, которые в свою очередь содержат состояние системы прерываний. Так как, по крайней мере для одной аппаратной платформы (SPARC), в этой переменной хранится информация о стеке, то параметр <code>flags</code> нельзя передавать в другие функции (другими словами, он должен оставаться в одном и том же стековом фрейме). По этой причине вызовы функций сохранения и восстановления должны выполняться в теле одной функции.</p>
     <p>Все описанные функции могут вызываться как из обработчика прерываний, так и из контекста процесса.</p>
     <cite>
      <subtitle>Больше нет глобального вызова <code>cli()</code></subtitle>
      <p>Ранее ядро предоставляло функцию, с помощью которой можно было запретить прерывания на всех процессорах системы. Более того, если какой-либо процессор вызывал эту функцию, то он должен был ждать, пока прерывания не будут разрешены. Эта функция называлась <code>cli()</code>, а соответствующая ей разрешающая функция — <code>sti()</code>; очень "x86-центрично" (хотя и было доступно для всех аппаратных платформ). Эти интерфейсы были изъяты во время разработки ядер серии 2.5, и, следовательно, все операции по синхронизации обработки прерываний должны использовать комбинацию функций по управлению локальными прерываниями и функций работы со спин-блокировками (обсуждаются в главе 9, "Средства синхронизации в ядре"). Это означает, что код, который ранее должен был всего лишь глобально запретить прерывания для того, чтобы получить монопольный доступ к совместно используемым данным, теперь должен выполнить несколько больше работы.</p>
      <p>Ранее разработчики драйверов могли считать, что если в их обработчике прерывания и в любом другом коде, который имеет доступ к совместно используемым данным, вызывается функция <code>cli()</code>, то это позволяет получить монопольный доступ. Функция <code>cli()</code> позволяла гарантировать, что ни один из обработчиков прерываний (в том числе и другие экземпляры текущего обработчика) не выполняется. Более того, если любой другой процессор входит в участок кода, защищенный с помощью функции <code>cli()</code>, то он не продолжит работу, пока первый процессор не выйдет из участка кода, защищенного с помощью функции <code>cli()</code>, т.е. не вызовет функцию <code>sti()</code>.</p>
      <p>Изъятие глобальной функции <code>cli()</code> имеет несколько преимуществ. Во-первых, это подталкивает разработчиков драйверов к использованию настоящих блокировок. Специальные блокировки на уровне мелких структурных единиц работают быстрее, чем глобальные блокировки, к которым относится и функция <code>cli()</code>. Во-вторых, это упрощает значительную часть кода и позволяет удалить большой объем кода. В результате система обработки прерываний стала проще и понятнее.</p>
     </cite>
    </section>
    <section>
     <title>
      <p>Запрещение определенной линии прерывания</p>
     </title>
     <p>В предыдущем разделе были рассмотрены функции, которые позволяют запретить доставку всех прерываний на определенном процессоре. В некоторых случаях полезным может оказаться запрещение <emphasis>определенной</emphasis> линии прерывания во <emphasis>всей</emphasis> системе. Это называется <emphasis>маскированием</emphasis> линии прерывания. Например, может потребоваться запрещение доставки прерывания от некоторого устройства перед манипуляциями с его состоянием. Для этой цели операционная система Linux предоставляет четыре интерфейса.</p>
     <p><code>void disable_irq(unsigned int irq);</code></p>
     <p><code>void disable_irq_nosync(unsigned int irq);</code></p>
     <p><code>void enable_irq(unsigned int irq);</code></p>
     <p><code>void synchronize_irq(unsigned int irq);</code></p>
     <p>Первые две функции позволяют запретить указанную линию прерывания в контроллере прерываний. Это запрещает доставку данного прерывания всем процессорам в системе. Кроме того, функция <code>disable_irq()</code> не возвращается до тех пор, пока все обработчики прерываний, которые в данный момент выполняются, не закончат работу. Таким образом гарантируется не только то, что прерывания с данной линии не будут доставляться, но и то, что все выполняющиеся обработчики закончили работу. Функция <code>disable_irq_nosync()</code> не имеет последнего свойства.</p>
     <p>Функция <code>synchronize_irq()</code> будет ожидать, пока не завершится указанный обработчик, если он, конечно, выполняется.</p>
     <p>Вызовы этих функций должны быть сгруппированы, т.е. каждому вызову функции <code>disable_irq()</code> или <code>disable_irq_nosync()</code> должен соответствовать вызов функции <code>enable_irq()</code>. Только после последнего вызова функции <code>enable_irq()</code> линия запроса на прерывание будет снова разрешена. Например, если функция <code>disable_irq()</code> последовательно вызвана два раза, то линия запроса на прерывание не будет разрешена, пока функция <code>enable_irq()</code> тоже не будет вызвана два раза.</p>
     <p>Эти три функции могут быть вызваны из контекста прерывания и из контекста процесса и не приводят к переходу в приостановленное состояние (sleep). При вызове из контекста прерывания следует быть осторожным! Например, нельзя разрешать линию прерывания во время выполнения обработчика прерывания (вспомним, что линия запроса на прерывание обработчика, который в данный момент выполняется, является замаскированной).</p>
     <p>Было бы также плохим тоном запрещать линию прерывания, которая совместно используется несколькими обработчиками. Запрещение линии прерывания запрещает доставку прерываний для <emphasis>всех</emphasis> устройств, которые используют эту линию. Поэтому в драйверах новых устройств не рекомендуется использовать эти интерфейсы<a l:href="#n33" type="note">[33]</a>. Так как устройства PCI должны согласно спецификации поддерживать совместное использование линий прерываний, они вообще не должны использовать эти интерфейсы. Поэтому функция <code>disable_irq()</code> и дружественные ей обычно используются для устаревших устройств, таких как параллельный порт персонального компьютера.</p>
    </section>
    <section>
     <title>
      <p>Состояние системы обработки прерываний</p>
     </title>
     <p>Часто необходимо знать состояние системы обработки прерываний (например, прерывания запрещены или разрешены, выполняется ли текущий код в контексте прерывания или в контексте процесса).</p>
     <p>Макрос <code>irq_disabled()</code>, который определен в файле <code>&lt;asm/system.h&gt;</code>, возвращает ненулевое значение, если обработка прерываний на локальном процессоре запрещена. В противном случае возвращается нуль. Два следующих макроса позволяют определить контекст, в котором в данный момент выполняется ядро.</p>
     <p><code>in_interrupt()</code></p>
     <p><code>in_irq()</code></p>
     <p>Наиболее полезный из них — это первый макрос. Он возвращает ненулевое значение, если ядро выполняется в контексте прерывания. Это включает выполнение как обработчика прерывания, так и обработчика нижней половины. Макрос <code>in_irq()</code> возвращает ненулевое значение, только если ядро выполняет обработчик прерывания.</p>
     <p>Наиболее часто необходимо проверять, выполняется ли код в контексте процесса, т.е. необходимо проверить, что код выполняется не в контексте прерывания. Это требуется достаточно часто, когда коду необходимо выполнять что-то, что может быть выполнено только из контекста процесса, например переход в приостановленное состояние. Если макрос <code>in_interrupt()</code> возвращает нулевое значение, то ядро выполняется в контексте процесса.</p>
     <empty-line/>
     <p><strong>Таблица 6.2</strong>. Список функций управления прерываниями</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Функция</th>
       <th align="left" valign="top">Описание</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>local_irq_disable()</code></td>
       <td align="left" valign="top">Запретить доставку прерываний на локальном процессоре</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>local_irq_enable()</code></td>
       <td align="left" valign="top">Разрешить доставку прерываний на локальном процессоре</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>local_irq_save(unsigned long flags)</code></td>
       <td align="left" valign="top">Сохранить текущее состояние системы обработки прерываний на локальном процессоре и запретить прерывания</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>local_irq_restore(unsigned long flags)</code></td>
       <td align="left" valign="top">Восстановить указанное состояние системы прерываний на локальном процессоре</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>disable_irq(unsigned int irq)</code></td>
       <td align="left" valign="top">Запретить указанную линию прерывания с гарантией, что после возврата из этой функции не выполняется ни один обработчик данной линии</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>disable_irq_nosync(unsigned int irq)</code></td>
       <td align="left" valign="top">Запретить указанную линию прерывания</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>enable_irq(unsigned int irq)</code></td>
       <td align="left" valign="top">Разрешить указанную линию прерываний</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>irqs_disabled()</code></td>
       <td align="left" valign="top">Возвратить ненулевое значение, если запрещена доставка прерываний на локальном процессоре, в противном случае возвращается нуль</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>in_interrupt()</code></td>
       <td align="left" valign="top">Возвратить ненулевое значение, если выполнение производится в контексте прерывания, и нуль — если в контексте процесса</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>in_irq()</code></td>
       <td align="left" valign="top">Возвратить ненулевое значение, если выполнение производится в контексте прерывания, и нуль — в противном случае</td>
      </tr>
     </table>
    </section>
   </section>
   <section>
    <title>
     <p>Не нужно прерывать, мы почти закончили!</p>
    </title>
    <p>В этой главе были рассмотрены прерывания, аппаратные ресурсы, которые используются устройствами для подачи асинхронных сигналов процессору. Прерывания используются аппаратным обеспечением, чтобы прервать работу операционной системы.</p>
    <p>Большинство современного аппаратного обеспечения использует прерывания, чтобы взаимодействовать с операционной системой. Драйвер устройства, который управляет некоторым оборудованием, должен зарегистрировать обработчик прерывания, чтобы отвечать на эти прерывания и обрабатывать их. Работа, которая выполняется обработчиками прерываний, включает отправку подтверждения устройству о получении прерывания, инициализацию аппаратного устройства, копирование данных из памяти устройства в память системы и, наоборот, обработку аппаратных запросов и отправку ответов на них.</p>
    <p>Ядро предоставляет интерфейсы для регистрации и освобождения обработчиков прерываний, запрещения прерываний, маскирования линий прерываний и проверки состояния системы прерываний. В табл. 6.2 приведен обзор некоторых из этих функций.</p>
    <p>Так как прерывания прерывают выполнение другого кода (кода процессов, кода ядра и другие обработчики прерываний), то они должны выполняться быстро. Тем не менее часто приходится выполнять много работы. Для достижения компромисса между большим количеством работы и необходимостью быстрого выполнения обработка прерывания делится на две половины. Верхняя половина — собственно обработчик прерывания — рассматривается в этой главе. Теперь давайте рассмотрим нижнюю половину процесса обработки прерывания.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 7</p>
    <p>Обработка нижних половин и отложенные действия</p>
   </title>
   <section>
    <p>В предыдущей главе были рассмотрены обработчики прерываний — механизм ядра, который позволяет решать задачи, связанные с аппаратными прерываниями. Конечно, обработчики прерываний очень полезны и являются необходимой частью ядра. Однако, в связи с некоторыми ограничениями, они представляют собой лишь часть процесса обработки прерываний. Эти ограничения включают следующие моменты.</p>
    <p>• Обработчики прерываний выполняются асинхронно и потенциально могут прерывать выполнение другого важного кода (даже другие обработчики прерываний). Поэтому обработчики прерываний должны выполняться как можно быстрее.</p>
    <p>• Обработчики прерываний выполняются в лучшем случае при запрещенной обрабатываемой линии прерывания и в худшем случае (когда установлен флаг <code>SA_INTERRUPT</code>) — при всех запрещенных линиях запросов на прерывания. И снова они должны выполняться как можно быстрее.</p>
    <p>• Обработчики прерываний очень критичны ко времени выполнения, так как они имеют дело с аппаратным обеспечением.</p>
    <p>• Обработчики прерываний не выполняются в контексте процесса, поэтому они не могут блокироваться.</p>
    <p>Теперь должно быть очевидным, что обработчики прерываний являются только частью полного решения проблемы обработки аппаратных прерываний. Конечно, необходим быстрый, простой и асинхронный обработчик, позволяющий немедленно отвечать на запросы оборудования и выполнять критичную ко времени выполнения работу. Эту функцию обработчики прерываний выполняют хорошо, но другая, менее критичная ко времени выполнения работа должна быть отложена до того момента, когда прерывания будут разрешены.</p>
    <p>Для этого обработка прерывания делится на две части или <emphasis>половины</emphasis>. Первая часть обработчика прерывания (<emphasis>top half</emphasis>, <emphasis>верхняя половина</emphasis>) выполняется асинхронно и немедленно в ответ на аппаратное прерывание так, как это обсуждалось в предыдущей главе. В этой главе мы рассмотрим вторую часть процесса обработки прерываний — <emphasis>нижние половины</emphasis> (<emphasis>bottom half</emphasis>).</p>
   </section>
   <section>
    <title>
     <p>Нижние половины</p>
    </title>
    <section>
     <p>Задача обработки нижних половин — это выполнить всю связанную с прерываниями работу, которую не выполнил обработчик прерывания. В идеальной ситуации — это почти вся работа, так как необходимо, чтобы обработчик прерывания выполнил по возможности меньшую часть работы (т.е. выполнился максимально быстро) и побыстрее возвратил управление.</p>
     <p>Тем не менее обработчик прерывания должен выполнить некоторые действия. Например, почти всегда обработчик прерывания должен отправить устройству уведомление, что прерывание получено. Он также должен произвести копирование некоторых данных из аппаратного устройства. Эта работа чувствительна ко времени выполнения, поэтому есть смысл выполнить ее в самом обработчике прерывания.</p>
     <p>Практически все остальные действия будет правильным выполнить в обработчике нижней половины. Например, если в верхней половине было произведено копирование данных из аппаратного устройства в память, то в обработчике нижней половины, конечно, имеет смысл эти данные обработать. К сожалению, не существует твердых правил, позволяющих определить, какую работу где нужно выполнять, — право решения остается за автором драйвера. Хотя ни одно из решений этой задачи не может быть неправильным, решение легко может оказаться неоптимальным. Следует помнить, что обработчики прерываний выполняются асинхронно при запрещенной, по крайней мере, текущей линии запроса на прерывание. Минимизация этой задержки является важной. Хотя нет строгих правил по поводу того, как делить работу между обработчиками верхней и нижней половин, все же можно привести несколько полезных советов.</p>
     <p>• Если работа критична ко времени выполнения, то ее необходимо выполнять в обработчике прерывания.</p>
     <p>• Если работа связана с аппаратным обеспечением, то ее следует выполнить в обработчике прерывания.</p>
     <p>• Если для выполнения работы необходимо гарантировать, что другое прерывание (обычно с тем же номером) не прервет обработчик, то работу нужно выполнить в обработчике прерывания.</p>
     <p>• Для всего остального работу стоит выполнять в обработчике нижней половины.</p>
     <p>При написании собственного драйвера устройства есть смысл посмотреть на обработчики прерываний и соответствующие им обработчики нижних половин других драйверов устройств — это может помочь. Принимая решение о разделении работы между обработчиками верхней и нижней половины, следует спросить себя: "Что <emphasis>должно</emphasis> быть в обработчике верхней половины, а что <emphasis>может</emphasis> быть в обработчике нижней половины". В общем случае, чем быстрее выполняется обработчик прерывания, тем лучше.</p>
    </section>
    <section>
     <title>
      <p>Когда нужно использовать нижние половины</p>
     </title>
     <p>Часто не просто понять, зачем нужно откладывать работу и когда именно ее нужно откладывать. Вам необходимо ограничить количество работы, которая выполняется в обработчике прерывания, потому что обработчик прерывания выполняется при запрещенной текущей линии прерывания. Хуже того, обработчики, зарегистрированные с указанием флага <code>SA_INTERRUPT</code>, выполняются при <emphasis>всех</emphasis> запрещенных линиях прерываний на локальном процессоре (плюс текущая линия прерывания запрещена глобально). Минимизировать время, в течение которого прерывания запрещены, важно для уменьшения времени реакции и увеличения производительности системы. Если к этому добавить, что обработчики прерываний выполняются асинхронно по отношению к другому коду, и даже по отношению к другим обработчикам прерываний, то становится ясно, что нужно минимизировать время выполнения обработчика прерывания. Решение — отложить некоторую часть работы на более поздний срок.</p>
     <p>Но что имеется в виду под более поздним сроком? Важно понять, что <emphasis>позже</emphasis> означает <emphasis>не сейчас</emphasis>. Основной момент в обработке нижних половин — это не отложить работу до <emphasis>определенного</emphasis> момента времени в будущем, а отложить работу до некоторого неопределенного момента времени в будущем, когда система будет не так загружена и все прерывания снова будут разрешены.</p>
     <p>Не только в операционной системе Linux, но и в других операционных системах обработка аппаратных прерываний разделяется на две части. Верхняя половина выполняется быстро, когда все или некоторые прерывания запрещены. Нижняя половина (если она реализована) выполняется позже, когда все прерывания разрешены. Это решение позволяет поддерживать малое время реакции системы, благодаря тому что работа при запрещенных прерываниях выполняется в течение возможно малого периода времени.</p>
    </section>
    <section>
     <title>
      <p>Многообразие нижних половин</p>
     </title>
     <p>В отличие от обработчиков верхних половин, которые могут быть реализованы только в самих обработчиках прерываний, для реализации обработчиков нижних половин существует несколько механизмов. Эти механизмы представляют собой различные интерфейсы и подсистемы, которые позволяют пользователю реализовать обработку нижних половин. В предыдущей главе мы рассмотрели единственный существующий механизм реализации обработчиков прерываний, а в этой главе рассмотрим несколько методов реализации обработчиков нижних половин. На самом деле за историю операционной системы Linux существовало много механизмов обработки нижних половин. Иногда сбивает с толку то, что эти механизмы имеют очень схожие или очень неудачные названия. Для того чтобы придумывать названия механизмам обработки нижних половин, необходимы "специальные программисты".</p>
     <p>В этой главе мы рассмотрим принципы работы и реализацию механизмов обработки нижних половин, которые существуют в ядрах операционной системы Linux серии 2.6. Также будет рассмотрено, как использовать эти механизмы в коде ядра, который вы можете написать. Старые и давно изъятые из употребления механизмы обработки нижних половин представляют собой историческую ценность, поэтому, где это важно, о них также будет рассказано.</p>
     <p>В самом начале своего существования операционная система Linux предоставляла единственный механизм для обработки нижних половин, который так и назывался "нижние половины" ("bottom half"). Это название было понятно, так как существовало только одно средство для выполнения отложенной обработки. Соответствующая инфраструктура называлась "BH" и мы ее так дальше и будем назвать, чтобы избежать путаницы с общим термином "bottom half (нижняя половина). Интерфейс BH был очень простым, как и большинство вещей в те старые добрые времена. Он предоставлял статический список из 32 обработчиков нижних половин. Обработчик верхней половины должен был отметить какой из обработчиков нижних половин должен выполняться путем установки соответствующего бита в 32-разрядном целом числе. Выполнение каждого обработчика BH синхронизировалось глобально, т.е. никакие два обработчика не могли выполняться одновременно, даже на разных процессорах. Такой механизм был простым в использовании, хотя и не гибким; простым в реализации, хотя представлял собой узкое место в плане производительности.</p>
     <p>Позже разработчики ядра предложили механизм очередей заданий (<emphasis>task queue</emphasis>) — одновременно как средство выполнения отложенной обработки и как замена для механизма BH. В ядре определялось семейство очередей. Каждая очередь содержала связанный список функций, которые должны были выполнять соответствующие действия. Функции, стоящие в очереди, выполнялись в определенные моменты времени, в зависимости от того, в какой очереди они находились. Драйверы могли регистрировать собственные обработчики нижних половин в соответствующих очередях. Этот механизм работал достаточно хорошо, но он был не настолько гибким, чтобы полностью заменить интерфейс BH. Кроме того, он был достаточно "тяжеловесным" для обеспечения высокой производительности критичных к этому систем, таких как сетевая подсистема.</p>
     <p>Во время разработки серии ядер 2.3 разработчики ядра предложили механизм <emphasis>отложенных прерываний</emphasis><a l:href="#n34" type="note">[34]</a> (<emphasis>softirq</emphasis>) и механизм <emphasis>тасклетов</emphasis> (<emphasis>tasklet</emphasis>).</p>
     <p>За исключением решения проблемы совместимости с существующими драйверами, механизмы отложенных прерываний и тасклетов были в состоянии полностью заменить интерфейс BH<a l:href="#n35" type="note">[35]</a>.</p>
     <p>Отложенные прерывания — это набор из 32 статически определенных обработчиков нижних половин, которые могут одновременно выполняться на разных процессорах, даже два обработчика одного типа могут выполняться параллельно. Тасклеты — это гибкие, динамически создаваемые обработчики нижних половин, которые являются надстройкой над механизмом отложенных прерываний и имеют ужасное название, смущающее всех<a l:href="#n36" type="note">[36]</a>.</p>
     <p>Два различных тасклета могут выполняться параллельно на разных процессорах, но при этом два тасклета одного типа не могут выполняться одновременно. Таким образом, тасклеты — это хороший компромисс между производительностью и простотой использования. В большинстве случаев для обработки нижних половин достаточно использования тасклетов. Обработчики отложенных прерываний являются полезными, когда критична производительность, например, для сетевой подсистемы. Использование механизма отложенных прерываний требует осторожности, потому что два обработчика одного и того же отложенного прерывания могут выполняться одновременно. В дополнение к этому, отложенные прерывания должны быть зарегистрированы статически на этапе компиляции. Тасклеты, наоборот, могут быть зарегистрированы динамически.</p>
     <p>Еще больше запутывает ситуацию то, что некоторые люди говорят о всех обработчиках нижних половин как о программных прерываниях, или отложенных прерываниях (software interrupt, или softirq). Другими словами, они называют механизм отложенных прерываний и в общем обработку нижних половин программными прерываниями. На таких людей лучше не обращать внимания, они из той же категории, что и те, которые придумали название "BH" и тасклет.</p>
     <p>Во время разработки ядер серии 2.5 механизм BH был в конце концов выброшен, потому что все пользователи этого механизма конвертировали свой код для использования других интерфейсов обработки нижних половин. В дополнение к этому, интерфейс очередей заданий был заменен на новый интерфейс очередей отложенных действий (work queue). Очереди отложенных действий— это простой и в то же время полезный механизм, позволяющий поставить некоторое действие в очередь для выполнения в контексте процесса в более поздний момент времени.</p>
     <p>Следовательно, сегодня ядро серии 2.6 предоставляет три механизма обработки нижних половин в ядре: отложенные прерывания, тасклеты и очереди отложенных действий. В ядре также использовались интерфейсы BH и очередей заданий, но сегодня от них осталась только светлая память.</p>
     <cite>
      <subtitle>Таймеры ядра</subtitle>
      <p>Еще один механизм выполнения отложенной работы — это таймеры ядра. В отличие от механизмов, рассмотренных в этой главе, таймеры позволяют отсрочить работу на указанный интервал времени. Инструменты, описанные в этой главе, могут быть полезны для откладывания работы <emphasis>с текущего момента до какого-нибудь момента времени</emphasis> в будущем. Таймеры используются для откладывания работы до того момента, пока не пройдет указанный период времени.</p>
      <p>Поэтому таймеры имеют другое назначение, чем механизмы, описанные в данной главе. Более полное обсуждение таймеров ядра будет приведено в главе 10, "Таймеры и управление временем".</p>
     </cite>
     <subtitle>Путаница с нижними половинами</subtitle>
     <p>Некоторая путаница с обработчиками нижних половин имеет место, но на самом деле — это только проблема названий. Давайте снова вернемся к этому вопросу.</p>
     <p>Термин "нижняя половина" ("bottom half") — это общий термин, который касается операционных систем и связан с тем, что некоторая часть процесса обработки прерывания откладывается на будущее. В операционной системе Linux сейчас этот термин означает то же самое. Все механизмы ядра, которые предназначены для отложенной обработки, являются обработчиками нижних половин.</p>
     <p>Некоторые люди также называют обработчики нижних половин программными прерываниями иди "softirq", но они просто пытаются досадить остальным.</p>
     <p>Термин "Bottom Half" также соответствует названию самого первого механизма выполнения отложенных действий в операционной системе Linux. Этот механизм еще называется "BH", поэтому далее так будем называть именно этот механизм, а термин "нижняя половина" ("bottom half") будет касаться общего названия. Механизм BH был некоторое время назад выключен из употребления и полностью изъят в ядрах серии 2.5.</p>
     <p>Сейчас есть три метода для назначения отложенных операций: механизм отложенных прерываний (softirq), механизм тасклетов и механизм очередей отложенных действий. Тасклеты построены на основе механизма softirq, а очереди отложенных действий имеют полностью отличную реализацию. В табл. 7.1 показана история обработчиков нижних половин.</p>
     <empty-line/>
     <p><strong>Таблица 7.1</strong>. Состояние обработчиков нижних половин</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Механизм обработчиков</th>
       <th align="left" valign="top">Состояние</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top">BH</td>
       <td align="left" valign="top">Изъято в серии 2.5</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">Очереди заданий</td>
       <td align="left" valign="top">Изъято в серии 2.5</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">Отложенные прерывания</td>
       <td align="left" valign="top">Доступно начиная с серии 2.3</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">Тасклеты</td>
       <td align="left" valign="top">Доступно начиная с серии 2.3</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">Очереди отложенных действий</td>
       <td align="left" valign="top">Доступно начиная с серии 2.3</td>
      </tr>
     </table>
     <p>Давайте продолжим рассмотрение каждого из механизмов в отдельности, пользуясь этой устойчивой путаницей в названиях.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Механизм отложенных прерываний (softirq)</p>
    </title>
    <section>
     <p>Обсуждение существующих методов обработки нижних половин начнем с механизма softirq. Обработчики на основе механизма отложенных прерываний используются редко. Тасклеты — это более часто используемая форма обработчика нижних половин. Поскольку тасклеты построены на основе механизма softirq, с механизма softirq и стоит начать. Код, который касается обработчиков отложенных прерываний, описан в файле <code>kernel/softirq.c</code>.</p>
    </section>
    <section>
     <title>
      <p>Реализация отложенных прерываний</p>
     </title>
     <p>Отложенные прерывания определяются статически во время компиляции. В отличие от тасклетов, нельзя динамически создать или освободить отложенное прерывание. Отложенные прерывания представлены с помощью структур <code>softirq_action</code>, определенных в файле <code>&lt;linux/interrupt.h&gt;</code> в следующем виде.</p>
     <p><code>/*</code></p>
     <p><code>* структура, представляющая одно отложенное прерывание</code></p>
     <p><code>*/</code></p>
     <empty-line/>
     <p><code>struct softirq_action {</code></p>
     <p><code> void (*action)(struct softirq_action*);</code></p>
     <p><code>       /* функция, которая должна выполниться */</code></p>
     <p><code> void *data; /* данные для передачи в функцию */</code></p>
     <p><code>};</code></p>
     <p>Массив из 32 экземпляров этой структуры определен в файле <code>kernel/softirq.с</code> в следующем виде.</p>
     <p><code>static struct softirq_action softirq_vec[32];</code></p>
     <p>Каждое зарегистрированное отложенное прерывание соответствует одному элементу этого массива. Следовательно, имеется возможность создать 32 обработчика softirq. Заметим, что это количество фиксировано. Максимальное число обработчиков softirq не может быть динамически изменено. В текущей версии ядра из 32 элементов используется только шесть<a l:href="#n37" type="note">[37]</a>.</p>
     <subtitle>Обработчик softirq</subtitle>
     <p>Прототип обработчика отложенного прерывания, поля <code>action</code>, выглядит следующим образом.</p>
     <p><code>void softirq_handler(struct softirg_action*);</code></p>
     <p>Когда ядро выполняет обработчик отложенного прерывания, то функция <code>action</code> вызывается С указателем на соответствующую структуру <code>softirq_action</code> в качестве аргумента. Например, если переменная <code>my_softirq</code> содержит указатель на элемент массива <code>softirq_vec</code>, то ядро вызовет функцию-обработчик соответствующего отложенного прерывания в следующем виде.</p>
     <p><code>my_softirq-&gt;action(my_softirq);</code></p>
     <p>Может быть, несколько удивляет, что ядро передает в обработчик указатель на всю структуру, а не только на поле data. Этот прием позволяет в будущем вводить дополнительные поля в структуру без необходимости внесения изменений в существующие обработчики. Обработчик может получить доступ к значению ноля <code>data</code> простым разыменованием указателя на структуру и чтением ее поля <code>data</code>.</p>
     <p>Обработчик одного отложенного прерывания никогда не вытесняет другой обработчик softirq. Б действительности, единственное событие, которое может вытеснить обработчик softirq, — это аппаратное прерывание. Однако на другом процессоре одновременно с обработчиком отложенного прерывания может выполняться другой (и даже этот же) обработчик отложенного прерывания.</p>
     <subtitle>Выполнение отложенных прерываний</subtitle>
     <p>Зарегистрированное отложенное прерывание должно быть отмечено для того, чтобы его можно было выполнить. Это называется <emphasis>генерацией отложенного прерывания</emphasis> (<emphasis>rise softirq</emphasis>). Обычно обработчик аппаратного прерывания перед возвратом отмечает свои обработчики отложенных прерываний. Затем в подходящий момент времени отложенное прерывание выполняется. Ожидающие выполнения обработчики отложенных прерываний проверяются и выполняются в следующих ситуациях.</p>
     <p>• После обработки аппаратного прерывания.</p>
     <p>• В контексте потока пространства ядра <code>ksoftirqd</code>.</p>
     <p>• В любом коде ядра, который явно проверяет и выполняет ожидающие обработчики отложенных прерываний, как, например, это делает сетевая подсистема.</p>
     <p>Независимо от того, каким способом выполняется отложенное прерывание, его выполнение осуществляется в функции <code>do_softirq()</code>. Эта функция по-настоящему проста. Если есть ожидающие отложенные прерывания, то функция <code>do_softirq()</code> в цикле проверяет их все и вызывает ожидающие обработчики. Давайте рассмотрим упрощенный вариант наиболее важной части функции <code>do_softirq()</code>.</p>
     <p><code>u32 pending = softirq_pending(cpu);</code></p>
     <empty-line/>
     <p><code>if (pending) {</code></p>
     <p><code> struct softirq_action *h = softirq_vec;</code></p>
     <p><code> softirq_pending(cpu) = 0;</code></p>
     <p><code> do {</code></p>
     <p><code>  if (pending &amp; 1)</code></p>
     <p><code>   h-&gt;action(h);</code></p>
     <p><code>  h++;</code></p>
     <p><code>  pending &gt;&gt;= 1;</code></p>
     <p><code> } while (pending);</code></p>
     <p><code>}</code></p>
     <p>Этот фрагмент кода является сердцем обработчика отложенных прерываний. Он проверяет и выполняет все ожидающие отложенные прерывания.</p>
     <p>• Присваивает локальной переменной pending значение, возвращаемое макросом <code>softirq_pending()</code>. Это значение — 32-х разрядная битовая маска ожидающих на выполнение отложенных прерываний. Если установлен бит с номером <code>n</code>, то отложенное прерывание с этим номером ожидает на выполнение.</p>
     <p>• Когда значение битовой маски отложенных прерываний сохранено, оригинальная битовая маска очищается<a l:href="#n38" type="note">[38]</a>.</p>
     <p>• Переменной <code>h</code> присваивается указатель на первый элемент массива <code>softirq_vec</code>.</p>
     <p>• Если первый бит маски, которая хранится в переменной <code>pending</code>, установлен, то вызывается функция <code>h-&gt;action(h)</code>.</p>
     <p>• Указатель <code>h</code> увеличивается на единицу, и теперь он указывает на второй элемент массива <code>softirq_vec</code>.</p>
     <p>• Осуществляется логический сдвиг битовой маски, хранящейся в переменной <code>pending</code>, вправо на один разряд. Эта операция отбрасывает самый младший бит и сдвигает все оставшиеся биты на одну позицию вправо. Следовательно, второй бит теперь стал первым и т.д.</p>
     <p>• Указатель <code>h</code> теперь указывает на второй элемент массива, а в битовой маске — второй бит стал первым. Теперь необходимо повторить все ранее проделанные шаги.</p>
     <p>• Последовательное повторение производится до тех пор, пока битовая маска не станет равной нулю. В этот момент больше нет ожидающих отложенных прерываний, и наша работа выполнена. Заметим, что такой проверки достаточно для того, чтобы гарантировать, что указатель <code>h</code> всегда указывает на законную запись в массиве <code>softirq_vec</code>, так как битовая маска pending имеет 32 бит и цикл не может выполниться больше 32 раз.</p>
    </section>
    <section>
     <title>
      <p>Использование отложенных прерываний</p>
     </title>
     <p>Отложенные прерывания зарезервированы для наиболее важных и критичных ко времени выполнения обработчиков нижних половин в системе. Сейчас только две подсистемы — подсистема SCSI и сетевая подсистема — напрямую используют механизм softirq. В дополнение к этому, таймеры ядра и тасклеты построены на базе отложенных прерываний. Если есть желание добавить новое отложенное прерывание, то стоит себя спросить, почему будет недостаточно использования тасклетов. Тасклеты могут создаваться динамически, а также их легче использовать в связи с более простыми требованиями к блокировкам. Кроме того, их производительность все еще остается очень хорошей. Тем не менее для задач, критичных ко времени выполнения, которые способны сами обеспечивать эффективные блокировки, использование механизма softirq — будет правильным решением.</p>
     <subtitle>Назначение индексов</subtitle>
     <p>Отложенные прерывания должны объявляться на этапе компиляции с помощью соответствующего перечисления (<code>enum</code>) в файле <code>&lt;linux/interrupt.h&gt;</code>. Ядро использует указанный в перечислении индекс, который начинается с нуля, как значение относительного приоритета отложенных прерываний. Отложенные прерывания с меньшим номером выполняются раньше отложенных прерываний с большим номером.</p>
     <p>Создание нового отложенного прерывания состоит в добавлении новой записи в этот перечень (<code>enum</code>). Однако нужно не просто добавить новую строчку в конец списка, как в других местах. Вместо этого нужно вставить строчку в соответствии с приоритетом, который дается этому прерыванию. Исторически, <code>HI_SOFTIRQ</code> — имеет наибольший приоритет, a <code>TASKLET_SOFTIRQ</code> — наименьший. Новая запись, скорее всего, должна быть где-то ниже записей для сетевых устройств и выше записи для <code>TASKLET_SOFTIRQ</code>. В табл. 7.2 показан список всех типов отложенных прерываний.</p>
     <empty-line/>
     <p><strong>Таблица 7.2</strong>. Список отложенных прерываний</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Отложенное прерывание</th>
       <th align="left" valign="top">Приоритет</th>
       <th align="left" valign="top">Описание</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>HI_SOFTIRQ</code></td>
       <td align="left" valign="top">0</td>
       <td align="left" valign="top">Высокоприоритетные тасклеты</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>TIMER_SOFTIRQ</code></td>
       <td align="left" valign="top">1</td>
       <td align="left" valign="top">Обработчик нижних половин таймеров</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>NET_TX_SOFTIRQ</code></td>
       <td align="left" valign="top">2</td>
       <td align="left" valign="top">Отправка сетевых пакетов</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>NET_RX_SOFTIRQ</code></td>
       <td align="left" valign="top">3</td>
       <td align="left" valign="top">Прием сетевых пакетов</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>SCSI_SOFTIRQ</code></td>
       <td align="left" valign="top">4</td>
       <td align="left" valign="top">Обработчик нижних половин подсистемы SCSI</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>TASKLET_SOFTIRQ</code></td>
       <td align="left" valign="top">5</td>
       <td align="left" valign="top">Тасклеты</td>
      </tr>
     </table>
     <subtitle>Регистрация обработчика</subtitle>
     <p>Далее во время выполнения должен быть зарегистрирован обработчик отложенного прерывания с помощью вызова <code>open_softirq()</code>, который принимает три параметра: индекс отложенного прерывания, функция-обработчик и значение поля <code>data</code>. Для сетевой подсистемы это делается, например, следующим образом.</p>
     <p><code>open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);</code></p>
     <p><code>open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);</code></p>
     <p>Обработчик отложенного прерывания выполняется при разрешенных прерываниях и не может переходить в состояние ожидания (sleep). Во время выполнения обработчика отложенные прерывания на данном процессоре запрещаются. Однако на другом процессоре обработчики отложенных прерываний могут выполняться. На самом деле, если вдруг генерируется отложенное прерывание в тот момент, когда выполняется его обработчик, то такой же обработчик может быть запущен на другом процессоре одновременно с первым обработчиком. Это означает, что любые совместно используемые данные, которые используются в обработчике отложенного прерывания, и даже глобальные данные, которые используются только в самом обработчике, должны соответствующим образом блокироваться (как показано в следующих двух разделах). Это очень важный момент, и именно по этой причине использование тасклетов обычно предпочтительнее. Простое предотвращение конкурентного выполнения обработчиков — это далеко не идеал. Если обработчик отложенного прерывания просто захватит блокировку, которая предотвращает его выполнение параллельно самому себе, то для использования отложенных прерываний не остается почти никакого смысла. Следовательно, большинство обработчиков отложенных прерываний используют данные, уникальные для каждого процессора (и следовательно, не требующие блокировок), или какие-нибудь другие ухищрения, чтобы избежать явного использования блокировок и обеспечить отличную масштабируемость.</p>
     <p>Главная причина использования отложенных прерываний — масштабируемость. Если нет необходимости масштабироваться на бесконечное количество процессоров, то лучше использовать механизм тасклетов. Тасклеты — это отложенные прерывания, для которых обработчик не может выполняться параллельно на нескольких процессорах.</p>
     <subtitle>Генерация отложенных прерываний</subtitle>
     <p>После того как обработчик добавлен в перечень и зарегистрирован с помощью вызова <code>open_softirq()</code>, он готов выполняться. Для того чтобы отметить его как ожидающего исполнения и, соответственно, чтобы он выполнился при следующем вызове функции <code>do_softirq()</code>, необходимо вызвать функцию <code>raise_softirq()</code>. Например, сетевая подсистема должна вызвать эту функцию в следующем виде.</p>
     <p><code>raise_softirq(NET_TX_SOFTIRQ);</code></p>
     <p>Этот вызов сгенерирует отложенное прерывание с индексом <code>NET_TX_SOFTIRQ</code>. Соответствующий обработчик <code>net_tx_action()</code> будет вызван при следующем выполнении программных прерываний ядром. Эта функция запрещает аппаратные прерывания перед тем, как сгенерировать отложенное прерывание, а затем восстанавливает их в первоначальное состояние. Если аппаратные прерывания в данный момент запрещены, то для небольшой оптимизации можно воспользоваться функцией <code>raise_softirq_irqoff()</code>, как показано в следующем примере.</p>
     <p><code>/*</code></p>
     <p><code>* прерывания должны быть запрещены!</code></p>
     <p><code>*/</code></p>
     <p><code>raise_softirq_irqoff(NET_TX_SOFTIRQ);</code></p>
     <p>Наиболее часто отложенные прерывания генерируются из обработчиков аппаратных прерываний. В этом случае обработчик аппаратного прерывания выполняет всю основную работу, которая касается аппаратного обеспечения, генерирует отложенное прерывание и завершается. После завершения обработки аппаратных прерываний ядро вызывает функцию <code>do_softirq()</code>. Обработчик отложенного прерывания выполняется и подхватывает работу с того места, где обработчик аппаратного прерывания ее отложил. В таком примере раскрывается смысл названий "верхняя половина" и "нижняя половина".</p>
    </section>
   </section>
   <section>
    <title>
     <p>Тасклеты</p>
    </title>
    <section>
     <p>Тасклеты — это механизм обработки нижних половин, построенный на основе механизма отложенных прерываний. Как уже отмечалось, они не имеют ничего общего с заданиями (task). Тасклеты по своей природе и принципу работы очень похожи на отложенные прерывания. Тем не менее они имеют более простой интерфейс и упрощенные правила блокировок.</p>
     <p>Решение о том, стоит ли использовать тасклеты, принять достаточно просто: в большинстве случаев необходимо использовать тасклеты. Как было показано в предыдущем разделе, примеры использования отложенных прерываний можно посчитать на пальцах одной руки. Отложенные прерывания необходимо использовать только в случае, когда необходима очень большая частота выполнений и интенсивно используется многопоточная обработка. Тасклеты используются в очень большом количестве случаев — они работают достаточно хорошо и их очень просто использовать.</p>
    </section>
    <section>
     <title>
      <p>Реализация тасклетов</p>
     </title>
     <p>Так как тасклеты реализованы на основе отложенных прерываний, они тоже являются <emphasis>отложенными прерываниями</emphasis> (softirq). Как уже рассказывалось, тасклеты представлены двумя типами отложенных прерываний: <code>HI_SOFTIRQ</code> и <code>TASKLET_SOFTIRQ</code>. Единственная разница между ними в том, что тасклеты типа <code>HI_SOFTIRQ</code> выполняются всегда раньше тасклетов типа <code>TASKLET_SOFTIRQ</code>.</p>
     <subtitle>Структуры тасклетов</subtitle>
     <p>Тасклеты представлены с помощью структуры <code>tasklet_struct</code>. Каждый экземпляр структуры представляет собой уникальный тасклет. Эта структура определена в заголовочном файле <code>&lt;linux/interrupt.h&gt;</code> в следующем виде.</p>
     <p><code>struct tasklet_struct {</code></p>
     <p><code> struct tasklet_struct *next;  /* указатель на следующий</code></p>
     <p><code>                                  тасклет в списке */</code></p>
     <p><code> unsigned long state;          /* состояние тасклета */</code></p>
     <p><code> atomic_t count;               /* счетчик ссылок */</code></p>
     <p><code> void (*func)(unsigned long);  /* функция-обработчик тасклета */</code></p>
     <p><code> unsigned long data; /* аргумент функции-обработчика тасклета */</code></p>
     <p><code>);</code></p>
     <p>Поле <code>func</code> — это функция-обработчик тасклета (эквивалент поля <code>action</code> для структуры, представляющей отложенное прерывание), которая получает поле <code>data</code> в качестве единственного аргумента при вызове.</p>
     <p>Поле <code>state</code> может принимать одно из следующих значений: нуль, <code>TASKLET_STATE_SCHED</code> или <code>TASLET_STATE_RUN</code>. Значение <code>TASKLET_STATE_SCHED</code> указывает на то, что тасклет запланирован на выполнение, а значение <code>TASLET_STATE_RUN</code> — что тасклет выполняется. Для оптимизации значение <code>TASLET_STATE_RUN</code> может использоваться только на многопроцессорной машине, так как на однопроцессорной машине и без этого точно известно, выполняется ли тасклет (действительно, ведь код, который выполняется, либо принадлежит тасклету, либо нет).</p>
     <p>Поле <code>count</code> используется как счетчик ссылок на тасклет. Если это значение не равно нулю, то тасклет запрещен и не может выполняться; если оно равно нулю, то тасклет разрешен и может выполняться в случае, когда он помечен как ожидающий выполнения.</p>
     <subtitle>Планирование тасклетов на выполнение</subtitle>
     <p><emphasis>Запланированные</emphasis> (<emphasis>scheduled</emphasis>) на выполнение тасклеты (эквивалент сгенерированных отложенных прерываний)<a l:href="#n39" type="note">[39]</a> хранятся в двух структурах, определенных для каждого процессора: структуре <code>tasklet_vec</code> (для обычных тасклетов) и структуре <code>tasklet_hi_vec</code> (для высокоприоритетных тасклетов). Каждая из этих структур — это связанный список структур <code>tasklet_struct</code>. Каждый экземпляр структуры <code>tasklet_struct</code> представляет собой отдельный тасклет.</p>
     <p>Тасклеты могут быть запланированы на выполнение с помощью функций <code>tasklet_schedule()</code> и <code>tasklet_hi_schedule()</code>, которые принимают единственный аргумент— указатель на структуру тасклета— <code>tasklet_struct</code>. Эти функции очень похожи (отличие состоит в том, что одна использует отложенное прерывание с номером <code>TASKLET_SOFTIRQ</code>, а другая — с номером <code>HI_SOFTIRQ</code>). К написанию и использованию тасклетов мы вернемся в следующем разделе. А сейчас рассмотрим детали реализации функции <code>tasklet_hi_schedule()</code>, которые состоят в следующем.</p>
     <p>• Проверяется, не установлено ли поле <code>state</code> в значение <code>TASKLET_STATE_SCHED</code>. Если установлено, то тасклет уже запланирован на выполнение и функция может возвратить управление.</p>
     <p>• Сохраняется состояние системы прерываний и запрещаются прерывания на локальном процессоре. Это гарантирует, что ничто на данном процессоре не будет мешать выполнению этого кода.</p>
     <p>• Добавляется тасклет, который планируется на выполнение, в начало связанного списка структуры <code>tasklet_vec</code> или <code>tasklet_hi_vec</code>, которые уникальны для каждого процессора в системе.</p>
     <p>• Генерируется отложенное прерывание с номером <code>TASKLET_SOFTIRQ</code> или <code>HI_SOFTIRQ</code>, чтобы в ближайшее время данный тасклет выполнился при вызове функции <code>do_softirq()</code>.</p>
     <p>• Устанавливается состояние системы прерываний в первоначальное значение и возвращается управление.</p>
     <p>При первой же удобной возможности функция <code>do_softirq()</code> выполнится, как это обсуждалось в предыдущем разделе. Поскольку большинство тасклетов помечаются как готовые к выполнению в обработчиках прерываний, то, скорее всего, функция <code>do_softirq()</code> вызывается сразу же, как только возвратится последний обработчик прерывания. Так как отложенные прерывания с номерами <code>TASKLET_SOFTIRQ</code> или <code>HI_SOFTIRQ</code> к этому моменту уже сгенерированы, то функция <code>do_softirq()</code> выполняет соответствующие обработчики. Эти обработчики, а также функции <code>tasklet_action()</code> и <code>tasklet_hi_action()</code> являются сердцем механизма обработки тасклетов- Давайте рассмотрим, что они делают.</p>
     <p>• Запрещаются прерывания, и получается весь список <code>tasklet_vec</code> или <code>tasklet_hi_vec</code> для текущего процессора.</p>
     <p>• Список текущего процессора очищается путем присваивания значения нуль указателю на него.</p>
     <p>• Разрешаются прерывания (нет необходимости восстанавливать состояние системы прерываний в первоначальное значение, так как этот код может выполняться только в обработчике отложенного прерывания, который вызывается только при разрешенных прерываниях).</p>
     <p>• Организовывается цикл по всем тасклетам в полученном списке.</p>
     <p>• Если данная машина является многопроцессорной, то нужно проверить не выполняется ли текущий тасклет на другом процессоре, то есть проверить не установлен ли флаг <code>TASLET_STATE_RUN</code>. Если тасклет уже выполняется, то его необходимо пропустить и перейти к следующему тасклету в списке (вспомним, что только один тасклет данного типа может выполняться в любой момент времени).</p>
     <p>• Если тасклет не выполняется, то нужно установить флаг <code>TASLET_STATE_RUN</code>, чтобы другой процессор не мог выполнить этот тасклет.</p>
     <p>• Проверяется значение поля <code>count</code> на равенство нулю, чтобы убедиться, что тасклет не запрещен. Если тасклет запрещен (поле count не равно нулю), то нужно перейти к следующему тасклету, который ожидает на выполнение.</p>
     <p>• Теперь можно быть уверенным, что тасклет нигде не выполняется, нигде не будет выполняться (так как он помечен как выполняющийся на данном процессоре) и что значение поля count равно нулю. Необходимо выполнить обработчик тасклета. После того как тасклет выполнился, следует очистить флаг <code>TASLET_STATE_RUN</code> и поле <code>state</code>.</p>
     <p>• Повторить описанный алгоритм для следующего тасклета, пока не останется ни одного тасклета, ожидающего выполнения.</p>
     <p>Реализация тасклетов проста, но в то же время очень остроумна. Как видно, все тасклеты реализованы на базе двух отложенных прерываний <code>TASKLET_SOFTIRQ</code> и <code>HI_SOFTIRQ</code>. Когда тасклет запланирован на выполнение, ядро генерирует одно из этих двух отложенных прерываний. Отложенные прерывания, в свою очередь, обрабатываются специальными функциями, которые выполняют все запланированные на выполнение тасклеты. Эти специальные функции гарантируют, что только один тасклет данного типа выполняется в любой момент времени (но тасклеты разных типов могут выполняться одновременно). Вся эта сложность спрятана за простым и ясным интерфейсом.</p>
    </section>
    <section>
     <title>
      <p>Использование тасклетов</p>
     </title>
     <p>В большинстве случаев тасклеты — это самый предпочтительный механизм, с помощью которого следует реализовать обработчики нижних половин для обычных аппаратных устройств. Тасклеты можно создавать динамически, их просто использовать, и они сравнительно быстро работают.</p>
     <subtitle>Объявление тасклетов</subtitle>
     <p>Тасклеты можно создавать статически и динамически. Какой вариант лучше выбрать, зависит от того, как необходимо (или желательно) пользователю обращаться к тасклету: прямо или через указатель. Для статического создания тасклета (и соответственно, обеспечения прямого доступа к нему) необходимо использовать один из двух следующих макросов, которые определены в файле <code>&lt;linux/interrupts.h&gt;</code>:</p>
     <p><code>DECLARE_TASKLET(name, func, data);</code></p>
     <p><code>DECLARE_TASKLET_DISABLED(name, func, data);</code></p>
     <p>Оба макроса статически создают экземпляр структуры <code>struct_tasklet_struct</code> с указанным именем (<code>name</code>). Когда тасклет запланирован на выполнение, то вызывается функция <code>func</code>, которой передается аргумент <code>data</code>. Различие между этими макросами состоит в значении счетчика ссылок на тасклет (поле <code>count</code>). Первый макрос создает тасклет, у которого значение поля count равно нулю, и, соответственно, этот тасклет разрешен. Второй макрос создает тасклет и устанавливает для него значение поля <code>count</code>, равное единице, и, соответственно, этот тасклет будет запрещен. Можно привести следующий пример.</p>
     <p><code>DECLARE_TASKLET(my_tasklet, my_tasklet_handler, dev);</code></p>
     <p>Эта строка эквивалентна следующей декларации.</p>
     <p><code>struct tasklet_struct my_tasklet = {</code></p>
     <p><code> NULL, 0, ATOMIC_INIT(0), tasklet_handler, dev</code></p>
     <p><code>};</code></p>
     <p>В данном примере создается тасклет с именем <code>my_tasklet</code>, который разрешен для выполнения. Функция <code>tasklet_handler</code> будет обработчиком этого тасклета. Значение параметра <code>dev</code> передается в функцию-обработчик при вызове данной функции.</p>
     <p>Для инициализации тасклета, на который указывает заданный указатель <code>struct tasklet_struct* t</code> — косвенная ссылка на динамически созданную ранее структуру, необходимо использовать следующий вызов.</p>
     <p><code>tasklet_init(t, tasklet_handler, dev); /* динамически, а не статически */</code></p>
     <subtitle>Написание собственной функции-обработчика тасклета</subtitle>
     <p>Функция-обработчик тасклета должна соответствовать правильному прототипу.</p>
     <p><code>void tasklet_handler(unsigned long data);</code></p>
     <p>Так же как и в случае отложенных прерываний, тасклет не может переходить в состояние ожидания (блокироваться). Это означает, что в тасклетах нельзя использовать семафоры или другие функции, которые могут блокироваться. Тасклеты также выполняются при всех разрешенных прерываниях, поэтому необходимо принять все меры предосторожности (например, может понадобиться запретить прерывания и захватить блокировку), если тасклет имеет совместно используемые данные с обработчиком прерывания. В отличие от отложенных прерываний, ни один тасклет не выполняется параллельно самому себе, хотя два разных тасклета могут выполняться на разных процессорах параллельно. Если тасклет совместно использует данные с обработчиком прерывания или другим тасклетом, то необходимо использовать соответствующие блокировки (см. главу 8, "Введение в синхронизацию выполнения кода ядра" и главу 9, "Средства синхронизации в ядре").</p>
     <subtitle>Планирование тасклета на выполнение</subtitle>
     <p>Для того чтобы запланировать тасклет на выполнение, должна быть вызвана функция <code>tasklet_schedule()</code>, которой в качестве аргумента передается указатель на соответствующий экземпляр структуры <code>tasklet_struct</code>.</p>
     <p><code>tasklet_schedule(&amp;my_tasklet); /* отметить, что тасклет my_tasklet</code></p>
     <p><code>                                  ожидает на выполнение */</code></p>
     <p>После того как тасклет запланирован на выполнение, он выполняется один раз в некоторый момент времени в ближайшем будущем. Если тасклет, который запланирован на выполнение, будет запланирован еще раз до того, как он выполнится, то он также выполнится всего один раз. Если тасклет уже выполняется, скажем, на другом процессоре, то будет запланирован снова и снова выполнится. Для оптимизации тасклет всегда выполняется на том процессоре, который его запланировал на выполнение, что дает надежду на лучшее использование кэша процессора.</p>
     <p>Указанный тасклет может быть запрещен с помощью вызова функции <code>tasklet_disable()</code>. Если тасклет в данный момент времени выполняется, то эта функция не возвратит управление, пока тасклет не закончит выполняться. Как альтернативу можно использовать функцию <code>tasklet_disable_nosync()</code>, которая запрещает указанный тасклет, но возвращается сразу и не ждет, пока тасклет завершит выполнение. Это обычно небезопасно, так как в данном случае нельзя гарантировать, что тасклет не закончил выполнение. Вызов функции <code>tasklet_enable()</code> разрешает тасклет. Эта функция также должна быть вызвана для того, чтобы можно было использовать тасклет, созданный с помощью макроса <code>DECLARE_TASKLET_DISABLED()</code>, как показано в следующем примере.</p>
     <p><code>tasklet_disable(&amp;my_tasklet); /* тасклет теперь запрещен */</code></p>
     <p><code>/* Мы можем делать все, что угодно, зная,</code></p>
     <p><code>   что тасклет не может выполняться. */</code></p>
     <p><code>tasklet_enable(&amp;my_tasklet); /* теперь тасклет разрешен */</code></p>
     <p>Из очереди тасклетов, ожидающих на выполнение, тасклет может быть удален с помощью функции <code>tasklet_kill()</code>. Эта функция получает указатель на соответствующую структуру <code>tasklet_struct</code> в качестве единственного аргумента. Удаление запланированного на выполнение тасклета из очереди очень полезно в случае, когда используются тасклеты, которые сами себя планируют на выполнение. Эта функция сначала ожидает, пока тасклет не закончит выполнение, а потом удаляет его из очереди. Однако это, конечно, не может предотвратить возможности, что другой код запланирует этот же тасклет на выполнение. Так как данная функция может переходить в состояние ожидания, то ее нельзя вызывать из контекста прерывания.</p>
    </section>
    <section>
     <title>
      <p>Демон ksoftirqd</p>
     </title>
     <p>Обработка отложенных прерываний (softirq) и, соответственно, тасклетов может осуществляться с помощью набора потоков пространства ядра (по одному потоку на каждый процессор). Потоки пространства ядра помогают обрабатывать отложенные прерывания, когда система перегружена большим количеством отложенных прерываний.</p>
     <p>Как уже упоминалось, ядро обрабатывает отложенные прерывания в нескольких местах, наиболее часто это происходит после возврата из обработчика прерывания. Отложенные прерывания могут генерироваться с очень большими частотами (как, например, в случае интенсивного сетевого трафика). Хуже того, функции-обработчики отложенных прерываний могут самостоятельно возобновлять свое выполнение (реактивизировать себя). Иными словами, во время выполнения функции-обработчики отложенных прерываний могут генерировать свое отложенное прерывание для того, чтобы выполниться снова (на самом деле, сетевая подсистема именно так и делает). Возможность больших частот генерации отложенных прерываний в сочетании с их возможностью активизировать самих себя может привести к тому, что программы, работающие в пространстве пользователя, будут страдать от недостатка процессорного времени. В свою очередь, не своевременная обработка отложенных прерываний также не допустима. Возникает дилемма, которая требует решения, но ни одно из двух очевидных решений не является подходящим. Давайте рассмотрим оба этих очевидных решения.</p>
     <p>Первое решение — это немедленная обработка всех отложенных прерываний, как только они приходят, а также обработка всех ожидающих отложенных прерываний перед возвратом из обработчика. Это решение гарантирует, что все отложенные прерывания будут обрабатываться немедленно и в то же время, что более важно, что все вновь активизированные отложенные прерывания также будут немедленно обработаны. Проблема возникает в системах, которые работают при большой загрузке и в которых возникает большое количество отложенных прерываний, которые постоянно сами себя активизируют. Ядро может постоянно обслуживать отложенные прерывания без возможности выполнять что-либо еще. Заданиями пространства пользователя пренебрегают, а выполняются только лишь обработчики прерываний и отложенные прерывания, в результате пользователи системы начинают нервничать. Подобный подход может хорошо работать, если система не находится под очень большой нагрузкой. Если же система испытывает хотя бы умеренную нагрузку, вызванную обработкой прерываний, то такое решение не применимо. Пространство пользователя не должно продолжительно страдать из-за нехватки процессорного времени.</p>
     <p>Второе решение — это вообще <emphasis>не обрабатывать</emphasis> реактивизированные отложенные прерывания. После возврата из очередного обработчика прерывания ядро просто просматривает список всех ожидающих на выполнение отложенных прерываний и выполняет их как обычно. Если какое-то отложенное прерывание реактивизирует себя, то оно не будет выполняться до того времени, пока ядро <emphasis>в следующий раз</emphasis> снова не приступит к обработке отложенных прерываний. Однако такое, скорее всего, произойдет, только когда поступит следующее аппаратное прерывание, что может быть равносильно ожиданию в течение длительного промежутка времени, пока новое (или вновь активизированное) отложенное прерывание будет выполнено. В таком решении плохо то, что на не загруженной системе выгодно обрабатывать отложенные прерывания сразу же. К сожалению, описанный подход не учитывает то, какие процессы могут выполняться, а какие нет. Следовательно, данный метод хотя и предотвращает нехватку процессорного времени для задач пространства пользователя, но создает нехватку ресурсов для отложенных прерываний, и к тому же такой подход не выгоден для систем, работающих при малых нагрузках.</p>
     <p>Необходим какой-нибудь компромисс. Решение, которое реализовано в ядре, — <emphasis>не обрабатывать</emphasis> немедленно вновь активизированные отложенные прерывания. Вместо этого, если сильно возрастает количество отложенных прерываний, ядро возвращает к выполнению (wake up) семейство потоков пространства ядра, чтобы они справились с нагрузкой. Данные потоки ядра работают с самым минимально возможным приоритетом (значение параметра nice равно 19). Это гарантирует, что они не будут выполняться вместо чего-то более важного. Но они в конце концов тоже когда-нибудь обязательно выполняются. Это предотвращает ситуацию нехватки процессорных ресурсов для пользовательских программ. С другой стороны, это также гарантирует, что даже в случае большого количества отложенных прерываний они все в конце концов будут выполнены. И наконец, такое решение гарантирует, что в случае незагруженной системы отложенные прерывания также обрабатываются достаточно быстро (потому что соответствующие потоки пространства ядра будут запланированы на выполнение немедленно).</p>
     <p>Для каждого процессора существует свой поток. Каждый поток имеет имя в виде <code>ksoftirqd/<emphasis>n</emphasis></code>, где <code><emphasis>n</emphasis></code> — номер процессора. Так в двухпроцессорной системе будут запущены два потока с именами <code>ksoftiqd/0</code> и <code>ksoftirqd/1</code>. To, что на каждом процессоре выполняется свой поток, гарантирует, что если в системе есть свободный процессор, то он всегда будет в состоянии выполнять отложенные прерывания. После того как потоки запущены, они выполняют замкнутый цикл, похожий на следующий.</p>
     <p><code>for (;;) {</code></p>
     <p><code> set_task_state(current, TASK_INTERRUPTIBLE);</code></p>
     <p><code> add_wait_queue(&amp;cwq-&gt;more_work, &amp;wait);</code></p>
     <empty-line/>
     <p><code> if (list_empty(&amp;cwq-&gt;worklist))</code></p>
     <p><code>  schedule();</code></p>
     <p><code> else</code></p>
     <p><code>  set_task_state(current, TASK_RUNNING);</code></p>
     <p><code> remove_wait_queue(&amp;cwq-&gt;more_work, &amp;wait);</code></p>
     <empty-line/>
     <p><code> if (!list_empty(&amp;cwq-&gt;worklist))</code></p>
     <p><code>  run_workqueue(cwq);</code></p>
     <p><code>}</code></p>
     <p>Если есть отложенные прерывания, ожидающие на обработку (что определяет вызов функции <code>softirq_pending()</code>), то поток ядра <code>ksoftirqd</code> вызывает функцию <code>do_softirq()</code>, которая эти прерывания обрабатывает. Заметим, что это делается периодически, чтобы обработать также вновь активизированные отложенные прерывания. После каждой итерации при необходимости вызывается функция <code>schedule()</code>, чтобы дать возможность выполняться более важным процессам. После того как вся обработка выполнена, поток ядра устанавливает свое состояние в значение <code>TASK_INTERRUPTIBLE</code> и активизирует планировщик для выбора нового готового к выполнению процесса.</p>
     <p>Поток обработки отложенных прерываний вновь возвращается в состояние готовности к выполнению, когда функция <code>do_softirq()</code> определяет, что отложенное прерывание реактивизировало себя.</p>
    </section>
    <section>
     <title>
      <p>Старый механизм BH</p>
     </title>
     <p>Хотя старый интерфейс BH, к счастью, уже отсутствует в ядрах серии 2.6, тем не менее им пользовались очень долгое время — с первых версий ядра. Учитывая, что этому интерфейсу удалось продержаться очень долго, он, конечно, представляет собой историческую ценность и заслуживает большего, чем просто беглого рассмотрения. Этот раздел никаким образом не касается ядер серии 2.6, но значение истории переоценить трудно.</p>
     <p>Интерфейс BH очень древний, и это заметно. Каждый обработчик BH должен быть определен статически, и количество этих обработчиков ограничено максимальным значением 32. Так как все обработчики BH должны быть определены на этапе компиляции, загружаемые модули ядра не могли напрямую использовать интерфейс BH. Тем не менее можно было встраивать функции в уже существующие обработчики BH. Со временем необходимость статического объявления и максимальное количество обработчиков нижних половин, равное 32, стали надоедать.</p>
     <p>Все обработчики BH выполнялись строго последовательно — никакие два обработчика BH, даже разных типов, не могли выполняться параллельно. Это позволяло обеспечить простую синхронизацию, но все же для получения высокой производительности при многопроцессорной обработке это было не очень хорошо. Драйверы, которые использовали интерфейс BH, очень плохо масштабировались на несколько процессоров. Например, страдала сетевая подсистема.</p>
     <p>В остальном, за исключением указанных ограничений, механизм BH был похож на механизм тасклетов. На самом деле, в ядрах серии 2.4 механизм BH был реализован на основе тасклетов. Максимальное количество обработчиков нижних половин, равное 32, обеспечивалось значениями констант, определенных в заголовочном файле <code>&lt;linux/interrupt.h&gt;</code>. Для того чтобы отметить обработчик BH как ожидающий на выполнение, необходимо было вызвать функцию <code>mark_bh()</code> с передачей номера обработчика BH в качестве параметра. В ядрах серии 2.4 при этом планировался на выполнение тасклет BH, который выполнялся с помощью обработчика <code>bh_action()</code>. До серии ядер 2.4 механизм BH существовал самостоятельно, как сейчас механизм отложенных прерываний.</p>
     <p>В связи с недостатками этого типа обработчиков нижних половин, разработчики ядра предложили механизм очередей заданий (task queue), чтобы заменить механизм нижних половин. Очереди заданий так и не смогли справиться с этой задачей, хотя и завоевали расположение большого количества пользователей. При разработке серии ядер 2.3 были предложены механизмы отложенных прерываний (softirq) и механизм тасклетов (tasklet), для того чтобы положить конец механизму BH. Механизм BH при этом был реализован на основе механизма тасклетов. К сожалению, достаточно сложно переносить обработчики нижних половин с использования интерфейса BH на использование механизм тасклетов или отложенных прерываний, в связи с тем что у новых интерфейсов нет свойства строгой последовательности выполнения<a l:href="#n40" type="note">[40]</a>.</p>
     <p>Однако при разработке ядер серии 2.5 необходимую конвертацию все же сделали, когда таймеры ядра и подсистему SCSI (единственные оставшиеся системы, которые использовали механизм BH) наконец-то перевели на использование отложенных прерываний. И в завершение, разработчики ядра совсем убрали интерфейс BH. Скатертью дорога тебе, интерфейс BH!</p>
    </section>
   </section>
   <section>
    <title>
     <p>Очереди отложенных действий</p>
    </title>
    <section>
     <p>Очереди отложенных действий (work queue) — это еще один способ реализации отложенных операций, который отличается от рассмотренных ранее. Очереди действий позволяют откладывать некоторые операции для последующего выполнения потоком пространства ядра — отложенные действия всегда выполняются в контексте процесса. Поэтому код, выполнение которого отложено с помощью постановки в очередь отложенных действий, получает все преимущества, которыми обладает код, выполняющийся в контексте процесса. Наиболее важное свойство — это то, что выполнение очередей действий управляется планировщиком процессов и, соответственно, выполняющийся код может переходить в состояние ожидания (sleep).</p>
     <p>Обычно принять решение о том, что необходимо использовать: очереди отложенных действий или отложенные прерывания/тасклеты, достаточно просто. Если отложенным действиям необходимо переходить в состояние ожидания, то следует использовать очереди действий. Если же отложенные операции не могут переходить в состояние ожидания, то воспользуйтесь тасклетами или отложенными прерываниями. Обычно альтернатива использованию очередей отложенных действий — это создание новых потоков пространства ядра. Поскольку при введении новых потоков пространства ядра разработчики ядра обычно хмурят брови (а у некоторых народов это означает смертельную обиду), настоятельно рекомендуется использовать очереди отложенных действий. Их действительно <emphasis>очень</emphasis> просто использовать.</p>
     <p>Если для обработки нижних половин необходимо использовать нечто, что планируется на выполнение планировщиком процессов, то воспользуйтесь очередями отложенных действий. Это единственный механизм обработки нижних половин, который всегда выполняется в контексте процесса, и, соответственно, единственный механизм, с помощью которого обработчики нижних половин могут переходить в состояние ожидания. Это означает, что они полезны в ситуациях, когда необходимо выделять много памяти, захватывать семафор или выполнять блочные операции ввода-вывода. Если для выполнения отложенных операций нет необходимости использовать поток ядра, то стоит подумать об использовании тасклетов.</p>
    </section>
    <section>
     <title>
      <p>Реализация очередей отложенных действий</p>
     </title>
     <p>В своей наиболее общей форме подсистема очередей отложенных действий — это интерфейс для создания потоков пространства ядра, которые выполняют некоторые действия, где-то поставленные в очередь. Эти потоки ядра называются рабочими потоками (<emphasis>worker threads</emphasis>). Очереди действий позволяют драйверам создавать специальные рабочие потоки ядра для того, чтобы выполнять отложенные действия. Кроме того, подсистема очередей действий содержит рабочие потоки ядра, которые работают по умолчанию. Поэтому в своей общей форме очереди отложенных действий — это простой интерфейс пользователя для откладывания работы, которая будет выполнена потоком ядра.</p>
     <p>Рабочие потоки, которые выполняются по умолчанию, называются <code>events/<emphasis>n</emphasis></code>, где <code><emphasis>n</emphasis></code> — номер процессора. Для каждого процессора выполняется один такой поток. Например, в однопроцессорной системе выполняется один поток <code>events/0</code>. Б двухпроцессорной системе добавляется еще один поток— <code>events/1</code>. Рабочие потоки, которые выполняются по умолчанию, обрабатывают отложенные действия, которые приходят из разных мест. Многие драйверы, которые работают в режиме ядра, откладывают обработку своих нижних половин с помощью потоков, работающих по умолчанию. Если для драйвера или подсистемы нет строгой необходимости в создании своего собственного потока ядра, то использование потоков, работающих по умолчанию, более предпочтительно.</p>
     <p>Тем не менее ничто не запрещает коду ядра создавать собственные потоки. Это может понадобиться, если в рабочем потоке выполняется большое количество вычислительных операций. Операции, критичные к процессорным ресурсам или к высокой производительности, могут получить преимущества от использования отдельного выделенного потока. Это также уменьшает нагрузку на потоки, работающие по умолчанию, и предотвращает нехватку ресурсов для остальных отложенных действий.</p>
     <subtitle>Структуры данных для представления потоков</subtitle>
     <p>Рабочие потоки представлены с помощью следующей структуры <code>workqueue_struct</code>.</p>
     <p><code>/*</code></p>
     <p><code>* Внешне видимая абстракция для представления очередей отложенных</code></p>
     <p><code>  действий представляет собой массив очередей для каждого процессора:</code></p>
     <p><code>*/</code></p>
     <p><code>struct workqueue_struct {</code></p>
     <p><code> struct cpu_workqueue_struct cpu_wq[NR_CPUS];</code></p>
     <p><code> const char* name;</code></p>
     <empty-line/>
     <p><code> struct list_head list;</code></p>
     <p><code>};</code></p>
     <p>Эта структура содержит массив структур <code>struct cpu_workqueue_struct</code>, по одному экземпляру на каждый возможный процессор в системе. Так как рабочий поток существует для каждого процессора в системе, то для каждого рабочего потока, работающего на каждом процессоре машины, существует такая структура.</p>
     <p>Структура <code>cpu_workqueue_struct</code> определена в файле <code>kernel/workqueue.c</code> и является основной. Эта структура показана ниже.</p>
     <p><code>/*</code></p>
     <p><code>* Очередь отложенных действий, связанная с процессором:</code></p>
     <p><code>*/</code></p>
     <p><code>struct cpu_workqueue_struct {</code></p>
     <p><code> spinlock_t lock; /* Очередь для защиты данной структуры */</code></p>
     <p><code> long remove_sequence; /* последний добавленный элемент</code></p>
     <p><code>                          (следующий для запуска ) */</code></p>
     <p><code> long insert_sequence; /* следующий элемент для добавления */</code></p>
     <p><code> struct list_head worklist; /* список действий */</code></p>
     <p><code> wait_queue_head_t more_work;</code></p>
     <p><code> wait_queue_head_t work_done;</code></p>
     <p><code> struct workqueue_struct *wq; /* соответствующая структура</code></p>
     <p><code>                                 workqueue_struct */</code></p>
     <p><code> task_t *thread; /* соответствующий поток */</code></p>
     <p><code> int run_depth; /* глубина рекурсии функции run_workqueue() */</code></p>
     <p><code>};</code></p>
     <p>Заметим, что каждый <emphasis>тип</emphasis> рабочих потоков имеет одну, связанную с этим типом структуру <code>workqueue_struct</code>. Внутри этой структуры имеется по одному экземпляру структуры <code>cpu_workqueue_struct</code> для каждого рабочего потока и, следовательно, для каждого процессора в системе, так как существует только один рабочий поток каждого типа на каждом процессоре.</p>
     <subtitle>Структуры для представления действий</subtitle>
     <p>Все рабочие потоки реализованы как обычные потоки пространства ядра, которые выполняют функцию <code>worker_thread()</code>. После начальной инициализации эта функция входит в бесконечный цикл и переходит в состояние ожидания. Когда какие-либо действия ставятся в очередь, поток возвращается к выполнению и выполняет эти действия. Когда в очереди не остается работы, которую нужно выполнять, поток снова возвращается в состояние ожидания. Каждое действие представлено с помощью структуры <code>work_struct</code>, определенной в файле <code>&lt;linux/workqueue.h&gt;</code>. Эта структура показана ниже.</p>
     <p><code>struct work_struct {</code></p>
     <p><code> unsigned long pending; /* ожидает ли это действие на выполнение? */</code></p>
     <p><code> struct list_head entry; /* связанный список всех действий */</code></p>
     <p><code> void (*func)(void*) ; /* функция-обработчик */</code></p>
     <p><code> void *data; /* аргумент функции-обработчика */</code></p>
     <p><code> void *wq_data; /* для внутреннего использования */</code></p>
     <p><code> struct timer_list timer; /* таймер, который используется для</code></p>
     <p><code>                             очередей отложенных действий с задержками */</code></p>
     <p><code>};</code></p>
     <p>Эти структуры объединены в связанный список, по одному списку на каждый тип очереди для каждого процессора. Например, для каждого процессора существует список отложенных действий, которые выполняются потоками, работающими по умолчанию. Когда рабочий поток возвращается к выполнению, он начинает выполнять все действия, которые находятся в его списке. После завершения работы рабочий поток удаляет соответствующие структуры <code>work_struct</code> из списка. Когда список становится пустым, поток переходит в состояние ожидания.</p>
     <p>Давайте рассмотрим упрощенную основную часть функции <code>worker_thread()</code>.</p>
     <p><code>for (;;) {</code></p>
     <p><code> set_task_state(current, TASK_INTERRUPTIBLE);</code></p>
     <p><code> add_wait_queue(&amp;cwq-&gt;more_work, &amp;wait);</code></p>
     <p><code> if (list_empty(&amp;cwq-&gt;worklist))</code></p>
     <p><code>  schedule();</code></p>
     <p><code> else</code></p>
     <p><code>  set_task_state(current, TASK_RUNNING);</code></p>
     <p><code> remove_wait_queue(&amp;cwq-&gt;more_work, &amp;wait);</code></p>
     <p><code> if (!list_empty(&amp;cwq-&gt;worklist))</code></p>
     <p><code>  run_workqueue(cwq);</code></p>
     <p><code>}</code></p>
     <p>Эта функция выполняет следующие действия в бесконечном цикле.</p>
     <p>• Поток переводит себя в состояние ожидания (флаг состояния устанавливается в значение <code>TASK_INTERRUPTIBLE</code>), и текущий поток добавляется в очередь ожидания.</p>
     <p>• Если связанный список действий пуст, то поток вызывает функцию <code>schedule()</code> и переходит в состояние ожидания.</p>
     <p>• Если список не пуст, то поток не переходит в состояние ожидания. Вместо этого он устанавливает свое состояние в значение <code>TASK_RUNNING</code> и удаляет себя из очереди ожидания.</p>
     <p>• Если список не пустой, то вызывается функция <code>run_workqueue()</code> для выполнения отложенных действий.</p>
     <subtitle>Функция <code>run_workqueue()</code></subtitle>
     <p>Функция <code>run_workqueue()</code> в свою очередь выполняет сами отложенные действия, как показано ниже.</p>
     <p><code>while (!list_empty(&amp;cwq-&gt;worklist)) {</code></p>
     <p><code> struct work_struct *work;</code></p>
     <p><code> void (*f)(void*);</code></p>
     <p><code> void *data;</code></p>
     <p><code> work = list_entry(cwq-&gt;worklist.next, struct work_struct, entry);</code></p>
     <p><code> f = work-&gt;func;</code></p>
     <p><code> data = work-&gt;data;</code></p>
     <p><code> list_del_init(cwq-&gt;worklist.next);</code></p>
     <p><code> clear_bit(0, &amp;work-&gt;pending);</code></p>
     <p><code> f(data);</code></p>
     <p><code>}</code></p>
     <p>Эта функция просматривает в цикле все элементы списка отложенных действий и выполняет для каждого элемента функцию, на которую указывает поле <code>func</code> соответствующей структуры <code>workqueue_struct</code>. Последовательность действий следующая.</p>
     <p>• Если список не пустой, получить следующий элемент списка.</p>
     <p>• Получить указатель на функцию (поле <code>func</code>), которую необходимо вызвать, и аргумент этой функции (поле <code>data</code>).</p>
     <p>• Удалить полученный элемент из списка и обнулить бит ожидания в структуре элемента.</p>
     <p>• Вызвать полученную функцию.</p>
     <p>• Повторить указанные действия.</p>
     <subtitle>Извините, если не понятно</subtitle>
     <p>Взаимоотношения между различными, рассмотренными в этом разделе структурами достаточно запутанные. На рис. 7.1 показана диаграмма, которая эти взаимоотношения поясняет.</p>
     <image l:href="#img_13.jpeg"/>
     <p><strong>Рис. 7.1</strong>. Соотношения между отложенными действиями, очередями, действий и рабочими потоками</p>
     <p>На самом верхнем уровне находятся рабочие потоки. Может существовать несколько типов рабочих потоков. Для каждого типа рабочих потоков существует один рабочий поток для каждого процессора. Различные части ядра при необходимости могут создавать рабочие потоки. По умолчанию выполняются только рабочие потоки <emphasis>events</emphasis> (события). Каждый рабочий поток представлен с помощью структуры <code>cpu_workqueue_struct</code>. Структура <code>workqueue_struct</code> представляет все рабочие потоки одного типа.</p>
     <p>Например, давайте будем считать, что в дополнение к обычному типу рабочих потоков <emphasis>events</emphasis> был создан еще один тип рабочих потоков — <emphasis>falcon</emphasis>. Также имеется в распоряжении четырехпроцессорный компьютер. Следовательно, выполняется четыре потока типа <emphasis>events</emphasis> (соответственно, определено четыре экземпляра структуры <code>cpu_workqueue_struct</code>) и четыре потока типа <emphasis>falcon</emphasis> (для которых тоже определены другие четыре экземпляра структуры <code>cpu_workqueue_struct</code>). Для потоков типа <emphasis>events</emphasis> определен один экземпляр структуры <code>workqueue_struct</code>, а для потоков типа <emphasis>falcon</emphasis> — другой экземпляр этой структуры.</p>
     <p>На самом нижнем уровне находятся отложенные действия. Драйвер создает отложенное действие, которой должно выполниться позже. Действия представлены структурами <code>work_struct</code>. Кроме других полей, эта структура содержит указатель на функцию, которая должна обработать отложенное действие. Отложенное действие отправляется на выполнение <emphasis>определенному</emphasis> потоку. Соответствующий поток переводится в состояние выполнения и выполняет отложенную работу.</p>
     <p>Большинство драйверов использует существующие по умолчанию рабочие потоки, которые называются <emphasis>events</emphasis>. Они просты в реализации и в использовании. Однако в некоторых, более серьезных ситуациях необходимо создавать новые специальные рабочие потоки. Например, драйвер файловой системы XFS создает два новых типа рабочих потоков.</p>
    </section>
    <section>
     <title>
      <p>Использование очередей отложенных действий</p>
     </title>
     <p>Использовать очереди действий просто. Сначала мы рассмотрим рабочие потоки, используемые по умолчанию, — <emphasis>events</emphasis>, а затем опишем создание новых типов рабочих потоков.</p>
     <subtitle>Создание отложенных действий</subtitle>
     <p>Первый этап — это создание самого действия, которое должно быть отложено. Для создания статической структуры на этапе компиляции необходимо использовать следующий макрос.</p>
     <p><code>DECLARE_WORK(name, void (*func)(void*), void *data);</code></p>
     <p>Это выражение создает структуру <code>work_struct</code> с именем <code>name</code>, с функцией- обработчиком <code>func</code> и аргументом функции-обработчика <code>data</code>.</p>
     <p>Во время выполнения отложенное действие можно создать с помощью передачи указателя на структуру, используя следующий макрос.</p>
     <p><code>INIT_WORK(struct work_struct *work, void (*func)(void*), void *data);</code></p>
     <p>Этот макрос динамически инициализирует отложенное действие, на структуру которого указывает указатель <code>work</code>, устанавливая функцию-обработчик <code>func</code> и аргумент <code>data</code>.</p>
     <subtitle>Обработчик отложенного действия</subtitle>
     <p>Прототип обработчика отложенного действия имеет следующий вид.</p>
     <p><code>void work_handler(void *data);</code></p>
     <p>Рабочий поток выполняет эту функцию, и, следовательно, эта функция выполняется в контексте процесса. По умолчанию при этом вес прерывания разрешены и никакие захваченные блокировки не удерживаются. Ели это необходимо, то функция может переходить в состояние ожидания. Следует заметить, что несмотря на то, что обработчики отложенных действий и выполняются в контексте процесса, эти обработчики не могут переходить в пространство пользователя, так как у потоков пространства ядра нет адресного пространства пользователя. Ядро может обращаться в пространство пользователя, только когда оно выполняется от имени пользовательского процесса, который имеет адресное пространство пользователя, отображенное на память, как, например, в случае выполнения системного вызова.</p>
     <p>Блокировки между очередями отложенных действий и другими частями ядра осуществляются также, как и в случае любого другого кода, работающего в контексте процесса. Это позволяет сделать написание обработчиков отложенных действий достаточно простым. В следующих двух главах это раскрывается более детально.</p>
     <subtitle>Планирование действий на выполнение</subtitle>
     <p>Теперь, когда отложенное действие создано, его нужно запланировать на выполнение. Для того чтобы поставить обработчик данного действия в очередь на выполнение потоками <emphasis>events</emphasis>, которые работают по умолчанию, необходимо просто вызвать следующую функцию.</p>
     <p><code>schedule_work(&amp;work);</code></p>
     <p>Действие планируется на выполнение немедленно и будет выполнено, как только рабочий поток <code>events</code>, работающий на данном процессоре, перейдет в состояние выполнения.</p>
     <p>Иногда необходимо, чтобы действие было выполнено не немедленно, а с некоторой задержкой. В этом случае работа может быть запланирована на выполнение в некоторый момент времени в будущем. Для этого используется следующая функция.</p>
     <p><code>schedule_delayed_work(&amp;work, delay);</code></p>
     <p>В этом случае действие, представленное структурой <code>work_struct</code>, с адресом <code>&amp;work</code>, не будет выполнено, пока не пройдет хотя бы заданное в параметре <code>delay</code> количество импульсов таймера. О том, как использовать импульсы таймера для измерения времени, рассказывается в главе 10, "Таймеры и управление временем".</p>
     <subtitle>Ожидание завершения действий</subtitle>
     <p>Действия, поставленные в очередь, выполняются, когда рабочий поток возвращается к выполнению. Иногда нужно гарантировать, что, перед тем как двигаться дальше, заданный пакет отложенных действий завершен. Это особенно важно для загружаемых модулей, которые, вероятно, должны вызывать эту функцию, перед выгрузкой. В других местах также может быть необходимо гарантировать, что нет ожидающих на выполнение действий, для предотвращения состояния конкуренции.</p>
     <p>Для этого есть следующая функция, которая позволяет ждать, пока очередь действий <emphasis>events</emphasis> не будет очищена.</p>
     <p><code>void flush_scheduled_work(void);</code></p>
     <p>Данная функция ожидает, пока все действия в очереди действий <emphasis>events</emphasis> не будут выполнены. В ожидании завершения всех заданий очереди, эта функция переводит вызывающий процесс в состояние ожидания. Поэтому ее можно вызывать только из контекста процесса.</p>
     <p>Заметим, что эта функция не отменяет никаких отложенных действий с задержками. Любые действия, которые запланированы на выполнение с помощью функции <code>schedule_delayed_work()</code> и задержки которых еще не закончены, — не очищаются с помощью функций <code>flush_scheduled_work()</code>. Для отмены отложенных действий с задержками следует использовать функцию</p>
     <p><code>int cancel_delayed_work(struct work_struct *work);</code></p>
     <p>Эта функция отменяет отложенное действие, которое связано с данной структурой <code>work_struct</code>, если оно запланировано.</p>
     <subtitle>Создание новых очередей отложенных действий</subtitle>
     <p>Если для поставленных целей недостаточно очереди отложенных действий, которая используется по умолчанию, то можно создать новую очередь действий и соответствующие рабочие потоки. Так как при этом создается по одному потоку на каждый процессор, то новые очереди действий необходимо создавать, только если необходима большая производительность за счет выделенного набора потоков.</p>
     <p>Новая очередь действий и связанные с ней рабочие потоки создаются с помощью простого вызова функции.</p>
     <p><code>struct workqueue_struct *create_workqueue(const char *name);</code></p>
     <p>Параметр name используется для того, чтобы присваивать имена потокам ядра. Например, очередь <code>events</code>, которая используется по умолчанию, создается с помощью следующего вызова.</p>
     <p><code>struct workqueue_struct *keventd_wq = create_workqueue("events");</code></p>
     <p>При этом также создаются все рабочие потоки (по одному на каждый процессор), которые подготавливаются к выполнению работы.</p>
     <p>Создание отложенных действий выполняется одинаково, независимо от тина очереди. После того как действия созданы, могут быть использованы функции, аналогичные функциям <code>schedule_work()</code> и <code>schedule_delayed_work()</code>, которые отличаются тем, что работают с заданной очередью действий, а не с очередью, используемой по умолчанию.</p>
     <p><code>int queue_work struct workqueue_struct *wq, struct work_struct *work);</code></p>
     <empty-line/>
     <p><code>intqueue_delayed_work(struct workqueue_struct *wq,</code></p>
     <p><code>struct work_struct *work, unsigned long delay);</code></p>
     <p>И наконец, ожидание завершения действий в заданной очереди может быть выполнено с помощью функции</p>
     <p><code>flush_workqueue(struct workqueue_struct *wq);</code></p>
     <p>Эта функция работает по аналогии с функцией <code>flush_scheduled_work()</code>, как описывалось ранее, за исключением того, что она ожидает, пока заданная очередь не станет пустой.</p>
    </section>
    <section>
     <title>
      <p>Старый механизм очередей заданий</p>
     </title>
     <p>Так же как и в случае интерфейса BH, который дал начало интерфейсам отложенных прерываний (softirq) и тасклетов (tasklet), интерфейс очередей действий возник благодаря недостаткам интерфейса очередей заданий (task queue). Интерфейс очередей заданий (который еще называют просто <emphasis>tq</emphasis>), так же как и тасклеты, не имеет ничего общего с заданиями (task), в смысле с процессами<a l:href="#n41" type="note">[41]</a>. Все подсистемы, которые использовали механизм очередей заданий, были разбиты на две группы еще во времена разработки серии ядер 2.5. Первая группа была переведена на использование тасклетов, а вторая— продолжала использовать интерфейс очередей заданий. Все, что осталось от интерфейса очередей заданий, перешло в интерфейс очередей отложенных действий. Краткое рассмотрение очередей заданий, которым пользовались в течение некоторого времени, — это хорошее упражнение по истории.</p>
     <p>Интерфейс очередей заданий позволял определять набор очередей. Очереди имели имена, такие как scheduler queue (очередь планировщика), immediate queue (немедленная очередь) или timer queue (очередь таймера). Каждая очередь выполнялась в определенных местах в ядре. Поток пространства ядра <emphasis>keventd</emphasis> выполнял работу, связанную с очередью планировщика. Эта очередь была предшественником интерфейса очередей отложенных действий. Очередь таймера выполнялась при каждом импульсе системного таймера, а немедленная очередь выполнялась в нескольких местах, чтобы гарантировать "немедленное" выполнение. Были также и другие очереди. Кроме того, можно было динамически создавать новые очереди.</p>
     <p>Все это может показаться полезным, но на практике интерфейс очередей заданий приносил только неприятности. Все очереди были, по сути, оторваны от действительности. Единственной ценной очередью оказалась очередь планировщика, которая предоставляла единственную возможность, чтобы выполнять отложенные действия в контексте процесса.</p>
     <p>Еще одним преимуществом механизма очередей заданий была простота интерфейса. Несмотря на большое количество очередей и разнообразие правил, по которым они выполнялись, интерфейс был максимально прост. Все остальное, что касается очередей заданий, необходимо было убрать.</p>
     <p>Различные использования очередей заданий были заменены другими механизмами обработки нижних половин; большинство — тасклетами. Осталось только то, что касалось очереди планировщика. В конце концов, код демона <code>keventd</code> был обобщен в отличный механизм очередей действий, который мы имеем сегодня, а очереди заданий были полностью удалены из ядра.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Какие обработчики нижних половин необходимо использовать</p>
    </title>
    <p>Решение о том, какой из механизмов обработки нижних половин следует использовать, является важным. В современных ядрах серии 2.6 есть три варианта выбора: отложенные прерывания (softirq), тасклеты (tasklet) и очереди отложенных действий (work queue). Тасклеты построены на основе отложенных прерываний, и поэтому эти два механизма похожи. Механизм очередей действий полностью от них отличается, он построен на базе потоков пространства ядра.</p>
    <p>Благодаря своей реализации, отложенные прерывания обеспечивают наибольший параллелизм. Это требует от обработчиков отложенных прерываний применения дополнительных мер для того, чтобы гарантировать безопасный доступ к совместно используемым данным, так как два или более экземпляров одного и того же отложенного прерывания могут выполняться параллельно на разных процессорах. Если код уже очень хорошо распараллелен для многопоточного выполнения, как, например, сетевая подсистема, которая использует данные, связанные с процессорами, то использование отложенных прерываний— это хороший выбор. Они, конечно, представляют собой наиболее быстрый механизм для критичных ко времени или частоте выполнения задач. Тасклеты имеет больший смысл использовать для кода, который не очень хорошо распараллелен для многопоточности. Они имеют более простой интерфейс, и поскольку тасклеты одного типа не могут выполняться параллельно, то их легко программировать. Тасклеты — это фактически отложенные прерывания, которые не могут выполняться параллельно. Разработчики драйверов всегда должны использовать тасклеты, а не отложенные прерывания, кроме, конечно, случаев, когда они готовы связываться с такими вещами, как переменные, связанные с процессорами (per-CPU data), или другими хитростями, чтобы гарантировать безопасное параллельное выполнение отложенных прерываний на разных процессорах.</p>
    <p>Если отложенные операции требуют выполнения в контексте процесса, то из трех возможных вариантов остается единственный выбор — это очереди действий. Если выполнение в контексте процесса не является обязательным, в частности, если нет необходимости переходить в состояние ожидания (sleep), то использование отложенных прерываний или тасклетов, скорее всего, подойдет больше. Очереди действий вносят наибольшие накладные расходы, так как они используют потоки ядра и, соответственно, переключение контекста. Нельзя сказать, что они не эффективны, но в свете тех тысяч прерываний в секунду, что сетевая подсистема может обеспечить, использование других механизмов может иметь больший смысл. Хотя для большинства ситуаций очередей действий также бывает достаточно.</p>
    <p>В плане простоты использования пальму первенства получают очереди действий. Использование очереди <emphasis>events</emphasis>, которая существует по умолчанию, — это просто детская игра. Далее идут тасклеты, которые тоже имеют простой интерфейс. Последними стоят отложенные прерывания, которые должны быть определены статически.</p>
    <p>В табл. 7.3 приведено сравнение различных механизмов обработки нижних половин.</p>
    <empty-line/>
    <p><strong>Таблица 7.3</strong>. Сравнение механизмов обработки нижних половин</p>
    <table>
     <tr align="left">
      <th align="left" valign="top">Механизм обработки нижних половин</th>
      <th align="left" valign="top">Контекст выполнения</th>
      <th align="left" valign="top">Сериализация</th>
     </tr>
     <tr align="left">
      <td align="left" valign="top">Отложенные прерывания (softirq)</td>
      <td align="left" valign="top">Прерывание</td>
      <td align="left" valign="top">Отсутствует</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">Тасклеты (tasklet)</td>
      <td align="left" valign="top">Прерывание</td>
      <td align="left" valign="top">По отношению к тасклету такого же типа</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">Очереди отложенных действий (work queue)</td>
      <td align="left" valign="top">Процесс</td>
      <td align="left" valign="top">Отсутствует (планируется на выполнение как контекст процесса)</td>
     </tr>
    </table>
    <p>Если коротко, то разработчики обычных драйверов имеют всего два варианта выбора. Необходимо ли использовать возможности планировщика, чтобы выполнять отложенные действия, т.е. необходимо ли переходить в состояние ожидания по какой-либо причине? Если да, то единственный вариант — очереди отложенных действий. В противном случае предпочтительно использовать тасклеты. Только если важна масштабируемость, то стоит обратиться к отложенным прерываниям.</p>
   </section>
   <section>
    <title>
     <p>Блокировки между обработчиками нижних половин</p>
    </title>
    <section>
     <p>Мы еще не касались вопросов, связанных с блокировками. Этой теме посвящены следующие две главы. Тем не менее очень важно понимать, что решающим моментом при обработке нижних половин является защита данных общего доступа от конкурентных изменений, даже на однопроцессорной машине. Следует помнить, что обработчик нижней половины прерывания потенциально может выполняться в любой момент времени. Может потребоваться вернуться к текущему разделу, после прочтения следующих двух глав, если вы далеки от вопросов, связанных с блокировками.</p>
     <p>Одно из преимуществ использования тасклетов состоит в том, что они всегда выполняются последовательно по отношению к себе: один и тот же тасклет никогда не будет выполняться параллельно себе даже на двух разных процессорах. Это означает, что нет необходимости заботиться о проблемах, связанных с конкурентным выполнением тасклетов одного типа. Конкурентное выполнение тасклетов нескольких разных типов (в случае, если они совместно используют одни данные) требует применения блокировок.</p>
     <p>Так как отложенные прерывания не обеспечивают строгой последовательности выполнения (даже два обработчика одного и того же отложенного прерывания могут выполняться параллельно), то все совместно используемые данные требуют соответствующих блокировок.</p>
     <p>Если из контекста процесса необходимо обращаться к данным, которые используются как контекстом процесса, так и обработчиком нижней половины, то необходимо запретить обработку нижних половин и захватить блокировку перед тем, как начинать работу с данными. Это позволяет гарантировать защиту совместно используемых данных как на локальном процессоре, так и на разных процессорах SMP системы, а также предотвратить взаимоблокировки.</p>
     <p>Если имеются данные, которые могут совместно использоваться в контексте прерывания и в обработчике нижней половины, то необходимо запретить прерывания и захватить блокировку перед тем, как обращаться к этим данным. Именно эти две операции позволяют предотвратить взаимоблокировку и обеспечить защиту для SMP-систем.</p>
     <p>Все совместно используемые данные, к которым необходимо обращаться из очередей действий, также требуют применения блокировок. Проблема блокировок в этом случае ничем не отличается от блокировок обычного кода ядра, так как очереди действий всегда выполняются в контексте процесса.</p>
     <p>В главе 8 будут рассмотрены хитрости, связанные с блокировками. В главе 9 будут описаны базовые элементы ядра, которые позволяют осуществлять блокировки.</p>
     <p>Далее в этом разделе рассказывается о том, как защитить данные, которые используются обработчиками нижних половин.</p>
    </section>
    <section>
     <title>
      <p>Запрещение обработки нижних половин</p>
     </title>
     <p>Обычно только одного запрещения обработки нижних половин недостаточно. Наиболее часто, чтобы полностью защитить совместно используемые данные, необходимо захватить блокировку и запретить обработку нижних половин. Методы, которые позволяют это сделать и которые обычно используются при разработке драйверов, будут рассмотрены в главе 9. Однако при разработке самого кода ядра иногда необходимо запретить только обработку нижних половин.</p>
     <p>Для того чтобы запретить обработку всех типов нижних половин (всех отложенных прерываний и, соответственно, тасклетов), необходимо вызвать функцию <code>local_bh_disable()</code>. Для разрешения обработки нижних половин необходимо вызвать функцию <code>local_bh_enable()</code>. Да, у этих функций "неправильные" названия. Никто не потрудился переименовать эти функции, когда интерфейс BH уступил место интерфейсу отложенных прерываний. В табл. 7.4 приведены сведения об этих функциях.</p>
     <empty-line/>
     <p><strong>Таблица 7.4</strong>. Список функций управления обработкой нижних половин</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Функция</th>
       <th align="left" valign="top">Описание</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>void local_bh_disable()</code></td>
       <td align="left" valign="top">Запретить обработку всех отложенных прерываний (softirq) и тасклетов (tasklet) на локальном процессоре</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>void local_bh_enable()</code></td>
       <td align="left" valign="top">Разрешить обработку всех отложенных прерываний (softirq) и тасклетов (tasklet) на локальном процессоре</td>
      </tr>
     </table>
     <p>Вызовы этих функций могут быть вложенными — при этом только последний вызов функции <code>local_bh_enable()</code> разрешает обработку нижних половин. Например, при первом вызове функции <code>local_bh_disable()</code> запрещается выполнение отложенных прерываний на текущем процессоре. Если функция <code>local_bh_disable()</code> вызывается еще три раза, то выполнение отложенных прерываний будет запрещено. Их выполнение не будет разрешено до тех пор, пока функция <code>local_bh_enable()</code> не будет вызвана четыре раза.</p>
     <p>Такая функциональность реализована с помощью счетчика <code>preempt_count</code>, который поддерживается для каждого задания (интересно, что этот же счетчик используется и для вытеснения процессов в режиме ядра)<a l:href="#n42" type="note">[42]</a>. Когда значение этого счетчика достигает нуля, то можно начать обработку нижних половин. Так как при вызове функции <code>local_bh_enable()</code> обработка нижних половин запрещена, то эта функция также проверяет наличие ожидающих на обработку нижних половин и выполняет их.</p>
     <p>Для каждой поддерживаемой аппаратной платформы имеются спои функции, которые обычно реализуются через сложные макросы, описанные в файле <code>&lt;asm/softirq.h&gt;</code>. Для любопытных ниже приведены соответствующие реализации на языке программирования С.</p>
     <p><code>/*</code></p>
     <p><code>* запрещение обработки нижних половин путем увеличения значения</code></p>
     <p><code>  счетчика preempt_count</code></p>
     <p><code>*/</code></p>
     <p><code>void local_bh_disable(void) {</code></p>
     <p><code> struct thread_info *t = current_thread_info();</code></p>
     <empty-line/>
     <p><code> t-&gt;preempt_count += SOFTIRQ_OFFSET;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>/*</code></p>
     <p><code>* уменьшение значения счетчика preempt_count "автоматически" разрешает</code></p>
     <p><code>* обработку нижних половин, если значение счетчика равно нулю</code></p>
     <p><code>*</code> </p>
     <p><code>* опционально запускает все обработчики нижних половин,</code></p>
     <p><code>* которые ожидают на обработку</code></p>
     <p><code>*/</code></p>
     <p><code>void local_bh_enable(void) {</code></p>
     <p><code> struct thread_info *t = current_thread_info();</code></p>
     <empty-line/>
     <p><code> t-&gt;preempt_count -= SOFTIRQ_OFFSET;</code></p>
     <empty-line/>
     <p><code> /*</code></p>
     <p><code> * равно ли значение переменной preempt_count нулю и ожидают ли</code></p>
     <p><code> * на обработку какие-либо обработчики нижних половин?</code></p>
     <p><code> * если да, то запустить их</code></p>
     <p><code> */</code></p>
     <p><code> if (unlikely(!t-&gt;preempt_count &amp;&amp;</code></p>
     <p><code>  softirq_pending(smp_processor_id())))</code></p>
     <p><code>  do_softirq();</code></p>
     <p><code>}</code></p>
     <p>Эти функции не запрещают выполнения очередей действий. Так как очереди действий выполняются в контексте процесса, нет никаких проблем с асинхронным выполнением и нет необходимости запрещать их. Поскольку отложенные прерывания и тасклеты могут "возникать" асинхронно (например, при возвращении из обработчика аппаратного прерывания), то ядру может потребоваться запрещать их. В случае использования очередей отложенных действий защита совместно используемых данных осуществляется так же, как и при работе в контексте процесса. Детали рассмотрены в главах 8 и 9.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Внизу обработки нижних половин</p>
    </title>
    <p>В этой главе были рассмотрены три механизма, которые используются для реализации отложенных действий в ядре Linux, — отложенные прерывания (softirq), тасклеты (tasklet) и очереди отложенных действий (work queue). Было показано, как эти механизмы работают и как они реализованы. Также обсуждались основные моменты, связанные с использованием этих механизмов в собственном программном коде, и было показано, какие у них неподходящие названия. Для того чтобы восстановить историческую справедливость, мы также рассмотрели те механизмы обработки нижних половин, которые существовали в предыдущих версиях ядра Linux: механизмы BH и task queue.</p>
    <p>Очень часто в главе поднимались вопросы, связанные с синхронизацией и параллельным выполнением, потому что эти моменты имеют прямое отношение к обработке нижних половин. В эту главу специально был включен раздел, который касается запрещения обработки нижних половин для защиты от конкурентного доступа, Теперь настало время углубиться в эти моменты с головой. В следующей главе будут рассмотрены особенности синхронизации и параллельного выполнения кода в ядре: основные понятия и соответствующие проблемы. Далее будут рассмотрены интерфейсы, которые позволяют осуществлять синхронизацию в ядре и решать указанные проблемы. Вооруженные следующими двумя главами, вы сможете покорить мир.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 8</p>
    <p>Введение в синхронизацию выполнения кода ядра</p>
   </title>
   <section>
    <p>В приложениях, рассчитанных на работу с совместно используемой памятью (shared memory), необходимо позаботиться о том, чтобы совместно используемые ресурсы были защищены от конкурентного доступа. Ядро — не исключение. Совместно используемые ресурсы требуют защиты от конкурентного доступа в связи с тем, что несколько потоков выполнения<a l:href="#n43" type="note">[43]</a> могут одновременно манипулировать одними и теми же данными: эти потоки могут переписывать изменения, сделанные другими потоками, а также обращаться к данным, которые находятся в несогласованном (противоречивом, неконсистентном) состоянии. Конкурентный доступ к совместно используемым данным — это хороший способ получить нестабильность системы, причины которой, как показывает опыт, впоследствии очень сложно обнаружить и исправить. В связи с этим важно при разработке сразу сделать все правильно.</p>
    <p>Осуществить необходимую защиту совместно используемых ресурсов может оказаться трудной задачей. Много лет назад, когда операционная система Linux не поддерживала симметричную многопроцессорную обработку, предотвратить конкурентный доступ к данным было просто. Так как поддерживался только один процессор, то единственная возможность конкурентного доступа к данным возникала при получении прерывания или когда выполнение кода ядра явно перепланировалось, давая возможность выполняться другому заданию. Да, раньше жить было проще.</p>
    <p>Эти дни закончились. Поддержка симметричной многопроцессорности была введена в ядрах серии 2.0, и с тех пор эта поддержка постоянно совершенствуется. Поддержка мультипроцессорности предполагает, что код ядра может одновременно выполняться на двух или более процессорах. Следовательно, без специальной защиты части кода ядра, которые выполняются на двух разных процессорах, принципиально могут обратиться к совместно используемым данным в один и тот же момент времени. Начиная с серии ядер 2.6 ядро операционной системы Linux является преемптивным (вытесняемым). Это подразумевает, что (при отсутствии необходимой защиты) планировщик может вытеснить код ядра в любой момент времени и запустить на выполнение другое задание. Сегодня есть много сценариев, благодаря которым может возникнуть конкурентный доступ к данным в ядре, и все эти варианты требуют защиты данных.</p>
    <p>В этой главе рассматриваются проблемы, связанные с параллельным выполнением кода и синхронизацией выполнения кода в ядре операционной системы. В следующей главе детально рассмотрены механизмы и интерфейсы, которые предоставляет ядро операционной системы Linux для решения проблем синхронизации и предотвращения состояния конкуренции за ресурс (race condition, состояние "гонок").</p>
   </section>
   <section>
    <title>
     <p>Критические участки и состояние конкуренции за ресурсы</p>
    </title>
    <section>
     <p>Ветки кода, которые получают доступ к совместно используемыми данным и манипулируют ими, называются <emphasis>критическими участками</emphasis> (<emphasis>critical region</emphasis>). Обычно небезопасно нескольким потокам выполнения одновременно обращаться к одному и тому же ресурсу. Для предотвращения конкурентного доступа во время выполнения критических участков программист, т.е. Вы, должен гарантировать, что код выполняется <emphasis>атомарно</emphasis> — без перерывов, так если бы весь критический участок был одной неделимой машинной инструкцией. Если два потока выполнения одновременно находятся в критическом участке, то это — ошибка в программе. Если такое вдруг случается, то такая ситуация называется <emphasis>состоянием, конкуренции за ресурс</emphasis> (состояние "гонок", race condition). Название связано с тем, что потоки как бы соревнуются друг с другом за доступ к ресурсу. Следует обратить внимание на то, насколько редко такая ситуация может возникать, — поэтому обнаружение состояний конкуренции за ресурсы при отладке программ часто очень сложная задача, потому что подобную ситуацию очень трудно воспроизвести. Обеспечение гарантии того, что конкуренции не будет и, следовательно, что состояний конкуренции за ресурсы возникнуть не может, называется <emphasis>синхронизацией</emphasis>.</p>
    </section>
    <section>
     <title>
      <p>Зачем нужна защита</p>
     </title>
     <p>Для лучшего понимания того, к чему может привести состояние конкуренции, давайте рассмотрим примеры повсеместно встречающихся критических участков.</p>
     <p>В качестве первого примера рассмотрим ситуацию из реальной жизни; банкомат (который еще называют ATM, Automated Teller Machine, или кэш-машиной).</p>
     <p>Одно из наиболее часто встречающихся действий, которые приходится выполнять с помощью банкомата — это снятие денег с персонального банковского счета физического лица. Человек подходит к банкомату, вставляет карточку, вводит PIN-код, проходит аутентификацию, выбирает пункт меню <strong>Снятие наличных</strong>, вводит необходимую сумму, нажимает <strong>OK</strong>, забирает деньги и отправляет их автору этой книги.</p>
     <p>После того как пользователь ввел необходимую сумму, банкомат должен проверить, что такая сумма действительно есть на счету. Если такие деньги есть, то необходимо вычесть снимаемую сумму из общего количества доступных денег. Код, который выполняет эту операцию, может выглядеть следующим образом.</p>
     <p><code>int total = get_total_from_account(); /* общее количество денег на счету */</code></p>
     <p><code>int withdrawal = get_withdrawal_amount(); /* количество денег,</code></p>
     <p><code>                                             которые хотят снять */</code></p>
     <empty-line/>
     <p><code>/* проверить, есть ли у пользователя деньги на счету */</code></p>
     <p><code>if (total &lt; withdrawal)</code></p>
     <p><code> error("У Вас нет таких денег!");</code></p>
     <empty-line/>
     <p><code>/* Да, у пользователя достаточно денег: вычесть снимаемую сумму из</code></p>
     <p><code>   общего количества денег на счету */</code></p>
     <p><code>total -= withdrawal;</code></p>
     <p><code>update_total_funds(total);</code></p>
     <empty-line/>
     <p><code>/* Выдать пользователю деньги */</code></p>
     <p><code>spit_out_money(withdrawal);</code></p>
     <p>Теперь представим, что в тот же самый момент времени со счета этого же пользователя снимается еще одна сумма денег. Не имеет значения, каким образом выполняется снятие второй суммы. Например, или супруг пользователя снимает деньги с другого банкомата, или кто-то переводит со счета деньги электронным платежом, или банк снимает со счета в качестве платы за что-то (как это обычно любят делать банки), или происходит что-либо еще.</p>
     <p>Обе системы, которые снимают деньги со счета, выполняют код, аналогичный только что рассмотренному: проверяется, что снятие денег возможно, после этого вычисляется новая сумма денег на счету и, наконец, деньги снимаются физически. Теперь рассмотрим некоторые численные значения. Допустим, что первая снимаемая сумма равна $100, а вторая — $10, например, за то, что пользователь зашел в банк (что не приветствуется: необходимо использовать банкомат, так как в банках людей не хотят видеть). Допустим также, что у пользователя на счету есть сумма, равная $105. Очевидно, что одна из этих двух транзакций не может завершиться успешно без получения минусов на счету.</p>
     <p>Можно ожидать, что получится что-нибудь вроде следующего: первой завершится транзакция по снятию платы за вход в банк. Десять долларов — это меньше чем $105, поэтому, если от $105 отнять $10, на счету останется $95, а $10 заработает банк. Далее начнет выполняться снятие денег через банкомат, но оно завершится неудачно, так как $95 — это меньше чем $100.</p>
     <p>Тем не менее жизнь может оказаться значительно интереснее, чем ожидалось. Допустим, что две указанные выше транзакции начинаются почти в один и тот же момент времени. Обе транзакции убеждаются, что на счету достаточно денег: $105 — это больше $100 и больше $10. После этого процесс снятия денег с банкомата вычтет $100 из $105 и получится $5. В это же время процесс снятия платы за вход сделает то же самое и вычтет $10 из $105, и получится $95. Далее процесс снятия денег обновит состояние счета пользователя: на счету окажется сумма $5. В конце транзакция снятия платы за вход также обновит состояние счета, и на счету окажется $95. Получаем деньги в подарок!</p>
     <p>Ясно, что финансовые учреждения считают своим долгом гарантировать, чтобы такой ситуации не могло возникнуть никогда. Необходимо блокировать счет во время выполнения некоторых операций, чтобы гарантировать атомарность транзакций по отношению к другим транзакциям. Такие транзакции должны полностью выполняться не прерываясь или не выполняться совсем.</p>
     <subtitle>Общая переменная</subtitle>
     <p>Теперь рассмотрим пример, связанный с компьютерами. Пусть у нас есть очень простой совместно используемый ресурс: одна глобальная целочисленная переменная и очень простой критический участок — операция инкремента значения этой переменной:</p>
     <p><code>i++</code></p>
     <p>Это выражение можно перевести в инструкции процессора следующим образом.</p>
     <p><code>Загрузить текущее значение переменной <strong>i</strong> из памяти в регистр.</code></p>
     <p><code>Добавить единицу к значению, которое находится в регистре.</code></p>
     <p><code>Записать новое значение переменной <strong>i</strong> обратно в память.</code></p>
     <p>Теперь предположим, что есть два потока, которые одновременно выполняют этот критический участок, и начальное значение переменной <code>i</code> равно 7. Результат выполнения будет примерно следующим (каждая строка соответствует одному интервалу времени ).</p>
     <p><strong>Поток 1                                                                 Поток 2</strong></p>
     <p><code>получить значение <strong>i</strong> из памяти <strong>(7)</strong> -</code></p>
     <p><code>увеличить <strong>i</strong> на <strong>1 (7-&gt;8)</strong>           -</code></p>
     <p><code>записать значение <strong>i</strong> в память <strong>(8)</strong>  -</code></p>
     <p><code>-                                 получить значение <strong>i</strong> из памяти <strong>(8)</strong></code></p>
     <p><code>-                                 увеличить <strong>i</strong> на <strong>1 (8-&gt;9)</strong></code></p>
     <p><code>-                                 записать значение <strong>i</strong> в память <strong>(9)</strong></code></p>
     <p>Как и ожидалось, значение переменной i, равное 7, было увеличено на единицу два раза и стало равно 9. Однако возможен и другой вариант.</p>
     <p><strong>Поток 1                                                                      Поток 2</strong></p>
     <p><code>получить значение <strong>i</strong> из памяти <strong>(7)</strong> -</code> </p>
     <p><code>-                                 получить значение <strong>i</strong> из памяти <strong>(7)</strong></code></p>
     <p><code>увеличить <strong>i</strong> на <strong>1 (7-&gt;8)</strong>           -</code> </p>
     <p><code>-                                 увеличить <strong>i</strong> на <strong>1 (7-&gt;8</strong>)</code></p>
     <p><code>записать значение <strong>i</strong> в память <strong>(8)</strong>  -</code></p>
     <p><code>-                                 записать значение <strong>i</strong> в память <strong>(8)</strong></code></p>
     <p>Если оба потока выполнения прочитают первоначальное значение переменной <code>i</code> перед тем, как оно было увеличено на 1, то оба потока увеличат его на единицу и запишут в память одно и то же значение. В результате переменная <code>i</code> будет содержать значение 8, тогда как она должна содержать значение 9. Это один из самых простых примеров критических участков. К счастью, решение этой проблемы простое — необходимо просто обеспечить возможность выполнения всех рассмотренных операций за один неделимый шаг. Для большинства процессоров есть машинная инструкция, которая позволяет атомарно считать данные из памяти, увеличить их значение на 1 и записать обратно в память, выделенную для переменной. Использование такой инструкции позволяет решить проблему. Возможно только два варианта правильного выполнения этого кода — следующий.</p>
     <p><strong>Поток 1                                                Поток 2</strong></p>
     <p><code>увеличить <strong>i</strong> на <strong>1 (7-&gt;8)</strong> -</code></p>
     <p><code>-                       увеличить <strong>i</strong> на <strong>1 (8-&gt;9</strong>)</code></p>
     <p>Или таким образом.</p>
     <p><strong>Поток 1                                                Поток 2</strong></p>
     <p><code>-                       увеличить <strong>i</strong> на <strong>1 (7-&gt;8)</strong></code></p>
     <p><code>увеличить <strong>i</strong> на <strong>1 (8-&gt;9)</strong> -</code> </p>
     <p>Две атомарные операции никогда не могут перекрываться. Процессор на физическом уровне гарантирует это. Использование такой инструкции решает проблему. Ядро предоставляет несколько интерфейсов, которые позволяют реализовать атомарные операции. Эти интерфейсы будут рассмотрены в следующей главе.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Блокировки</p>
    </title>
    <section>
     <p>Теперь давайте рассмотрим более сложный пример конкуренции за ресурсы, который требует более сложного решения. Допустим, что у нас есть очередь запросов, которые должны быть обработаны. Как реализована очередь — не существенно, но мы будем считать, что это — связанный список, в котором каждый узел соответствует одному запросу. Очередью управляют две функции: одна— добавляет новый запрос в конец очереди, а другая — извлекает запрос из головы очереди и делает с ним нечто полезное. Различные части ядра вызывают обе эти функции, поэтому запросы могут постоянно поступать, удаляться и обрабатываться. Все манипуляции очередью запросов, конечно, требуют нескольких инструкций. Если один из потоков пытается считывать данные из очереди, а другой поток находится в средине процесса манипуляции очередью, то считывающий поток обнаружит, что очередь находится в несогласованном состоянии. Легко понять, что при конкурентном обращении к очереди может произойти разрушение структуры данных. Часто ресурс общего доступа — это сложная структура данных, и в результате состояния конкуренции возникает разрушение этой структуры.</p>
     <p>Вначале кажется, что описанная ситуация не имеет простого решения. Как можно предотвратить чтение очереди на одном процессоре в тот момент, когда другой процессор обновляет ее? Вполне логично аппаратно реализовать простые инструкции, такие как атомарные арифметические операции или операции сравнения, тем не менее было бы смешно аппаратно реализовывать критические участки неопределенного размера, как в приведенном примере. Все что нужно — это предоставить метод, который позволяет отметить начало и конец; критического участка, и предотвратить или <emphasis>заблокировать</emphasis> (lock) доступ к этому участку, пока другой поток выполняет его.</p>
     <p>Блокировки (lock) предоставляют такой механизм. Он работает почти так же, как и дверной замок. Представим, что комната, которая находится за дверью, — это критический участок. Внутри комнаты в любой момент времени может присутствовать только один поток выполнения. Когда поток входит в комнату, он запирает за собой дверь. Когда поток заканчивает манипуляции с совместно используемыми данными, он выходит из комнаты, отпирая дверь перед выходом. Если другой поток подходит к двери, когда она заперта, то он должен ждать, пока поток, который находится внутри комнаты, не отопрет дверь и не выйдет. Потоки удерживают блокировки, а блокировки защищают данные.</p>
     <p>В приведенном выше примере очереди запросов для защиты очереди может использоваться одна блокировка. Как только необходимо добавить запрос в очередь, поток должен вначале захватить блокировку. Затем он может безопасно добавить запрос в очередь и после этого освободить блокировку. Если потоку необходимо извлечь запрос из очереди, он тоже должен захватить блокировку. После этого он может прочитать запрос и удалить его из очереди. В конце поток освобождает блокировку. Любому другому потоку для доступа к очереди также необходимо аналогичным образом захватывать блокировку. Так как захваченная блокировка может удерживаться только одним потоком в любой момент времени, то только один поток может производить манипуляции с очередью в любой момент времени. Блокировка позволяет предотвратить конкуренцию и защитить очередь от состояния конкуренции за ресурс.</p>
     <p>Код, которому необходимо получить доступ к очереди, должен захватить соответствующую блокировку. Если неожиданно появляется другой поток выполнения, то это позволяет предотвратить конкуренцию.</p>
     <p><strong>Поток 1                                                                   Поток 2</strong></p>
     <p><code>Попытаться заблокировать очередь попытаться заблокировать очередь</code></p>
     <empty-line/>
     <p><code>успешно: блокировка захвачена    неудачно: ожидаем...</code></p>
     <p><code>                                 ожидаем...</code></p>
     <p><code>обратиться к очереди...          ожидаем...</code></p>
     <empty-line/>
     <p><code>разблокировать очередь           успешно: блокировка захвачена</code></p>
     <p><code>...</code></p>
     <p><code>                                 обратиться к очереди...</code></p>
     <p><code>                                 разблокировать очередь</code></p>
     <p><code>                                 ...</code></p>
     <p>Заметим, что блокировки бывают <emphasis>необязательными</emphasis> (рекомендуемыми, advisory) и <emphasis>обязательными</emphasis> (навязываемыми, voluntary). Блокировки — это чисто программные конструкции, преимуществами которых должны пользоваться программисты. Никто не запрещает писать код, который манипулирует нашей воображаемой очередью без использования блокировок. Однако такая практика в конечном итоге приведет к состоянию конкуренции за ресурс и разрушению данных.</p>
     <p>Блокировки бывают различных "форм" и "размеров". В операционной системе Linux реализовано несколько различных механизмов блокировок. Наиболее существенная разница между ними — это поведение кода в условиях, когда блокировка захватывается (конфликт при захвате блокировки, contended lock). Для некоторых типов блокировок, задания просто ожидают освобождения блокировки, постоянно выполняя проверку освобождения в замкнутом цикле (busy wait<a l:href="#n44" type="note">[44]</a>), в то время как другие тины блокировок переводят задание в состояние ожидания до тех пор, пока блокировка не освободится.</p>
     <p>В следующей главе рассказывается о том, как ведут себя различные типы блокировок в операционной системе Linux, и об интерфейсах взаимодействия с этими блокировками.</p>
     <p>Проницательный читатель в этом месте должен воскликнуть: "Блокировки не решают проблемы, они просто сужают набор всех возможных критических участков до кода захвата и освобождения блокировок. Тем не менее, здесь потенциально может возникать состояние конкуренции за ресурсы, хотя и с меньшими последствиями!" К счастью, блокировки реализованы на основе атомарных операций, которые гарантируют, что состояние конкуренции за ресурсы не возникнет. С помощью одной машинной инструкции выполняется проверка захвачен ли ключ, и, если нет, то этот ключ захватывается. То, как это делается, очень сильно зависит от аппаратной платформы, но почти для всех процессоров определяется машинная инструкция <emphasis>test-and-set</emphasis> (проверить и установить), которая позволяет проверить значение целочисленной переменной и присвоить этой переменной указанное число, если се значение равно нулю. Значение нуль соответствует незахваченной блокировке.</p>
    </section>
    <section>
     <title>
      <p>Откуда берется параллелизм</p>
     </title>
     <p>При работе в пространстве пользователя необходимость синхронизации возникает из того факта, что программы выполняются преемптивно, т.е. могут быть вытеснены другой программой по воле планировщика. Поскольку процесс может быть вытеснен в любой момент и другой процесс может быть запущен планировщиком для выполнения на этом же процессоре, появляется возможность того, что процесс может быть вытеснен независящим от него образом во время выполнения критического участка. Если новый, запланированный на выполнение процесс входит в тот же критический участок (скажем, если оба процесса — потоки одной программы, которые могут обращаться к общей памяти), то может возникнуть состояние конкуренции за ресурс. Аналогичная проблема может возникнуть даже в однопоточной программе при использовании сигналов, так как сигналы приходят асинхронно. Такой тип параллелизма, когда два события происходят не одновременно, а накладываются друг на друга, так вроде они происходят в один момент времени, называется <emphasis>псевдопараллелизмом</emphasis> (pseudo-concurrency).</p>
     <p>На машине с симметричной многопроцессорностью два процесса могут действительно выполнять критические участки в один и тот же момент времени. Это называется <emphasis>истинным, параллелизмом</emphasis> (true concurrency). Хотя причины и семантика истинного и псевдопараллелизма разные, они могут приводить к совершенно одинаковым состояниям конкуренции и требуют аналогичных средств защиты. В ядре причины параллельного выполнения кода следующие.</p>
     <p>• <emphasis>Прерывания</emphasis>. Прерывания могут возникать асинхронно, практически в любой момент времени, прерывая код, который выполняется в данный момент.</p>
     <p>• <emphasis>Отложенные прерывания и тасклеты</emphasis>. Ядро может выполнять обработчики softirq и тасклеты практически в любой момент времени и прерывать код, который выполняется в данный момент времени.</p>
     <p>• <emphasis>Преемптивность</emphasis> ядра. Так как ядро является вытесняемым, то одно задание, которое работает в режиме ядра, может вытеснить другое задание, тоже работающее в пространстве ядра.</p>
     <p>• <emphasis>Переход в состояние ожидания и синхронизация с пространством пользователя</emphasis>. Задание, работающее в пространстве ядра, может переходить в состояние ожидания, что вызывает активизацию планировщика и выполнение нового процесса.</p>
     <p>• <emphasis>Симметричная многопроцессорность</emphasis>. Два или больше процессоров могут выполнять код в один и тот же момент времени.</p>
     <p>Важно, что разработчики ядра поняли все причины и подготовились к возможным случаям параллелизма. Если прерывание возникает во время выполнения кода, который работает с некоторым ресурсом, и обработчик прерывания тоже обращается к этому же ресурсу, то это является ошибкой. Аналогично ошибкой является и то, что код ядра вытесняется в тот момент, когда он обращается к совместно используемому ресурсу. Переход в состояние ожидания во время выполнения критического участка в ядре открывает большой простор для состояний конкуренции за ресурсы. И наконец, два процессора никогда не должны одновременно обращаться к совместно используемым данным. Когда ясно, какие данные требуют защиты, то уже нетрудно применить соответствующие блокировки, чтобы обеспечить всем безопасность. Сложнее идентифицировать возможные условия возникновения таких ситуаций и определить, что для предотвращения конкуренции необходима та или иная форма защиты. Давайте еще раз пройдем через этот момент, потому что он очень важен. Применить блокировки в коде для того, чтобы защитить совместно используемые данные, — это не тяжело, особенно если это делается на самых ранних этапах разработки кода. Сложность состоит в том, чтобы найти эти самые совместно используемые данные и эти самые критические участки. Именно поэтому требование аккуратного использования блокировок с самого начала разработки кода — а не когда-нибудь потом — имеет первостепенную важность. Постфактум очень сложно отследить, что необходимо блокировать, и правильно внести изменения в существующий код. Результаты подобной разработки обычно не очень хорошие. Мораль — всегда нужно аккуратно учитывать необходимость применения блокировок с самого начала процесса разработки кода.</p>
     <p>Код, который безопасно выполнять параллельно с обработчиком прерывания, называется <emphasis>безопасным при прерываниях</emphasis> (interrupt-safe). Код, который содержит защиту от конкурентного доступа к ресурсам при симметричной многопроцессорной обработке, называется <emphasis>безопасным при SMP-обработке</emphasis> (SMP-safe). Код, который имеет защиту от конкурентного доступа к ресурсам при вытеснении кода ядра, называется <emphasis>безопасным при вытеснения</emphasis><a l:href="#n45" type="note">[45]</a> (preempt-safe). Механизмы, которые во всех этих случаях используются для обеспечения синхронизации и защиты от состояний конкуренции, будут рассмотрены в следующей главе.</p>
    </section>
    <section>
     <title>
      <p>Что требует защиты</p>
     </title>
     <p>Жизненно важно определить, какие данные требуют защиты. Так как любой код, который может выполняться параллельно, может потребовать защиты. Вероятно, легче определить, какие данные <emphasis>не требуют</emphasis> защиты, и работать дальше, отталкиваясь от этого. Очевидно, что все данные, которые доступны только одному потоку выполнения, не требуют защиты, поскольку только этот поток может обращаться к этим данным. Например, локальные переменные, которые выделяются в автоматической памяти (и те, которые находятся в динамически выделяемой памяти, если их адреса хранятся только в стеке), не требуют никаких блокировок, так как они существуют только в стеке выполняющегося потока. Точно так же данные, к которым обращается только одно задание, не требуют применения блокировок (так как один поток может выполняться только на одном процессоре в любой момент времени).</p>
     <p>Что же тогда <emphasis>требует</emphasis> применения блокировок? Это — большинство глобальных структур данных ядра. Есть хорошее эмпирическое правило: если, кроме одного, еще и другой поток может обращаться к данным, то эти данные требуют применения какого-либо типа блокировок. Если что-то видно кому-то еще — блокируйте его. Помните, что блокировать необходимо <emphasis>данные</emphasis>, а не <emphasis>код</emphasis>.</p>
     <cite>
      <subtitle>Параметры КОНФИГУРАЦИИ ядра: SMP или UP</subtitle>
      <p>Так как ядро операционной системы Linux может быть сконфигурировано на этапе компиляции, имеет смысл "подогнать" ядро под данный тип машины. Важной функцией ядра является поддержка симметричной многопроцессорной обработки (SMP), которая включается с помощью параметра конфигурации ядра <code>CONFIG_SMP</code>. На однопроцессорной (uniprocessor, UP) машине исчезают многие проблемы, связанные с блокировками, и, следовательно, если параметр <code>CONFIG_SMP</code> не установлен, то код, в котором нет необходимости, не компилируется в исполняемый образ ядра. Например, это позволяет на однопроцессорной машине отказаться от накладных расходов, связанных со спин-блокировками. Аналогичный прием используется для параметра <code>CONFIG_PREEMPT</code> (параметр ядра, который указывает, будет ли ядро вытесняемым). Такое решение является отличным проектным решение, поскольку позволяет использовать общий четкий исходный код, а различные механизмы блокировок используются при необходимости. Различные комбинации параметров <code>CONFIG_SMP</code> и <code>CONFIG_PREEMPT</code> на различных аппаратных платформах позволяют компилировать в ядро различные механизмы блокировок.</p>
      <p>При написании кода необходимо обеспечить все возможные варианты защиты для всех возможных случаев жизни и всех возможных сценариев, которые будут рассмотрены.</p>
     </cite>
     <p>При написании кода ядра следует задать себе следующие вопросы.</p>
     <p>• Являются ли данные глобальными? Может ли другой поток выполнения, кроме текущего, обращаться к этим данным?</p>
     <p>• Являются ли данные совместно используемыми из контекста процесса и из контекста прерывания? Используют ли их совместно два обработчика прерываний?</p>
     <p>• Если процесс во время доступа к данным будет вытеснен, может ли новый процесс, который запланирован на выполнение, обращаться к этим же данным?</p>
     <p>• Может ли текущий процесс перейти в состояние ожидания (заблокироваться) на какой-либо операции? Если да, то в каком состоянии он оставляет все совместно используемые данные?</p>
     <p>• Что запрещает освободить память, в которой находятся данные?</p>
     <p>• Что произойдет, если эта же функция будет вызвана на другом процессоре?</p>
     <p>• Как все это учесть?</p>
     <p>Если коротко, то почти все глобальные данные требуют применения тех или других методов синхронизации, которые будут рассмотрены в следующей главе.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Взаимоблокировки</p>
    </title>
    <p>Взаимоблокировка (тупиковая ситуация, deadlock) — это состояние, при котором каждый поток ожидает на освобождение одного из ресурсов, а все ресурсы при этом захвачены. Потоки будут ожидать друг друга, и они никогда не смогут освободить захваченные ресурсы. Поэтому ни один из потоков не сможет продолжать выполнение, что означает наличие взаимоблокировки.</p>
    <p>Хорошая аналогия — это перекресток, на котором стоят четыре машины, которые подъехали с четырех разных сторон. Каждая машина ожидает, пока не уедут остальные машины, и ни одна из машин не сможет уехать; в результате получается тупиковая ситуация.</p>
    <p>Самый простой пример взаимоблокировки— это <emphasis>самоблокировка</emphasis><a l:href="#n46" type="note">[46]</a> (self-deadlock). Если поток выполнения пытается захватить ту блокировку, которую он уже удерживает, то ему необходимо дождаться, пока блокировка не будет освобождена. Но поток никогда не освободит блокировку, потому что он ожидает на ее захват, и это приводит к тупиковой ситуации.</p>
    <p><code>захватить блокировку</code></p>
    <p><code>захватить блокировку еще раз</code></p>
    <p><code>ждать, пока блокировка не будет освобождена</code></p>
    <p><code>...</code></p>
    <p>Аналогично рассмотрим <code>n</code> потоков и <code>n</code> блокировок. Если каждый поток удерживает блокировку, на которую ожидает другой поток, то все потоки будут заблокированы до тех пор, пока не освободятся те блокировки, на освобождение которых ожидают потоки. Наиболее часто встречающийся пример — это два потока и две блокировки, что часто называется <emphasis>взаимоблокировка типа ABBA</emphasis> (ABBA deadlock).</p>
    <p><strong>Поток 1                                                                Поток 2</strong></p>
    <p>з<code>ахватить блокировку А             захватить блокировку В</code></p>
    <p><code>попытка захватить блокировку В     попытка захватить блокировку А</code></p>
    <p><code>ожидание освобождения блокировки В ожидание освобождения блокировки А</code></p>
    <p>Оба потока будут ожидать друг друга, и ни один из потоков никогда не освободит первоначально захваченной блокировки, поэтому ни одна из блокировок не будет освобождена. Такая тупиковая ситуация еще называется <emphasis>deadly embrace</emphasis> (буквально. смертельные объятия).</p>
    <p>Важно не допустить появление взаимоблокировок. Хотя сложно проверить готовый код на наличие взаимоблокировок, можно написать код, который не содержит взаимоблокировок. Такую возможность дает соблюдение нескольких простых правил.</p>
    <p>• Жизненно важным является порядок захвата блокировок. Вложенные блокировки всегда должны захватываться в одном и том же порядке. Это предотвращает взаимоблокировку нескольких потоков (deadly embrace). Порядок захвата блокировок необходимо документировать, чтобы другие тоже могли его соблюдать.</p>
    <p>• Необходимо предотвращать зависания. Следует спросить себя: <emphasis>"Всегда ли этот код сможет завершиться?"</emphasis>. Если не выполнится какое-либо условие, то не будет ли что-то ожидать вечно?</p>
    <p>• Не захватывать одну и ту же блокировку дважды.</p>
    <p>• Сложность в схеме блокировок — верный путь к тупиковым ситуациям, поэтому при разработке необходимо стремиться к простоте.</p>
    <p>Первый пункт важный и наименее сложный для выполнения. Если две или более блокировок захватываются в одном месте, то они <emphasis>всегда</emphasis> должны захватываться в строго определенном порядке. Допустим, у нас есть три блокировки <code>cat</code>, <code>dog</code> и <code>fox</code>, которые используются для защиты данных с такими же именами. И еще допустим, что у нас есть функция, которая должна работать с этими тремя структурами данных одновременно— например, может копировать данные между ними. В любом случае, для того чтобы гарантировать безопасность доступа, эти структуры данных необходимо защищать блокировками. Если одна функция захватывает эти блокировки в следующем порядке: <code>cat</code>, <code>dog</code> и в конце <code>fox</code>, то любая другая функция должна захватывать эти блокировки (или только некоторые из них) в том же порядке. Например, если захватывать сначала блокировку <code>fox</code>, а потом блокировку <code>dog</code>, то это потенциальная возможность взаимоблокировки (а значит, ошибки в работе), потому что блокировка <code>dog</code> всегда должна захватываться перед блокировкой <code>fox</code>. И еще раз рассмотрим пример, как может возникнуть взаимоблокировка.</p>
    <p><strong>Поток 1                                                                   Поток 2</strong></p>
    <p><code>захватить блокировку cat             захватить блокировку fox</code></p>
    <p><code>захватить блокировку dog             попытка захватить блокировку dog</code></p>
    <p><code>попытка захватить блокировку fox     ожидание освобождения блокировки dog</code></p>
    <p><code>ожидание освобождения блокировки fox —</code></p>
    <p><code>Поток 1</code> ожидает освобождения блокировки <code>fox</code>, которую удерживает <code>поток 2</code>, а <code>поток 2</code> в это время ожидает освобождения блокировки <code>dog</code>, которую удерживает <code>поток 1</code>. Ни один из потоков никогда не освободит своих блокировок, и, соответственно, оба потока будут ждать вечно — возникает тупиковая ситуация. Если оба потока всегда захватывают блокировки в одном и том же порядке, то подобной тупиковой ситуации возникнуть не может.</p>
    <p>Если несколько процедур захвата блокировок вложены друг в друга, то должен быть принят определенный порядок захвата. Хорошая практика — всегда использовать комментарий сразу перед объявлением блокировки, который указывает на порядок захвата. Использовать что-нибудь вроде следующего будет хорошей идеей.</p>
    <p><code>/*</code></p>
    <p><code>* cat_lock - всегда захватывать перед блокировкой dog</code></p>
    <p><code>* (и всегда захватывать блокировку dog перед блокировкой fox)</code></p>
    <p><code>*/</code></p>
    <p>Следует заметить, что порядок <emphasis>освобождения</emphasis> блокировок не влияет на возможность появления взаимоблокировок, хотя освобождать блокировки в обратном порядке по отношению к их захвату — это хорошая привычка.</p>
    <p>Очень важно предотвращать взаимоблокировки. В ядре Linux есть некоторые отладочные возможности, которые позволяют обнаруживать взаимоблокировки при выполнении кода ядра. Эти возможности будут рассмотрены в следующем разделе.</p>
   </section>
   <section>
    <title>
     <p>Конфликт при захвате блокировки и масштабируемость</p>
    </title>
    <p>Термин "<emphasis>конфликт при захвате блокировки</emphasis>" (lock contention, или просто contention) используется для описания блокировки, которая в данный момент захвачена и на освобождение которой ожидают другие потоки. Блокировки с <emphasis>высоким уровнем конфликтов</emphasis> (highly contended) — это те, на освобождение которых всегда ожидает много потоков. Так как задача блокировок — это сериализация доступа к ресурсу, то не вызовет большого удивления тот факт, что блокировки снижают производительность системы. Блокировка с высоким уровнем конфликтов может стать узким местом в системе, быстро уменьшая производительность. Конечно, блокировки необходимы для того, чтобы предотвратить "развал" системы, поэтому решение проблемы высокого уровня конфликтов при блокировках также должно обеспечивать необходимую защиту от состояний конкуренции за ресурсы.</p>
    <p><emphasis>Масштабируемость</emphasis> (scalability) — это мера того, насколько система может быть расширена. В случае операционных систем, когда говорят о масштабируемости, подразумевают большее количество процессов, большее количество процессоров, больший объем памяти. О маштабируемости можно говорить в приложении практически к любому компоненту компьютера, который можно охарактеризовать количественным параметром. В идеале удвоение количества процессоров должно приводить к удвоению процессорной производительности системы. Однако на практике, конечно, такого не бывает никогда.</p>
    <p>Масштабируемость операционной системы Linux на большее количество процессоров возросла поразительным образом с того времени, когда поддержка многопроцессорной обработки была встроена в ядра серии 2.0. В те дни, когда поддержка многопроцессорности в операционной системе Linux только появилась, лишь одно задание могло выполняться в режиме ядра в любой момент времени. В ядрах серии 2.2 это ограничение было снято, так как механизмы блокировок стали более мелкоструктурными. В серии 2.4 и выше блокировки стали еще более мелкоструктурными. Сегодня в ядрах серии 2.6 блокировки имеют очень мелкую гранулярность, а масштабируемость получается очень хорошей.</p>
    <p><emphasis>Структурность</emphasis> (гранулярность, granularity) блокировки — это описание объемов тех данных, которые защищаются блокировкой, например все структуры данных одной подсистемы. С другой стороны, блокировка на уровне очень мелких структурных единиц (fine grained), используется для защиты очень маленького объема данных, например одного поля структуры. В реальных ситуациях большинство блокировок попадают между этими крайностями, они используются не для защиты целой подсистемы, но и не для защиты одного поля, а возможно для защиты отдельного экземпляра структуры. Большинство блокировок начинали использоваться на уровне крупных структурных единиц (coarse grained), а потом их стали разделять на более мелкие структурные уровни, как только конфликты при захвате этих блокировок становились проблемой.</p>
    <p>Один из примеров перевода блокировок на более мелкий структурный уровень — это блокировки очередей выполнения планировщика (runqueue), которые рассмотрены в главе 4, "Планирование выполнения процессов". В ядрах серии 2.4 и более ранних планировщик имел всего одну очередь выполнения (вспомним, что очередь выполнения— это список готовых к выполнению процессов). В серии 2.6 был предложен O(1)-планировщик, в котором для каждого процессора используется своя очередь выполнения, каждая очередь имеет свою блокировку. Соответствующие блокировки развились из одной глобальной блокировки в несколько отдельных блокировок для каждой очереди, а использование блокировок развилось из глобального блокирования в использование блокировок на отдельных процессорах. Эта оптимизация очень существенна, так как на больших машинах блокировка очереди выполнения имеет очень высокий уровень конфликтов при захвате, что приводит к сериализации планирования выполнения процессов. Иными словами, код планировщика выполнял только один процессор системы в любой момент времени, а остальные процессоры — ждали.</p>
    <p>В общем такое повышение масштабируемости — это очень хорошая вещь, которая позволяет повысить производительность операционной системы Linux на больших и более мощных системах. Чрезмерное увлечение "ростом" масштабируемости может привести к снижению производительности на небольших многопроцессорных и однопроцессорных машинах, потому что для небольших машин не требуются такие мелкоструктурные блокировки, а им приходится иметь дело с большей сложностью и с большими накладными расходами. Рассмотрим связанный список. Первоначальная схема блокировки обеспечивает одну блокировку на весь список. Со временем эта одна блокировка может стать узким местом на очень большой многопроцессорной машине, на которой очень часто обращаются к связанному списку. Для решения проблемы одна блокировка может быть разбита на большое количество блокировок — одна блокировка на один элемент списка. Для каждого элемента списка, который необходимо прочитать или записать, необходимо захватывать уникальную блокировку этого элемента. Теперь конфликт при захвате блокировки будет только в случае, когда несколько процессоров обращаются к одному элементу списка. Что делать, если все равно есть высокий уровень конфликтов? Может быть, необходимо использовать блокировку для каждого поля элемента списка? (Ответ: НЕТ.) Если серьезно, даже когда очень мелко структурированные блокировки хорошо работают на очень больших SMP-машинах, то как они будут работать на двухпроцессорных машинах? Накладные расходы, связанные с дополнительными блокировками, будут напрасными, если на двухпроцессорной машине нет существенных конфликтов при работе с блокировками.</p>
    <p>Тем не менее масштабируемость — это важный фактор. Важно с самого начала разрабатывать схему блокировок для обеспечения хорошей масштабируемости. Блокировки на уровне крупных структурных единиц могут стать узким местом даже на машинах с небольшим количеством процессоров. Между крупноструктурными и мелкоструктурными блокировками очень тонкая грань. Слишком крупноструктурные блокировки приводят к большому уровню конфликтов, а слишком мелкоструктурные — к напрасным накладным расходам, если уровень конфликтов при захвате блокировок не очень высокий. Оба варианта эквивалентны плохой производительности.</p>
    <p><strong>Необходимо начинать с простого и переходить к сложному только при необходимости. Простота — это ключевой момент.</strong></p>
   </section>
   <section>
    <title>
     <p>Блокировки в вашем коде</p>
    </title>
    <p>Обеспечение безопасности кода при SMP-обработке — это не то, что можно откладывать на потом. Правильная синхронизация, блокировки без тупиковых ситуаций, масштабируемость и ясность кода- все это следует учитывать при разработке с самого начала и до самого конца. При написании кода ядра, будь то новый системный вызов или переписывание драйвера устройства, необходимо, прежде всего, позаботиться об обеспечении защиты данных от конкурентного доступа.</p>
    <p>Обеспечение достаточной защиты для любого случая — SMP, вытеснение кода ядра и так далее — в результате приведет к гарантии того, что все данные будут защищены на любой машине и в любой конфигурации. В следующей главе будет рассказано о том, как это осуществить.</p>
    <p>Теперь, когда мы хорошо подкованы в теории параллелизма, синхронизации и блокировок, давайте углубимся в то, какие существуют конкретные инструменты, предоставляемые ядром Linux, для того чтобы гарантировать отсутствие состояний конкуренции и тупиковых ситуаций в коде.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 9</p>
    <p>Средства синхронизации в ядре</p>
   </title>
   <section>
    <p>В предыдущей главе обсуждались источники и решения проблем, связанных с конкуренцией за ресурсы. К счастью, в ядре Linux реализовано большое семейство средств синхронизации. В этой главе обсуждаются эти средства, интерфейсы к ним, а также особенности их работы и использования. Эти решения позволяют разработчикам писать код, в котором отсутствуют состояния конкуренции за ресурсы.</p>
   </section>
   <section>
    <title>
     <p>Атомарные операции</p>
    </title>
    <section>
     <p>Атомарные операции (atomic operations) предоставляют инструкции, которые выполняются <emphasis>атомарно</emphasis>, — т.е. не прерываясь. Так же как и атом вначале считался неделимой частицей, атомарные операции являются неделимыми инструкциями. Например, как было показано в предыдущей главе, операция атомарного инкремента позволяет считывать из памяти и увеличивать на единицу значение переменной за один неделимый и непрерывный шаг. В отличие от состояния конкуренции за ресурс, которая обсуждалась в предыдущей главе, результат выполнения такой операции всегда один и тот же, например, как показано в следующем примере (допустим, что значение переменной i вначале равно 7).</p>
     <p><strong>Поток 1                               Поток 2</strong></p>
     <p><code>инкремент i <strong>(7-&gt;8)</strong> -</code></p>
     <p><code>-                  инкремент i <strong>(8-&gt;9)</strong></code></p>
     <p>Результирующее значение 9 — правильное. Параллельное выполнение двух атомарных операций с одной и той же переменной невозможно никогда. Таким образом, для такой операции инкремента состояние конкуренции за ресурс возникнуть не может.</p>
     <p>Ядро предоставляет два набора интерфейсов для выполнения атомарных операций: один — для работы с целыми числами, а другой — для работы с отдельными битами. Эти интерфейсы реализованы для всех аппаратных платформ, которые поддерживаются операционной системой Linux. Большинство аппаратных платформ поддерживают атомарные операции или непосредственно, или путем блокировки шины доступа к памяти при выполнении одной операции (что в свою очередь гарантирует, что другая операция не может выполниться параллельно). Это как-то позволяет справиться с проблемой в случае аппаратных платформ, таких как SPARC, которые не поддерживают базовых машинных инструкций для выполнения атомарных операций.</p>
    </section>
    <section>
     <title>
      <p>Целочисленные атомарные операции</p>
     </title>
     <p>Средства выполнения атомарных операций с целыми числами работают с типом данных <code>atomic_t</code>. Вместо того, чтобы использовать функции, которые работают непосредственно с типом данных <code>int</code> языка С, по ряду причин используется специальный тип данных. Во-первых, функции, которые выполняют атомарные операции, принимают только аргументы типа <code>atomic_t</code>, это гарантирует, что атомарные операции выполняются только с данными этого специального типа. В то же время это также гарантирует, что данные этого типа не смогут передаваться в другие функции, которые не выполняют атомарных операций. Действительно, ничего хорошего не будет от таких атомарных операций, которые иногда атомарные, а иногда — нет. Следующий момент — использование типа <code>atomic_t</code> позволяет гарантировать, что компилятор (по ошибке, но для повышения эффективности) не будет оптимизировать операции обращения к атомарным переменным. Важно, чтобы атомарные операции получали правильное значение адреса переменной в памяти, а не адреса временных копий. И наконец, за типом <code>atomic_t</code> скрываются различия между реализациями для различных аппаратных платформ.</p>
     <p>Кроме того, что тип <code>atomic_t</code> — это 32-разрядное целое число на всех машинах, которые поддерживаются операционной системой Linux, при разработке кода необходимо учитывать, что максимальный диапазон значений переменной этого типа не может быть больше 24 бит. Это связано с аппаратной платформой SPARC, для которой используется несколько странная реализация атомарных операций: в младшие 8 бит 32-разрядного целого числа типа <code>int</code> встроена блокировка, как показано на рис. 9.1.</p>
     <image l:href="#img_14.jpeg"/>
     <p><strong>Рис. 9.1</strong>. Структура 32-битового типа <code>atomic_t</code> для аппаратной платформы SPARC в старой реализации</p>
     <p>Блокировка используется для предотвращения параллельного доступа к переменной атомарного типа, так как для аппаратной платформы SPARC отсутствует соответствующая поддержка на уровне машинных инструкций. Следовательно, на машинах SPARC могут быть использованы только 24 бит. Хотя код, который рассчитан на использование полного 32-битового диапазона значений, будет работать и на машинах других типов, он может приводить к странным и коварным ошибкам на машинах типа SPARC, и так делать не нужно. В последнее время умные хакеры додумались, как для аппаратной платформы SPARC обеспечить тип <code>atomic_t</code>, который позволяет хранить полноценное 32-разрядное целое число, и указанного ограничения больше не существует. Тем не менее старая 24-битовая реализация все еще используется в старом коде для аппаратной платформы SPARC, и этот код все еще имеется в файле <code>&lt;asm/atomic.h&gt;</code> для этой аппаратной платформы.</p>
     <p>Объявления всего, что необходимо для использования целочисленных атомарных операций, находятся в заголовочном файле <code>&lt;asm/atomic.h&gt;</code>. Для некоторых аппаратных платформ существуют дополнительные средства, которые уникальны только для этой платформы, но для всех аппаратных платформ существует минимальный набор операций, которые используются в ядре повсюду. При написании кода ядра необходимо гарантировать, что соответствующие операции доступны и правильно реализованы для всех аппаратных платформ.</p>
     <p>Объявление переменных типа <code>atomic_t</code> производится обычным образом. При необходимости можно установить заданное значение этой переменной.</p>
     <p><code>atomic_t u; /* определение переменной u */</code></p>
     <p><code>atomic_t v = ATOMIC_INIT(0); /* определение переменной v</code></p>
     <p><code>                    и инициализация ее в значение нуль */</code></p>
     <p>Выполнять операции так же просто.</p>
     <p><code>atomic_set(&amp;v, 4); /* v=4 (атомарно) */</code></p>
     <p><code>atomic_add(2, &amp;v); /* v = v + 2 = 6 (атомарно) */</code></p>
     <p><code>atomic_inc(&amp;v); /* v = v+1 = 7 (атомарно) */</code></p>
     <p>Если необходимо конвертировать тип <code>atomic_t</code> в тип <code>int</code>, то нужно использовать функцию <code>atomic_read()</code>.</p>
     <p><code>printk("%d\n", atomic_read(&amp;v)); /* будет напечатано "7" */</code></p>
     <p>Наиболее частое использование атомарных целочисленных операций — это инкремент счетчиков. Защищать один счетчик с помощью сложной системы блокировок — это глупо, поэтому разработчики используют вызовы <code>atomic_inc()</code> и <code>atomic_dec()</code>, которые значительно быстрее. Еще одно использование атомарных целочисленных операций — это атомарное выполнение операции с проверкой результата. Наиболее распространенный пример — это атомарные декремент и проверка результата, с помощью функции</p>
     <p><code>int atomic_dec_and_test(atomic_t *v);</code></p>
     <p>Эта функция уменьшает на единицу значение заданной переменной атомарного типа. Если результат выполнения операции равен нулю, то возвращается значение <code>true</code>, иначе возвращается <code>false</code>. Полный список всех атомарных операций с целыми числами (т.е. тех, которые доступны для всех аппаратных платформ) приведен в табл. 9.1. Все операции, которые реализованы для определенной аппаратной платформы, приведены в файле <code>&lt;asm/atomic.h&gt;</code>.</p>
     <empty-line/>
     <p><strong>Таблица 9.1</strong>. Полный список всех атомарных операций с целыми числами</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Атомарная целочисленная операция</th>
       <th align="left" valign="top">Описание</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>ATOMIC_INIT(int i)</code></td>
       <td align="left" valign="top">Объявление и инициализация в значение i переменной типа <code>atomic</code>_t</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>int atomic_ read(atomic_t *v)</code></td>
       <td align="left" valign="top">Атомарное считывание значения целочисленной переменной <code>v</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>void atomic_set(atomic_t *v, int i)</code></td>
       <td align="left" valign="top">Атомарно установить переменную <code>v</code> в значение <code>i</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>void atomic_add(int i, atomic_t *v)</code></td>
       <td align="left" valign="top">Атомарно прибавить значение <code>i</code> к переменной v</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>void atomic_sub(int i, atomic_t *v)</code></td>
       <td align="left" valign="top">Атомарно вычесть значение 1 из переменной <code>v</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>void atomic_inc(atomic_t *v)</code></td>
       <td align="left" valign="top">Атомарно прибавить единицу к переменной <code>v</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>void atomic_dec(atomic_t *v)</code></td>
       <td align="left" valign="top">Атомарно вычесть единицу из переменной <code>v</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>int atomic_sub_and_test(int i, atomic_t *v)</code></td>
       <td align="left" valign="top">Атомарно вычесть значение <code>i</code> из переменной <code>v</code> и возвратить <code>true</code>, если результат равен нулю, и <code>false</code> в противном случае</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>int atomic_add_negative(int i, atomic_t *v)</code></td>
       <td align="left" valign="top">Атомарно прибавить значение <code>i</code> к переменной <code>v</code> и возвратить <code>true</code>, если результат операции меньше нуля, иначе возвратить <code>false</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>int atomic_dec_and_test(atomic_t *v)</code></td>
       <td align="left" valign="top">Атомарно вычесть единицу из переменной <code>v</code> и возвратить <code>true</code>, если результат операции равен нулю, иначе возвратить <code>false</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>int atomic_inc_and_test(atomic_t *v)</code></td>
       <td align="left" valign="top">Атомарно прибавить единицу к переменной <code>v</code> и возвратить <code>true</code>, если результат операции равен нулю, иначе возвратить <code>false</code></td>
      </tr>
     </table>
     <p>Обычно атомарные операции реализованы как функции с подстановкой тела и встраиваемыми инструкциями на языке ассемблера (разработчики ядра любят <code>inline</code>). В случае если какая-либо из функций обладает внутренней атомарностью, то обычно она выполняется в виде макроса. Например, для большинства нормальных аппаратных платформ считывание одного машинного слова данных — это атомарная операция. Операция считывания всегда возвращает машинное слово в непротиворечивом состоянии или перед операцией записи, или после нее, но во время операции записи чтение не может быть выполнено никогда. Следовательно, функция <code>atomic_read()</code> обычно реализуется как макрос, который возвращает целочисленное значение переменной типа <code>atomic_t</code>.</p>
     <cite>
      <subtitle>Атомарность и порядок выполнения</subtitle>
      <p>От атомарных операций чтения перейдем к различиям между атомарностью и порядком выполнения. Как уже рассказывалось, операции чтения одного машинного слова всегда выполняются атомарно. Эти операции никогда не перекрываются операциями записи того же машинного слова. Иными словами, операция чтения данных всегда возвращает машинное слово в консистентном состоянии: иногда возвращается значение, которое было до записи, а иногда — то, которое стало после записи, но никогда не возвращается значение, которое было во время записи. Например, если целочисленное значение вначале было равно 42, а потом стало 365, то операция чтения всегда вернет значение 42 или 365, но никогда не смешанное значение. Это называется атомарностью.</p>
      <p>Иногда бывает так, что вашему коду необходимо нечто большее, например операция чтения всегда выполняется перед ожидающей операцией записи. Это называется не атомарностью, а <emphasis>порядком выполнения</emphasis> (<emphasis>ordering</emphasis>). Атомарность гарантирует, что инструкции выполняются не прерываясь и что они либо выполняются полностью, либо не выполняются совсем. Порядок выполнения же гарантирует, что две или более инструкций, даже если они выполняются разными потоками или разными процессами, всегда выполняются в нужном порядке.</p>
      <p>Атомарные операции, которые обсуждаются в этом разделе, гарантируют только атомарность. Порядок выполнения гарантируется с помощью операций <emphasis>барьеров</emphasis> (<emphasis>barrier</emphasis>), которые будут рассмотрены дальше в текущей главе.</p>
     </cite>
     <p>В любом коде использование атомарных операций, где это возможно, более предпочтительно по сравнению со сложными механизмами блокировок. Для большинства аппаратных платформ одна или две атомарные операции приводят к меньшим накладным затратам и к более эффективному использованию процессорного кэша, чем в случае более сложных методов синхронизации. Как и в случае любого кода, который чувствителен к производительности, всегда разумным будет протестировать несколько вариантов.</p>
    </section>
    <section>
     <title>
      <p>Битовые атомарные операции</p>
     </title>
     <p>В дополнение к атомарным операциям с целыми числами, ядро также предоставляет семейство функций, которые позволяют работать на уровне отдельных битов. Не удивительно, что эти операции зависят от аппаратной платформы и определены в файле <code>&lt;asm/bitops.h&gt;</code>.</p>
     <p>Тем не менее может вызвать удивление то, что функции, которые реализуют битовые операции, работают с обычными адресами памяти. Аргументами функций являются указатель и номер бита. Бит 0 — это наименее значащий бит числа, которое находится по указанному адресу. На 32-разрядных машинах бит 31 — это наиболее значащий бит, а бит 0 — наименее значащий бит машинного слова. Нет ограничений на значение номера бита, которое передается в функцию, хотя большинство пользователей работают с машинными словами и номерами битов от 0 до 31 (или до 63 для 64-битовых машин).</p>
     <p>Так как функции работают с обычными указателями, то в этом случае нет аналога типу <code>atomic_t</code>, который используется для операций с целыми числами. Вместо этого можно использовать указатель на любые данные. Рассмотрим следующий пример.</p>
     <p><code>unsigned long word = 0;</code></p>
     <empty-line/>
     <p><code>set_bit(0, &amp;word);     /* атомарно устанавливается бит 0 */</code></p>
     <p><code>set_bit(1, &amp;word);     /* атомарно устанавливается бит 1 */</code></p>
     <p><code>printk("%ul\n", word); /* будет напечатано "3" */</code></p>
     <p><code>clear_bit(1, &amp;word);   /* атомарно очищается бит 1 */</code></p>
     <p><code>change_bit(0, &amp;word);  /* атомарно изменяется значение бита 1,</code></p>
     <p><code>                          теперь он очищен */</code></p>
     <empty-line/>
     <p><code>/* атомарно устанавливается бит нуль и возвращается предыдущее</code></p>
     <p><code>   значение этого бита (нуль) */</code></p>
     <p><code>if (test_and_set_bit(0, &amp;word)) {</code></p>
     <p><code> /* условие никогда не выполнится ... */</code></p>
     <p><code>}</code></p>
     <p>Список стандартных атомарных битовых операций приведен в табл. 9.2.</p>
     <empty-line/>
     <p><strong>Таблица 9.2</strong>. Список стандартных атомарных битовых операций</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Атомарная битовая операция</th>
       <th align="left" valign="top">Описание</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>void set_bit(int nr, void *addr)</code></td>
       <td align="left" valign="top">Атомарно установить <code>nr</code>-й бит в области памяти, которая начинается с адреса <code>addr</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>void clear_bit(int nr, void *addr)</code></td>
       <td align="left" valign="top">Атомарно очистить <code>nr</code>-й бит в области памяти, которая начинается с адреса <code>addr</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>void change_bit(int nr, void *addr)</code></td>
       <td align="left" valign="top">Атомарно изменить значение <code>nr</code>-го бита в области памяти, которая начинается с адреса <code>addr</code>, на инвертированное</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>int test_and_set_bit(int nr, void *addr)</code></td>
       <td align="left" valign="top">Атомарно установить значение <code>nr</code>-го бита в области памяти, которая начинается с адреса <code>addr</code>, и возвратить предыдущее значение этого бита</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>int test_and_clear_bit(int nr, void *addr)</code></td>
       <td align="left" valign="top">Атомарно очистить значение <code>nr</code>-го бита в области памяти, которая начинается с адреса <code>addr</code>, и возвратить предыдущее значение этого бита</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>int test_and_change_bit(int nr, void *addr)</code></td>
       <td align="left" valign="top">Атомарно изменить значение <code>nr</code>-го бита в области памяти, которая начинается с адреса <code>addr</code>, на инвертированное и возвратить предыдущее значение этого бита</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>int test_bit(int nr, void *addr)</code></td>
       <td align="left" valign="top">Атомарно возвратить значение <code>nr</code>-го бита в области памяти, которая начинается с адреса <code>addr</code></td>
      </tr>
     </table>
     <p>Для удобства работы также предоставляются неатомарные версии всех битовых операций. Эти операции работают так же, как и их атомарные аналоги, но они не гарантируют атомарности выполнения операций, и имена этих функций начинаются с двух символов подчеркивания. Например, неатомарная форма функции <code>test_bit()</code> будет иметь имя <code>__test_bit()</code>. Если нет необходимости в том, чтобы операции были атомарными, например, когда данные уже защищены с помощью блокировки, неатомарные операции могут выполняться быстрее.</p>
     <cite>
      <subtitle>Откуда берутся неатомарные битовые операции</subtitle>
      <p>На первый взгляд, такое понятие, как неатомарная битовая операция, вообще не имеет смысла. Задействован только один бит, и здесь не может быть никакого нарушения целостности. Одна из операций всегда завершится успешно, что еще нужно? Да, <emphasis>порядок выполнения</emphasis> может быть важным, но <emphasis>атомарность</emphasis>-то тут при чем? В конце концов, если значение бита равно тому, которое устанавливается хотя бы одной из операций, то все хорошо, не так ли?</p>
      <p>Давайте вспомним, что такое атомарность? Атомарность означает, что операция или завершается полностью, не прерываясь, или не выполняется вообще. Следовательно, если выполняется две атомарные битовые операции, то предполагается, что они обе должны выполниться. Понятно, что значение бита должно быть правильным (и равным тому значению, которое устанавливается с помощью последней операции, как рассказано в конце предыдущего параграфа). Более того, если другие битовые операции тоже выполняются успешно, то в некоторые моменты времени значение бита должно соответствовать тому, которое устанавливается этими промежуточными операциями.</p>
      <p>Допустим, выполняются две атомарные битовые операции: первоначальная установка бита, а затем очистка бита. Без атомарности этот бит может быть очищен, но никогда не установлен. Операция установки может начаться одновременно с операцией очистки и не выполниться совсем. Операция очистки бита может завершиться успешно, и бит будет очищен, как и предполагалось. В случае атомарных операций, установка бита выполнится на самом деле. Будет существовать момент времени, в который операция считывания покажет, что бит установлен, после этого выполнится операция очистки и значение бита станет равным нулю.</p>
      <p>Иногда может требоваться именно такое поведение, особенно если критичен порядок выполнения.</p>
     </cite>
     <p>Ядро также предоставляет функции, которые позволяют найти номер первого установленного (или не установленного) бита, в области памяти, которая начинается с адреса <code>addr</code>:</p>
     <p><code>int find_first_bit(unsigned long *addr, unsigned int size);</code></p>
     <p><code>int find_first_zero_bit(unsigned long *addr, unsigned int size);</code></p>
     <p>Обе функции в качестве первого аргумента принимают указатель на область памяти и в качестве второго аргумента — количество битов, по которым будет производиться поиск. Эти функции возвращают номер первого установленного или не установленного бита соответственно. Если код производит поиск в одном машинном слове, то оптимальным решением будет использовать функции <code>__ffs()</code> и <code>__ffz()</code>, которые в качестве единственного параметра принимают машинное слово, где будет производиться поиск.</p>
     <p>В отличие от атомарных операций с целыми числами, при написании кода обычно нет возможности выбора, использовать или не использовать рассмотренные битовые операции, они являются единственными переносимыми средствами, которые позволяют установить или очистить определенный бит. Вопрос лишь в том, какие разновидности этих операций использовать — атомарные или неатомарные. Если код по своей сути является защищенным от состояний конкуренции за ресурсы, то можно использовать неатомарные операции, которые могут выполняться быстрее для определенных аппаратных платформ.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Спин-блокировки</p>
    </title>
    <section>
     <p>Было бы очень хорошо, если бы все критические участки были такие же простые, как инкремент или декремент переменной, однако в жизни все более серьезно. В реальной жизни критические участки могут включать в себя несколько вызовов функций. Например, очень часто данные необходимо извлечь из одной структуры, затем отформатировать, произвести анализ этих данных и добавить результат в другую структуру. Весь этот набор операций должен выполняться атомарно. Никакой другой код не должен иметь возможности читать ни одну из структур данных до того, как данные этих структур будут полностью обновлены. Так как ясно, что простые атомарные операции не могут обеспечить необходимую защиту, то используется более сложный метод защиты — блокировки (lock).</p>
     <p>Наиболее часто используемый тип блокировки в ядре Linux — это <emphasis>спин-блокировки</emphasis> (<emphasis>spin lock</emphasis>). Спин-блокировка — это блокировка, которую может удерживать не более чем один поток выполнения. Если поток выполнения пытается захватить блокировку, которая находится в <emphasis>состоянии конфликта</emphasis> (<emphasis>contended</emphasis>), т.е. уже захвачена, поток начинает выполнять постоянную циклическую проверку (busy loop) — "<emphasis>вращаться</emphasis>" (<emphasis>spin</emphasis>), ожидая на освобождение блокировки. Если блокировка не находится в состоянии конфликта при захвате, то поток может сразу же захватить блокировку и продолжить выполнение. Циклическая проверка предотвращает ситуацию, в которой более одного потока одновременно может находиться в критическом участке. Следует заметить, что одна и та же блокировка может использоваться в нескольких разных местах кода, и при этом всегда будет гарантирована защита и синхронизация при доступе, например, к какой-нибудь структуре данных.</p>
     <p>Тот факт, что спин-блокировка, которая находится в состоянии конфликта, заставляет потоки, ожидающие на освобождение этой блокировки, выполнять замкнутый цикл (и, соответственно, тратить процессорное время), является важным. <emphasis>Неразумно</emphasis> удерживать спин-блокировку в течение длительного времени. По своей сути спин-блокировка — это быстрая блокировка, которая должна захватываться на короткое время одним потоком. Альтернативным является поведение, когда при попытке захватить блокировку, которая находится в состоянии конфликта, поток переводится в состояние ожидания и возвращается к выполнению, когда блокировка освобождается. В этом случае процессор может начать выполнение другого кода. Такое поведение вносит некоторые накладные затраты, основные из которых — это два переключения контекста. Вначале переключение на новый поток, а затем обратное переключение на заблокированный поток. Поэтому разумным будет использовать спин-блокировку, когда время удержания этой блокировки меньше длительности двух переключений контекста. Так как у большинства людей есть более интересные занятия, чем измерение времени переключения контекста, то необходимо стараться удерживать блокировки по возможности в течение максимально короткого периода времени<a l:href="#n47" type="note">[47]</a>. В следующем разделе будут описаны <emphasis>семафоры</emphasis> (<emphasis>semaphore</emphasis>) — механизм блокировок, который позволяет переводить потоки, ожидающие на освобождение блокировки, в состояние ожидания, вместо того чтобы периодически проверять, не освободилась ли блокировка, находящаяся в состоянии конфликта.</p>
     <p>Спин-блокировки являются зависимыми от аппаратной платформы и реализованы на языке ассемблера. Зависимый от аппаратной платформы код определен в заголовочном файле <code>&lt;asm/spinlock.h&gt;</code>. Интерфейс пользователя определен в файле <code>&lt;linux/spinlock.h&gt;</code>. Рассмотрим пример использования спин-блокировок.</p>
     <p><code>spinlock_t mr_lock = SPIN_LOCK_UNLOCKED;</code></p>
     <empty-line/>
     <p><code>spin_lock(&amp;mr_lock);</code></p>
     <empty-line/>
     <p><code>/* критический участок ... */</code></p>
     <empty-line/>
     <p><code>spin_unlock(&amp;mr_lock);</code></p>
     <p>В любой момент времени блокировка может удерживаться не более чем одним потоком выполнения. Следовательно, только одному потоку позволено войти в критический участок в данный момент времени. Это позволяет организовать защиту от состояний конкуренции на многопроцессорной машине. Заметим, что на однопроцессорной машине блокировки не компилируются в исполняемый код, и, соответственно, их просто не существует. Блокировки играют роль маркеров, чтобы запрещать и разрешать вытеснение кода (преемптивность) в режиме ядра. Если преемптивность ядра отключена, то блокировки совсем не компилируются.</p>
     <cite>
      <subtitle>Внимание: спин-блокировки не рекурсивны!</subtitle>
      <p>В отличие от реализаций в других операционных системах, спин-блокировки в операционной системе Linux не рекурсивны. Это означает, что если поток пытается захватить блокировку, которую он уже удерживает, то этот поток начнет периодическую проверку, ожидая, пока он сам не освободит блокировку. Но поскольку поток будет периодически проверять, не освободилась ли блокировка, он никогда не сможет ее освободить, и возникнет тупиковая ситуация (самоблокировка). Нужно быть внимательными!</p>
     </cite>
     <p>Спин-блокировки могут использоваться в обработчиках прерываний (семафоры не могут использоваться, поскольку они переводят процесс в состояние ожидания). Если блокировка используется в обработчике прерывания, то перед тем, как захватить эту блокировку (в другом месте — не в обработчике прерывания), необходимо запретить все локальные прерывания (запросы на прерывания на данном процессоре). В противном случае может возникнуть такая ситуация, что обработчик прерывания прерывает выполнение кода ядра, Который уже удерживает данную блокировку, и обработчик прерывания также пытается захватить эту же блокировку. Обработчик прерывания постоянно проверяет (spin), не освободилась ли блокировка. С другой стороны, код ядра, который удерживает блокировку, не будет выполняться, пока обработчик прерывания не закончит выполнение. Это пример взаимоблокировки (двойной захват), который обсуждался в предыдущей главе. Следует заметить, что прерывания необходимо запрещать только на <emphasis>текущем</emphasis> процессоре. Если прерывание возникает на другом процессоре (по отношению к коду ядра, захватившего блокировку) и обработчик будет ожидать на освобождение блокировки, то это не приведет к тому, что код ядра, который захватил блокировку, не сможет никогда ее освободить.</p>
     <p>Ядро предоставляет интерфейс, который удобным способом позволяет запретить прерывания и захватить блокировку. Использовать его можно следующим образом.</p>
     <p><code>spinlock_t mr_lock = SPIN_LOCK_UNLOCKED;</code></p>
     <p><code>unsigned long flags;</code></p>
     <empty-line/>
     <p><code>spin_lock_irqsave(&amp;mr_lock, flags);</code></p>
     <empty-line/>
     <p><code>/* критический участок ... */</code></p>
     <empty-line/>
     <p><code>spin_unlock_irqrestore(&amp;mr_lock, flags);</code></p>
     <p>Подпрограмма <code>spin_lock_irqsave()</code> сохраняет текущее состояние системы прерываний, запрещает прерывания и захватывает указанную блокировку. Функция <code>spin_unlock_irqrestore()</code>, наоборот, освобождает указанную блокировку и восстанавливает предыдущее состояние системы прерываний. Таким образом, если прерывания были запрещены, показанный код не разрешит их по ошибке. Заметим, что переменная <code>flags</code> передается по значению. Это потому, что указанные функции частично выполнены в виде макросов.</p>
     <p>На однопроцессорной машине показанный пример только лишь запретит прерывания, чтобы предотвратить доступ обработчика прерывания к совместно используемым данным, а механизм блокировок скомпилирован не будет. Функции захвата и освобождения блокировки также соответственно запрещают и разрешают преемптивность ядра.</p>
     <cite>
      <subtitle>Что необходимо блокировать</subtitle>
      <p>Важно, чтобы каждая блокировка была четко связана с тем, что она блокирует. Еще более важно — это защищать <emphasis>данные</emphasis>, а не <emphasis>код</emphasis>. Несмотря на то что во всех примерах этой главы рассматриваются критические участки, в основе этих критических участков лежат данные, которые требуют защиты, а никак не код. Если блокировки просто блокируют участки кода, то такой код труднопонимаем и подвержен состояниям гонок. Необходимо ассоциировать данные с соответствующими блокировками. Например, структура <code>struct foo</code> блокируется с помощью блокировки <code>foo_lock</code>. С данной блокировкой также необходимо ассоциировать некоторые данные. Если к некоторым данным осуществляется доступ, то необходимо гарантировать, что этот доступ будет безопасным. Наиболее часто это означает, что перед тем, как осуществить манипуляции с данными, необходимо захватить соответствующую блокировку и освободить эту блокировку нужно после завершения манипуляций.</p>
     </cite>
     <p>Если точно известно, что прерывания разрешены, то нет необходимости восстанавливать предыдущее состояние системы прерываний. Можно просто разрешить прерывания при освобождении блокировки. В этом случае оптимальным будет использование функций <code>spin_lock_irq()</code> и <code>spin_unlock_irq()</code>.</p>
     <p><code>spinlock_t mr_lock = SPIN_LOCK_UNLOCKED;</code></p>
     <empty-line/>
     <p><code>spin_lock_irq(&amp;mr_lock) ;</code></p>
     <empty-line/>
     <p><code>/* критический участок ... */</code></p>
     <empty-line/>
     <p><code>spin_unlock_irq(&amp;mr_lock);</code></p>
     <p>Для любого участка кода очень сложно гарантировать, что прерывания всегда разрешены. В связи с этим не рекомендуется использовать функцию <code>spinlock_irq()</code>. Если стоит вопрос об использовании этих функций, то лучше быть точно уверенным, что прерывания запрещены, а не огорчаться, когда найдете, что прерывания разрешены не там, где нужно.</p>
     <cite>
      <subtitle>Отладка спин-блокировок</subtitle>
      <p>Параметр конфигурации ядра <code>CONFIG_DEBUG_SPINLOCK</code> включает несколько отладочных проверок в коде спин-блокировок. Например, с этим параметром код спин-блокировок будет проверять использование неинициализированных спин-блокировок и освобождение блокировок, которые не были захваченными. При тестировании кода всегда необходимо включать отладку спин-блокировок.</p>
     </cite>
    </section>
    <section>
     <title>
      <p>Другие средства работы со спин-блокировками</p>
     </title>
     <p>Функция <code>spin_lock_init()</code> используется для инициализации спин-блокировок, которые были созданы динамически (переменная типа <code>spinlock_t</code>, к которой нет прямого доступа, а есть только указатель на нее).</p>
     <p>Функция <code>spin_try_lock()</code> производит попытку захватить указанную спин-блокировку. Если блокировка находится в состоянии конфликта, то, вместо циклической проверки и ожидания на освобождение блокировки, эта функция возвращает ненулевое значение. Если блокировка была захвачена успешно, то функция возвращает нуль. Аналогично функция <code>spin_is_locked()</code> возвращает ненулевое значение, если блокировка в данный момент захвачена. В противном случае возвращается нуль. Эта функция никогда не захватывает блокировку<a l:href="#n48" type="note">[48]</a>.</p>
     <p>В табл. 9.3 приведен полный список функций работы со спин-блокировками.</p>
     <empty-line/>
     <p><strong>Таблица 9.3</strong>. Список функций работы со спин-блокировками</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Функция</th>
       <th align="left" valign="top">Описание</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>spin_lock()</code></td>
       <td align="left" valign="top">Захватить указанную блокировку</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>spin_lock_irq()</code></td>
       <td align="left" valign="top">Запретить прерывания на локальном процессоре и захватить указанную блокировку</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>spin_lock_irqsave()</code></td>
       <td align="left" valign="top">Сохранить текущее состояние системы прерываний, запретить прерывания на локальном процессоре и захватить указанную блокировку</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>spin_unlock()</code></td>
       <td align="left" valign="top">Освободить указанную блокировку</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>spin_unlock_irq()</code></td>
       <td align="left" valign="top">Освободить указанную блокировку и разрешить прерывания на локальном процессоре</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>spin_unlock_irqrestore()</code></td>
       <td align="left" valign="top">Освободить указанную блокировку и восстановить состояние системы прерываний на локальном процессоре в указанное первоначальное значение</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>spin_lock_init()</code></td>
       <td align="left" valign="top">Инициализировать объект типа <code>spinlock_t</code> в заданной области памяти</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>spin_trylock()</code></td>
       <td align="left" valign="top">Выполнить попытку захвата указанной блокировки и в случае неудачи возвратить ненулевое значение</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>spin_is_locked()</code></td>
       <td align="left" valign="top">Возвратить ненулевое значение, если указанная блокировка в данный момент захвачена, и нулевое значение в противном случае</td>
      </tr>
     </table>
    </section>
    <section>
     <title>
      <p>Спин-блокировки и обработчики нижних половин</p>
     </title>
     <p>Как было указано в главе 7, "Обработка нижних половин и отложенные действия", при использовании блокировок в работе с обработчиками нижних половин необходимо принимать некоторые меры предосторожности. Функция <code>spin_lock_bh()</code> позволяет захватить указанную блокировку и запретить все обработчики нижних половин. Функция <code>spin_unlock_bh()</code> выполняет обратные действия.</p>
     <p>Обработчик нижних половин может вытеснять код, который выполняется в контексте процесса, поэтому, если данные совместно используются обработчиком нижней половины и контекстом процесса, в контексте процесса эти данные необходимо защищать путем запрещения обработки нижних половин и захвата блокировки. Аналогично, поскольку обработчик прерывания может вытеснить обработчик нижней половины, необходимо запрещать прерывания и захватывать блокировку.</p>
     <p>Вспомним, что два тасклета (tasklet) одного типа не могут выполняться параллельно. Поэтому нет необходимости защищать данные, которые используются только тасклетами одного типа.</p>
     <p>Если данные используются тасклетами разных типов, то необходимо использовать обычную спин-блокировку перед тем, как обращаться к таким данным в обработчике нижней половины. В этом случае нет необходимости запрещать обработку нижних половин, так как тасклет никогда не вытесняет другой тасклет, выполняющийся на том же процессоре.</p>
     <p>В случае отложенных прерываний (softirq), независимо от того, это отложенные прерывания одного типа или разных, данные, совместно используемые обработчиками отложенных прерываний, необходимо защищать с помощью блокировки. Вспомним, что обработчики отложенных прерываний, даже одного типа, могут выполняться одновременно на разных процессорах системы. Обработчик отложенного прерывания никогда не вытесняет другие обработчики отложенных прерываний, которые выполняются на одном процессоре с ним, поэтому запрещать обработку нижних половин в этом случае не нужно.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Спин-блокировки чтения-записи</p>
    </title>
    <p>Иногда в соответствии с целью использования блокировок их можно разделить два типа — блокировки чтения (reader lock) и блокировки записи (writer lock). Рассмотрим некоторый список, который может обновляться и в котором может выполняться поиск. Когда список обновляется (в него осуществляется запись), никакой другой код не может параллельно осуществлять запись или чтение этого списка. Запись означает исключительный доступ. С другой стороны, если в списке выполняется поиск (чтение информации), важно только, чтобы никто другой не выполнял записи в список. Работа со списком заданий в системе (как обсуждалось в главе 3, "Управление процессами") аналогична только что описанной ситуации. Не удивительно, что список заданий в системе защищен с помощью спин-блокировки чтения- записи (reader-writer spin lock).</p>
    <p>Если работа со структурой данных может быть четко разделена на этапы чтения/записи, как в только что рассмотренном случае, то имеет смысл использовать механизмы блокировок с аналогичной семантикой. Для таких ситуаций операционная система Linux предоставляет спин-блокировки чтения-записи. Спин-блокировки чтения-записи обеспечивают два варианта блокировки. Один или больше потоков выполнения, которые одновременно выполняют операции считывания, могут удерживать такую блокировку. Блокировка на запись, наоборот, может удерживаться в любой момент времени только одним потоком, осуществляющим запись, и никаких параллельных считываний не разрешается. Блокировки чтения-записи иногда также называются соответственно shared/exclusive (общая/ исключающая) или concurrent/exclusive (параллельная/исключающая).</p>
    <p>Инициализировать блокировку для чтения-записи можно с помощью следующего программного кода.</p>
    <p><code>rwlock_t mr_rwlock = RW_LOCK_UNLOCKED;</code></p>
    <p>Следующий код осуществляет считывание.</p>
    <p><code>read_lock(&amp;mr_rwlock);</code></p>
    <p><code>/* критический участок (только для считывания) ... */</code></p>
    <p><code>read unlock(&amp;mr_rwlock);</code></p>
    <p>И наконец, показанный ниже код осуществляет запись.</p>
    <p><code>write_lock(&amp;mr_rwlock);</code></p>
    <p><code>/* критический участок (чтение и запись) ... */</code></p>
    <p><code>write_unlock{&amp;mr_rwlock);</code></p>
    <p>Обычно считывание и запись информации осуществляются в разных участках кода, как это показано в данном примере.</p>
    <p>Заметим, что блокировку, захваченную для чтения, нельзя "повышать" до блокировки, захваченной для записи. В следующем коде</p>
    <p><code>read_lock(&amp;mr_rwlock);</code></p>
    <p><code>write_lock(&amp;mr_rwlock);</code></p>
    <p>возникнет самоблокировка, так как при захвате блокировки на запись будет выполняться периодическая проверка, пока все потоки, которые захватили блокировку для чтения, ее не освободят; это касается и текущего потока. Если в каком-либо месте будет необходима запись, то нужно сразу же захватывать блокировку для записи. Если в вашем коде нет четкого разделения на код, который осуществляет считывание, и код, который осуществляет запись, то нет необходимости использовать блокировки чтения- записи. В таком случае оптимальным будет использование обычных спин-блокировок.</p>
    <p>Несколько потоков чтения безопасно могут удерживать одну и ту же блокировку чтения-записи. На самом деле один поток также может безопасно рекурсивно захватывать одну и ту же блокировку для чтения. Это позволяет выполнить полезную и часто используемую оптимизацию. Если в обработчиках прерываний осуществляется только чтение и не выполняется запись, то можно "смешивать" использование блокировок с запрещением прерываний и без запрещения. Для защиты данных при чтении можно использовать функцию <code>read_lock()</code> вместо <code>read_lock_irqsave()</code>. При обращении к данным для записи все равно необходимо запрещать прерывания, например использовать функцию <code>write_lock_irqsave()</code>, так как в обработчике прерывания может возникнуть взаимоблокировка в связи с ожиданием захвата блокировки на чтение при захваченной блокировке на запись. В табл. 9.4 показан полный список средств работы с блокировками чтения-записи.</p>
    <empty-line/>
    <p><strong>Таблица 9.4</strong>. Список функций работы со спин-блокировками чтения-записи</p>
    <table>
     <tr align="left">
      <th align="left" valign="top">Функция</th>
      <th align="left" valign="top">Описание</th>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>read_lock()</code></td>
      <td align="left" valign="top">Захватить указанную блокировку на чтение</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>read_lock_irq()</code></td>
      <td align="left" valign="top">Запретить прерывания на локальном процессоре и захватить указанную блокировку на чтение</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>read_lock_irqsave()</code></td>
      <td align="left" valign="top">Сохранить состояние системы прерываний на текущем процессоре, запретить прерывания на локальном процессоре и захватить указанную блокировку на чтение</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>read_unlock()</code></td>
      <td align="left" valign="top">Освободить указанную блокировку, захваченную для чтения</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>read_unlock_irq()</code></td>
      <td align="left" valign="top">Освободить указанную блокировку, захваченную для чтения, и разрешить прерывания на локальном процессоре</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>read_unlock_irqrestore()</code></td>
      <td align="left" valign="top">Освободить указанную блокировку, захваченную для чтения, и восстановить состояние системы прерываний в указанное значение</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>write_lock()</code></td>
      <td align="left" valign="top">Захватить заданную блокировку на запись</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>write_lock_irq()</code></td>
      <td align="left" valign="top">Запретить прерывания на локальном процессоре и захватить указанную блокировку на запись</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>write_lock_irqsave()</code></td>
      <td align="left" valign="top">Сохранить состояние системы прерываний на текущем процессоре, запретить прерывания на локальном процессоре и захватить указанную блокировку на запись</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>write_unlock()</code></td>
      <td align="left" valign="top">Освободить указанную блокировку, захваченную для записи</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>write_unlock_irq()</code></td>
      <td align="left" valign="top">Освободить указанную блокировку, захваченную для записи, и разрешить прерывания на локальном процессоре</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>write_unlock_irqrestore()</code></td>
      <td align="left" valign="top">Освободить указанную блокировку, захваченную для записи, и восстановить состояние системы прерываний в указанное значение</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>write_trylock()</code></td>
      <td align="left" valign="top">Выполнить попытку захватить заданную блокировку на запись и в случае неудачи возвратить ненулевое значение</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>rw_lock_init()</code></td>
      <td align="left" valign="top">Инициализировать объект типа <code>rwlock_t</code> в заданной области памяти</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>rw_is_locked()</code></td>
      <td align="left" valign="top">Возвратить ненулевое значение, если указанная блокировка захвачена, иначе возвратить нуль</td>
     </tr>
    </table>
    <p>Еще один факт, который необходимо принимать во внимание при работе с блокировками чтения-записи в операционной системе Linux, — это то, что блокировка на чтение всегда имеет большее преимущество по сравнению с блокировкой на запись. Если блокировка захвачена на чтение и поток записи ожидает на ее освобождение, то все потоки, которые будут пытаться захватить блокировку на чтение, будут добиваться успеха. Поток записи, который периодически проверяет на освобождение блокировки, не сможет захватить блокировку, пока все потоки чтения эту блокировку не освободят. Поэтому большое количество потоков чтения будет приводить к "подвисанию" ожидающих потоков записи. Это важное обстоятельство всегда нужно помнить при разработке схемы блокировок.</p>
    <p>Спин-блокировки обеспечивают очень быстрые и простые блокировки. Выполнение постоянных проверок в цикле является оптимальным, когда блокировки захватываются на очень короткое время и код не может переходить в состояние ожидания (например, в обработчиках прерываний). Если же период времени ожидания на освобождение блокировки может быть большим или имеется потенциальная возможность перехода в состояние ожидания при захваченной блокировке, то задача может быть решена с помощью семафоров.</p>
    <empty-line/>
   </section>
   <section>
    <title>
     <p>Семафоры</p>
    </title>
    <section>
     <p>В операционной системе Linux семафоры (semaphore) — это блокировки, которые переводят процессы в состояние ожидания. Когда задание пытается захватить семафор, который уже удерживается, семафор помещает это задание в очередь ожидания (wait queue) и переводит это задание в состояние ожидания (sleep). Когда процессы<a l:href="#n49" type="note">[49]</a>, которые удерживают семафор, освобождают блокировку, одно из заданий очереди ожидания возвращается к выполнению и может захватить семафор.</p>
     <p>Давайте снова возвратимся к аналогии двери и ключа. Когда человек, который хочет открыть дверь, подходит к двери, он может захватить ключ и войти в комнату. Отличие состоит в том, что произойдет, если к двери подойдет другой человек. В этом случае он записывает свое имя в список на двери и ложится поспать.</p>
     <p>Когда человек, который находился внутри комнаты, выходит из нее, он проверяет список на двери. Если в списке есть чье-либо имя, то он выбирает первое из имен списка, дает соответствующему человеку пинка, будит его и позволяет войти в комнату. Таким образом, ключ (т.е. семафор) позволяет только одному человеку (т.е. потоку) находиться в комнате (т.е. в критическом разделе) в один момент времени. Если комната занята, то, вместо того чтобы периодически проверять, человек записывает свое имя в список (т.е. в очередь ожидания) и засыпает (т.е. блокируется в очереди ожидания и переходит в приостановленное состояние), что позволяет процессору выполнять некоторый другой код. Такой режим работы позволяет лучше использовать процессор, чем в случае спин-блокировок, так как при этом не тратится процессорное время на выполнение периодических проверок в цикле. Тем не менее использование семафоров, по сравнению со спин-блокировками, связано со значительно большими накладными расходами.</p>
     <p>Из такого поведения семафоров, связанного с переводом процессов в состояние ожидания, можно сделать следующие интересные заключения.</p>
     <p>• Так как задания, которые конфликтуют при захвате блокировки, переводятся в состояние ожидания и в этом состоянии ждут, пока блокировка не будет освобождена, семафоры хорошо подходят для блокировок, которые могут удерживаться в течение длительного времени.</p>
     <p>• С другой стороны, семафоры не оптимальны для блокировок, которые удерживаются в течение очень короткого периода времени, так как накладные затраты на перевод процессов в состояние ожидания могут "перевесить" время, в течение которого удерживается блокировка.</p>
     <p>• Так как поток выполнения во время конфликта при захвате блокировки находится в состоянии ожидания, то семафоры можно захватывать только в контексте процесса. Контекст прерывания планировщиком не управляется.</p>
     <p>• При удержании семафора (хотя разработчик может и не очень хотеть этого) процесс может переходить в состояние ожидания. Это не может привести к тупиковой ситуации, когда другой процесс попытается захватить блокировку (он просто переходит в состояние ожидания, что в конце концов дает возможность выполняться первому процессу).</p>
     <p>• При захвате семафора нельзя удерживать спин-блокировку, поскольку процесс может переходить в состояние ожидания, ожидая на освобождение семафора, а при удержании спин-блокировки в состояние ожидания переходить нельзя.</p>
     <p>Эти факты подчеркивают особенности использования семафоров по сравнению со спин-блокировками. В большинстве случаев выбор решения, использовать или не использовать семафоры, прост. Если код должен переходить в состояние ожидания, что очень часто возникает при необходимости синхронизации с пространством пользователя, семафоры — единственное решение. Использовать семафоры всегда проще, даже если в них нет строгой необходимости, так как они предоставляют гибкость, связанную с переходом процессов в состояние ожидания. Если же возникает необходимость выбора между семафорами и спид-блокировками, то решение должно основываться на времени удержания блокировки. В идеале, все блокировки необходимо удерживать, по возможности, в течение наиболее короткого времени. При использовании семафоров, однако, допустимы более длительные периоды удержания блокировок. В дополнение ко всему, семафоры не запрещают преемптивность ядра, и, следовательно, код, который удерживает семафор, может быть вытеснен. Это означает, что семафоры не оказывают вредного влияния на задержки (латентность) планировщика.</p>
     <p>Последняя полезная функция семафоров — это то, что они позволяют иметь любое количество потоков, которые одновременно удерживают семафор. В то время как спин-блокировки позволяют удерживать блокировку только одному заданию в любой момент времени, количество заданий, которым разрешено одновременно удерживать семафор, может быть задано при декларации семафора. Это значение называется <emphasis>счетчиком использования</emphasis> (<emphasis>usage count</emphasis>) или просто <emphasis>счетчиком</emphasis> (<emphasis>count</emphasis>). Наиболее часто встречается ситуация, когда разрешенное количество потоков, которые одновременно могут удерживать семафор, равно одному, как и для спин-блокировок. В таком случае счетчик использования равен единице и семафоры называются <emphasis>бинарными семафорами</emphasis> (<emphasis>binary semaphore</emphasis>) (потому что он может удерживаться только одним заданием или совсем никем не удерживаться) или <emphasis>взаимоисключающими блокировками</emphasis> (<emphasis>mutex</emphasis>, <emphasis>мьютекс</emphasis>) (потому что он гарантирует взаимоисключающий доступ — mutual exclusion). Кроме того, счетчику при инициализации может быть присвоено значение, большее единицы. В этом случае семафор называется <emphasis>счетным семафором</emphasis> (<emphasis>counting semaphore</emphasis>, <emphasis>семафор-счетчик</emphasis>), и он допускает количество потоков, которые одновременно удерживают блокировку, не большее чем значение счетчика использования. Семафоры-счетчики не используются для обеспечения взаимоисключающего доступа, так как они позволяют нескольким потокам выполнения одновременно находиться в критическом участке. Вместо этого они используются для установки лимитов в определенном коде. В ядре они используются мало. Если вы используете семафор, то, скорее всего, вы используете взаимоисключающую блокировку (семафор со счетчиком, равным единице).</p>
     <p>Семафоры были формализованы Эдсгером Вайбом Дейкстрой<a l:href="#n50" type="note">[50]</a> (Edsger Wybe Dijkstra) в 1968 году как обобщенный механизм блокировок. Семафор поддерживает две атомарные операции <code>P()</code> и <code>V()</code>, название которых происходит от голландских слов <emphasis>Proben</emphasis> (тестировать) и <emphasis>Verhogen</emphasis> (выполнить инкремент). Позже эти операции начали называть <code>down()</code> и <code>up()</code> соответственно.</p>
     <p>В операционной системе Linux они имеют такое же название. Операция <code>down()</code> используется для того, чтобы захватить семафор путем уменьшения его счетчика на единицу. Если значение этого счетчика больше или равно нулю, то блокировка захватывается успешно и задание может входить в критический участок. Если значение счетчика меньше нуля, то задание помещается в очередь ожидания и процессор переходит к выполнению каких-либо других операций. Об использовании этой функции говорят в форме глагола— семафор <emphasis>опускается</emphasis> (<emphasis>down</emphasis>) для того, чтобы его захватить. Метод <code>up()</code> используется для того, чтобы освободить семафор после завершения выполнения критического участка. Эту операцию называют <emphasis>поднятием</emphasis> (<emphasis>upping</emphasis>) семафора.</p>
     <p>Последний метод используется для инкремента значения счетчика. Если очередь ожидания семафора не пуста, то одно из заданий этой очереди возвращается к выполнению и захватывает семафор.</p>
    </section>
    <section>
     <title>
      <p>Создание и инициализация семафоров</p>
     </title>
     <p>Реализация семафоров зависит от аппаратной платформы и определена в файле <code>&lt;asm/semaphore.h&gt;</code>. Структура <code>struct semaphore</code> представляет объекты типа семафор. Статическое определение семафоров выполняется следующим образом.</p>
     <p><code>static DECLARE_SEMAPHORE_GENERIC(name, count);</code></p>
     <p>где <code>name</code> — имя переменной семафора, a <code>count</code> — счетчик семафора. Более короткая запись для создания взаимоисключающей блокировки (mutex), которая используются наиболее часто, имеет следующий вид.</p>
     <p><code>static DECLARE_MUTEX(name);</code></p>
     <p>где <code>name</code> — это снова имя переменной типа семафор. Чаще всего семафоры создаются динамически, как часть больших структур данных. В таком случае для инициализации семафора, который создается динамически и на который есть только непрямая ссылка через указатель, необходимо использовать функцию</p>
     <p><code>sema_init(sem, count);</code></p>
     <p>где <code>sem</code> — это указатель, a <code>count</code> — счетчик использования семафора. Аналогично для инициализации динамически создаваемой взаимоисключающей блокировки можно использовать функцию</p>
     <p><code>init_MUTEX(sem);</code></p>
     <p>Неизвестно, почему слово "mutex" в имени функции <code>init_MUTEX()</code> выделено большими буквами и почему слово "init" идет перед ним, в то время как имя функции <code>sema_init()</code> таких особенностей не имеет. Тем не менее ясно, что это выглядит не логично, и я приношу свои извинения за это несоответствие. Надеюсь, что после прочтения главы 7 ни у кого уже не будет вызывать удивление то, какие имена придумывают символам ядра.</p>
    </section>
    <section>
     <title>
      <p>Использование семафоров</p>
     </title>
     <p>Функция <code>down_interruptible()</code> выполняет попытку захватить данный семафор. Если эта попытка неудачна, то задание переводится в состояние ожидания с флагом <code>TASK_INTERRUPTIBLE</code>. Из материала главы 3 следует вспомнить, что такое состояние процесса означает, что задание может быть возвращено к выполнению с помощью сигнала и что такая возможность обычно очень ценная. Если сигнал приходит в тот момент, когда задание ожидает на освобождение семафора, то задание возвращается к выполнению, а функция <code>down_interruptible()</code> возвращает значение <code>-EINTR</code>. Альтернативой рассмотренной функции выступает функция <code>down()</code>, которая переводит задание в состояние ожидания с флагом <code>TASK_UNINTERRUPTIBLE</code>. В большинстве случаев это нежелательно, так как процесс, который ожидает на освобождение семафора, не будет отвечать на сигналы. Поэтому функция <code>down_interruptible()</code> используется значительно более широко, чем функция <code>down()</code>. Да, имена этих функций, конечно, далеки от идеала.</p>
     <p>Функция <code>down_trylock()</code> используется для неблокирующего захвата указанного семафора. Если семафор уже захвачен, то функция немедленно возвращает ненулевое значение. В случае успеха по захвату блокировки возвращается нулевое значение и захватывается блокировка.</p>
     <p>Для освобождения захваченного семафора необходимо вызвать функцию <code>up()</code>. Рассмотрим следующий пример.</p>
     <p><code>/* объявление и описание семафора с именем mr_sem и</code></p>
     <p><code>   первоначальным значением счетчика, равным 1 */</code></p>
     <p><code>static DECLARE_MUTEX(mr_sem);</code></p>
     <p><code>...</code></p>
     <p><code>if (down_interruptible(&amp;mr_sem))</code></p>
     <p><code> /* получен сигнал и семафор не захвачен */</code></p>
     <empty-line/>
     <p><code>/* критический участок ... */</code></p>
     <empty-line/>
     <p><code>/* освободить семафор */</code></p>
     <p><code>up(&amp;mr_sem);</code></p>
     <p>Полный список функций работы с семафорами приведен в табл. 9.5.</p>
     <empty-line/>
     <p><strong>Таблица 9.5</strong>. Список функций работы с семафорами</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Функция</th>
       <th align="left" valign="top">Описание</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>sema_init(struct semaphore*, int)</code></td>
       <td align="left" valign="top">Инициализация динамически созданного семафора и установка для него указанного значения счетчика использования</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>init_MUTEX(struct semaphore*)</code></td>
       <td align="left" valign="top">Инициализация динамически созданного семафора и установка его счетчика использования в значение 1</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>init_MUTEX_LOCKED (struct semaphore*)</code></td>
       <td align="left" valign="top">Инициализация динамически созданного семафора и установка его счетчика использования в значение 0 (т.е. семафор изначально заблокирован)</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>down_interruptible(struct semaphore *)</code></td>
       <td align="left" valign="top">Выполнить попытку захватить семафор и перейти в прерываемое состояние ожидания, если семафор находится в состоянии конфликта при захвате (contended)</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>down(struct semaphore*)</code></td>
       <td align="left" valign="top">Выполнить попытку захватить семафор и перейти в непрерываемое состояние ожидания, если семафор находится в состоянии конфликта при захвате (contended)</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>down_trylock(struct semaphore*)</code></td>
       <td align="left" valign="top">Выполнить попытку захватить семафор и немедленно возвратить ненулевое значение, если семафор находится в состоянии конфликта при захвате (contended)</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>up(struct semaphore*)</code></td>
       <td align="left" valign="top">Освободить указанный семафор и возвратить к выполнению ожидающее задание, если такое есть</td>
      </tr>
     </table>
    </section>
   </section>
   <section>
    <title>
     <p>Семафоры чтения-записи</p>
    </title>
    <p>Семафоры, так же как и спин-блокировки, могут быть типа чтения-записи. Ситуации, в которых предпочтительнее использовать семафоры чтения-записи такие же как и в случае использования спин-блокировок чтения-записи.</p>
    <p>Семафоры чтения-записи представляются с помощью структуры <code>struct rw_semaphore</code>, которая определена в файле <code>&lt;asm/rwsem.h&gt;</code>. Статически определенный семафор чтения-записи может быть создан с помощью функции</p>
    <p><code>static DECLARE_RWSEM(name);</code></p>
    <p>где <code>name</code> — это имя нового семафора.</p>
    <p>Семафоры чтения-записи, которые создаются динамически, могут быть инициализированы с помощью следующей функции.</p>
    <p><code>init_rwsem(struct rw_semaphore *sem);</code></p>
    <p>Все семафоры чтения-записи являются взаимоисключающими (mutex), т.е. их счетчик использования равен единице. Любое количество потоков чтения может одновременно удерживать блокировку чтения, если при этом нет ни одного потока записи. И наоборот, только один поток записи может удерживать блокировку, захваченную на запись, если нет ни одного потока чтения. Все семафоры чтения-записи используют непрерываемое состояние ожидания, поэтому существует только одна версия функции <code>down().</code> Рассмотрим следующий пример.</p>
    <p><code>static DECLARE_RWSEM(mr_rwsem);</code></p>
    <empty-line/>
    <p><code>/* попытка захватить семафор для чтения */</code></p>
    <p><code>down_read(&amp;mr_rwsem);</code></p>
    <empty-line/>
    <p><code>/* критический участок (только чтение) ... */</code></p>
    <empty-line/>
    <p><code>/* освобождаем семафор */</code></p>
    <p><code>up_read(&amp;mr_rwsem);</code></p>
    <p><code>/* ... * /</code></p>
    <empty-line/>
    <p><code>/* попытка захватить семафор на запись */</code></p>
    <p><code>down_write(&amp;mr_rwsem);</code></p>
    <empty-line/>
    <p><code>/* освобождаем семафор */</code></p>
    <p><code>/* критический участок (чтение и запись) ... */</code></p>
    <p><code>up write(&amp;mr_rwsem);</code></p>
    <p>Для семафоров есть реализации функций <code>down_read_trylock()</code> и <code>down_write_trylock()</code>. Каждая из них принимает один параметр — указатель на семафор чтения-записи. Обе функции возвращают ненулевое значение, если блокировка захвачена успешно, и нуль, если блокировка находится в состоянии конфликта. Следует быть внимательными — поведение этих функций противоположно поведению аналогичных функций для обычных семафоров, причем без всякой на то причины!</p>
    <p>Семафоры чтения-записи имеют уникальную функцию, аналога которой нет для спин-блокировок чтения-записи. Это функция <code>downgrade_writer()</code>, которая автоматически превращает блокировку, захваченную на запись, в блокировку, захваченную на чтение.</p>
    <p>Семафоры чтения-записи, так же как и спин-блокировки аналогичного типа, должны использоваться, только если есть четкое разделение между участками кода, которые осуществляют чтение, и участками кода, которые осуществляют запись. Использование механизмов блокировок чтения-записи приводит к дополнительным затратам, поэтому их стоит использовать, только если код можно четко разделить на участки чтения и записи.</p>
   </section>
   <section>
    <title>
     <p>Сравнение спин-блокировок и семафоров</p>
    </title>
    <p>Понимание того, когда использовать спин-блокировки, а когда семафоры является важным для написания оптимального кода. Однако во многих случаях выбирать очень просто. В контексте прерывания могут использоваться только спин-блокировки, и только семафор может удерживаться процессом, который находится в состоянии ожидания. В табл. 9.6 показан обзор требований того, какой тип блокировок использовать.</p>
    <empty-line/>
    <p><strong>Таблица 9.6</strong>. Что следует использовать: семафоры или спин-блокировки</p>
    <table>
     <tr align="left">
      <th align="left" valign="top">Требование</th>
      <th align="left" valign="top">Рекомендуемый тип блокировки</th>
     </tr>
     <tr align="left">
      <td align="left" valign="top">Блокировка с малыми накладными затратами (low overhead)</td>
      <td align="left" valign="top">Спин-блокировки более предпочтительны</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">Малое время удержания блокировки</td>
      <td align="left" valign="top">Спин-блокировки более предпочтительны</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">Длительное время удержания блокировки</td>
      <td align="left" valign="top">Семафоры более предпочтительны</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">Необходимо использовать блокировку в контексте прерывания</td>
      <td align="left" valign="top">Необходима спин-блокировка</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">Необходимо переходить в состояние ожидания (steep) при захваченной блокировке</td>
      <td align="left" valign="top">Необходимо использовать семафоры</td>
     </tr>
    </table>
   </section>
   <section>
    <title>
     <p>Условные переменные</p>
    </title>
    <p>Условные переменные (conditional variable, completion variable) — простое средство синхронизации между двумя заданиями, которые работают в режиме ядра, когда необходимо, чтобы одно задание послало сигнал другому о том, что произошло некоторое событие. При этом одно задание ожидает <emphasis>на</emphasis> условной переменной, пока другое задание не выполнит некоторую работу. Когда другое задание завершит выполнение своей работы, оно использует условную переменную для того, чтобы возвратить к выполнению все ожидающие на ней задания. Если это кажется похожим на работу семафора, то именно так оно и есть, идея та же. В действительности, условные переменные просто обеспечивают простое решение проблемы, для которой в других ситуациях используются семафоры. Например, в системном вызове <code>vfork()</code> условная переменная используется для возврата к выполнению родительского процесса при завершении порожденного.</p>
    <p>Условные переменные представляются с помощью структуры <code>struct completion</code>, которая определена в файле <code>&lt;linux/completion.h&gt;</code>.</p>
    <p>Статически условная переменная может быть создана с помощью макроса</p>
    <p><code>DECLARE_COMPLETION(mr_comp);</code></p>
    <p>Динамически созданная условная переменная может быть инициализирована с помощью функции <code>init_completion()</code>.</p>
    <p>Задание, которое должно ожидать на условной переменной, вызывает функцию <code>wait_for_completion()</code>. После того как наступило ожидаемое событие, вызов функции <code>complete()</code> посылает сигнал заданию, которое ожидает на условной переменной, и это задание возвращается к выполнению. В табл. 9.7 приведены методы работы с условными переменными.</p>
    <empty-line/>
    <p><strong>Таблица. 9.7</strong>. Методы работы с условными переменными</p>
    <table>
     <tr align="left">
      <th align="left" valign="top">Метод</th>
      <th align="left" valign="top">Описание</th>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>init_completion(struct completion*)</code></td>
      <td align="left" valign="top">Инициализация динамически созданной условной переменной в заданной области памяти</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>wait_for_completion(struct completion*)</code></td>
      <td align="left" valign="top">Ожидание сигнала на указанной условной переменной</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>complete(struct completion*)</code></td>
      <td align="left" valign="top">Отправка сигнала всем ожидающим заданиям и возвращение их к выполнению</td>
     </tr>
    </table>
    <p>Для примеров использования условных переменных смотрите файлы <code>kernel/sched.c</code> и <code>kernel/fork.с</code>. Наиболее часто используются условные переменные, которые создаются динамически, как часть структур данных. Код ядра, который ожидает на инициализацию структуры данных, вызывает функцию <code>wait_for_completion()</code>. Когда инициализация закончена, ожидающие задания возвращаются к выполнению с помощью вызова функции <code>complete()</code>.</p>
   </section>
   <section>
    <title>
     <p>BKL: Большая блокировка ядра</p>
    </title>
    <p>Добро пожаловать к "рыжему пасынку" ядра. Большая блокировка ядра (Big Kernel Lock, BKL) — это глобальная спин-блокировка, которая была создана специально для того, чтобы облегчить переход от первоначальной реализации SMP в операционной системе Linux к мелкоструктурным блокировкам. Блокировка BKL имеет следующие интересные свойства.</p>
    <p>• Во время удержания BKL можно переходить в состояние ожидания. Блокировка автоматически освобождается, когда задание переходит в состояние ожидания, и снова захватывается, когда задание планируется на выполнение. Конечно, это не означает, что <emphasis>безопасно</emphasis> переходить в состояние ожидания при удержании BKL, просто это <emphasis>можно делать</emphasis> и это не приведет к взаимоблокировке.</p>
    <p>• Блокировка BKL рекурсивна. Один процесс может захватывать эту блокировку несколько раз подряд, и это не приведет к самоблокировке, как в случае обычных спин-блокировок.</p>
    <p>• Блокировка BKL может использоваться только в контексте процесса.</p>
    <p>• Блокировка BKL — это от лукавого.</p>
    <p>Рассмотренные свойства дали возможность упростить переход от ядер серии 2.0 к серии 2.2. Когда в ядро 2.0 была введена поддержка SMP, только одно задание могло выполняться в режиме ядра в любой момент времени (конечно, сейчас ядро распараллелено очень хорошо — пройден огромный путь). Целью создания ядра серии 2.2 было обеспечение возможности параллельного выполнения кода ядра на нескольких процессорах. Блокировка BKL была введена для того, чтобы упростить переход к мелкоструктурным блокировкам. В те времена она оказала большую помощь, а сегодня она приводит к ухудшению масштабируемости<a l:href="#n51" type="note">[51]</a>.</p>
    <p>Использовать блокировку BKL не рекомендуется. На самом деле, новый код никогда не должен использовать BKL. Однако эта блокировка все еще достаточно интенсивно используется в некоторых частях ядра. Поэтому важно понимать особенности большой блокировки ядра и интерфейса к ней. Блокировка BKL ведет себя, как обычная спин-блокировка, за исключением тех особенностей, которые были рассмотрены выше. Функция <code>lock_kernel()</code> позволяет захватить блокировку, а функция <code>unlock_kernel()</code> — освободить блокировку. Каждый поток выполнения может рекурсивно захватывать эту блокировку, но после этого необходимо столько же раз вызвать функцию <code>unlock_kernel()</code>. При последнем вызове функции освобождения блокировки блокировка будет освобождена. Функция <code>kernel_locked()</code> возвращает ненулевое значение, если блокировка в данный момент захвачена, в противном случае возвращается нуль. Эти интерфейсы определены в файле <code>&lt;linux/smp_lock.h&gt;</code>. Рассмотрим простой пример использования этой блокировки.</p>
    <p><code>lock_kernel();</code></p>
    <empty-line/>
    <p><code>/*</code></p>
    <p><code>* Критический раздел, который синхронизирован со всеми пользователями</code></p>
    <p><code>* блокировки BKL...</code></p>
    <p><code>* Заметим, что здесь можно безопасно переходить в состояние ожидания</code></p>
    <p><code>* и блокировка будет прозрачным образом освобождаться.</code></p>
    <p><code>* После перепланирования блокировка будет прозрачным образом снова</code></p>
    <p><code>* захватываться.</code></p>
    <p><code>* Это гарантирует, что не возникнет состояния взаимоблокировки,</code></p>
    <p><code>* но все-таки лучше не переходить в состояние ожидания,</code></p>
    <p><code>* если необходимо гарантировать защиту данных!</code></p>
    <p><code>*/</code></p>
    <empty-line/>
    <p><code>unlock_kernel();</code></p>
    <p>Когда эта блокировка захвачена, происходит запрещение преемптивности. Для ядер, скомпилированных под однопроцессорную машину, код BKL на самом деле не выполняет никаких блокировок. В табл. 9.8 приведен полный список функций работы с BKL.</p>
    <empty-line/>
    <p><strong>Таблица 9.8</strong>. Функции работы с большой блокировкой ядра</p>
    <table>
     <tr align="left">
      <th align="left" valign="top">Функция</th>
      <th align="left" valign="top">Описание</th>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>lock_kernel()</code></td>
      <td align="left" valign="top">Захватить блокировку BKL</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>unlock_kernel()</code></td>
      <td align="left" valign="top">Освободить блокировку BKL</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>kernel_locked()</code></td>
      <td align="left" valign="top">Возвратить ненулевое значение, если блокировка захвачена, и нуль- в противном случае</td>
     </tr>
    </table>
    <p>Одна из самых главных проблем, связанных с большой блокировкой ядра, — как определить, что защищается с помощью данной блокировки. Часто блокировка BKL ассоциируется с кодом (например, она "синхронизирует вызовы функции <code>foo()</code>"), а не с данными ("защита структуры <code>foo</code>"). Это приводит к тому, что заменить BKL обычными спин-блокировками бывает сложно, потому что нелегко определить, что же все-таки необходимо блокировать. На самом деле, подобная замена еще более сложна, так как необходимо учитывать все взаимоотношения между всеми участками кода, которые используют эту блокировку.</p>
   </section>
   <section>
    <title>
     <p>Секвентные блокировки</p>
    </title>
    <p>Секвентная блокировка (seq lock) — это новый тип блокировки, который появился в ядрах серии 2.6. Эти блокировки предоставляют очень простой механизм чтения и записи совместно используемых данных. Работа таких блокировок основана на счетчике последовательности событий. Перед записью рассматриваемых данных захватывается спин-блокировка, и значение счетчика увеличивается на единицу. После записи данных значение счетчика снова увеличивается на единицу, и спин-блокировка освобождается, давая возможность записи другим потокам. Перед чтением и после чтения данных проверяется значение счетчика. Если два полученных значения одинаковы, то во время чтения данных новый акт записи не начинался, Если к тому же оба эти значения четные, то к моменту начала чтения акт записи был закончен (при захвате блокировки на запись значение счетчика становится нечетным, а перед освобождением — снова четным, так как изначальное значение счетчика равно нулю).</p>
    <p>Определение секвентной блокировки можно записать следующим образом.</p>
    <p><code>seqlock_t mr_seq_lock = SEQLOCK_UNLOCKED;</code></p>
    <p>Участок кода, который осуществляет запись, может выглядеть следующим образом.</p>
    <p><code>write_seqlock(&amp;mr_seq_lock);</code></p>
    <p><code>/* блокировка захвачена на запись ... */</code></p>
    <p><code>write_sequnlock(&amp;mr_seq_lock);</code></p>
    <p>Это выглядит, как работа с обычной спин-блокировкой. Необычность появляется в коде чтения, который несколько отличается от ранее рассмотренных.</p>
    <p><code>unsigned long seq;</code></p>
    <p><code>do {</code></p>
    <p><code> seq = read_seqbegin(&amp;mr_seq_lock);</code></p>
    <p><code> /* здесь нужно читать данные ... */</code></p>
    <p><code>} while (read_seqretry(&amp;mr_seq_lock, seq));</code></p>
    <p>Секвентные блокировки полезны для обеспечения очень быстрого доступа к данным в случае, когда применяется много потоков чтения и мало потоков записи. Кроме того, при использовании этого типа блокировок потоки записи получают более высокий приоритет перед потоками чтения. Блокировка записи всегда будет успешно захвачена, если нет других потоков записи. Потоки чтения никак не влияют на захват блокировки записи, в противоположность тому, что имеет место для спин-блокировок и семафоров чтения-записи. Более того, потоки, которые ожидают на запись, будут вызывать постоянные повторения цикла чтения (как в показанном примере) до тех пор, пока не останется ни одного потока, удерживающего блокировку записи во время чтения данных.</p>
   </section>
   <section>
    <title>
     <p>Средства запрещения преемптивности</p>
    </title>
    <p>Так как ядро является вытесняемым, процесс, работающий в режиме ядра, может прекратить выполнение в любой момент, чтобы позволить выполняться более высокоприоритетному процессу. Это означает, что новое задание может начать выполняться в том же критическом участке, в котором выполнялось вытесненное задание. Для того чтобы предотвратить такую возможность, код, который отвечает за преемптивность ядра, использует спин-блокировки в качестве маркеров, чтобы отмечать участки "непреемптивности". Если спин-блокировка захвачена, то ядро является невытесняемым. Так как проблемы, связанные с параллелизмом, в случае SMP и преемптивного ядра одинаковы, то, если ядро уже является безопасным для SMP-обработки, такое простое дополнение позволяет также сделать ядро безопасным и при вытеснении.</p>
    <p>Будем надеяться, что это действительно так. На самом деле возникают некоторые ситуации, в которых нет необходимости использовать спин-блокировки, но нужно запрещать преемптивность ядра. Наиболее часто ситуация такого рода возникает из-за данных, привязанных к определенным процессорам (per-processor data). Если используются данные, уникальные для каждого процессора, то может быть необязательным защищать их с помощью спин-блокировок, потому что только один процессор может получать доступ к этим данным. Если никакая спин-блокировка не захвачена и ядро является преемптивным, то появляется возможность доступа к тем же переменным для вновь запланированного задания, как показано в следующем примере.</p>
    <p><code>задание А манипулирует переменной foo</code></p>
    <p><code>задание А вытесняется</code></p>
    <p><code>задание В планируется на выполнение</code></p>
    <p><code>задание В манипулирует переменной foo</code></p>
    <p><code>задание В завершается</code></p>
    <p><code>задание А планируется на выполнение</code></p>
    <p><code>задание А манипулирует переменной foo</code></p>
    <p>Следовательно, даже для однопроцессорного компьютера к некоторой переменной может псевдопараллельно обращаться несколько процессов. В обычной ситуации для такой переменной требуется спин-блокировка (для защиты при истинном параллелизме на многопроцессорной машине). Если эта переменная связана с одним процессором, то для нее не требуется блокировка.</p>
    <p>Для решения указанной проблемы преемптивность ядра можно запретить с помощью функции <code>preempt_disable()</code>. Этот вызов может быть вложенным, т.е. функцию можно вызывать много раз подряд. Для каждого такого вызова требуется соответствующий вызов функции <code>preempt_enable()</code>. Последний вызов функции <code>preempt_enable()</code> разрешает преемптивность, как показано в следующем примере.</p>
    <p><code>preempt_disable();</code></p>
    <p><code>/* преемптивность запрещена ... */</code></p>
    <p><code>preempt_enable();</code></p>
    <p>Счетчик преемптивности текущего процесса содержит значение, равное количеству захваченных этим процессом блокировок плюс количество вызовов функции <code>preempt_disable()</code>. Если значение этого счетчика равно нулю, то ядро является вытесняемым. Если значение этого счетчика больше или равно единице, то ядро не вытесняемое. Данный счетчик невероятно полезен для отладки атомарных операций совместно с переходами в состояние ожидания. Функция <code>preempt_count()</code> возвращает значение данного счетчика. В табл. 9.9 показан полный список функций управления преемптивностью.</p>
    <empty-line/>
    <p><strong>Таблица 9.9</strong>. Функции управления преемптивностью ядра</p>
    <table>
     <tr align="left">
      <th align="left" valign="top">Функция</th>
      <th align="left" valign="top">Описание</th>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>preempt_disable()</code></td>
      <td align="left" valign="top">Запретить вытеснение кода ядра</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>preempt_enable()</code></td>
      <td align="left" valign="top">Разрешить вытеснение кода ядра</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>preempt_enable_no_resched()</code></td>
      <td align="left" valign="top">Разрешить вытеснение кода ядра, но не перепланировать выполнение процесса</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>preempt count()</code></td>
      <td align="left" valign="top">Возвратить значение счетчика преемптивности</td>
     </tr>
    </table>
    <p>Более полное решение задачи работы с данными, связанными с определенным процессором, — это получение номера процессора (который используется в качестве индекса для доступа к данным, связанным с определенным процессором) с помощью функции <code>get_cpu()</code>. Эта функция запрещает преемптивность ядра перед тем, как возвратить номер текущего процессора.</p>
    <p><code>int cpu = get_cpu();</code></p>
    <empty-line/>
    <p><code>/* работаем с данными, связанными с текущим процессором ... */</code></p>
    <empty-line/>
    <p><code>/* работа закончена, снова разрешаем вытеснение кода ядра */</code></p>
    <p><code>put_cpu();</code></p>
   </section>
   <section>
    <title>
     <p>Барьеры и порядок выполнения</p>
    </title>
    <p>В случае, когда необходимо иметь дело с синхронизацией между разными процессорами или разными аппаратными устройствами, иногда возникает требование, чтобы чтение памяти (load) или запись в память (save) выполнялись в том же порядке, как это указано в исходном программном коде. При работе с аппаратными устройствами часто необходимо, чтобы некоторая указанная операция чтения была выполнена перед другими операциями чтения или записи. В дополнение к этому, на симметричной многопроцессорной системе может оказаться необходимым, чтобы операции записи выполнялись строго в том порядке, как это указано в исходном программном коде (обычно для того, чтобы гарантировать, что последовательные операции чтения получают данные в том же порядке). Эти проблемы усложняются тем, что как компилятор, так и процессор могут менять порядок операций чтения и записи<a l:href="#n52" type="note">[52]</a> для повышения производительности. К счастью, все процессоры, которые переопределяют порядок операций чтения или записи предоставляют машинные инструкции, которые требуют выполнения операций чтения-записи памяти в указанном порядке. Также существует возможность дать инструкцию компилятору, что нельзя изменять порядок выполнения операций при переходе через определенную точку программы. Эти инструкции называются <emphasis>барьерами</emphasis> (<emphasis>barrier</emphasis>).</p>
    <p>Рассмотрим следующий код.</p>
    <p><code>а = 1;</code></p>
    <p><code>b = 2;</code></p>
    <p>На некоторых процессорах запись нового значения в область памяти, занимаемую переменной <code>b</code>, может выполниться до того, как будет записано новое значение в область памяти переменной <code>а</code>. Компилятор может выполнить такую перестановку статически и внести в файл объектного кода, что значение переменной <code>b</code> должно быть установлено перед переменной <code>a</code>. Процессор может изменить порядок выполнения динамически путем предварительной выборки и планирования выполнения внешне вроде бы независимых инструкций для повышения производительности. В большинстве случаев такая перестановка операций будет оптимальной, так как между переменными <code>a</code> и <code>b</code> нет никакой зависимости. Тем не менее иногда программисту все-таки виднее.</p>
    <p>Хотя в предыдущем примере и может быть изменен порядок выполнения, ни процессор, ни компилятор никогда не будут менять порядок выполнения следующего кода, где переменные <code>а</code> и <code>b</code> являются глобальными.</p>
    <p><code>а = 1;</code></p>
    <p><code>b = а;</code></p>
    <p>Это происходит потому, что в последнем случае четко видно зависимость между переменными <code>a</code> и <code>b</code>. Однако ни компилятор, ни процессор не имеют никакой информации о коде, который выполняется в других контекстах. Часто важно, чтобы результаты записи в память "виделись" в нужном порядке другим кодом, который выполняется за пределами нашей досягаемости. Такая ситуация часто имеет место при работе с аппаратными устройствами, а также возникает на многопроцессорных машинах.</p>
    <p>Функция <code>rmb()</code> позволяет установить барьер чтения памяти (read memory barrier). Она гарантирует, что никакие операции чтения памяти, которые выполняются перед вызовом функции <code>rmb()</code>, не будут переставлены местами с операциями, которые выполняются после этого вызова. Иными словами, все операции чтения, которые указаны до этого вызова, будут выполнены перед этим вызовом, а все операции чтения, которые указаны после этого вызова никогда не будут выполняться перед ним.</p>
    <p>Функция <code>wmb()</code> позволяет установить барьер записи памяти (write barrier). Она работает так же, как и функция <code>rmb()</code>, но не с операциями чтения, а с операциями записи — гарантируется, что операции записи, которые находятся по разные стороны барьера, никогда не будут переставлены местами друг с другом.</p>
    <p>Функция <code>mb()</code> позволяет создать барьер на чтение и запись. Никакие операции чтения и записи, которые указаны по разные стороны вызова функции <code>mb()</code>, не будут переставлены местами друг с другом. Эта функция предоставляется пользователю, так как существует машинная инструкция (часто та же инструкция, что используется вызовом <code>rmb()</code>), которая позволяет установить барьер на чтение и запись.</p>
    <p>Вариант функции <code>rmb()</code> — <code>read_barrier_depends()</code> — обеспечивает создание барьера чтения, но только для тех операций чтения, от которых зависят следующие, за ними операции чтения. Гарантируется, что все операции чтения, которые указаны перед барьером выполнятся перед теми операциями чтения, которые находятся после барьера и зависят от операций чтения, идущих перед барьером. Все понятно? В общем, эта функция позволяет создать барьер чтения, так же как и функция <code>rmb()</code>, но этот барьер будет установлен только для некоторых операций чтения — тех, которые зависят друг от друга.</p>
    <p>Для некоторых аппаратных платформ функция <code>read_barrier_depends()</code> выполняется значительно быстрее, чем функция <code>rmb()</code>, так как для этих платформ функция read<code>_barrier_depends()</code> просто не нужна и вместо нее выполняется инструкция <code>noop</code> (нет операции).</p>
    <p>Рассмотрим пример использования функций <code>mb()</code> и <code>rmb()</code>. Первоначальное значение переменной <code>а</code> равно 1, а переменной <code>b</code> равно 2.</p>
    <p><strong>Поток 1  Поток 2</strong></p>
    <p><code>а = 3;   -</code></p>
    <p><code>mb();    -</code></p>
    <p><code>b=4;     c=b;</code></p>
    <p><code>-        rmb();</code></p>
    <p><code>-        d=a;</code></p>
    <p>Без использования барьеров памяти для некоторых процессоров возможна ситуация, в которой после выполнения этих фрагментов кода переменной <code>с</code> присвоится <emphasis>новое</emphasis>, значение переменной <code>b</code>, в то время как переменной <code>d</code> присвоится <emphasis>старое</emphasis> значение переменной <code>а</code>. Например, переменная <code>с</code> может стать равной 4 (что мы и хотим), а переменная <code>d</code> может остаться равной 1 (чего мы не хотим). Использование функции <code>mb()</code> позволяет гарантировать, что переменные <code>a</code> и <code>b</code> записываются в указанном порядке, а функция <code>rmb()</code> гарантирует, что чтение переменных <code>b</code> и <code>а</code> будет выполнено в указанном порядке.</p>
    <p>Такое изменение порядка выполнения операций может возникнуть из-за того, что современные процессоры обрабатывают и передают на выполнение инструкции в измененном порядке для того, чтобы оптимизировать использование конвейеров. Это может привести к тому, что инструкции чтения переменных <code>b</code> и <code>а</code> выполнятся не в том порядке. Функции <code>rmb()</code> и <code>wmb()</code> соответствуют инструкциям, которые заставляют процессор выполнить все незаконченные операции чтения и записи перед тем, как продолжить работу далее.</p>
    <p>Рассмотрим простой пример случая, когда можно использовать функцию <code>read_barrier_depends()</code> вместо функции <code>rmb()</code>. В этом примере изначально переменная <code>а</code> равна 1, <code>b</code> — 2, а <code>p</code> — <code>&amp;b</code>.</p>
    <p><strong>Поток 1  Поток 2</strong></p>
    <p><code>а=3;    -</code></p>
    <p><code>mb();   -</code></p>
    <p><code>p=&amp;а;   pp=p;</code></p>
    <p><code>-       read_barrier_depends();</code></p>
    <p><code>-       b=*pp;</code></p>
    <p>Снова без использования барьеров памяти появляется возможность того, что переменной <code>b</code> будет присвоено значение <code>*pp</code> до того, как переменной <code>pp</code> будет присвоено значение переменной <code>p</code>. Функция <code>read_barrier_depends()</code> обеспечивает достаточный барьер, так как считывание значения <code>*pp</code> зависит от считывания переменной <code>p</code>. Здесь также будет достаточно использовать функцию <code>rmb()</code>, но поскольку операции чтения зависимы между собой, то можно использовать потенциально более быструю функцию <code>read_barrier_depends()</code>. Заметим, что в обоих случаях требуется использовать функцию <code>mb()</code> для того, чтобы гарантировать необходимый порядок выполнения операций чтения-записи в потоке 1.</p>
    <p>Макросы <code>smp_rmb()</code>, <code>smp_wmb()</code>, <code>smp_mb()</code> и <code>smpread_barrier_depends()</code> позволяют выполнить полезную оптимизацию. Для SMP-ядра они определены как обычные барьеры памяти, а для ядра, рассчитанного на однопроцессорную машину, — только как барьер компилятора. Эти SMP-варианты барьеров можно использовать, когда ограничения на порядок выполнения операций являются специфичными для SMP-систем.</p>
    <p>Функция <code>barrier()</code> предотвращает возможность оптимизации компилятором операций считывания и записи данных, если эти операции находятся по разные стороны от вызова данной функции (т.е. запрещает изменение порядка операций). Компилятор не изменяет порядок операций записи и считывания в случаях, когда это может повлиять на правильность выполнения кода, написанного на языке С, или на существующие зависимости между данными. Однако у компилятора нет информации о событиях, которые могут произойти вне текущего контекста. Например, компилятор не может иметь информацию о прерываниях, в контексте которых может выполняться считывание данных, которые в данный момент записываются. Например, по этой причине может оказаться необходимым гарантировать, что операция записи выполнится перед операцией считывания. Указанные ранее барьеры памяти работают и как барьеры компилятора, но барьер компилятора значительно быстрее, чем барьер памяти (практически не влияет на производительность). Использование барьера компилятора на практике является опциональным, так как он просто предотвращает возможность того, что компилятор что-либо изменит.</p>
    <p>В табл. 9.10 приведен полный список функций установки барьеров памяти и компилятора, которые доступны для разных аппаратных платформ, поддерживаемых ядром Linux.</p>
    <empty-line/>
    <p><strong>Таблица 9.10</strong>. Средства установки барьеров компилятора и памяти</p>
    <table>
     <tr align="left">
      <th align="left" valign="top">Барьер</th>
      <th align="left" valign="top">Описание</th>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>rmb()</code></td>
      <td align="left" valign="top">Предотвращает изменение порядка выполнения операций чтения данных из памяти при переходе через барьер</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>read_barrier_depends()</code></td>
      <td align="left" valign="top">Предотвращает изменение порядка выполнения операций чтения данных из памяти при переходе через барьер, но только для операций чтения, которые зависимы друг от друга</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>wmb()</code></td>
      <td align="left" valign="top">Предотвращает изменение порядка выполнения операций записи данных в память при переходе через барьер</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>mb()</code></td>
      <td align="left" valign="top">Предотвращает изменение порядка выполнения операций чтения и записи данных при переходе через барьер</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>smp_rmb()</code></td>
      <td align="left" valign="top">Для SMP-ядер эквивалентно функции <code>rmb()</code>, а для ядер, рассчитанных на однопроцессорные машины, эквивалентно функции <code>barrier()</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>smp_read_barrier_depends()</code></td>
      <td align="left" valign="top">Для SMP-ядер эквивалентно функции <code>read_barrier_depends()</code>, а для ядер, рассчитанных на однопроцессорные машины, эквивалентно функции <code>barrier()</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>smp_wmb()</code></td>
      <td align="left" valign="top">Для SMP-ядер эквивалентно функции <code>wmb()</code>, а для ядер, рассчитанных на однопроцессорные машины, эквивалентно функции <code>barrier()</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>smp_mb()</code></td>
      <td align="left" valign="top">Для SMP-ядер эквивалентно функции <code>mb()</code>, а для ядер, рассчитанных на однопроцессорные машины, эквивалентно функции <code>barrier()</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>barrier()</code></td>
      <td align="left" valign="top">Предотвращает оптимизации компилятора по чтению и записи данных при переходе через барьер</td>
     </tr>
    </table>
    <p>Следует заметить, что эффекты установки барьеров могут быть разными для разных аппаратных платформ. Например, если машина не изменяет порядок операций записи (как в случае набора микросхем Intel x86), то функция <code>wmb()</code> не выполняет никаких действий. Можно использовать соответствующий барьер памяти для самой плохой ситуации (т.е. для процессора с самым плохим порядком выполнения), и ваш код будет скомпилирован оптимально для вашей аппаратной платформы.</p>
   </section>
   <section>
    <title>
     <p>Резюмирование по синхронизации</p>
    </title>
    <p>В этой главе было рассказано о том, как применять на практике понятия, описанные в предыдущей главе, чтобы лучше разобраться с функциями ядра, которые помогают осуществить синхронизацию и параллелизм. Вначале были рассмотрены самые простые методы, которые позволяют гарантировать сихронизацию, — атомарные операции. Далее были описаны спин-блокировки — наиболее часто используемые типы блокировок в ядре, которые построены на основе периодической проверки в цикле условия освобождения блокировки и позволяют гарантировать, что доступ к ресурсу получит только один поток выполнения. После этого были рассмотрены семафоры — блокировки, которые переводят вызывающий процесс в состояние ожидания, а также более специализированные типы элементов синхронизации — условные переменные и секвентные блокировки. Мы получили удовольствие от блокировки BKL, рассмотрели методы запрещения вытеснения кода ядра и коснулись барьеров. Диапазон большой.</p>
    <p>Вооружённые арсеналом методов синхронизации из данной главы теперь вы сможете писать код ядра, который защищён от состояний конкуренции за ресурсы и позволяет обеспечить необходимую синжронизацию с помощью самого подходящего для этого инструментария.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 10</p>
    <p>Таймеры и управление временем</p>
   </title>
   <section>
    <p>Отслеживание хода времени очень важно для ядра. Большое количество функций, которые выполняет ядро, управляются временем (time driven), в отличие от тех функций, которые выполняются по событиям<a l:href="#n53" type="note">[53]</a> (event driven). Некоторые из этих функций выполняются периодически, как, например, балансировка очередей выполнения планировщика или обновление содержимого экрана. Такие функции вызываются в соответствии с постоянным планом, например 100 раз в секунду. Другие функции, такие как отложенные дисковые операции ввода-вывода, ядро планирует на выполнение в некоторый относительный момент времени в будущем. Например, ядро может запланировать работу на выполнение в момент времени, который наступит позже текущего на 500 миллисекунд. Наконец, ядро должно вычислять время работы системы (uptime), а также текущую дату и время.</p>
    <p>Следует обратить внимание на разницу между относительным и абсолютным временем. Планирование выполнения некоторой работы через 5 секунд в будущем не требует учета <emphasis>абсолютного</emphasis> времени, а только <emphasis>относительного</emphasis> (например, через пять секунд от текущего момента времени). В рассмотренной ситуации расчет текущей даты и времени требует от ядра не только учета хода времени, но и абсолютного измерения времени. Обе концепции являются важными для управления временем.</p>
    <p>Также следует обратить внимание на отличия между событиями, которые возникают периодически, и событиями, которые ядро планирует на выполнение в некоторый фиксированный момент времени в будущем. События, которые возникают периодически, скажем каждые 10 миллисекунд, управляются <emphasis>системным, таймером</emphasis>. Системный таймер — это программируемое аппаратное устройство, которое генерирует аппаратное прерывание с фиксированной частотой. Обработчик этого прерывания, который называется <emphasis>прерыванием таймера</emphasis> (<emphasis>timer interrupt</emphasis>), обновляет значение системного времени и выполняет периодические действия. Системный таймер и его прерывание являются важными для работы операционной системы Linux, и в текущей главе им уделяется главное внимание.</p>
    <p>Кроме того, в этой главе будут рассмотрены <emphasis>динамические таймеры</emphasis> (<emphasis>dynamic timers</emphasis>) — средства, позволяющие планировать события, которые выполняются один раз, после того как истек некоторый интервал времени. Например, драйвер накопителя на гибких магнитных дисках использует таймер, чтобы остановить двигатель дисковода, если дисковод неактивен в течение некоторого периода времени. В ядре можно динамически создавать и ликвидировать таймеры. В данной главе рассказывается о реализации динамических таймеров, а также об интерфейсе, который доступен для использования в программном коде.</p>
   </section>
   <section>
    <title>
     <p>Информация о времени в ядре</p>
    </title>
    <section>
     <p>Концепция времени для компьютера является несколько неопределенной. В действительности, для того чтобы получать информацию о времени и управлять системным временем, ядро должно взаимодействовать с системным аппаратным обеспечением. Аппаратное обеспечение предоставляет системный таймер, который используется ядром для измерения времени. Системный таймер работает от электронного эталона времени, такого как цифровые электронные часы или тактовый генератор процессора. Интервал времени системного таймера периодически истекает (еще говорят таймер <emphasis>срабатывает</emphasis> — <emphasis>hitting</emphasis>, <emphasis>popping</emphasis>) с определенной запрограммированной частотой. Эта частота называется <emphasis>частотой импульсов таймера</emphasis>, (<emphasis>tick rate</emphasis>). Когда срабатывает системный таймер, он генерирует прерывание, которое ядро обрабатывает с помощью специального обработчика прерывания.</p>
     <p>Так как в ядре есть информация о запрограммированной частоте следования импульсов таймера, ядро может вычислить интервал времени между двумя успешными прерываниями таймера. Этот интервал называется <emphasis>временной отметкой</emphasis> или <emphasis>импульсом таймера</emphasis> (<emphasis>tick</emphasis>) и в секундах равен <emphasis>единице, деленной на частоту импульсов</emphasis>. Как будет показано дальше, именно таким способом ядро отслеживает абсолютное время (wall time) и время работы системы (uptime). Абсолютное время— это фактическое время дня, которое наиболее важно для пользовательских приложений. Ядро отслеживает это время просто потому, что оно контролирует прерывание таймера. В ядре есть семейство системных вызовов, которое позволяет пользовательским приложениям получать информацию о дате и времени дня. Это необходимо, так как многие программы должны иметь информацию о ходе времени. Разница между двумя значениями времени работы системы — "сейчас" и "позже" — это простой способ измерения относительности событий.</p>
     <p>Прерывание таймера очень важно для управления работой всей операционной системы. Большое количество функций ядра действуют и завершаются в соответствии с ходом времени. Следующие действия периодически выполняются системным таймером.</p>
     <p>• Обновление значения времени работы системы (uptime).</p>
     <p>• Обновление значения абсолютного времени (time of day).</p>
     <p>• Для SMP-систем выполняется проверка балансировки очередей выполнения планировщика, и если они не сбалансированы, то их необходимо сбалансировать (как было рассказано в главе 4, "Планирование выполнения процессов").</p>
     <p>• Проверка, не израсходовал ли текущий процесс свой квант времени, и если израсходовал, то выполнятся планирование выполнения нового процесса (как это было рассказано в главе 4).</p>
     <p>• Выполнение обработчиков всех динамических таймеров, для которых истек период времени.</p>
     <p>• Обновление статистики по использованию процессорного времени и других ресурсов.</p>
     <p>Некоторые из этих действий выполняются при каждом прерывании таймера, т.е. эта работа выполняется с частотой системного таймера. Другие действия также выполняются периодически, но только через каждые n прерываний системного таймера. Иными словами, эти функции выполняются с частотой, которая равна некоторой доле частоты системного таймера. В разделе "Обработчик прерываний таймера" будет рассмотрена сама функция обработки прерываний системного таймера.</p>
    </section>
    <section>
     <title>
      <p>Частота импульсов таймера: <code>HZ</code></p>
     </title>
     <p>Частота системного таймера (частота импульсов, tick rate) программируется при загрузке системы на основании параметра ядра <code>НZ</code>, который определен с помощью директивы препроцессора. Значение параметра <code>HZ</code> отличается для различных поддерживаемых аппаратных платформ. На самом деле, для некоторых аппаратных платформ значение параметра <code>HZ</code> отличается даже для разных типов машин.</p>
     <p>Данный параметр ядра определен в файле <code>&lt;asm/param.h&gt;</code>. Частота системного таймера равна значению параметра <code>HZ</code>, период таймера равен <code>1/HZ</code>. Например, в файле <code>include/asm-i386/param.h</code> для аппаратной платформы i386 этот параметр определен следующим образом.</p>
     <p><code>#define HZ 1000 /* internal kernel time frequency */</code></p>
     <p>Поэтому для аппаратной платформы i386 прерывание таймера генерируется с частотой 1000 Гц, т.е. 1000 раз в секунду (каждую тысячную долю секунды или одну миллисекунду). Для большинства других аппаратных платформ значение частоты системного таймера равно 100 Гц. В табл. 10.1 приведен полный список всех поддерживаемых аппаратных платформ и определенных для них значений частоты системного таймера.</p>
     <empty-line/>
     <p><strong>Таблица 10.1</strong>. Значение частоты системного таймера</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Аппаратная платформа</th>
       <th align="left" valign="top">Частота (в герцах)</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top">alpha</td>
       <td align="left" valign="top">1024</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">arm</td>
       <td align="left" valign="top">100</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">cris</td>
       <td align="left" valign="top">100</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">h8300</td>
       <td align="left" valign="top">100</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">i386</td>
       <td align="left" valign="top">1000</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">ia64</td>
       <td align="left" valign="top">32 или 1024<a l:href="#n54" type="note">[54]</a></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">m68k</td>
       <td align="left" valign="top">100</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">m68knommu</td>
       <td align="left" valign="top">50, 100 или 1000</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">mips</td>
       <td align="left" valign="top">100</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">mips64</td>
       <td align="left" valign="top">100</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">parisc</td>
       <td align="left" valign="top">100 или 1000</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">ppc</td>
       <td align="left" valign="top">100</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">ppc64</td>
       <td align="left" valign="top">1000</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">s390</td>
       <td align="left" valign="top">100</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">sh</td>
       <td align="left" valign="top">100</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">spare</td>
       <td align="left" valign="top">100</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">sparc64</td>
       <td align="left" valign="top">100</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">um</td>
       <td align="left" valign="top">100</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">v850</td>
       <td align="left" valign="top">24, 100 или 122</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">x86-64</td>
       <td align="left" valign="top">1000</td>
      </tr>
     </table>
     <p>При написании кода ядра нельзя считать, что параметр HZ имеет определенное заданное значение. В наши дни это уже не такая часто встречающаяся ошибка, так как поддерживается много различных аппаратных платформ с разными частотами системного таймера. Раньше аппаратная платформа Alpha была единственной, для которой частота системного таймера отличалась от 100 Гц, и часто можно было встретить код, в котором жестко было прописано значение 100 там, где нужно использовать параметр <code>HZ</code>. Примеры использования параметра <code>HZ</code> в коде ядра будут приведены ниже.</p>
     <p>Частота системного таймера достаточно важна. Как будет видно, обработчик прерывания таймера выполняет много работы. Вся информация о времени в ядре получается из периодичности системного таймера. Весь компромисс состоит только в том, чтобы выбрать правильное значение данного параметра исходя из взаимоотношения между разными факторами.</p>
    </section>
    <section>
     <title>
      <p>Идеальное значение параметра <code>HZ</code></p>
     </title>
     <p>Для аппаратной платформы i386, начиная с самых первых версий операционной системы Linux, значение частоты системного таймера было равно 100 Гц. Однако во время разработки ядер серии 2.5 это значение было увеличено до 1000 Гц, что (как всегда бывает в подобных ситуациях) вызвало споры. Так как в системе очень многое зависит от прерывания таймера, то изменение значения частоты системного таймера должно оказывать сильное влияние на систему. Конечно, как в случае больших, так и в случае маленьких значений параметра HZ есть свои положительные и отрицательные стороны.</p>
     <p>Увеличение значения частоты системного таймера означает, что обработчик прерываний таймера выполняется более часто. Следовательно, вся работа, которую он делает, также выполняется более часто. Это позволяет получить следующие преимущества.</p>
     <p>• Прерывание таймера имеет большую разрешающую способность по времени, и следовательно, все событии, которые выполняются во времени, также имеют большую разрешающую способность.</p>
     <p>• Увеличивается точность выполнения событий во времени.</p>
     <p>Разрешающая способность увеличивается во столько же раз, во сколько раз возрастает частота импульсов. Например, гранулярность таймеров при частоте импульсов 100 Гц равна 10 миллисекунд. Другими словами, все периодические события выполняются прерыванием таймера, которое генерируется с предельной точностью по времени, равной 10 миллисекунд, и большая точность<a l:href="#n55" type="note">[55]</a> не гарантируется. При частоте, равной 1000 Гц, разрешающая способность равна 1 миллисекунде, т.е. в 10 раз выше. Хотя ядро позволяет создавать таймеры с временным разрешением, равным 1 миллисекунде, однако при частоте системного таймера в 100 Гц нет возможности гарантированно получить временной интервал, короче 10 миллисекунд.</p>
     <p>Точность измерения времени также возрастает аналогичным образом. Допустим, что таймеры ядра запускаются в случайные моменты времени, тогда в среднем таймеры будут срабатывать с точностью по времени до половины периода прерывания таймера, потому что период времени таймера может закончиться в любой момент, а обработчик таймера может выполниться, только когда генерируется прерывание таймера. Например, при частоте 100 Гц описанные события в среднем будут возникать с точностью ±5 миллисекунд от желаемого момента времени. Поэтому ошибка измерения в среднем составит 5 миллисекунд. При частоте 1000 Гц ошибка измерения в среднем уменьшается до 0.5 миллисекунд — получает десятикратное улучшение.</p>
     <p>Более высокое разрешение и большая точность обеспечивают следующие преимущества.</p>
     <p>• Таймеры ядра выполняются с большим разрешением и с лучшей точностью (это позволяет получить много разных улучшений, некоторые из которых описаны дальше).</p>
     <p>• Системные вызовы, такие как <code>poll()</code> и <code>select()</code>, которые позволяют при желании использовать время ожидания (timeout) в качестве параметра, выполняются с большей точностью.</p>
     <p>• Измерения, такие как учет использования ресурсов или измерения времени работы системы, выполняются с большей точностью.</p>
     <p>• Вытеснение процессов выполняется более правильно.</p>
     <p>Некоторые из наиболее заметных улучшений производительности — это улучшения точности измерения периодов времени ожидания при выполнении системных вызовов <code>poll()</code> и <code>select()</code>. Это улучшение может быть достаточно большим. Прикладная программа, которая интенсивно использует эти системные вызовы, может тратить достаточно много времени, ожидая на прерывания таймера, хотя в действительности интервал времени ожидания уже истек. Следует вспомнить, что средняя ошибка измерения времени (т.е. потенциально зря потраченное время) равна половине периода прерывания таймера.</p>
     <p>Еще одно преимущество более высокой частоты следования импульсов таймера — это более правильное вытеснение процессов, что проявляется в уменьшении задержки за счет планирования выполнения процессов. Вспомним из материала главы 4, что прерывание таймера ответственно за уменьшение кванта времени выполняющегося процесса. Когда это значение уменьшается до нуля, устанавливается флаг <code>need_resched</code>, и ядро активизирует планировщик как только появляется такая возможность. Теперь рассмотрим ситуацию, когда процесс в данный момент выполняется и у него остался квант времени, равный 2 миллисекундам. Это означает, что через 2 миллисекунды планировщик должен вытеснить этот процесс и запустить на выполнение другой процесс. К сожалению, это событие не может произойти до того момента, пока не будет сгенерировано следующее прерывание таймера. В самом худшем случае следующее прерывание таймера может возникнуть через <code>1/HZ</code> секунд! В случае, когда параметр <code>HZ=100</code>, процесс может получить порядка 10 лишних миллисекунд. Конечно, в конце концов все будет сбалансировано и равнодоступность ресурсов не нарушится, потому что все задания планируются с одинаковыми ошибками, и проблема состоит не в этом. Проблемы возникают из-за латентности, которую вносят задержки вытеснения процессов. Если задание, которое планируется на выполнение, должно выполнить какие-нибудь чувствительные ко времени действия, как, например, заполнить буфер аудиоустройства, то задержка не допустима. Увеличение частоты до 1000 Гц уменьшает задержку планировщика в худшем случае до 1 миллисекунды, а в среднем — до 0.5 миллисекунды.</p>
     <p>Должна, однако, существовать и обратная сторона увеличения частоты системного таймера, иначе она была бы с самого начала равна 1000 Гц (или даже больше). На самом деле существует одна большая проблема. Более высокая частота вызывает более частые прерывания таймера, что означает большие накладные затраты. Чем выше частота, тем больше времени процессор должен тратить на выполнение прерываний таймера. Это приводит не только к тому, что другим задачам отводится меньше процессорного времени, но и к периодическому трешингу (trashing) кэша процессора (т.е. кэш заполняется данными, которые не используются процессором). Проблема, связанная с накладными расходами, вызывает споры. Ясно, что переход от значения <code>HZ=100</code> до значения <code>HZ=1000</code> в 10 раз увеличивает накладные затраты, связанные с прерываниями таймера. Однако от какого реального значения накладных затрат следует отталкиваться? Если "ничего" умножить на 10, то получится тоже "ничего". Решающее соглашение состоит в том, что по крайней мере для современных систем, значение параметра <code>HZ=1000</code> не приводит к недопустимым накладным затратам. Тем не менее для ядер серии 2.6 существует возможность скомпилировать ядро с другим значением параметра <code>HZ</code><a l:href="#n56" type="note">[56]</a>.</p>
     <cite>
      <subtitle id="___temp_view_cursor_for_clear_format__1">Возможна ли операционная система без периодических отметок времени</subtitle>
      <p>Может возникнуть вопрос, всегда ли для функционирования операционной системы необходимо использовать фиксированное прерывание таймера? Можно ли создать операционную систему, в которой не используются периодические отметки времени? Да, можно, <emphasis>но результат будет не очень привлекательным</emphasis>.</p>
      <p>Нет строгой необходимости в использовании прерывания таймера, которое возникает с фиксированной частотой. Вместо этого ядро может использовать динамически программируемый таймер для каждого ожидающего события. Такое решение сразу же приведет к дополнительным накладным затратам процессорного времени в связи с обработкой событий таймера, поэтому лучшим решением будет использовать один таймер и программировать его так, чтобы он срабатывал тогда, когда должно наступить ближайшее событие.</p>
      <p>Когда обработчик таймера сработает, создается новый таймер для следующего события и так повторяется постоянно. При таком подходе не требуется периодическое прерывание таймера и нет необходимости в параметре <code>HZ</code>.</p>
      <p>Однако при указанном подходе необходимо решить две проблемы. Первая проблема — это как в таком случае реализовать концепцию периодических отметок времени, хотя бы для того, чтобы ядро могло отслеживать относительные интервалы времени. Эту проблему решить не сложно. Вторая проблема — это как избежать накладных затрат, связанных с управлением динамическими таймерами, даже при наличии оптимизации. Данную проблему решить сложнее. Накладные расходы и сложность реализации получаются настолько высокими, что в операционной системе Linux такой подход решили не использовать. Тем не менее так пробовали делать, и результаты получаются интересными. Если интересно, то можно поискать в Интернет-архивах.</p>
     </cite>
    </section>
   </section>
   <section>
    <title>
     <p>Переменная <code>jiffies</code></p>
    </title>
    <section>
     <p>Глобальная переменная <code>jiffies</code> содержит количество импульсов системного таймера, которые были получены со времени загрузки системы. При загрузке ядро устанавливает значение этого параметра в нуль и он увеличивается на единицу при каждом прерывании системного таймера. Так как в секунду возникает <code>HZ</code> прерываний системного таймера, то за секунду значение переменной <code>jiffies</code> увеличивается на <code>HZ</code>. Время работы системы (uptime) поэтому равно <code>jiffies/HZ</code> секунд.</p>
     <cite>
      <subtitle>Этимология слова jiffy</subtitle>
      <p>Происхождение слова <emphasis>jiffy</emphasis> (миг, мгновение) точно неизвестно. Считается, что фразы типа "<emphasis>in a jiffy</emphasis>" (в одно мгновение) появились в Англии в восемнадцатом веке. В быту термин <emphasis>jiffy</emphasis> (<emphasis>миг</emphasis>) означает неопределенный, но очень короткий промежуток времени.</p>
      <p>В научных приложениях слово <emphasis>jiffy</emphasis> используется для обозначения различных интервалов времени (обычно порядка 10 ms). В физике это слово иногда используется для указания интервала времени, который требуется свету, чтобы пройти определенное расстояние (обычно, фут, сантиметр, или расстояние, равное размеру нуклона).</p>
      <p>В вычислительной технике термин <emphasis>jiffy</emphasis> — это обычно интервал времени между двумя соседними импульсами системного таймера, которые были успешно обработаны. В электричестве <emphasis>jiffy</emphasis> — период переменного тока. В США <emphasis>jiffy —</emphasis> это 1/60 секунды.</p>
      <p>В приложении к операционным системам, в частности к Unix, <emphasis>jiffy </emphasis>— это интервал времени между двумя соседними успешно обработанными импульсами системного таймера. Исторически это значение равно 100 ms. Как уже было показано, интервал времени jiffy в операционной системе Linux может иметь разные значения.</p>
     </cite>
     <p>Переменная <code>jiffies</code> определена в файле <code>&lt;linux/jiffies.h&gt;</code> следующим образом.</p>
     <p><code>extern unsigned long volatile jiffies;</code></p>
     <p>Определение этой переменной достаточно специфичное, и оно будет рассмотрено более подробно в следующем разделе. Сейчас давайте рассмотрим пример кода ядра. Пересчет из секунд в значение переменной <code>jiffies</code> можно выполнить следующим образом.</p>
     <p><code>(секунды * HZ)</code></p>
     <p>Отсюда следует, что преобразование из значения переменной <code>jiffies</code> в секунды можно выполнить, как показано ниже.</p>
     <p><code>(jiffies / HZ)</code></p>
     <p>Первый вариант встречается более часто. Например, часто необходимо установить значение некоторого момента времени в будущем.</p>
     <p><code>unsigned long time_stamp = jiffies;    /* сейчас */</code></p>
     <p><code>unsigned long next_tick = jiffies + 1; /* через один импульс таймера</code></p>
     <p><code>                                          от текущего момента */</code></p>
     <p><code>unsigned long later = jiffies + 5*HZ;  /* через пять секунд от текущего</code></p>
     <p><code>                                          момента */</code></p>
     <p>Последний пример обычно используется при взаимодействии с пространством пользователя, так как в самом ядре редко используется абсолютное время.</p>
     <p>Заметим, что переменная <code>jiffies</code> имеет тип <code>unsigned long</code> и использовать какой-либо другой тип будет неправильным.</p>
    </section>
    <section>
     <title>
      <p>Внутреннее представление переменной <code>jiffies</code></p>
     </title>
     <p>Переменная <code>jiffies</code> исторически всегда представлялась с помощью типа <code>unsigned long</code> и, следовательно, имеет длину 32 бит для 32-разрядных аппаратных платформ и 64 бит для 64-разрядных. В случае 32-разрядного значения переменной <code>jiffies</code> и частоты появления временных отметок 100 раз в секунду, переполнение этой переменной будет происходить примерно каждые 497 дней, что является вполне возможным событием. Увеличение значения параметра <code>HZ</code> до 1000 уменьшает период переполнения до 49.7 дней! В случае 64-разрядного типа переменной <code>jiffies</code>, переполнение этой переменной невозможно за время существования чего-либо при любых возможных значениях параметра <code>HZ</code> для любой аппаратной платформы.</p>
     <p>Из соображений производительности и по историческим причинам — в основном, для совместимости с уже существующим кодом ядра — разработчики ядра предпочли оставить тип переменной <code>jiffies</code> — <code>unsigned long</code>. Для решения проблемы пришлось немного подумать и применить возможности компоновщика.</p>
     <p>Как уже говорилось, переменная <code>jiffies</code> определяется в следующем виде и имеет тип <code>unsigned long</code>.</p>
     <p><code>extern unsigned long volatile jiffies;</code></p>
     <p>Вторая переменная определяется в файле <code>&lt;linux/jiffies.h&gt;</code> в следующем виде.</p>
     <p><code>extern u64 jiffies_64;</code></p>
     <p>Директивы компоновщика <code>ld(1)</code>, которые используются для сборки главного образа ядра (для аппаратной платформы x86 описаны в файле <code>arch/i386/kernel/vmlinux.lds.S</code>), указывают компоновщику, что переменную <code>jiffies</code> необходимо совместить с началом переменной <code>jiffies_64</code>.</p>
     <p><code>jiffies = jiffies_64;</code></p>
     <p>Следовательно, переменная <code>jiffies</code> — это просто 32 младших разряда полной 64-разрядной переменной <code>jiffies_64</code>. Так как в большинстве случаев переменная <code>jiffies</code> используется для измерения промежутков времени, то для большей части кода существенными являются только младшие 32 бит.</p>
     <p>В случае применения 64-разрядного значения, переполнение не может возникнуть за время существования чего-либо. В следующем разделе будут рассмотрены проблемы, связанные с переполнением (хотя переполнение счетчика импульсов системного таймера и не желательно, но это вполне нормальное и ожидаемое событие). Код, который используется для управления ходом времени, использует все 64 бит, и это предотвращает возможность переполнения 64-разрядного значения. На рис. 10.1 показана структура переменных <code>jiffies</code> и <code>jiffies_64</code>.</p>
     <image l:href="#img_15.jpeg"/>
     <p><strong>Рис. 10.1</strong>. Структура переменных <code>jiffies</code> и <code>jiffies_64</code></p>
     <empty-line/>
     <p>Код, который использует переменную <code>jiffies</code>, просто получает доступ к тридцати двум младшим битам переменной <code>jiffies_64</code>. Функция <code>get_jiffies_64()</code> может быть использована для получения полного 64-разрядного значения<a l:href="#n57" type="note">[57]</a>. Такая необходимость возникает редко, следовательно большая часть кода просто продолжает считывать младшие 32 разряда непосредственно из переменной <code>jiffies</code>.</p>
     <p>На 64-разрядных аппаратных платформах переменные <code>jiffies_64</code> и <code>jiffies</code> просто совпадают. Код может либо непосредственно считывать значение переменной <code>jiffies</code>, либо использовать функцию <code>get_jiffies_64()</code>, так как оба этих способа позволяют получить аналогичный эффект.</p>
    </section>
    <section>
     <title>
      <p>Переполнение переменной <code>jiffies</code></p>
     </title>
     <p>Переменная <code>jiffies</code>, так же как и любое целое число языка программирования С, после достижения максимально возможного значения переполняется. Для 32-разрядного беззнакового целого числа максимальное значение равно 2&#179;&#178;- 1. Поэтому перед тем как счетчик импульсов системного таймера переполнится, должно прийти 4294967295 импульсов таймера. Если значение счетчика равно этому значению и счетчик увеличивается на 1, то значение счетчика становится равным нулю.</p>
     <p>Рассмотрим пример переполнения.</p>
     <p><code>unsigned long timeout = jiffies + HZ/2; /* значение лимита времени</code></p>
     <p><code>                                           равно 0.5 с */</code></p>
     <empty-line/>
     <p><code>/* выполним некоторые действия и проверим, не слишком ли это много</code></p>
     <p><code>   заняло времени ... */</code></p>
     <p><code>if (timeout &lt; jiffies) {</code></p>
     <p><code> /* мы превысили лимит времени — это ошибка ... */</code></p>
     <p><code>} else {</code></p>
     <p><code> /* мы не превысили лимит времени — это хорошо ... */</code></p>
     <p><code>}</code></p>
     <p>Назначение этого участка кода — установить лимит времени до наступления некоторого события в будущем, а точнее полсекунды от текущего момента. Код может продолжить выполнение некоторой работы — возможно, записать некоторые данные в аппаратное устройство и ожидать ответа. После выполнения, если весь процесс превысил лимит установленного времени, код соответственным образом обрабатывает ошибку.</p>
     <p>В данном примере может возникнуть несколько потенциальных проблем, связанных с переполнением. Рассмотрим одну из них. Что произойдет, если переменная <code>jiffies</code> переполнится и снова начнет увеличиваться с нуля после того, как ей было присвоено значение переменной <code>timeout</code>? При этом условие гарантированно не выполнится, так как значение переменной <code>jiffies</code> будет меньше, чем значение переменной <code>timeout</code>, хотя логически оно должно быть больше. По идее значение переменной <code>jiffies</code> должно быть огромным числом, всегда большим значения переменной <code>timeout</code>. Так как эта переменная переполнилась, то теперь ее значение стало очень маленьким числом, которое, возможно, отличается от нуля на несколько импульсов таймера. Из-за переполнения результат выполнения оператора <code>if</code> меняется на противоположный!</p>
     <p>К счастью, ядро предоставляет четыре макроса для сравнения двух значений счетчика импульсов таймера, которые корректно обрабатывают переполнение счетчиков. Они определены в файле <code>&lt;linux/jiffies.h&gt;</code> следующим образом.</p>
     <p><code>#define time_after(unknown, known) ((long)(known) - (long)(unknown) &lt; 0)</code></p>
     <p><code>#define time_before(unknown, known) \</code></p>
     <p><code> ((long) (unknown) - (long) (known) &lt; 0)</code></p>
     <p><code>#define time_after_eq(unknown, known) \</code></p>
     <p><code> ((long)(unknown) - (long) (known) &gt;= 0)</code></p>
     <p><code>#define \</code></p>
     <p><code> time_before_eq(unknown, known) ((long)(known) - (long) (unknown) &gt;= 0)</code></p>
     <p>Параметр <code>unknown</code> — это обычно значение переменной <code>jiffies</code>, а параметр <code>known</code> — значение, с которым его необходимо сравнить.</p>
     <p>Макрос <code>time_after(unknown, known)</code> возвращает значение <code>true</code>, если момент времени unknown происходит после момента времени <code>known</code>, в противном случае возвращается значение <code>false</code>. Макрос <code>time_before(unknown, known)</code> возвращает значение true, если момент времени <code>unknown</code> происходит раньше, чем момент времени known, в противном случае возвращается значение <code>false</code>. Последние два макроса работают аналогично первым двум, за исключением того, что возвращается значение "истинно", если оба параметра равны друг другу.</p>
     <p>Версия кода из предыдущего примера, которая предотвращает ошибки, связанные с переполнением, будет выглядеть следующим образом.</p>
     <p><code>unsigned long timeout = jiffies + HZ/2; /* значение лимита времени</code></p>
     <p><code>                                           равно 0.5 с */</code></p>
     <empty-line/>
     <p><code>/* выполним некоторые действия и проверим, не слишком ли это много</code></p>
     <p><code>   заняло времени ... */</code></p>
     <p><code>if (time_after(jiffies, timeout}) {</code></p>
     <p><code> /* мы превысили лимит времени — это ошибка ... */</code></p>
     <p><code>} else {</code></p>
     <p><code> /* мы не превысили лимит времени — это хорошо ... */</code></p>
     <p><code>}</code></p>
     <p>Если любопытно, каким образом эти макросы предотвращают ошибки, связанные с переполнением, то попробуйте подставить различные значения параметров. А затем представьте, что один из параметров переполнился, и посмотрите, что при этом произойдет.</p>
    </section>
    <section>
     <title>
      <p>Пространство пользователя и параметр <code>HZ</code></p>
     </title>
     <p>Раньше изменение параметра <code>НZ</code> приводило к аномалиям в пользовательских программах. Это происходило потому, что значения параметров, связанных со временем, экспортировались в пространство пользователя в единицах, равных количеству импульсов системного таймера в секунду. Так как такой интерфейс использовался давно, то в пользовательских приложениях считалось, что параметр HZ имеет определенное конкретное значение. Следовательно, при изменении значения параметра HZ изменялись значения, которые экспортируются в пространство пользователя, в одинаковое число раз. Информация о том, во сколько раз изменились значения, в пространство пользователя не передавалась! Полученное от ядра значение времени работы системы могло интерпретироваться как 20 часов, хотя на самом деле оно равнялось только двум часам.</p>
     <p>Чтобы исправить это, код ядра должен нормировать все значения переменной <code>jiffies</code>, которые экспортируются в пространство пользователя. Нормировка реализуется путем определения константы <code>USER_HZ</code>, равной значению параметра <code>HZ</code>, которое <emphasis>ожидается</emphasis> в пространстве пользователя. Так как для аппаратной платформы x86 значение параметра <code>HZ</code> исторически равно 100, то значение константы <code>USER_HZ=100</code>. Макрос <code>jiffies_to_clock_t()</code> используется для нормировки значения счетчика импульсов системного таймера, выраженного в единицах <code>HZ</code>, в значение счетчика импульсов, выраженное в единицах <code>USER_HZ</code>. Используемый макрос зависит от того, кратны ли значения параметров <code>HZ</code> и <code>USER_HZ</code> один другому. Если кратны, то этот макрос имеет следующий очень простой вид.</p>
     <p><code>#define jiffies_to_clock_t(x) ((x) / (HZ / USER_HZ))</code></p>
     <p>Если не кратны, то используется более сложный алгоритм.</p>
     <p>Функция <code>jiffies_64_to_clock_t()</code> используется для конвертирования 64-битового значения переменной <code>jiffies</code> из единиц <code>HZ</code> в единицы <code>USER_HZ</code>.</p>
     <p>Эти функции используются везде, где значения данных, выраженных в единицах числа импульсов системного таймера в секунду, должны экспортироваться в пространство пользователя, как в следующем примере.</p>
     <p><code>unsigned long start = jiffies;</code></p>
     <p><code>unsigned long total_time;</code></p>
     <p><code>/* выполнить некоторую работу ... */</code></p>
     <p><code>total_time = jiffies - start;</code></p>
     <p><code>printk("Это заняло %lu импульсов таймера\n",</code></p>
     <p><code> jiffies_to_clock_t(total_time));</code></p>
     <p>В пространстве пользователя передаваемое значение должно быть таким, каким оно было бы, если бы выполнялось равенство <code>HZ=USER_HZ</code>. Если это равенство не справедливо, то макрос выполнит нужную нормировку и все будут счастливы. Конечно, этот пример несколько нелогичный: больше смысла имело бы печатать значение времени не в импульсах системного таймера, а в секундах следующим образом.</p>
     <p><code>printk("Это заняло %lu секунд\n", total time / HZ);</code></p>
    </section>
   </section>
   <section>
    <title>
     <p>Аппаратные часы и таймеры</p>
    </title>
    <section>
     <p>Различные аппаратные платформы предоставляют два аппаратных устройства, которые помогают вести учет времени, — это системный таймер, о котором уже было рассказано, и часы реального времени. Реализация и поведение этих устройств могут быть различными для машин разного типа, но общее их назначение и принципы работы с ними почти всегда одинаковы.</p>
    </section>
    <section>
     <title>
      <p>Часы реального времени</p>
     </title>
     <p>Часы реального времени (real-time clock, RTC) представляют собой энергонезависимое устройство для сохранения системного времени. Устройство RTC продолжает отслеживать время, даже когда система отключена, благодаря небольшой батарее, которая обычно находится на системной плате. Для аппаратной платформы PC устройство RTC интегрировано в КМОП-микросхему BIOS. При этом используется общая батарея и для работы устройства RTC и для сохранения установок BIOS.</p>
     <p>При загрузке ядро считывает информацию из устройства RTC и использует ее для инициализации значения абсолютного времени, которое хранится в переменной <code>xtime</code>. Обычно ядро не считывает это значение снова, однако для некоторых поддерживаемых аппаратных платформ, таких как x86, значение абсолютного времени периодически записывается в устройство RTC. Тем не менее, часы реального времени важны в первую очередь на этапе загрузки системы, когда инициализируется переменная <code>xtime</code>.</p>
    </section>
    <section>
     <title>
      <p>Системный таймер</p>
     </title>
     <p>Системный таймер играет более значительную роль для отслеживания хода времени ядром. Независимо от аппаратной платформы, идея, которая лежит в основе системного таймера, одна и та же — это обеспечение механизма управления прерываниями, которые возникают периодически с постоянной частотой. Для некоторых аппаратных платформ это реализуется с помощью электронных часов, которые генерируют колебания с программируемой частотой. В других аппаратных платформах используется декрементный счетчик (decrementer), куда можно записать некоторое начальное значение, которое будет периодически, с фиксированной частотой, уменьшаться на единицу, пока значение счетчика не станет равным нулю. Когда значение счетчика становится равным нулю, генерируется прерывание. В любом случае эффект получается один и тот же.</p>
     <p>Для аппаратной платформы x86 главный системный таймер — это программируемый интервальный таймер (programmable interval timer, PIT). Таймер PIT существует на всех машинах платформы PC. Co времен операционной системы DOS он используется для управления прерываниями. Ядро программирует таймер PIT при загрузке, для того чтобы периодически генерировать прерывание номер нуль с частотой <code>HZ</code>. Этот таймер— простое устройство с ограниченными возможностями, но, тем не менее, хорошо выполняющее свою работу. Другие эталоны времени для аппаратной платформы x86 включают таймер APIC (Advanced Programmable Interrupt Controller, расширенный программируемый контроллер прерываний) и счетчик отметок времени (TSC, Time Stamp Counter).</p>
    </section>
   </section>
   <section>
    <title>
     <p>Обработчик прерываний таймера</p>
    </title>
    <p>Теперь, когда мы разобрались, что такое <code>jiffies</code> и <code>HZ</code>, а также какова роль системного таймера, рассмотрим реализацию обработчика прерываний системного таймера. Обработчик прерываний таймера разбит на две части: часть, зависимую от аппаратной платформы, и независимую часть.</p>
    <p>Подпрограмма, которая зависит от аппаратной платформы, регистрируется в качестве обработчика прерываний системного таймера и выполняется, когда срабатывает системный таймер. Конкретная работа, конечно, зависит от аппаратной платформы, но большинство обработчиков выполняют следующие действия.</p>
    <p>• Захватывается блокировка <code>xtime_lock</code>, которая защищает доступ к переменной <code>jiffies_64</code> и значению текущего времени— переменной <code>xtime</code>.</p>
    <p>• Считывается или сбрасывается состояние системного таймера, если это необходимо.</p>
    <p>• Периодически записывается новое значение абсолютного времени в часы реального времени.</p>
    <p>• Вызывается аппаратно-независимая подпрограмма таймера <code>do_timer()</code>.</p>
    <p>Аппаратно-независимая функция <code>do_timer()</code> выполняет значительно больше действий.</p>
    <p>• Увеличивается значение переменной <code>jiffies_64</code> на единицу (это безопасная операция даже для 32-разрядных аппаратных платформ, так как блокировка <code>xtime_lock</code> была захвачена раньше).</p>
    <p>• Обновляется статистка использования системных ресурсов, таких как затраченное процессорное время в режиме пользователя и в режиме ядра, для процесса, который в данный момент выполняется.</p>
    <p>• Выполняются обработчики динамических таймеров, для которых истек период времени ожидания (это будет рассмотрено в следующем разделе).</p>
    <p>• Вызывается функция <code>scheduler_tick()</code>, как было рассмотрено в главе 4.</p>
    <p>• Обновляется значение абсолютного времени, которое хранится в переменной <code>xtime</code>.</p>
    <p>• Вычисляются значения печально известной средней загруженности системы (load average).</p>
    <p>Сама по себе подпрограмма очень проста, так как большинство рассмотренных действий выполняются другими функциями.</p>
    <p><code>void do_timer(struct pt_regs *regs) {</code></p>
    <p><code> jiffies_64++;</code></p>
    <p><code> update_process_times(user_mode(regs));</code></p>
    <p><code> update_times();</code></p>
    <p><code>}</code></p>
    <p>Макрос <code>user_mode()</code> просматривает состояние регистров процессора, <code>regs</code>, и возвращает значение 1, если прерывание таймера возникло в пространстве пользователя, и значение 0 — если в пространстве ядра. Это позволяет функции <code>update_process_times()</code> учесть, что за время между предыдущим и данным импульсами системного таймера процесс выполнялся в режиме задачи или в режиме ядра.</p>
    <p><code>void update_process_times(int user_tick) {</code></p>
    <p><code> struct task_struct *p = current;</code></p>
    <p><code> int cpu = smp_processor_id();</code></p>
    <p><code> int system = user_tick ^ 1;</code></p>
    <empty-line/>
    <p><code> update_one_process(p, user_tick, system, cpu);</code></p>
    <p><code> run_local_timers();</code></p>
    <p><code> scheduler_tick(user_tick, system);</code></p>
    <p><code>}</code></p>
    <p>Функция <code>update_process()</code> собственно обновляет значения параметров времени выполнения процесса. Эта функция тщательно продумана. Следует обратить внимание, каким образом с помощью операции исключающее ИЛИ (XOR) достигается, что одна из переменных <code>user_tick</code> и <code>system</code> имеет значение, равное нулю, а другая— единице. Поэтому в функции <code>update_one_process()</code> можно просто прибавить необходимое значение к соответствующим счетчикам без использования оператора ветвления.</p>
    <p><code>/*</code></p>
    <p><code>* увеличиваем значения соответствующего</code></p>
    <p><code>* счетчика импульсов таймера на единицу</code></p>
    <p><code>*/</code></p>
    <p><code>p-&gt;utime += user;</code></p>
    <p><code>p-&gt;stime += system;</code></p>
    <p>Необходимое значение увеличивается на 1, а другое остается без изменений. Легко заметить, что в таком случае предполагается, что за время импульса системного таймера процесс выполнялся <emphasis>в том же</emphasis> режиме, в котором он выполняется во время прихода прерывания. На самом деле процесс мог несколько раз переходить в режим задачи и в режим ядра за последний период системного таймера. Кроме того, текущий процесс может оказаться не единственным процессом, который выполнялся за последний период системного таймера. К сожалению, без применения более сложной системы учета, такой способ является лучшим из всех тех, которые предоставляет ядро. Это также одна из причин увеличения частоты системного таймера.</p>
    <p>Далее функция <code>run_local_timers()</code> помечает отложенные прерывания, как готовые к выполнению (см. главу 7, "Обработка нижних половин и отложенные действия"), для выполнения всех таймеров, для которых закончился период времени ожидания. Таймеры будут рассмотрены ниже, в разделе "Таймеры".</p>
    <p>Наконец, функция <code>schedule_tick()</code> уменьшает значение кванта времени для текущего выполняющегося процесса и устанавливает флаг <code>need_resched</code> при необходимости. Для SMP-машин в этой функции также при необходимости выполняется балансировка очередей выполнения. Все это обсуждалось в главе 4.</p>
    <p>После возврата из функции <code>update_process_times()</code> вызывается функция <code>update_times()</code>, которая обновляет значение абсолютного времени.</p>
    <p><code>void update_times(void) {</code></p>
    <p><code> unsigned long ticks;</code></p>
    <p><code> ticks = jiffies - wall_jiffies;</code></p>
    <p><code> if (ticks) {</code></p>
    <p><code>  wall_jiffies += ticks;</code></p>
    <p><code>  update_wall_time(ticks);</code></p>
    <p><code> }</code></p>
    <p><code> last_time_offset = 0;</code></p>
    <p><code> calc_load(ticks);</code></p>
    <p><code>}</code></p>
    <p>Значение переменной <code>ticks</code> вычисляется как изменение количества импульсов системного таймера с момента последнего обновления абсолютного времени. В нормальной ситуации это значение, конечно, равно 1. В редких случаях прерывание таймера может быть пропущено, и в таком случае говорят, что импульсы таймера потеряны. Это может произойти, если прерывания запрещены в течение длительного времени. Такая ситуация не является нормальной и часто указывает на ошибку программного кода. Значение переменной <code>wall_jiffies</code> увеличивается на значение <code>ticks</code>, поэтому она равна значению переменной <code>jiffies</code> в момент самого последнего обновления абсолютного времени. Далее вызывается функция <code>update_wall_time()</code> для того, чтобы обновить значение переменной <code>xtime</code>, которая содержит значение абсолютного времени. Наконец вызывается функция <code>calc_load()</code> для того, чтобы обновить значение средней загруженности системы, после чего функция <code>update_times()</code> возвращает управление.</p>
    <p>Функция <code>do_timer()</code> возвращается в аппаратно-зависимый обработчик прерывания, который выполняет все необходимые завершающие операции, освобождает блокировку <code>xtime_lock</code> и в конце концов возвращает управление.</p>
    <p>Всё это происходит каждые <code>1/HZ</code> секунд, т.е. 1000 раз в секунду на машине типа PC.</p>
   </section>
   <section>
    <title>
     <p>Абсолютное время</p>
    </title>
    <p>Текущее значение абсолютного времени (time of day, wall time, время дня) определено в файле <code>kernel/timer.c</code> следующим образом.</p>
    <p><code>struct timespec xtime;</code></p>
    <p>Структура данных <code>timespec</code> определена в файле <code>&lt;linux/time.h&gt;</code> в следующем виде.</p>
    <p><code>struct timespec {</code></p>
    <p><code> time_t tv_sec; /* seconds */</code></p>
    <p><code> long tv_nsec; /* nanoseconds */</code></p>
    <p><code>};</code></p>
    <p>Поле <code>xtime.tv_sec</code> содержит количество секунд, которые прошли с 1 января 1970 года (UTC, Universal Coordinated Time, всеобщее скоординированное время). Указанная дата называется <emphasis>epoch</emphasis> (начало эпохи). В большинстве Unix-подобных операционных систем счет времени ведется с начала эпохи. В поле <code>xtime.tv_nsec</code> хранится количество наносекунд, которые прошли в последней секунде.</p>
    <p>Чтение или запись переменной <code>xtime</code> требует захвата блокировки <code>xtime_lock</code>. Это блокировка — не обычная спин-блокировка, а <emphasis>секвентная</emphasis> блокировка, которая рассматривается в главе 9, "Средства синхронизации в ядре".</p>
    <p>Для обновления значения переменной <code>xtime</code> необходимо захватить секвентную блокировку на запись следующим образом.</p>
    <p><code>write_seqlock(&amp;xtime_lock);</code></p>
    <empty-line/>
    <p><code>/* обновить значение переменной xtime ... */</code></p>
    <empty-line/>
    <p><code>write_sequnlock(&amp;xtime_lock);</code></p>
    <p>Считывание значения переменной <code>xtime</code> требует применения функций <code>read_seqbegin()</code> и <code>read_seqretry()</code> следующим образом.</p>
    <p><code>do {</code></p>
    <p><code> unsigned long lost;</code></p>
    <p><code> seq = read_seqbegin(&amp;xtime_lock);</code></p>
    <empty-line/>
    <p><code> usec = timer-&gt;get_offset();</code></p>
    <p><code> lost = jiffies — wall_jiffies;</code></p>
    <p><code> if (lost)</code></p>
    <p><code>  usec += lost * (1000000 / HZ);</code></p>
    <p><code> sec = xtime.tv_sec;</code></p>
    <p><code> usec += (xtime.tv_nsec / 1000);</code></p>
    <p><code>} while (read_seqretry(&amp;xtime_lock, seq));</code></p>
    <p>Этот цикл повторяется до тех пор, пока не будет гарантии того, что во время считывания данных не было записи. Если во время выполнения цикла приходит прерывание таймера и переменная <code>xtime</code> обновляется во время выполнения цикла, возвращаемый номер последовательности будет неправильным и цикл повторится снова.</p>
    <p>Главный пользовательский интерфейс для получения значения абсолютного времени — это системный вызов <code>gettimeofday()</code>, который реализован как функция <code>sys_gettimeofday()</code> следующим образом.</p>
    <p><code>asmlinkage long sys_gettimeofday(struct timeval *tv,</code></p>
    <p><code> struct timezone *tz) {</code></p>
    <p><code> if (likely(tv !=NULL)) {</code></p>
    <p><code>  struct timeval_ktv;</code></p>
    <p><code>  do_gettimeofday(&amp;ktv);</code></p>
    <p><code>  if (copy_to_userftv, &amp;ktv, sizeof(ktv))</code></p>
    <p><code>   return -EFAULT;</code></p>
    <p><code> }</code></p>
    <p><code> if (unlikely(tz != NULL)) {</code></p>
    <p><code>  if (copy_to_user(tz, &amp;sys_tz, sizeof(sys_tz)))</code></p>
    <p><code>   return -EFAULT;</code></p>
    <p><code> }</code></p>
    <p><code> return 0;</code></p>
    <p><code>}</code></p>
    <p>Если из пространства пользователя передано ненулевое значение параметра <code>tv</code>, то вызывается аппаратно-зависимая функция <code>do_gettimeofday()</code>. Эта функция главным образом выполняет цикл считывания переменной <code>xtime</code>, который был только что рассмотрен. Аналогично, если параметр <code>tz</code> не равен нулю, пользователю возвращается значение часового пояса (time zone), в котором находится операционная система. Этот параметр хранится в переменной <code>sys_tz</code>. Если при копировании в пространство пользователя значения абсолютного времени или часового пояса возникли ошибки, то функция возвращает значение <code>-EFAULT</code>. В случае успеха возвращается нулевое значение.</p>
    <p>Ядро предоставляет системный вызов <code>time()</code><a l:href="#n58" type="note">[58]</a>, однако системный вызов <code>gettimeofday()</code> полностью перекрывает его возможности. Библиотека функций языка С также предоставляет другие функции, связанные с абсолютным временем, такие как <code>ftime()</code> и <code>ctime()</code>.</p>
    <p>Системный вызов <code>settimeofday()</code> позволяет установить абсолютное время в указанное значение. Для того чтобы его выполнить, процесс должен иметь возможность использования <code>CAP_SYS_TIME</code>.</p>
    <p>Если не считать обновления переменной <code>xtime</code>, то ядро не так часто использует абсолютное время, как пространство пользователя. Одно важное исключение— это код файловых систем, который хранят в индексах файлов значения моментов времени доступа к файлам.</p>
   </section>
   <section>
    <title>
     <p>Таймеры</p>
    </title>
    <section>
     <p><emphasis>Таймеры</emphasis> (timers), или, как их еще иногда называют, <emphasis>динамические таймеры</emphasis>, или <emphasis>таймеры ядра</emphasis>, необходимы для управления ходом времени в ядре. Коду ядра часто необходимо откладывать выполнение некоторых функций на более позднее время. Здесь намеренно выбрано не очень четкое понятие "<emphasis>позже</emphasis>". Назначение механизма нижних половин — это не <emphasis>задерживать</emphasis> выполнение, а <emphasis>не выполнять работу прямо сейчас</emphasis>. В связи с этим необходим инструмент, который позволяет задержать выполнение работы на некоторый интервал времени. Если этот интервал времени не очень маленький, но и не очень большой, то решение проблемы — таймеры ядра.</p>
     <p>Таймеры очень легко использовать. Необходимо выполнить некоторые начальные действия, указать момент времени окончания ожидания, указать функцию, которая будет выполнена, когда закончится интервал времени ожидания, и активизировать таймер. Указанная функция будет выполнена, когда закончится интервал времени таймера. Таймеры <emphasis>не являются</emphasis> циклическими. Когда заканчивается интервал времени ожидания, таймер ликвидируется. Это одна из причин, почему таймеры называют <emphasis>динамическими</emphasis><a l:href="#n59" type="note">[59]</a>. Таймеры постоянно создаются и ликвидируются, на количество таймеров не существует ограничений. Использование таймеров очень популярно во всех частях ядра.</p>
    </section>
    <section>
     <title>
      <p>Использование таймеров</p>
     </title>
     <p>Таймеры представлены с помощью структур <code>timer_list</code>, которая определена в файле <code>&lt;linux/timer.h&gt;</code> следующим образом.</p>
     <p><code>struct timer_list {</code></p>
     <p><code> struct list_head entry; /* таймеры хранятся в связанном списке */</code></p>
     <p><code> unsigned long expires; /* время окончание срока ожидания в</code></p>
     <p><code>                           импульсах системного таймера (jiffies) */</code></p>
     <p><code> spinlock_t lock; /* блокировка для защиты данного таймера */</code></p>
     <p><code> void (*function)(unsigned long); /*функция-обработчик таймера */</code></p>
     <p><code> unsigned long data; /* единственный аргумент обработчика */</code></p>
     <p><code> struct tvec_t_base_s *base; /* внутренние данные таймера, не трогать! */</code></p>
     <p><code>};</code></p>
     <p>К счастью, использование таймеров не требует глубокого понимания назначения полей этой структуры. На самом деле, крайне не рекомендуется использовать поля этой структуры не по назначению, чтобы сохранить совместимость с возможными будущими изменениями кода. Ядро предоставляет семейство интерфейсов для работы с таймерами, чтобы упростить эту работу. Все необходимые определения находятся в файле <code>&lt;linux/timer.h&gt;</code>. Большинство реализаций находится в файле <code>kernel/timers</code>.</p>
     <p>Первый шаг в создании таймера — это его объявление в следующем виде.</p>
     <p><code>struct timer_list my_timer;</code></p>
     <p>Далее должны быть инициализированы поля структуры, которые предназначены для внутреннего использования. Это делается с помощью вспомогательной функции перед вызовом любых функций, которые работают с таймером.</p>
     <p><code>init_timer(&amp;my_timer);</code></p>
     <p>Далее необходимо заполнить все остальные поля структуры, например, следующим образом.</p>
     <p><code>my_timer.expires = jiffies + delay; /* интервал времени таймера</code></p>
     <p><code>                                       закончится через delay импульсов */</code></p>
     <p><code>my_timer.data = 0; /* в функцию-обработчик будет передан параметр,</code></p>
     <p><code>                       равный нулю */</code></p>
     <p><code>my_timer.function = my_function; /* функция, которая будет выполнена,</code></p>
     <p><code>                            когда интервал времени таймера истечет */</code></p>
     <p>Значение поля <code>my_timer.expires</code> указывает время ожидания в импульсах системного таймера (необходимо указывать абсолютное количество импульсов). Когда текущее значение переменной <code>jiffies</code> становится большим или равным значению поля <code>my_timer.expires</code>, вызывается функция-обработчик <code>my_timer.function</code> с параметром <code>my_timer.data</code>. Как видно из описания структуры <code>timer_list</code>, функция-обработчик должна соответствовать следующему прототипу.</p>
     <p><code>void my_timer_function(unsigned long data);</code></p>
     <p>Параметр <code>data</code> позволяет регистрировать несколько таймеров с одним обработчиком и отличать таймеры с различными значениями этого параметра. Если в аргументе нет необходимости, то можно просто указать нулевое (или любое другое) значение.</p>
     <p>Последняя операция — это активизация таймера.</p>
     <p><code>add_timer(&amp;my_timer);</code></p>
     <p>И таймер запускается! Следует обратить внимание на важность значения поля <code>expired</code>. Ядро выполняет обработчик, когда текущее значение счетчика импульсов системного таймера <emphasis>больше</emphasis>, чем указанное значение времени срабатывания таймера, <emphasis>или равно</emphasis> ему. Хотя ядро и гарантирует, что никакой обработчик таймера не будет выполняться до истечения срока ожидания таймера, тем не менее возможны задержки с выполнением обработчика таймера. Обычно обработчики таймеров выполняются в момент времени, близкий к моменту времени срабатывания, однако они могут быть отложены и до следующего импульса системного таймера. Следовательно, таймеры нельзя использовать для работы в жестком режиме реального времени.</p>
     <p>Иногда может потребоваться изменить момент времени срабатывания таймера, который уже активизирован. В ядре реализована функция <code>mod_timer()</code>, которая позволяет изменить момент времени срабатывания активного таймера.</p>
     <p><code>mod_timer(&amp;my_timer, jiffies + new_delay); /* установка нового времени</code></p>
     <p><code>                                              срабатывания */</code></p>
     <p>Функция <code>mod_timer()</code> позволяет также работать с таймером, который проинициализирован, но не активен. Если таймер не активен, то функция <code>mod_timer()</code> активизирует его. Эта функция возвращает значение 0, если таймер был неактивным, и значение 1, если таймер был активным. В любом случае перед возвратом из функции <code>mod_timer()</code> таймер будут активизирован, и его время срабатывания будет установлено в указанное значение.</p>
     <p>Для того чтобы деактивизировать таймер до момента его срабатывания, необходимо использовать функцию <code>del_timer()</code> следующим образом.</p>
     <p><code>del_timer(&amp;my_timer);</code></p>
     <p>Эта функция работает как с активными, так и неактивными таймерами. Если таймер уже неактивен, то функция возвращает значение 0, в другом случае возвращается значение 1. Следует обратить внимание, что нет необходимости вызывать эту функцию для таймеров, интервал ожидания которых истек, так как они автоматически деактивизируются.</p>
     <p>При удалении таймеров потенциально может возникнуть состояние конкуренции. Когда функция <code>del_timer()</code> возвращает управление, она гарантирует только то, что таймер будет неактивным (т.е. его обработчик не будет выполнен в будущем). Однако на многопроцессорной машине обработчик таймера может выполняться в этот момент на другом процессоре. Для того чтобы деактивизировать таймер и подождать, пока завершится его обработчик, который потенциально может выполняться, необходимо использовать функцию <code>del_timer_sync()</code>:</p>
     <p><code>del_timer_sync(&amp;my_timer);</code></p>
     <p>В отличие от функции <code>del_timer()</code>, функция <code>del_timer_sync()</code> не может вызываться из контекста прерывания.</p>
    </section>
    <section>
     <title>
      <p>Состояния конкуренции, связанные с таймерами</p>
     </title>
     <p>Так как таймеры выполняются асинхронно по отношению к выполняемому в данный момент коду, то потенциально могут возникнуть несколько типов состояний конкуренции за ресурсы. Во-первых, никогда нельзя использовать следующий код, как замену функции <code>mod_timer()</code>.</p>
     <p><code>del_timer(my_timer);</code></p>
     <p><code>my_timer-&gt;expires = jiffies + new_delay;</code></p>
     <p><code>add_timer(my_timer);</code></p>
     <p>Во-вторых, практически во всех случаях следует использовать функцию <code>del_timer_sync()</code>, а не функцию <code>del_timer()</code>. В противном случае нельзя гарантировать, что обработчик таймера в данный момент не выполняется. Представьте себе, что после удаления таймера код освободит память или каким-либо другим образом вмешается в ресурсы, которые использует обработчик таймера. Поэтому синхронная версия более предпочтительна.</p>
     <p>Наконец, необходимо гарантировать защиту всех совместно используемых дан- пых, к которым обращается функция-обработчик таймера. Ядро выполняет эту функцию асинхронно по отношению к другому коду. Совместно используемые данные должны защищаться так, как рассматривалось в главах 8 и 9.</p>
    </section>
    <section>
     <title>
      <p>Реализация таймеров</p>
     </title>
     <p>Ядро выполняет обработчики таймеров в контексте обработчика отложенного прерывания после завершения обработки прерывания таймера. Обработчик прерывания таймера вызывает функцию <code>update_process_times()</code>, которая в свою очередь вызывает функцию <code>run_local_timers()</code>, имеющую следующий вид.</p>
     <p><code>void run_local_timers(void) {</code></p>
     <p><code> raise_softirq(TIMER_SOFTIRQ);</code></p>
     <p><code>}</code></p>
     <p>Отложенное прерывание с номером <code>TIMER_SOFTIRQ</code> обрабатывается функцией <code>run_timer_softirq()</code>. Эта функция выполняет на локальном процессоре обработчики всех таймеров, для которых истек период времени ожидания (если такие есть).</p>
     <p>Таймеры хранятся в связанном списке. Однако в ядре было бы неразумным просматривать весь список в поисках таймеров, для которых истекло время ожидания, или поддерживать список в отсортированном состоянии на основании времени срабатывания таймеров. В последнем случае вставка и удаление таймеров заняли бы много времени. Вместо этого таймеры разбиваются на 5 групп на основании времени срабатывания. Таймеры перемещаются из одной группы в другую, по мере того как приближается момент времени срабатывания. Такое разбиение на группы гарантирует, что в большинстве случаев при выполнении обработчика отложенного прерывания, ответственного за выполнение обработчиков таймеров, ядро будет выполнять мало работы для поиска таймеров, у которых истек период ожидания. Следовательно, код управления таймерами очень эффективен.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Задержка выполнения</p>
    </title>
    <section>
     <p>Часто коду ядра (особенно драйверам) необходимо задерживать выполнение действий на некоторый период времени без использования таймеров или механизма нижних половин. Это обычно необходимо для того, чтобы дать аппаратному обеспечению время на завершение выполнения задачи. Такой интервал времени обычно достаточно короткий. Например, в спецификации сетевой интерфейсной платы может быть указано время изменения режима работы Ethernet-контроллера, равное 2 микросекундам, т.е. после установки желаемой скорости передачи драйвер должен ожидать хотя бы в течение двух микросекунд перед тем, как продолжить работу.</p>
     <p>Ядро предоставляет несколько решений этой задачи, в зависимости от семантики задержки. Эти решения имеют разные свойства. Некоторые решения во время задержки загружают процессор, не давая возможности выполнять другую, более полезную работу. Другие решения не загружают процессор, но не дают гарантии того, что код возобновит выполнение точно в необходимый момент времени<a l:href="#n60" type="note">[60]</a>.</p>
    </section>
    <section>
     <title>
      <p>Задержка с помощью цикла</p>
     </title>
     <p>Наиболее простое для реализации (хотя обычно не оптимальное) решение — это использование <emphasis>задержки с помощью цикла</emphasis> или <emphasis>ожидания в состоянии занятости</emphasis> (<emphasis>busy loop</emphasis>, <emphasis>busy waiting</emphasis>). Эта техника работает, только если интервал времени задержки является кратным периоду системного таймера или когда точность не очень важна.</p>
     <p>Идея проста — выполнить постоянный цикл, пока не будет получено необходимое количество импульсов системного таймера, как в следующем примере.</p>
     <p><code>unsigned long delay = jiffies + 10; /* десять импульсов таймера */</code></p>
     <empty-line/>
     <p><code>while (time_before(jiffies, delay))</code></p>
     <p><code> ;</code></p>
     <p>Цикл будет выполняться, пока значение переменной <code>jiffies</code> не станет больше, чем значение переменной <code>delay</code>, что может произойти только после того, как будут получены 10 импульсов системного таймера. Для аппаратной платформы x86 со значением параметра <code>HZ</code>, равным 1000, этот интервал равен 10 миллисекунд.</p>
     <p>Аналогично можно поступить следующим образом.</p>
     <p><code>unsigned long delay = jiffies + 2*HZ; /* две секунды */</code></p>
     <empty-line/>
     <p><code>while (time_before(jiffies, delay))</code></p>
     <p><code> ;</code></p>
     <p>В этом случае цикл будет выполняться, пока не поступит <code>2*HZ</code> импульсов системного таймера, что всегда равно 2 секундам, независимо от частоты системного таймера.</p>
     <p>Такой подход не очень хорош для всей системы. Пока код ожидает, процессор загружен выполнением бесполезного цикла и никакой полезной работы при этом не выполняется! На самом деле к такому "глупому" подходу нужно прибегать по возможности реже, и он показан здесь, потому что является понятным и простым способом осуществить задержку. Его можно встретить в чьем-нибудь не очень хорошем коде.</p>
     <p>Лучшим решением является перепланирование для того, чтобы процессор мог выполнить полезную работу, пока ваш код ожидает:</p>
     <p><code>unsigned long delay = jiffies + 5*HZ;</code></p>
     <empty-line/>
     <p><code>while (time_before(jiffies, delay))</code></p>
     <p><code> cond_resched();</code></p>
     <p>Вызов функции <code>cond_resched()</code> планирует выполнение другого процесса, но только в случае, если установлен флаг <code>need_resched</code>. Другими словами, данное решение позволяет активизировать планировщик, но только в случае, когда есть более важное задание, которое нужно выполнить. Следует обратить внимание, что. поскольку используется планировщик, такое решение нельзя применять в контексте прерывания, а только в контексте процесса. Задержки лучше использовать только в контексте процесса, поскольку обработчики прерываний должны выполняться по возможности быстро (а цикл задержки не дает такой возможности!). Более того, любые задержки выполнения, по возможности, не должны использоваться при захваченных блокировках и при запрещенных прерываниях.</p>
     <p>Поклонники языка С могут поинтересоваться, какие есть гарантии, что указанные циклы будут действительно выполняться? Обычно компилятор С может выполнить чтение указанной переменной всего один раз. В обычной ситуации нет никакой гарантии, что переменная <code>jiffies</code> будет считываться на каждой итерации цикла. Нам же необходимо, чтобы значение переменной <code>jiffies</code> считывалось на каждой итерации цикла, так как это значение увеличивается в другом месте, а именно в прерывании таймера. Именно поэтому данная переменная определена в файле <code>&lt;linux/jiffies.h&gt;</code> с атрибутом <code>volatile</code>. Ключевое слово <code>volatile</code> указывает компилятору, что эту переменную необходимо считывать из того места, где она хранится в оперативной памяти, и никогда не использовать копию, хранящуюся в регистре процессора. Это гарантирует, что указанный цикл выполнится, как и ожидается.</p>
    </section>
    <section>
     <title>
      <p>Короткие задержки</p>
     </title>
     <p>Иногда коду ядра (и снопа обычно драйверам) необходимы задержки на очень короткие интервалы времени (короче, чем период системного таймера), причем интервал должен отслеживаться с достаточно высокой точностью. Это часто необходимо для синхронизации с аппаратным обеспечением, для которого описано некоторое минимальное время выполнения действий, и которое часто бывает меньше одной миллисекунды. В случае таких малых значений времени невозможно использовать задержки на основании переменной <code>jiffies</code>, как показано в предыдущем примере. При частоте системного таймера, равной 100 Гц, значение периода системного таймера достаточно большое — 10 миллисекунд! Даже при частоте системного таймера 1000 Гц, период системного таймера равен одной миллисекунде. Ясно, что необходимо другое решение, которое обеспечивает более короткие и точные задержки.</p>
     <p>Ядро предоставляет две функции для обеспечения микросекундных и миллисекундных задержек, которые определены в файле <code>&lt;linux/delay.h&gt;</code> и не используют переменную <code>jiffies</code>.</p>
     <p><code>void udelay(unsigned long usecs);</code></p>
     <p><code>void mdelay(unsigned long msecs);</code></p>
     <p>Первая функция позволяет задержать выполнение на указанное количество <emphasis>микросекунд</emphasis> с использованием цикла. Вторая функция задерживает выполнение на указанное количество миллисекунд. Следует вспомнить, что одна секунда равна 1000 миллисекундам, что эквивалентно 1000000 <emphasis>микросекунд</emphasis>. Использование этих функций тривиально.</p>
     <p><code>udelay(150); /* задержка на 150 &#956;s */</code></p>
     <p>Функция <code>udelay()</code> выполнена на основе цикла, для которого известно, сколько итераций необходимо выполнить за указанный период времени. Функция <code>mdelay()</code> выполнена на основе функции <code>udelay()</code>. Так как в ядре известно, сколько циклов процессор может выполнить в одну секунду (смотрите ниже замечание по поводу характеристики BogoMlPS), функция <code>udelay()</code> просто масштабирует это значение для того, чтобы скорректировать количество итераций цикла для получения указанной задержки.</p>
     <cite>
      <subtitle>Мой BogoMIPS больше, чем у Вас!</subtitle>
      <p>Характеристика BogoMlPS всегда была источником недоразумений и шуток. На самом деле вычисленное значение BogoMlPS не имеет ничего общего с производительностью компьютера и используется только для функций <code>udelay()</code> и <code>mdelay()</code>. Название этого параметра состоит из двух частей <emphasis>bogus</emphasis> (фиктивный) и <emphasis>MIPS</emphasis> (million of instructions per second, миллион инструкций в секунду). Все знакомы с сообщением, которое выдается при загрузке системы и похоже на следующее (данное сообщение соответствует процессору Pentium III с частотой 1 ГГц).</p>
      <p><code>Detected 1004.932 MHz processor.</code></p>
      <p><code>Calibrating delay loop... 1990.65 BogoMlPS</code></p>
      <p>Значение параметра BogoMIPS - это количество циклов, которые процессор может выполнить за указанный период времени, В действительности эта характеристика показывает, насколько быстро процессор может ничего не делать! Это значение хранится в переменной <code>loops_per_jiffy</code>, и его можно считать из файла <code>/proc/cpuinfo</code>.</p>
      <p>Функции, использующие циклы задержки, используют данное значение, чтобы вычислить (и это получается достаточно точно), сколько итераций цикла необходимо выполнить, чтобы обеспечить необходимую задержку.</p>
      <p>Ядро вычисляет значение переменной <code>loops_per_jiffy</code> при загрузке системы в функции <code>calibrate_delay()</code>, реализация которой описана в файле <code>init/main.c</code>.</p>
     </cite>
     <p>Функция <code>udelay()</code> должна вызываться только для небольших задержек, поскольку при большом времени задержки на быстрой машине может возникнуть переполнение в переменных цикла. Общее правило: по возможности не использовать функцию <code>udelay()</code> для задержек, больше одной миллисекунды. Для более продолжительных задержек хорошо работает функция <code>mdelay()</code>. Так же как и другие методы задержки выполнения, основанные на циклах, эти функции (особенно функция <code>mdelay()</code>, так как она дает длительные задержки) должны использоваться, только если это абсолютно необходимо. Следует помнить, что очень плохо использовать циклы задержек, когда удерживается блокировка или запрещены прерывания, потому что это очень сильно влияет на производительность и время реакции системы. Если необходимо обеспечить точное время задержки, то эти функции — наилучшее решение. Обычное использование этих функций — это задержки на не очень короткий период времени, который лежит в микросекундном диапазоне.</p>
    </section>
    <section>
     <title>
      <p>Функция <code>schedule_timeout()</code></p>
     </title>
     <p>Более оптимальный метод задержки выполнения — это использование функции <code>schedule_timeouit()</code>. Этот вызов переводит вызывающее задание в состояние ожидания (sleep) по крайней до тех пор, пока не пройдет указанный период времени. Нет никакой гарантии, что время ожидания будет <emphasis>точно</emphasis> равно указанному значению, гарантируется только, что задержка будет не меньше указанной. Когда проходит указанный период времени, ядро возвращает задание в состояние готовности к выполнению (wake up) и помещает его в очередь выполнения. Использовать эту функцию просто.</p>
     <p><code>/* установить состояние задания в значение прерываемого ожидания */</code></p>
     <p><code>set_current_state(TASK_INTERRUPTIBLE);</code></p>
     <empty-line/>
     <p><code>/* перейти в приостановленное состояние на s секунд */</code></p>
     <p><code>schedule_timeout(s * HZ);</code></p>
     <p>Единственный параметр функции — это желаемое относительное время, выраженное в количестве импульсов системного таймера. В этом примере задание переводится в прерываемое состояние ожидания, которое будет длиться s секунд. Поскольку задание отмечено как <code>TASK_INTERRUPTIBLE</code>, то оно может быть возвращено к выполнению раньше времени, как только оно получит сигнал. Если не нужно, чтобы код обрабатывал сигналы, то можно использовать состояние <code>TASK_UNINTERRUPTIBLE</code>. Перед вызовом функции <code>schedule_timeout()</code> задание должно быть в одном из этих двух состояний, иначе задание в состояние ожидания переведено не будет.</p>
     <p>Следует обратить внимание, что поскольку функция <code>schedule_timeout()</code> использует планировщик, то код, который ее вызывает, должен быть совместим с состоянием ожидания. Обсуждение, посвященное атомарности и переходу в состояние ожидания, приведено в главах 8 и 9. Если коротко, то эту функцию необходимо вызывать в контексте процесса и не удерживать при этом блокировку.</p>
     <p>Функция <code>schedule_timeout()</code> достаточно проста. Она просто использует таймеры ядра. Рассмотрим эту функцию подробнее.</p>
     <p><code>signed long schedule_timeout(signed long timeout) {</code></p>
     <p><code> timer_t timer;</code></p>
     <p><code> unsigned long expire;</code></p>
     <empty-line/>
     <p><code> switch (timeout) {</code></p>
     <p><code> case MAX_SCHEDULE_TIMEOUT:</code></p>
     <p><code>  schedule();</code></p>
     <p><code>  goto out;</code></p>
     <p><code> default:</code></p>
     <p><code>  if (timeout &lt; 0) {</code></p>
     <p><code>   printk(KERN_ERR "schedule_timeout: wrong timeout "</code></p>
     <p><code>    "value %lx from %p\n", timeout, builtin_return_address(0));</code></p>
     <p><code>   current-&gt;state = TASK_RUNNING;</code></p>
     <p><code>   goto out;</code></p>
     <p><code>  }</code></p>
     <p><code> }</code></p>
     <empty-line/>
     <p><code> expire = timeout + jiffies;</code></p>
     <empty-line/>
     <p><code> init_timer(&amp;timer);</code></p>
     <p><code> timer.expires = expire;</code></p>
     <p><code> timer.data = (unsigned long) current;</code></p>
     <p><code> timer.function = process_timeout;</code></p>
     <empty-line/>
     <p><code> add_timer(&amp;timer);</code></p>
     <p><code> schedule();</code></p>
     <p><code> del_timer_sync(&amp;timer);</code></p>
     <empty-line/>
     <p><code> timeout = expire - jiffies;</code></p>
     <p><code>out:</code></p>
     <p><code> return timeout &lt; 0 ? 0 : timeout;</code></p>
     <p><code>}</code></p>
     <p>Эта функция создает таймер <code>timer</code> и устанавливает время срабатывания в значение <code>timeout</code> импульсов системного таймера в будущем. В качестве обработчика таймера устанавливается функция <code>process_timeout()</code>, которая вызывается, когда истекает период времени таймера. Далее таймер активизируется, и вызывается функция <code>schedule()</code>. Так как предполагается, что текущее задание находится в состоянии <code>TASK_INTERRUPTIBLE</code> или <code>TASK_UNINTERRUPTIBLE</code>, то планировщик не будет выполнять текущее задание, а выберет для выполнения другой процесс.</p>
     <p>Когда интервал времени таймера истекает, то вызывается функция <code>process_timeout()</code>, которая имеет следующий вид.</p>
     <p><code>void process_timeout(unsigned long data) {</code></p>
     <p><code> wake_up_process((task_t*)data);</code></p>
     <p><code>}</code></p>
     <p>Эта функция устанавливает задание в состояние <code>TASK_RUNNING</code> и помещает его в очередь выполнения.</p>
     <p>Когда задание снова планируется на выполнение, то оно возвращается в функцию <code>schedule_timeout()</code> (сразу после вызова функции <code>schedule()</code>). Если задание возвращается к выполнению преждевременно, то таймер ликвидируется. После этого задание возвращается из функции ожидания по тайм-ауту.</p>
     <p>Код оператора <code>switch()</code> служит для обработки специальных случаев и не является основной частью функции. Проверка на значение <code>MAX_SCHEDULE_TIMEOUT</code> позволяет заданию находиться в состоянии ожидания неопределенное время. В этом случае таймер не устанавливается (поскольку нет ограничений на интервал времени ожидания), и сразу же активизируется планировщик. Если вы это применяете, то, наверное, у вас есть лучший способ вернуть задание в состояние выполнения!</p>
     <subtitle>Ожидание в очереди wait queue в течение интервала времени</subtitle>
     <p>В главе 4 рассматривалось, как контекст процесса в ядре может поместить себя в очередь ожидания для того, чтобы ждать наступления некоторого события, а затем вызвать планировщик, который выберет новое задание для выполнения. Если где-то в другом месте произойдет указанное событие, то вызывается функция <code>wake_up()</code> для всех заданий, которые ожидают в очереди. Эти задания возвращаются к выполнению и могут продолжать работу.</p>
     <p>Иногда желательно ожидать наступления некоторого события <emphasis>или</emphasis> пока не пройдет определенный интервал времени, в зависимости от того, что наступит раньше, В этом случае код должен просто вызвать функцию <code>schedule_timeout()</code> вместо функции <code>schedule()</code> после того, как он поместил себя в очередь ожидания. Задание будет возвращено к выполнению, когда произойдет желаемое событие или пройдет указанный интервал времени. Код обязательно должен проверить, <emphasis>почему</emphasis> он возвратился к выполнению — это может произойти потому, что произошло событие, прошел интервал времени или был получен сигнал — после этого необходимо соответственным образом продолжить выполнение.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Время вышло</p>
    </title>
    <p>В этой главе были рассмотрены понятия, связанные с представлением о времени в ядре и с тем, как при этом происходит управление абсолютным и относительным ходом времени. Были показаны отличия абсолютного и относительного времени, а также периодических и относительных событий. Далее были рассмотрены прерывания таймера, импульсы таймера, константа <code>HZ</code> и переменная <code>jiffies</code>.</p>
    <p>После этого было рассказано о том, как реализованы таймеры ядра и как их можно использовать в собственном коде ядра. В конце главы были представлены другие методы, которые разработчики могут использовать для учета времени.</p>
    <p>Большая часть кода ядра, который вам придется писать, требует понимания того, как время течет в ядре и как его отслеживать. С очень большой вероятностью, особенно при разработке драйверов, вам необходимо будет иметь дело с таймерами ядра. Материал этой главы принесет практическую пользу.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 11</p>
    <p>Управление памятью</p>
   </title>
   <section>
    <p>Выделить память <emphasis>внутри</emphasis> ядра не так просто, как <emphasis>вне</emphasis> ядра. Это связано со многими факторами. Главным образом, причина в том, что в ядре не доступны те элементы роскоши, которыми можно пользоваться в пространстве пользователя, В отличие от пространства пользователя, в ядре не всегда можно позволить себе легко выделять память. Например, в режиме ядра часто нельзя переходить в состояние ожидания. Более того, в ядре не так просто справиться с ошибками, которые возникают при работе с памятью. Из-за этих ограничений и из-за необходимости, чтобы схема выделения памяти была быстрой, работа с памятью в режиме ядра становится более сложной, чем в режиме пользователя. Конечно, нельзя сказать, что выделение памяти в ядре — очень сложная процедура, однако скоро все будет ясно — просто это делается несколько по-другому.</p>
    <p>В этой главе рассматриваются средства, которые предназначены для выделения памяти внутри ядра. Перед изучением интерфейсов, предназначенных для выделения памяти, необходимо рассмотреть, как ядро управляет памятью.</p>
   </section>
   <section>
    <title>
     <p>Страницы памяти</p>
    </title>
    <p>Ядро рассматривает страницы физической памяти как основные единицы управления памятью. Хотя наименьшая единица памяти, которую может адресовать процессор, — это машинное слово, модуль управления памятью (MMU, Memory Management Unit) — аппаратное устройство, которое управляет памятью и отвечает за трансляцию виртуальных адресов в физические  — обычно работает со страницами. Поэтому модуль MMU управляет таблицами страниц на уровне страничной детализации (отсюда и название). С точки зрения виртуальной памяти, страница — это наименьшая значащая единица.</p>
    <p>Как будет показано в главе 19, "Переносимость", каждая аппаратная платформа поддерживает свой характерный размер страницы. Многие аппаратные платформы поддерживают даже несколько разных размеров страниц. Большинство 32-разрядных платформ имеют размер страницы, равный 4 Кбайт, а большинство 64-разрядных платформ — 8 Кбайт. Это значит, что на машине, размер страницы которой равен 4 Кбайт, при объеме физической памяти, равном 1 Гбайт, эта физическая память разбивается на 262 144 страницы.</p>
    <p>Ядро сопоставляет <emphasis>каждой</emphasis> странице физической памяти в системе структуру <code>struct page</code>. Эта структура определена в файле <code>&lt;linux/mm.h&gt;</code> следующим образом.</p>
    <p><code>struct page {</code></p>
    <p><code> page_flags_t         flags;</code></p>
    <p><code> atomic_t             _count;</code></p>
    <p><code> atomic_t             _mapcount;</code></p>
    <p><code> unsigned long        private;</code></p>
    <p><code> struct address_space *mapping;</code></p>
    <p><code> pgoff_t              index;</code></p>
    <p><code> struct list_head     lru;</code></p>
    <p><code> void                 *virtual;</code></p>
    <p><code>};</code></p>
    <p>Рассмотрим самые важные поля этой структуры. Поле <code>flags</code> содержит состояние страницы. Это поле включает следующую информацию: является ли страница измененной (dirty) или заблокированной (locked) в памяти. Значение каждого флага представлено одним битом, поэтому всего может быть до 32 разных флагов. Значения флагов определены в файле <code>&lt;linux/page-flags.h&gt;</code>.</p>
    <p>Поле <code>_count</code> содержит счетчик использования страницы — т.е. сколько на эту страницу имеется ссылок. Когда это значение равно нулю, это значит, что никто не использует страницу, и она становится доступной для использования при новом выделении памяти. Код ядра не должен явно проверять значение этого поля, вместо этого необходимо использовать функцию <code>page_count()</code>, которая принимает указатель на структуру <code>page</code> в качестве единственного параметра. Хотя в случае незанятой страницы памяти значение счетчика <code>_count</code> может быть отрицательным (во внутреннем представлении), функция <code>page_count()</code> возвращает значение нуль для незанятой страницы памяти и положительное значение — для страницы, которая в данный момент используется. Страница может использоваться страничным кэшем (в таком случае поле <code>mapping</code> указывает на объект типа <code>address_space</code>, который связан с данной страницей памяти), может использоваться в качестве частных данных (на которые в таком случае указывает поле <code>private</code>) или отображаться в таблицу страниц процесса.</p>
    <p>Поле <code>virtual</code> — это виртуальный адрес страницы. Обычно это просто адрес данной страницы в виртуальной памяти ядра. Некоторая часть памяти (называемая областью верхней памяти, high memory) не отображается в адресное пространство ядра (т.е. не входит в него постоянно). В этом случае значение данного поля равно <code>NULL</code> и страница при необходимости должна отображаться динамически. Верхняя память будет рассмотрена в одном из следующих разделов.</p>
    <p>Наиболее важный момент, который необходимо понять, это то, что структура <code>page</code> связана со страницами физической, а не виртуальной памяти. Поэтому то, чему соответствует экземпляр этой структуры, в лучшем случае, очень быстро изменяется. Даже если данные, которые содержались в физической странице, продолжают существовать, то это не значит, что эти данные будут всегда соответствовать одной и той же физической странице памяти и соответственно одной и той же структуре <code>page</code>, например из-за вытеснения страниц (swapping) или по другим причинам. Ядро использует эту структуру данных для описания всего того, что содержится в данный момент в странице физической памяти, соответствующей данной структуре. Назначение этой структуры— описывать область физической памяти, а не данных, которые в ней содержатся.</p>
    <p>Ядро использует рассматриваемую структуру данных для отслеживания всех страниц физической памяти в системе, так как ядру необходима информация о том, свободна ли страница (т.е. соответствующая область физической памяти никому не выделена). Если страница не свободна, то ядро должно иметь информацию о том, чему принадлежит эта страница. Возможные обладатели: процесс пространства пользователя, данные в динамически выделенной памяти в пространстве ядра, статический код ядра, страничный кэш (page cache) и т.д.</p>
    <p>Разработчики часто удивляются, что для каждой физической страницы в системе создается экземпляр данной структуры. Они думают: "Как много для этого используется памяти!" Давайте посмотрим, насколько плохо (или хорошо) расходуется адресное пространство для хранения информации о страницах памяти. Размер структуры <code>struct page</code> равен 40 байт. Допустим, что система имеет страницы размером 1 Кбайт, а объем физической памяти равен 128 Мбайт. Тогда все структуры раде в системе займут немного больше 1 Мбайт памяти — не очень большая плата за возможность управления всеми страницами физической памяти.</p>
   </section>
   <section>
    <title>
     <p>Зоны</p>
    </title>
    <p>В связи с ограничениями аппаратного обеспечения, ядро не может рассматривать все страницы памяти как идентичные. Некоторые страницы, в связи со значениями их физических адресов памяти, не могут использоваться для некоторых типов задач. Из-за этого ограничения ядро делит физическую память на <emphasis>зоны</emphasis>. Ядро использует зоны, чтобы группировать страницы памяти с аналогичными свойствами. В частности, операционная система Linux должна учитывать следующие недостатки аппаратного обеспечения, связанные с адресацией памяти.</p>
    <p>• Некоторые аппаратные устройства могут выполнять прямой доступ к памяти (ПДП, DMA, Direct Memory Access) только в определенную область адресов.</p>
    <p>• На некоторых аппаратных платформах для физической адресации доступны большие объемы памяти, чем для виртуальной адресации. Следовательно, часть памяти не может постоянно отображаться в адресное пространство ядра.</p>
    <p>В связи с этими ограничениями, в операционной системе Linux выделяют три зоны памяти.</p>
    <p>• <code>ZONE_DMA</code>. Содержит страницы, которые совместимы с режимом DMA.</p>
    <p>• <code>ZONE_NORMAL</code>. Содержит страницы памяти, которые отображаются в адресные пространства обычным образом.</p>
    <p>• <code>ZONE_HIGHMEM</code>. Содержит "верхнюю память", состоящую из страниц, которые не могут постоянно отображаться в адресное пространство ядра.</p>
    <p>Эти зоны определяются в заголовочном файле <code>&lt;linux/mmzone.h&gt;</code>.</p>
    <p>То, как используется разделение памяти на зоны, зависит от аппаратной платформы. Например, для некоторых аппаратных платформ нет проблем с прямым доступом к памяти ни по какому адресу. Для таких платформ зона <code>ZONE_DMA</code> является пустой, и для всех типов выделения памяти используется зона <code>ZONE_NORMAL</code>.</p>
    <p>Как противоположный пример можно привести платформу x86, для которой устройства ISA<a l:href="#n61" type="note">[61]</a> не могут выполнять операции DMA в полном 32-разрядном пространстве адресов, так как устройства ISA могут обращаться только к первым 16 Мбайт физической памяти. Следовательно, зона <code>ZONE_DMA</code> для платформы x86 содержит только страницы памяти с физическими адресами в диапазоне 0-16 Мбайт.</p>
    <p>Аналогично используется и зона <code>ZONE_HIGHMEM</code>. To, что аппаратная платформа может отображать и чего она не может отображать в адресное пространство ядра, отличается для разных аппаратных платформ. Для платформы x86 зона <code>ZONE_HIGHMEM</code> — это вся память, адреса которой лежат выше отметки 896 Мбайт. Для других аппаратных платформ зона <code>ZONE_HIGHMEM</code> пуста, так как вся память может непосредственно отображаться. Память, которая содержится в зоне <code>ZONE_HIGHMEM</code>, называется <emphasis>верхней памятью</emphasis><a l:href="#n62" type="note">[62]</a> (<emphasis>high memory</emphasis>). Вся остальная память в системе называется <emphasis>нижней памятью</emphasis> (<emphasis>low memory</emphasis>).</p>
    <p>Зона <code>ZONE_NORMAL</code> обычно содержит все, что не попало в две предыдущие зоны памяти. Для аппаратной платформы x86, например, зона <code>ZONE_NORMAL</code> содержит всю физическую память от 16 до 896 Мбайт. Для других, более удачных аппаратных платформ, <code>ZONE_NORMAL</code> — это вся доступная память. В табл. 11.1 приведен список зон для аппаратной платформы x86.</p>
    <empty-line/>
    <p><strong>Таблица 11.1</strong>. Зоны памяти для аппаратной платформы x86</p>
    <table>
     <tr align="left">
      <th align="left" valign="top">Зона</th>
      <th align="left" valign="top">Описание</th>
      <th align="left" valign="top">Физическая память</th>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>ZONE_DMA</code></td>
      <td align="left" valign="top">Страницы памяти, совместимые с ПДП</td>
      <td align="left" valign="top">&lt; 16 Мбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>ZONE_NORMAL</code></td>
      <td align="left" valign="top">Нормально адресуемые страницы</td>
      <td align="left" valign="top">16 - 896 Мбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>ZONE_HIGHMEM</code></td>
      <td align="left" valign="top">Динамически отображаемые страницы</td>
      <td align="left" valign="top">&gt; 896 Мбайт</td>
     </tr>
    </table>
    <p>Операционная система разделяет страницы системной памяти на зоны, чтобы иметь пулы страниц для удовлетворения требований выделения памяти. Например, пул зоны <code>ZONE_DMA</code> дает возможность ядру удовлетворить запрос на выделение памяти, которая необходима для операций DMA. Если нужна такая память, ядро может просто выделить необходимое количество страниц из зоны <code>ZONE_DMA</code>. Следует обратить внимание, что зоны не связаны с аппаратным обеспечением— это логическое группирование, которое позволяет ядру вести учет страниц; памяти.</p>
    <p>Хотя некоторые запросы на выделение памяти могут требовать страницы из определенной зоны, это требование не обязательно может быть жестким. Например, выделение памяти для ПДП требует страницы из зоны <code>ZONE_DMA</code>, а для обычного выделения памяти могут подойти страницы как из зоны <code>ZONE_NORMAL</code> так и из зоны <code>ZONE_DMA</code>. Конечно, для удовлетворения запросов по обычному выделению памяти ядро будет стараться выделять страницы из зоны <code>ZONE_NORMAL</code>, чтобы сохранить страницы в зоне <code>ZONE_DMA</code> для случая, когда эти страницы действительно нужны, Если же наступает решающий момент (становится недостаточно памяти), то ядро может обратиться к любой доступной и подходящей зоне.</p>
    <p>Каждая зона представлена с помощью структуры <code>struct zone</code>, которая определена в файле <code>&lt;linux/mmzone.h&gt;</code> в следующем виде.</p>
    <p><code>struct zone {</code></p>
    <p><code> spinlock_t             lock;</code></p>
    <p><code> unsigned long          free_pages;</code></p>
    <p><code> unsigned long          pages_min;</code></p>
    <p><code> unsigned long          pages_low;</code></p>
    <p><code> unsigned long          pages_high;</code></p>
    <p><code> unsigned long          protection[MAX_NR_ZONES];</code></p>
    <p><code> spinlock_t             lru_lock;</code></p>
    <p><code> struct list_head       active_list;</code></p>
    <p><code> struct list_head       inactive_list;</code></p>
    <p><code> unsigned long          nr_scan_active;</code></p>
    <p><code> unsigned long          nr_scan_inactive;</code></p>
    <p><code> unsigned long          nr_active;</code></p>
    <p><code> unsigned long          nr_inactive;</code></p>
    <p><code> int                    all_unreclaimable;</code></p>
    <p><code> unsigned long          pages_scanned;</code></p>
    <p><code> int                    temp_priority;</code></p>
    <p><code> int                    prev_priority;</code></p>
    <p><code> struct free_area       free_area[MAX_ORDER];</code></p>
    <p><code> wait_queue_head_t      *wait_table;</code></p>
    <p><code> unsigned long          wait_table_size;</code></p>
    <p><code> unsigned long          wait_table_bits;</code></p>
    <p><code> struct per_cpu_pageset pageset[NR_CPUS];</code></p>
    <p><code> struct pglist_data     *zone_pgdat;</code></p>
    <p><code> struct page            *zone_mem_map;</code></p>
    <p><code> unsigned long          zone_start_pfn;</code></p>
    <p><code> char                   *name;</code></p>
    <p><code> unsigned long          spanned_pages;</code></p>
    <p><code> unsigned long          present_pages;</code></p>
    <p><code>};</code></p>
    <p>Эта структура большая, но в системе всего три зоны и соответственно три такие структуры. Рассмотрим наиболее важные поля данной структуры.</p>
    <p>Поле <code>lock</code>— это спин-блокировка, которая защищает структуру от параллельного доступа. Обратите внимание, что она защищает только структуру, а не страницы, которые принадлежат зоне. Для защиты отдельных страниц нет блокировок, хотя отдельные части кода могут блокировать данные, которые могут оказаться в указанных страницах.</p>
    <p>Поле <code>free_pages</code> — это количество свободных страниц в соответствующей зоне. Ядро старается поддерживать свободными хотя бы <code>pages_min</code> страниц зоны, если это возможно (например, с помощью вытеснения на диск).</p>
    <p>Поле <code>name</code> — это строка, оканчивающаяся нулем, которая содержит имя соответствующей зоны (что не удивительно). Ядро инициализирует указанное поле при загрузке системы с помощью кода, который описан n файле <code>mm/page_alloc.с.</code> Три зоны имеют имена "DMA", "Normal" и "HighMem".</p>
   </section>
   <section>
    <title>
     <p>Получение страниц памяти</p>
    </title>
    <section>
     <p>Теперь, имея некоторое понятие о том, как ядро управляет памятью с помощью страниц, зон и так далее, давайте рассмотрим интерфейсы, которые реализованы в ядре для того, чтобы выделять и освобождать память внутри ядра. Ядро предоставляет один низкоуровневый интерфейс для выделения памяти и несколько интерфейсов для доступа к ней. Все эти интерфейсы выделяют память в объеме, кратном размеру страницы, и определены в файле <code>&lt;linux/gfp.h&gt;</code>. Основная функция выделения памяти следующая.</p>
     <p><code>struct page * alloc_pages(unsigned int gfp_mask, unsigned int order);</code></p>
     <p>Данная функция позволяет выделить 2<code><sup>order</sup></code> (т.е. <code>1 &lt;&lt; order</code>) смежных страниц (один непрерывный участок) физической памяти и возвращает указатель на структуру <code>page</code>, которая соответствует первой выделенной странице памяти. В случае ошибки возвращается значение <code>NULL</code>. Параметр <code>gfp_mask</code> будет рассмотрен несколько позже. Полученную страницу памяти можно конвертировать в ее логический адрес с помощью следующей функции.</p>
     <p><code>void *page_address(struct page *page);</code></p>
     <p>Эта функция возвращает указатель на логический адрес, которому в данный момент соответствует начало указанной страницы физической памяти. Если нет необходимости в соответствующей структуре <code>struct page</code>, то можно использовать следующую функцию.</p>
     <p><code>unsigned long __get_free_pages(unsigned int gfp_mask,</code></p>
     <p><code> unsigned int order);</code></p>
     <p>Эта функция работает так же, как и функция <code>alloc_pages()</code>, за исключением того, что она сразу возвращает логический адрес первой выделенной страницы памяти. Так как выделяются смежные страницы памяти, то другие страницы просто следуют за первой.</p>
     <p>Если необходима всего одна страница памяти, то для этой цели определены следующие функции-оболочки, которые позволяют уменьшить количество работы по набору кода программы.</p>
     <p><code>struct page * alloc_page(unsigned int gfp_mask);</code></p>
     <p><code>unsigned long __get_free_page(unsigned int gfp_mask);</code></p>
     <p>Эти функции работают так же, как и ранее описанные, по для них в качестве параметра <code>order</code> передается нуль (2<sup>0</sup> = одна страница памяти).</p>
    </section>
    <section>
     <title>
      <p>Получение страниц заполненных нулями</p>
     </title>
     <p>Для того чтобы получаемые страницы памяти были заполнены нулями, необходимо использовать следующую функцию.</p>
     <p><code>unsigned long get_zeroed_page(unsigned int gfp_mask);</code></p>
     <p>Эта функция аналогична функции <code>__get_free_page()</code>, за исключением того, что после выделения страницы памяти она заполняется нулями. Это полезно для страниц памяти, которые возвращаются в пространство пользователя, так как случайный "мусор", который находится в страницах памяти, может оказаться не совсем случайным и случайно может содержать некоторые (например, секретные) данные. Все данные необходимо обнулить или очистить каким-либо другим образом перед тем, как возвращать информацию в пространство пользователя, чтобы при этом не пострадала безопасность системы. В табл. 11.2 приведен список всех низкоуровневых средств выделения памяти.</p>
     <empty-line/>
     <p><strong>Таблица 11.2</strong>. Низкоуровневые средства выделения памяти</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Функция</th>
       <th align="left" valign="top">Описание</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>alloc_page(gfp_mask)</code></td>
       <td align="left" valign="top">Выделяет одну страницу памяти и возвращает указатель на соответствующую ей структуру <code>page</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>alloc_pages(gfp_mask, order)</code></td>
       <td align="left" valign="top">Выделяет 2<code><sup>order</sup></code> страниц памяти и возвращает указатель на структуру <code>page</code> первой страницы</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>__get_free_page(gfp_mask)</code></td>
       <td align="left" valign="top">Выделяет одну страницу памяти и возвращает указатель на ее логический адрес</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>__get_free_pages(gfp_mask, order)</code></td>
       <td align="left" valign="top">Выделяет 2<code><sup>order</sup></code> страниц памяти и возвращает указатель на логический адрес первой страницы</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>get_zeroed_page(gfp_mask)</code></td>
       <td align="left" valign="top">Выделяет одну страницу памяти, обнуляет ее содержимое и возвращает указатель на ее логический адрес</td>
      </tr>
     </table>
    </section>
    <section>
     <title>
      <p>Освобождение страниц</p>
     </title>
     <p>Для освобождения страниц, которые больше не нужны, можно использовать следующие функции.</p>
     <p><code>void __free_pages(struct page *page, unsigned int order);</code></p>
     <p><code>void free_pages(unsigned long addr, unsigned int order);</code></p>
     <p><code>void free_page(unsigned long addr);</code></p>
     <p>Необходимо быть внимательными и освобождать только те страницы памяти, которые вам выделены. Передача неправильного значения параметра <code>page</code>, <code>addr</code> или <code>order</code> может привести к порче данных. Следует помнить, что ядро доверяет себе. В отличие от пространства пользователя, ядро с удовольствием зависнет, если вы попросите. Рассмотрим пример. Мы хотим выделить 8 страниц памяти.</p>
     <p><code>page = __get_free_pages(GFP_KERNEL, 3);</code></p>
     <empty-line/>
     <p><code>if (!page) {</code></p>
     <p><code> /* недостаточно памяти: эту ошибку необходимо обработать самим! */</code></p>
     <p><code> return -ENOMEM;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>/* переменная 'page' теперь содержит адрес</code></p>
     <p><code>   первой из восьми страниц памяти */</code></p>
     <empty-line/>
     <p><code>free_pages(page, 3);</code></p>
     <p><code>/*</code></p>
     <p><code>* наши страницы памяти теперь освобождены и нам больше нельзя</code></p>
     <p><code>* обращаться по адресу, который хранится в переменной 'page'</code></p>
     <p><code>*/</code></p>
     <p>Значение <code>GFP_KERNEL</code>, которое передается в качестве параметра, — это пример флага <code>gfp_mask</code>, который скоро будет рассмотрен детально.</p>
     <p>Обратите внимание на проверку ошибок после вызова функции <code>__get_free_pages()</code>. Выделение памяти в ядре <emphasis>может</emphasis> быть неудачным, поэтому код <emphasis>должен</emphasis> проверить и при необходимости обработать соответствующую ошибку. Это может означать, что придется пересмотреть все операции, которые были до этого сделаны. В связи с этим, часто имеет смысл выделять память в самом начале подпрограммы, чтобы упростить обработку ошибок. В противном случае после попытки выделения памяти отмена ранее выполненных действий может оказаться сложной.</p>
     <p>Низкоуровневые функции выделения памяти полезны, когда необходимы участки памяти, которые находятся в смежных физических страницах, особенно если необходима одна страница или очень большое количество страниц. Для более общего случая, когда необходимо выделить заданное количество байтов памяти, ядро предоставляет функцию <code>kmalloc()</code>.</p>
    </section>
    <section>
     <title>
      <p>Функция <code>kmalloc()</code></p>
     </title>
     <p>Функция <code>kmalloc()</code> работает аналогично функции <code>malloc()</code> пространства пользователя, за исключением того, что добавляется еще один параметр <code>flags</code>. Функция <code>kmalloc()</code> — это простой интерфейс для выделения в ядре участков памяти размером в заданное количество байтов. Если необходимы смежные страницы физической памяти, особенно когда их количество близко целой степени двойки, то ранее рассмотренные интерфейсы могут оказаться лучшим выбором. Однако для большинства операций выделения памяти в ядре функция <code>kmalloc()</code> — наиболее предпочтительный интерфейс.</p>
     <p>Рассматриваемая функция определена в файле <code>&lt;linux/slab.h&gt;</code> следующим образом.</p>
     <p><code>void * kmalloc(size_t size, int flags);</code></p>
     <p>Данная функция возвращает указатель на участок памяти, который имеет размер <emphasis>хотя бы</emphasis> <code>size</code> байт<a l:href="#n63" type="note">[63]</a>. Выделенный участок памяти содержит физически смежные страницы. В случае ошибки функция возвращает значение <code>NULL</code>. Выделение памяти в ядре заканчивается успешно, только если доступно достаточное количество памяти. Поэтому после вызова функции <code>kmalloc()</code> всегда необходимо проверять возвращаемое значение на равенство значению <code>NULL</code> и соответственным образом обрабатывать ошибку.</p>
     <p>Рассмотрим пример. Допустим, нам необходимо выделить достаточно памяти для того, чтобы в ней можно было разместить некоторую воображаемую структуру <code>dog</code>.</p>
     <p><code>struct dog *ptr;</code></p>
     <p><code>ptr = kmalloc(sizeof (struct dog), GFP_KERNEL);</code></p>
     <p><code>if (!ptr)</code></p>
     <p><code> /* здесь обработать ошибку ... */</code></p>
     <p>Если вызов функции <code>kmalloc()</code> завершится успешно, то переменная <code>ptr</code> будет указывать на область памяти, размер которой больше указанного значения или равен ему. Флаг <code>GFP_KERNEL</code> определяет тип поведения системы выделения памяти, когда она пытается выделить необходимую память при вызове функции <code>kmalloc()</code>.</p>
    </section>
    <section>
     <title>
      <p>Флаги <code>gfp_mask</code></p>
     </title>
     <p>Выше были показаны различные примеры использования флагов, которые модифицируют работу системы выделения памяти, как при вызове низкоуровневых функций, работающих на уровне страниц, так и при использовании функции <code>kmalloc()</code>. Теперь давайте рассмотрим их более детально.</p>
     <p>Флаги разбиты на три категории: модификаторы операций, модификаторы зон и флаги типов. Модификаторы операций указывают, <emphasis>каким образом</emphasis> ядро должно выделять указанную память. В некоторых ситуациях только некоторые методы могут использоваться для выделения памяти. Например, обработчики прерываний могут потребовать от ядра, что нельзя переходить в состояние ожидания при выделении памяти (поскольку обработчики прерывания не могут быть перепланированы), Модификаторы зоны указывают, <emphasis>откуда</emphasis> нужно выделять память. Как было рассказано, ядро делит физическую память на несколько зон, каждая из которых служит для различных целей. Модификаторы зоны указывают, из какой зоны выделять память. Флаги типов представляют собой различные комбинации модификаторов операций и зон, которые необходимы для определенного <emphasis>типа</emphasis> выделения памяти. Флаги типов содержат в себе различные модификаторы, вместо которых можно просто использовать один флаг типа. Флаг <code>GFP_KERNEL</code> — это флаг типа, который используется кодом, выполняющимся в ядре в контексте процесса. Рассмотрим все флаги отдельно.</p>
     <subtitle>Модификаторы операций</subtitle>
     <p>Все флаги, включая модификаторы операций, определены в заголовочном файле <code>&lt;linux/gfp.h&gt;</code>. Подключение файла <code>&lt;linux/slab.h&gt;</code> также подключает и этот заголовочный файл, поэтому его не часто приходится подключать явно. На практике обычно лучше использовать флаги типов, которые будут рассмотрены дальше. Тем не менее полезно иметь представление об индивидуальных флагах. В табл. 11.3 показан список модификаторов операций.</p>
     <empty-line/>
     <p><strong>Таблица 11.3</strong>. Модификаторы операций</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Флаг</th>
       <th align="left" valign="top">Описание</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>__GFP_WAIT</code></td>
       <td align="left" valign="top">Операция выделения памяти может переводить текущий процесс в состояние ожидания</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>__GFP_HIGH</code></td>
       <td align="left" valign="top">Операция выделения памяти может обращаться к аварийным запасам</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>__GFP_IO</code></td>
       <td align="left" valign="top">Операция выделения памяти может использовать дисковые операции ввода-вывода</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>__GFP_FS</code></td>
       <td align="left" valign="top">Операция выделения памяти может использовать операции ввода- вывода файловой системы</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>__GFP_COLD</code></td>
       <td align="left" valign="top">Операция выделения памяти должна использовать страницы памяти, содержимое которых не находится в кэше процессора (cache cold)</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>__GFP_NOWARN</code></td>
       <td align="left" valign="top">Операция выделения памяти не будет печатать сообщения об ошибках</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>__GFP_REPEAT</code></td>
       <td align="left" valign="top">Операция выделения памяти повторит попытку выделения в случае ошибки</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>__GFP_NOFAIL</code></td>
       <td align="left" valign="top">Операция выделения памяти будет повторять попытки выделения неопределенное количество раз</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>__GFP_NORETRY</code></td>
       <td align="left" valign="top">Операция выделения памяти никогда не будет повторять попытку выделения памяти</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>__GFP_NO_GROW</code></td>
       <td align="left" valign="top">Используется внутри слябового распределителя памяти (slab layer)</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>__GFP_COMP</code></td>
       <td align="left" valign="top">Добавить метаданные составной (compound) страницы памяти. Используется кодом поддержки больших страниц памяти (hugetlb)</td>
      </tr>
     </table>
     <p>Описанные модификаторы можно указывать вместе, как показано в следующем примере.</p>
     <p><code>ptr = kmalloc(size, __GFP_WAIT | __GFP_IO | __GFP_FS);</code></p>
     <p>Этот код дает инструкцию ядру (а именно функции <code>alloc_pages()</code>), что операция выделения памяти может быть блокирующей, выполнять операции ввода-вывода и операции файловой системы, если это необходимо. В данном случае ядру предоставляется большая свобода в отношении того, где оно будет искать необходимую память, чтобы удовлетворить запрос.</p>
     <p>Большинство запросов на выделение памяти указывают эти модификаторы, но это делается косвенным образом с помощью флагов типа, которые скоро будут рассмотрены. Не нужно волноваться, у вас не будет необходимости каждый раз разбираться, какие из этих ужасных флагов использовать при выделении памяти!</p>
     <subtitle>Модификаторы зоны</subtitle>
     <p>Модификаторы зоны указывают, из какой зоны должна выделяться память. Обычно выделение может происходить из любой зоны. Однако ядро предпочитает зону <code>ZONE_NORMAL</code>, чтобы гарантировать, что в других зонах, когда это необходимо, есть свободные страницы.</p>
     <p>Существует всего два модификатора зоны, поскольку, кроме зоны <code>ZONE_NORMAL</code> (из которой по умолчанию идет выделение памяти), существует всего две зоны. В табл. 11.4 приведен список модификаторов зоны.</p>
     <empty-line/>
     <p><strong>Таблица 11.4</strong>. Модификаторы зоны</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Флаг</th>
       <th align="left" valign="top">Описание</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>__GFP_DMA</code></td>
       <td align="left" valign="top">Выделять память только из зоны <code>ZONE_DMA</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>__GFP_HIGHMEM</code></td>
       <td align="left" valign="top">Выделять память только из зон <code>ZONE_HIGHMEM</code> и <code>ZONE_NORMAL</code></td>
      </tr>
     </table>
     <p>Указание одного из этих флагов изменяет зону, из которой ядро пытается выделить память. Флаг <code>__GFP_DMA</code> требует, чтобы ядро выделило память только из зоны <code>ZONE_DMA</code> Этот флаг эквивалентен следующему высказыванию в форме жесткого требования: "<emphasis>Мне абсолютно необходима память, в которой можно выполнять операции прямого доступа к памяти</emphasis>". Флаг <code>__GFP_HIGHMEM</code>, наоборот, требует, чтобы выделение памяти было из зон <code>ZONE_NORMAL</code> и <code>ZONE_HIGHMEM</code> (вторая более предпочтительна). Этот флаг эквивалентен запросу: "<emphasis>Я могу использовать верхнюю память, но мне на самом деле, все равно, и делайте, что хотите, обычная память тоже подойдет</emphasis>".</p>
     <p>Если не указан ни один из флагов, то ядро пытается выделять память из зон <code>ZONE_NORMAL</code> и <code>ZONE_DMA</code>, отдавая значительное предпочтение зоне <code>ZONE_NORMAL</code>.</p>
     <p>Флаг <code>__GFP_HIGHMEM</code> нельзя укалывать при вызове функций <code>__get_free_pages()</code> или <code>kmalloc()</code>. Это связано с тем, что они возвращают логический адрес, а не структуру page, и появляется возможность, что эти функции выделят память, которая в данный момент не отображается в виртуальное адресное пространство ядра и поэтому не имеет логического адреса. Только функция <code>alloc_pages()</code> может выделять страницы в верхней памяти. Однако в большинстве случаев в запросах на выделение памяти не нужно указывать модификаторы зоны, так как достаточно того, что используется зона <code>ZONE_NORMAL</code>.</p>
     <subtitle>Флаги типов</subtitle>
     <p>Флаги типов указывают модификаторы операций и зон, которые необходимы для выполнения запросов определенных типов. В связи с этим, в коде ядра стараются использовать правильный флаг типа и не использовать больших наборов модификаторов. Это одновременно и проще и при этом меньше шансов ошибиться. В табл. 11.5 приведен список возможных флагов типов, а в табл. 11.6 показано, какие модификаторы соответствуют какому флагу.</p>
     <empty-line/>
     <p><strong>Таблица 11.5</strong>. Флаги типов</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Флаг</th>
       <th align="left" valign="top">Описание</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>GFP_ATOMIC</code></td>
       <td align="left" valign="top">Запрос на выделение памяти высокоприоритетный и в состояние ожидания переходить нельзя. Этот флаг предназначен для использования в обработчика: прерываний, нижних половин и в других ситуациях, когда нельзя переходить в состояние ожидания</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>GFP_NOIO</code></td>
       <td align="left" valign="top">Запрос на выделение памяти может блокироваться, но при его выполнении нельзя выполнять операции дискового ввода-вывода. Этот флаг предназначен для использования в коде блочного ввода-вывода, когда нельзя инициировать новые операции ввода-вывода</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>GFP_NOFS</code></td>
       <td align="left" valign="top">Запрос на выделение памяти может блокироваться и выполнять дисковые операции ввода-вывода, но запрещено выполнять операции, связанные с файловыми системами. Этот флаг предназначен для использования в коде файловых систем, когда нельзя начинать выполнение новых файловых операций</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>GFP_KERNEL</code></td>
       <td align="left" valign="top">Обычный запрос на выделение памяти, который может блокироваться. Этот флаг предназначен для использования в коде, который выполняется в контексте процесса, когда безопасно переходить в состояние ожидания</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>GFP_USER</code></td>
       <td align="left" valign="top">Обычный запрос на выделение памяти, который может блокироваться. Этот флаг используется для выделения памяти процессам пространства пользователя</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>GFP_HIGHUSER</code></td>
       <td align="left" valign="top">Запрос на выделение памяти из зоны <code>ZONE_HIGHMEM</code>, который может блокироваться. Этот флаг используется для выделения памяти процессам пространства пользователя</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>GFP_DMA</code></td>
       <td align="left" valign="top">Запрос на выделение памяти из зоны <code>ZONE_DMA</code>. Драйверам устройств, которым нужна память для выполнения операций по ПДП, необходимо использовать этот флаг обычно в комбинации с одним из описанных выше флагов</td>
      </tr>
     </table>
     <empty-line/>
     <p><strong>Таблица 11.6</strong>. Список модификаторов, соответствующих каждому флагу типа</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Флаг</th>
       <th align="left" valign="top">Модификаторы</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>GFP_ATOMIC</code></td>
       <td align="left" valign="top"><code>__GFP_HIGH</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>GFP_NOIO</code></td>
       <td align="left" valign="top"><code>__GFP_WAIT</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>GFP_NOFS</code></td>
       <td align="left" valign="top"><code>(__GFP_WAIT | __GFP_IO)</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>GFP_KERNEL</code></td>
       <td align="left" valign="top"><code>(__GFP_WAIT | __GFP_IO | __GFP_FS)</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>GFP_USER</code></td>
       <td align="left" valign="top"><code>(__GFP_WAIT | __GFP_IO | __GFP_FS)</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>GFP_HIGHUSER</code></td>
       <td align="left" valign="top"><code>(__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HIGHMEM)</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>GFP_DMA</code></td>
       <td align="left" valign="top"><code>__GFP_DMA</code></td>
      </tr>
     </table>
     <p>Рассмотрим наиболее часто используемые флаги и для чего они могут быть нужны. Подавляющее большинство операций выделения памяти в ядре используют флаг <code>GFP_KERNEL</code>. В результате операция выделения памяти имеет обычный приоритет и может переводить процесс в состояние ожидания. Поскольку этот вызов может блокироваться, его можно использовать только в контексте процесса, выполнение которого может быть безопасно перепланировано (т.е. нет удерживаемых блокировок и т.д.). При использовании этого флага нет никаких оговорок по поводу того, каким образом ядро может получить необходимую память, поэтому операция выделения памяти имеет большой шанс выполниться успешно.</p>
     <p>Можно сказать, что свойства флага <code>GFP_ATOMIC</code> лежат на противоположном конце спектра. Так как этот флаг указывает, что операция выделения памяти не может переходить в состояние ожидания, то такая операция очень ограничена в том, какую память можно использовать для выделения. Если нет доступного участка памяти заданного размера, то ядро, скорее всего, не будет пытаться освободить память, поскольку вызывающий код не может переходить в состояние ожидания. При использовании флага <code>GFP_KERNEL</code>, наоборот, ядро может перевести вызывающий код в состояние ожидания, чтобы во время ожидания вытеснить страницы на диск (swap out), очистить измененные страницы памяти путем записи их в дисковый файл (flush dirty pages) и т.д. Поскольку при использовании флага <code>GFP_ATOMIC</code> нет возможности выполнить ни одну из этих операций, то и шансов успешно выполнить выделение памяти тоже меньше (по крайней мере, когда в системе недостаточно памяти). Тем не менее использование флага <code>GFP_ATOMIC</code> — это единственная возможность, когда вызывающий код не может переходить в состояние ожидания, как в случае обработчиков прерываний и нижних половин.</p>
     <p>По своим свойствам между рассмотренными флагами находятся флаги <code>GFP_NOIC</code> и <code>GFP_NOFS</code>. Операции выделения памяти, которые запущены с этими флагами, могут блокироваться, но они воздерживаются от выполнения некоторых действий. Выделение памяти с флагом <code>GFP_NOIO</code> не будет запускать никаких операций дискового ввода-вывода. С другой стороны, при использовании флага <code>GFP_NOFS</code> могут запускаться операции дискового ввода-вывода, но не могут запускаться операции файловых систем. Когда эти флаги могут быть полезны? Они соответственно необходимы для определенного низкоуровневого кода блочного ввода-вывода или кода файловых систем. Представьте себе, что в некотором часто используемом участке кода файловых систем используется выделение памяти <emphasis>без указания</emphasis> флага <code>GFP_NOFS</code>. Если выделение памяти требует выполнения операций файловой системы, то выделение памяти приведет к еще <emphasis>большему</emphasis> количеству операций файловой системы, которые потребуют дополнительного выделения памяти и еще большего количества файловых операций! При разработке кода, который использует выделение памяти, необходимо гарантировать, что операции выделения памяти не будут использовать этот код, как в рассмотренном случае, иначе может возникнуть самоблокировка. Не удивительно, что и ядре рассматриваемые флаги используются только в небольшом количестве мест.</p>
     <p>Флаг <code>GFP_DMA</code> применяется для указания, что система выделения памяти должна при выполнении запроса предоставить память из зоны <code>ZONE_DMA</code>. Этот флаг используется драйверами устройств, для которых необходимо выполнение операций прямого доступа к памяти. Обычно этот флаг должен комбинироваться с флагами <code>GFP_ATOMIC</code> или <code>GFP_KERNEL</code>.</p>
     <p>В подавляющем большинстве случаев при разработке кода вам будет необходимо использовать флаги <code>GFP_ATOMIC</code> или <code>GFP_KERNEL</code>. В табл. 11.7 показано какие флаги и в каких ситуациях необходимо использовать. Независимо от типа операции выделения памяти, необходимо проверять результат и обрабатывать ошибки.</p>
     <empty-line/>
     <p><strong>Таблица 11.7</strong>. Какой флаг и когда необходимо использовать</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Ситуация</th>
       <th align="left" valign="top">Решение</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top">Контекст процесса, можно переходить в состояние ожидания</td>
       <td align="left" valign="top">Используется флаг <code>GFP_KERNEL</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">Контекст процесса, нельзя переходить в состояние ожидания</td>
       <td align="left" valign="top">Используется флаг <code>GFP_ATOMIC</code> или память выделяется с использованием флага <code>GFP_KERNEL</code> но в более ранний или поздний момент, когда можно переходить в состояние ожидания</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">Обработчик прерывания</td>
       <td align="left" valign="top">Используется флаг <code>GFP_ATOMIC</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">Обработка нижней половины</td>
       <td align="left" valign="top">Используется флаг <code>GFP_ATOMIC</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">Необходима память для выполнения операций ПДП, можно переходить в состояние ожидания</td>
       <td align="left" valign="top">Используются флаги <code>(GFP_DMA | GFP_KERNEL)</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">Необходима память для выполнения операций ПДП, нельзя переходить в состояние ожидания</td>
       <td align="left" valign="top">Используются флаги <code>(GFP_DMA | GFP_ATOMIC)</code> или выделение выполняется в более поздний или более ранний момент времени</td>
      </tr>
     </table>
    </section>
    <section>
     <title>
      <p>Функция <code>kfree()</code></p>
     </title>
     <p>Обратной к функции <code>kmalloc()</code> является функция <code>kfree()</code>, которая определена в файле <code>&lt;linux/slab.h&gt;</code> следующим образом.</p>
     <p><code>void kfree(const void *ptr);</code></p>
     <p>Функция <code>kfree()</code> позволяет освободить память, ранее выделенную с помощью функции <code>kmalloc()</code>. Вызов этой функции для памяти, которая ранее не была выделена с помощью функции <code>kmalloc()</code> или уже была освобождена, приводит к очень плохим последствиям, таким как освобождение памяти, которая принадлежит другим частям ядра. Так же как и в пространстве пользователя, количество операций выделения памяти должно быть равно количеству операций освобождения, чтобы предотвратить утечку памяти и другие проблемы. Следует обратить внимание, что случай вызова <code>kfree(NULL)</code> специально проверяется и поэтому является безопасным.</p>
     <p>Рассмотрим пример выделения памяти в обработчике прерывания. В этом примере обработчику прерывания необходимо выделить буфер памяти для хранения входных данных. С помощью препроцессора определяется константа. <code>BUF_SIZE</code>, как размер буфера памяти в байтах, который, скорее всего, должен быть больше, чем несколько байт.</p>
     <p><code>char *buf;</code></p>
     <p><code>buf = kmalloc(BUF_SIZE, GFP_ATOMIC);</code></p>
     <p><code>if (!buf)</code></p>
     <p><code> /* ошибка выделения памяти! */</code></p>
     <p>Позже, когда память больше не нужна, нужно не забыть освободить ее с помощью вызова функции</p>
     <p><code>kfree(buf);</code></p>
    </section>
    <section>
     <title>
      <p>Функция <code>vmalloc()</code></p>
     </title>
     <p>Функция <code>vmalloc()</code> работает аналогично функции <code>kmalloc()</code>, за исключением того, что она выделяет страницы памяти, которые только виртуально смежные и необязательно смежные физически. Точно так же работают и функции выделения памяти в пространстве пользователя: страницы памяти, которые выделяются с помощью функции <code>malloc()</code>, являются смежными в виртуальном адресном пространстве процесса, но нет никакой гарантии, что они смежные в физической оперативной памяти. Функция <code>kmalloc()</code> отличается тем, что гарантирует, что страницы памяти будут физически (и виртуально) смежными. Функция <code>vmalloc()</code> гарантирует только, что страницы будут смежными в виртуальном адресном пространстве ядра. Это реализуется путем выделения потенциально несмежных участков физической памяти и "исправления" таблиц страниц, чтобы отобразить эту физическую память в непрерывный участок логического адресного пространства.</p>
     <p>Большей частью, только аппаратным устройствам необходимо выделение физически непрерывных участков памяти. Аппаратные устройства существуют по другую сторону модуля управления памятью и не "понимают" виртуальной адресации. Следовательно, все области памяти, с которыми работают аппаратные устройства, должны состоять из физически смежных блоков, а не из виртуально непрерывных участков. Для участков памяти, которые используются только программным обеспечением, например буферы памяти, связанные с процессами, прекрасно подходят области памяти, которые только виртуально непрерывны. При программировании заметить разницу невозможно. Это связано с тем, что память ядром воспринимается как логически непрерывная.</p>
     <p>Несмотря на то что физически смежные страницы памяти необходимы только в определенных случаях, большая часть кода ядра использует для выделения памяти функцию <code>kmalloc()</code>, а не <code>vmalloc()</code>. Это, в основном, делается из соображений производительности. Для того чтобы физически несмежные страницы памяти сделать смежными в виртуальном адресном пространстве, функция <code>vmalloc()</code> должна соответствующим образом заполнить таблицы страниц. Хуже того, страницы памяти, которые получаются с помощью функции <code>vmalloc()</code>, должны отображаться посредством страниц памяти, которые принадлежат к таблицам страниц (потому что выделяемые страницы памяти физически несмежные). Это приводит к значительно менее эффективному использованию буфера TLB<a l:href="#n64" type="note">[64]</a>, чем в случае, когда страницы памяти отображаются напрямую. Исходя из этих соображений функция <code>vmalloc()</code> используется только тогда, когда она абсолютно необходима, обычно это делается для выделения очень больших областей памяти. Например, при динамической загрузке модулей ядра, модули загружаются в память, которая выделяется с помощью функции <code>vmalloc()</code>.</p>
     <p>Функция <code>vmalloc()</code> объявлена в файле <code>&lt;linux/vmalloc.h&gt;</code> и определена в файле <code>mm/vmalloc.с</code>. Использование этой функции аналогично функции <code>malloc()</code> пространства пользователя.</p>
     <p><code>void *vmalloc(unsigned long size);</code></p>
     <p>Функция возвращает указатель на виртуально непрерывную область памяти размером по крайней мере <code>size</code> байт. В случае ошибки эта функция возвращает значение <code>NULL</code>. Данная функция может переводить процесс в состояние ожидания и соответственно не может вызываться в контексте прерывания или в других ситуациях, когда блокирование недопустимо.</p>
     <p>Для освобождения памяти, выделенной с помощью функции <code>vmalloc()</code>, необходимо использовать функцию</p>
     <p><code>void vfree(void *addr);</code></p>
     <p>Эта функция освобождает участок памяти, который начинается с адреса <code>addr</code> и был ранее выделен с помощью функции <code>vmalloc()</code>. Данная функция также может переводить процесс в состояние ожидания и поэтому не может вызываться из контекста прерывания. Функция не возвращает никаких значений.</p>
     <p>Использовать рассмотренные функции очень просто. Например, следующим образом.</p>
     <p><code>char *buf;</code></p>
     <empty-line/>
     <p><code>buf = vmalloc(16 * PAGE_SIZE); /* получить 16 страниц памяти */</code></p>
     <p><code>if (!buf)</code></p>
     <p><code> /* ошибка! Не удалось выделить память */</code></p>
     <p><code>/*</code></p>
     <p><code>* переменная buf теперь указывает на область памяти</code></p>
     <p><code>* размером, по крайней мере, 16*PAGE_SIZE байт, которая состоит</code></p>
     <p><code>* из виртуально смежных блоков памяти</code></p>
     <p><code>*/</code></p>
     <p>После того как память больше не нужна, необходимо убедиться, что она освобождается с помощью следующего вызова.</p>
     <p><code>vfree(buf);</code></p>
    </section>
   </section>
   <section>
    <title>
     <p>Уровень слябового распределителя памяти</p>
    </title>
    <section>
     <p>Выделение и освобождение структур данных — это одна из наиболее частых операций, которые выполняются в любом ядре. Для того чтобы облегчить процедуру частого выделения и освобождения данных при программировании, вводятся <emphasis>списки свободных ресурсов</emphasis> (<emphasis>free list</emphasis>). Список свободных ресурсов содержит некоторый набор уже выделенных структур данных. Когда коду необходим новый экземпляр структуры данных, он может взять одну структуру из списка свободных ресурсов, вместо того чтобы выделять необходимый объем памяти и заполнять его данными соответствующей структуры. Позже, когда структура данных больше не нужна, она снова возвращается в список свободных ресурсов, вместо того чтобы освобождать память. В этом смысле список свободных ресурсов представляет собой кэш объектов, в котором хранятся объекты одного определенного, часто используемого типа.</p>
     <p>Одна из наибольших проблем, связанных со списком свободных ресурсов в ядре, это то, что над ними нет никакого централизованного контроля. Когда ощущается недостаток свободной памяти, нет никакой возможности взаимодействовать между ядром и всеми списками свободных ресурсов, которые в такой ситуации должны уменьшить размер своего кэша, чтобы освободить память. В ядре нет никакой информации о случайно созданных списках свободных ресурсов. Для исправления положения и для универсальности кода ядро предоставляет уровень слябового распределения памяти (slab layer), который также называется просто слябовым распределителем памяти (slab allocator). Уровень слябового распределения памяти выполняет функции общего уровня кэширования структур данных.</p>
     <p>Концепции слябового распределения памяти впервые были реализованы в операционной системе SunOS 5.4 фирмы Sun Microsystems<a l:href="#n65" type="note">[65]</a>. Для уровня кэширования структур данных в операционной системе Linux используется такое же название и похожие особенности реализации.</p>
     <p>Уровень слябового распределения памяти служит для достижения следующих целей.</p>
     <p>• Часто используемые структуры данных, скорее всего, будут часто выделяться и освобождаться, поэтому их следует кэшировать.</p>
     <p>• Частые операции выделения и освобождения памяти могут привести к фрагментации памяти (к невозможности найти большие участки физически однородной памяти). Для предотвращения этого, кэшированные списки свободных ресурсов организованы непрерывным образом в физической памяти. Так как структуры данных возвращаются снова в список свободных ресурсов, то в результате никакой фрагментации не возникает.</p>
     <p>• Список свободных ресурсов обеспечивает улучшенную производительность при частых выделениях и освобождениях объектов, так как освобожденные объекты сразу же готовы для нового выделения.</p>
     <p>• Если распределитель памяти может использовать дополнительную информацию, такую как размер объекта, размер страницы памяти и общий размер кэша, то появляется возможность принимать в критических ситуациях более интеллектуальные решения.</p>
     <p>• Если кэш организован, как связанный с определенным процессором (т.е. для каждого процессора в системе используется свой уникальный отдельный кэш), то выделение и освобождение структур данных может выполняться без использования SMP-блокировок.</p>
     <p>• Если распределитель памяти рассчитан на доступ к неоднородной памяти (Non-Uniform Memory Access NUMA), то появляется возможность выделения памяти с того же узла (node), на котором эта память запрашивается.</p>
     <p>• Хранимые объекты могут быть "<emphasis>окрашены</emphasis>", чтобы предотвратить отображение разных объектов на одни и те же строки системного кэша.</p>
     <p>Уровень слябового распределения памяти в ОС Linux был реализован с учетом указанных принципов.</p>
    </section>
    <section>
     <title>
      <p>Устройство слябового распределителя памяти</p>
     </title>
     <p>Уровень слябового распределения памяти делит объекты на группы, которые называются <emphasis>кэшами</emphasis> (<emphasis>cache</emphasis>). Разные кэши используются для хранения объектов различных типов. Для каждого типа объектов существует свой уникальный кэш. Например, один кэш используется для дескрипторов процессов (список свободных структур <code>struct task_struct</code>), а другой — для индексов файловых систем (<code>struct inode</code>). Интересно, что интерфейс <code>kmalloc()</code> построен на базе уровня слябового распределения памяти и использует семейство кэшей общего назначения.</p>
     <p>Далее кэши делятся на <emphasis>слябы</emphasis> (буквально slab — однородная плитка, отсюда и название всей подсистемы). Слябы занимают одну или несколько физически смежных страниц памяти. Обычно сляб занимает только одну страницу памяти. Каждый кэш может содержать несколько слябов.</p>
     <p>Каждый сляб содержит некоторое количество <emphasis>объектов</emphasis>, которые представляют собой кэшируемые структуры данных. Каждый сляб может быть в одном из трех состояний: полный (full), частично заполненный (partial) и пустой (empty). Полный сляб не содержит свободных объектов (все объекты сляба выделены для использования). Частично заполненный сляб содержит часть выделенных и часть свободных объектов. Когда некоторая часть ядра запрашивает новый объект, то этот запрос удовлетворяется из частично заполненного сляба, если такой существует. В противном случае запрос выполняется из пустого сляба. Если пустого сляба не существует, то он создается. Очевидно, что полный сляб никогда не может удовлетворить запрос, поскольку в нем нет свободных объектов. Такая политика уменьшает фрагментацию памяти.</p>
     <p>В качестве примера рассмотрим структуры <code>inode</code>, которые являются представлением в оперативной памяти индексов дисковых файлов (см. главу 12). Эти структуры часто создаются и удаляются, поэтому есть смысл управлять ими с помощью слябового распределителя памяти. Структуры <code>struct inode</code> выделяются из кэша <code>inode_cachep</code> (такое соглашение по присваиванию названий является стандартом). Этот кэш состоит из одного или более слябов, скорее всего слябов много, поскольку много объектов. Каждый сляб содержит максимально возможное количество объектов типа <code>struct inode</code>. Когда ядро выделяет новую структуру типа <code>struct inode</code>, возвращается указатель на уже выделенную, но не используемую структуру из частично заполненного сляба или, если такого нет, из пустого сляба. Когда ядру больше не нужен объект типа <code>inode</code>, то слябовый распределитель памяти помечает этот объект как свободный. На рис. 11.1 показана диаграмма взаимоотношений между кэшами, слябами и объектами.</p>
     <image l:href="#img_16.jpeg"/>
     <p><strong>Рис. 11.1</strong>. Взаимоотношения между кэшами, слябами и объектами</p>
     <p>Каждый кэш представляется структурой <code>kmem_cache_s</code>. Эта структура содержит три списка <code>slab_full</code>, <code>slab_partial</code> и <code>slab_empty</code>, которые хранятся в структуре <code>kmem_list3</code>. Эти списки содержат все слябы, связанные с данным кэшем. Каждый сляб представлен следующей структурой <code>struct slab</code>, которая является дескриптором сляба.</p>
     <p><code>struct slab {</code></p>
     <p><code> struct list head list;   /* список полных, частично заполненных</code></p>
     <p><code>                             или пустых слябов */</code></p>
     <p><code> unsigned long colouroff; /* смещение для окрашивания слябов */</code></p>
     <p><code> void *s_mem;             /* первый объект сляба */</code></p>
     <p><code> unsigned int inuse;      /* количество выделенных объектов */</code></p>
     <p><code> kmem_bufctl_tfree;       /* первый свободный объект, если есть */</code></p>
     <p><code>};</code></p>
     <p>Дескриптор сляба выделяется или за пределами сляба, в кэше общего назначения, или в начале самого сляба. Дескриптор хранится внутри сляба, либо если общий размер сляба достаточно мал, либо если внутри самого сляба остается достаточно места, чтобы разместить дескриптор.</p>
     <p>Слябовый распределитель создает новые слябы, вызывая интерфейс ядра нижнего уровня для выделения памяти <code>__get_free_pages()</code> следующим образом.</p>
     <p><code>static void *kmem_getpages(kmem_cache_t *cachep,</code></p>
     <p><code> int flags, int nodeid) {</code></p>
     <p><code> struct page *page;</code></p>
     <p><code> void *addr;</code></p>
     <p><code> int i;</code></p>
     <empty-line/>
     <p><code> flags |= cachep-&gt;gfpflags;</code></p>
     <p><code> if (likely(nodeid == -1)) {</code></p>
     <p><code>  addr = (void*)__get_free_pages(flags, cachep-&gt;gfporder);</code></p>
     <p><code>  if (!addr)</code></p>
     <p><code>   return NULL;</code></p>
     <p><code>  page = virt_to_page(addr);</code></p>
     <p><code> } else {</code></p>
     <p><code>  page = alloc_pages_node(nodeid, flags, cachep-&gt;gfporder);</code></p>
     <p><code>  if (!page)</code></p>
     <p><code>   return NULL;</code></p>
     <p><code>  addr = page_address(page);</code></p>
     <p><code> }</code></p>
     <empty-line/>
     <p><code> i = (1 &lt;&lt; cachep-&gt;gfporder);</code></p>
     <p><code> if (cachep-&gt;flags &amp; SLAB_RECLAIM_ACCOUNT)</code></p>
     <p><code>  atomic_add(i, &amp;slab_reclaim_pages);</code></p>
     <p><code> add_page_state(nr_slab, i);</code></p>
     <p><code> while (i-- ) {</code></p>
     <p><code>  SetPageSlab(page);</code></p>
     <p><code>  page++;</code></p>
     <p><code> }</code></p>
     <p><code> return addr;</code></p>
     <p><code>}</code></p>
     <p>Первый параметр этой функции указывает на определенный кэш, для которого нужны новые страницы памяти. Второй параметр содержит флаги, которые предаются в функцию <code>__get_free_pages()</code>. Следует обратить внимание на то, как значения этих флагов объединяются с другими значениями с помощью логической операции <code>ИЛИ</code>. Данная операция дополняет флаги значением флагов кэша, которые используются по умолчанию и которые обязательно должны присутствовать n значении параметра <code>flags</code>. Количество страниц памяти — целая степень двойки — хранится в поле <code>cachep-&gt;gfporder</code>. Рассмотренная функция выглядит более сложной, чем это может показаться сначала, поскольку она также рассчитана на NUMA-системы (Non-Uniform Memory Access, системы с неоднородным доступом к памяти). Если параметр <code>nodeid</code> на равен <code>-1</code>, то предпринимается попытка выделить память с того же узла памяти, на котором выполняется запрос. Такое решение позволяет получить более высокую производительность для NUMA-систем. Для таких систем обращение к памяти других узлов приводит к снижению производительности.</p>
     <p>Для образовательных целей можно убрать код, рассчитанный на NUMA-системы, и получить более простой вариант функции <code>kmem_getpages()</code> в следующем виде.</p>
     <p><code>static inline void* kmem_getpages(kmem_cache_t *cachep,</code></p>
     <p><code> unsigned long flags) {</code></p>
     <p><code> void *addr;</code></p>
     <p><code> flags |= cachep-&gt;gfpflags;</code></p>
     <p><code> addr = (void*)__get_free_pages(flags, cachep-&gt;gfporder);</code></p>
     <p><code> return addr;</code></p>
     <p><code>}</code></p>
     <p>Память освобождается с помощью функции <code>kmem_freepages()</code>, которая вызывает функцию <code>free_pages()</code> для освобождения необходимых страниц кэша. Конечно, назначение уровня слябового распределения — это воздержаться от выделения и освобождения страниц памяти. На самом деле слябовый распределитель использует функции выделения памяти только тогда, когда в данном кэше не доступен ни один частично заполненный или пустой сляб. Функция освобождения памяти вызывается только тогда, когда становится мало доступной памяти и система пытается освободить память или когда кэш полностью ликвидируется.</p>
     <p>Уровень слябового распределения управляется с помощью простого интерфейса, и это можно делать отдельно для каждого кэша. Интерфейс позволяет создавать или ликвидировать новые кэши, а также выделять или уничтожать объекты в этих кэшах. Все механизмы оптимизации управления кэшами и слябами полностью управляются внутренними элементами уровня слябового распределения памяти. После того как кэш создан, слябовый распределитель памяти работает, как специализированная система создания объектов определенного типа.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Интерфейс слябового распределителя памяти</p>
    </title>
    <section>
     <p>Новый кэш можно создать с помощью вызова следующей функции.</p>
     <p><code>kmem_cache_t * kmem_cache_create(const char *name, size_t size,</code></p>
     <p><code> size_t offset, unsigned long flags,</code></p>
     <p><code>void (*ctor)(void*, kmem_cache_t*, unsigned long),</code></p>
     <p><code>void (*dtor)(void*, kmem_cache_t*, unsigned long));</code></p>
     <p>Первый параметр — это строка, которая содержит имя кэша. Второй параметр — это размер каждого элемента кэша. Третий параметр — это смещение первого объекта в слябе. Он нужен для того, чтобы обеспечить необходимое выравнивание по границам страниц памяти. Обычно достаточно указать значение, равное нулю, которое соответствует выравниванию по умолчанию. Параметр <code>flags</code> указывает опциональные параметры, которые управляют поведением кэша. Он может быть равен нулю, что выключает все специальные особенности поведения, или состоять из одного или более значений, показанных ниже и объединенных с помощью логической операции ИЛИ.</p>
     <p>• <code>SLAB_NO_REAP</code> — этот флаг указывает, что кэш не должен автоматически "убирать мусор" (т.е. освобождать память, в которой хранятся неиспользуемые объекты) при нехватке памяти в системе. Обычно этот флаг <emphasis>не нужно</emphasis> устанавливать, поскольку если этот флаг установлен, то созданный кэш может помешать нормальной работе системы при нехватке памяти.</p>
     <p>• <code>SLAB_HWCACHE_ALIGN</code> — этот флаг указывает уровню слябового распределения памяти, что расположение каждого объекта в слябе должно выравниваться по строкам процессорного кэша. Это предотвращает так называемое "ошибочное распределение", когда два или более объектов отображаются в одну и ту же строку системного кэша, несмотря на то что они находятся по разным адресам памяти. При использовании этого флага возрастает производительность, но это делается ценой увеличения занимаемой памяти, потому что строгое выравнивание приводит к тому, что часть памяти сляба не используется. Степень увеличения занимаемой памяти зависит от размера объектов кэша и от того, каким образом происходит их выравнивание по отношению к строкам системного кэша. Для кэшей, которые часто используются в коде, критичном к производительности, будет правильным установить этот флаг, в остальных случаях следует подумать, стоит ли это делать.</p>
     <p>• <code>SLAB_MUST_HWCACHE_ALIGN</code>. Если включен режим отладки, то может оказаться невозможным одновременная отладка и выравнивание положения объектов по строкам системного кэша. Этот флаг указывает уровню слябового распределения памяти, что необходимо форсировать выравнивание положения объектов по строкам системного кэша. В обычной ситуации этот флаг указывать необязательно, и достаточно использовать предыдущий. Установка этого флага в режиме отладки слябового распределителя памяти (по умолчанию отладка запрещена) может привести к значительному увеличению затрат памяти. Только для объектов, которые критичны к выравниванию по строкам системного кэша, таких как дескрипторы процессов, необходимо указывать данный флаг.</p>
     <p>• <code>SLAB_POISON</code> — этот флаг указывает на необходимость заполнения слябов известным числовым значением (a5a5a5a5). Эта операция называется "отравлением" (<emphasis>poisoning</emphasis>) и служит для отслеживания доступа к неинициализированной памяти.</p>
     <p>• <code>SLAB_RED_ZONE</code> — этот флаг указывает на необходимость выделения так называемых "красных зон" (<emphasis>red zone</emphasis>) для облегчения детектирования переполнений буфера.</p>
     <p>• <code>SLAB_PANIC</code> — этот флаг указывает на необходимость перевода ядра в состояние паники, если выделение памяти было неудачным. Данный флаг полезен, если выделение памяти всегда должно завершаться успешно, как, например, в случае создания кэша структур VMA (областей виртуальной памяти, см. главу 14, "Адресное пространство процесса") при загрузке системы.</p>
     <p>• <code>SLAB_CACHE_DMA</code> — этот флаг указывает уровню слябового распределения, что все слябы должны выделяться в памяти, с которой возможны операции прямого доступа к памяти. Это необходимо, когда объекты используются в операциях ПДП и должны находиться в зоне <code>ZONE_DMA</code>. В противном случае эта возможность не нужна и этот флаг не нужно устанавливать.</p>
     <p>Два последних параметра <code>ctor</code> и <code>dtor</code> — это конструктор и деструктор кэша соответственно. Конструктор вызывается, когда в кэш добавляются новые страницы памяти. Деструктор вызывается, когда из кэша удаляются страницы памяти. Если указан деструктор, то должен быть указан и конструктор. На практике кэши ядра ОС Linux обычно не используют функции конструктора и деструктора. В качестве этих параметров можно указывать значение <code>NULL</code>.</p>
     <p>В случае успешного выполнения функция <code>kmem_cache_create()</code> возвращает указатель на созданный кэш. В противном случае возвращается <code>NULL</code>. Данная функция не может вызываться в контексте прерывания, так как она может переводить процесс в состояние ожидания. Для ликвидации кэша необходимо вызвать следующую функцию.</p>
     <p><code>int kmem_cache_destroy(kmem_cache_t *cachep);</code></p>
     <p>Эта функция ликвидирует указанный кэш. Она обычно вызывается при выгрузке модуля, который создает свой кэш. Из контекста прерывания эту функцию вызывать нельзя, так как она может переводить вызывающий процесс в состояние ожидания. Перед вызовом этой функции необходимо, чтобы были выполнены следующие два условия.</p>
     <p>• Все слябы кэша являются пустыми. Действительно, если в каком-либо слябе существует объект, который все еще используется, то как можно ликвидировать кэш?</p>
     <p>• Никто не будет обращаться к кэшу во время и особенно после вызова функции <code>kmem_cache_destroy()</code>. Эту синхронизацию должен обеспечить вызывающий код.</p>
     <p>В случае успешного выполнения функция возвращает нуль, в других случаях возвращается ненулевое значение.</p>
     <p>После того как кэш создан, из него можно получить объект путем вызова следующей функции.</p>
     <p><code>void* kmem_cache_alloc(kmem_cache_t *cachep, int flags);</code></p>
     <p>Эта функция возвращает указатель на объект из кэша, на который указывает параметр <code>cachep</code>. Если ни в одном из слябов нет свободных объектов, то уровень слябового распределения должен получить новые страницы памяти с помощью функции <code>kmem_getpages()</code>, значение параметра <code>flags</code> передается в функцию <code>__get_free_pages()</code>. Это те самые флаги, которые были рассмотрены ранее. Скорее всего, необходимо указывать <code>GFP_KERNEL</code> или <code>GFP_ATOMIC</code>.</p>
     <p>Далее для удаления объекта и возвращения его в сляб, из которого он был выделен, необходимо использовать следующую функцию.</p>
     <p><code>void kmem_cache_free(kmem_cache_t *cachep, void *objp);</code></p>
     <p>Данная функция помечает объект, на который указывает параметр <code>objp</code>, как свободный.</p>
    </section>
    <section>
     <title>
      <p>Пример использования слябового распределителя памяти</p>
     </title>
     <p>Давайте рассмотрим пример из реальной жизни, связанный с работой со структурами <code>task_struct</code> (дескрипторы процессов). Показанный ниже код в несколько более сложной форме приведен в файле <code>kernel/fork.c.</code></p>
     <p>В ядре определена глобальная переменная, в которой хранится указатель на кэш объектов <code>task_struct</code>:</p>
     <p><code>kmem_cache_t *task_struct_cachep;</code></p>
     <p>Во время инициализации ядра, в функции <code>fork_init()</code>, этот кэш создается следующим образом.</p>
     <p><code>task_struct_cachep = kmem_cache_create("task_struct",</code></p>
     <p><code> sizeof(struct task_struct), ARCH_MIN_TASKALIGN,</code></p>
     <p><code> SLAB_PANIC, NULL, NULL);</code></p>
     <p>Данный вызов создает кэш с именем <code>"task_struct"</code>, который предназначен для хранения объектов тина <code>struct task_struct</code>. Объекты создаются с начальным смещением в слябе, равным <code>ARCH_MIN_TASKALIGN</code> байт, и положение всех объектов выравнивается по границам строк системного кэша, значение этого выравнивания зависит от аппаратной платформы. Обычно значение выравнивания задается для каждой аппаратной платформы с помощью определения препроцессора <code>L1_CACHE_BYTES</code>, которое равно размеру процессорного кэша первого уровня в байтах. Конструктор и деструктор отсутствуют. Следует обратить внимание, что возвращаемое значение не проверяется на равенство <code>NULL</code>, поскольку указан флаг <code>SLAB_PANIC</code>. В случае, когда при выделении памяти произошла ошибка, слябовый распределитель памяти вызовет функцию <code>panic()</code>. Если этот флаг не указан, то нужно проверять возвращаемое значение на равенство <code>NULL</code>, что сигнализирует об ошибке. Флаг <code>SLAB_PANIC</code> здесь используется потому, что этот каш является необходимым для работы системы (без дескрипторов процессов работать как-то не хорошо).</p>
     <p>Каждый раз, когда процесс вызывает функцию <code>fork()</code>, должен создаваться новый дескриптор процесса (вспомните главу 3, "Управление процессами"). Это выполняется следующим образом в функции <code>dup_task_struct()</code>, которая вызывается из функции <code>do_fork()</code>.</p>
     <p><code>struct task_struct *tsk;</code></p>
     <empty-line/>
     <p><code>tsk = kmem_cache_alloc(task struct_cachep, GFP_KERNEL);</code></p>
     <p><code>if (!tsk)</code></p>
     <p><code> return NULL;</code></p>
     <p>Когда процесс завершается, если нет порожденных процессов, которые ожидают на завершение родительского процесса, то дескриптор освобождается и возвращается обратно в кэш <code>task_struct_cachep</code>. Эти действия выполняются в функции <code>free_task_struct()</code>, как показано ниже (где параметр <code>tsk</code> указывает на удаляемый дескриптор).</p>
     <p><code>kmem_cache_free(task_struct_cachep, tsk);</code></p>
     <p>Так как дескрипторы процессов принадлежат к основным компонентам ядра и всегда необходимы, то кэш <code>task_struct_cachep</code> никогда не ликвидируется. Если бы он ликвидировался, то делать это необходимо было бы следующим образом.</p>
     <p><code>int err;</code></p>
     <empty-line/>
     <p><code>err = kmem_cache_destroy(task_struct_cachep);</code></p>
     <p><code>if (err)</code></p>
     <p><code> /* ошибка ликвидации кэша */</code></p>
     <p>Достаточно просто, не так ли? Уровень слябового распределения памяти скрывает все низкоуровневые операции, связанные с выравниванием, "раскрашиванием", выделением и освобождением памяти, "сборкой мусора" в случае нехватки памяти. Коли часто необходимо создавать много объектов одного типа, то следует подумать об использовании слябового кэша. И уж точно не нужно писать свою реализацию списка свободных ресурсов!</p>
    </section>
   </section>
   <section>
    <title>
     <p>Статическое выделение памяти в стеке</p>
    </title>
    <section>
     <p>В пространстве пользователя многие операции выделения памяти, в частности некоторые рассмотренные ранее примеры, могут быть выполнены с использованием стека, потому что априори известен размер выделяемой области памяти. В пространстве пользователя доступна такая роскошь, как очень большой и динамически увеличивающийся стек задачи, однако в режиме ядра такой роскоши нет — стек ядра маленький и фиксирован по размеру. Когда процессу выделяется небольшой и фиксированный по размеру стек, то затраты памяти уменьшаются и ядру нет необходимости выполнять дополнительные функции по управлению памятью.</p>
     <p>Размер стека зависит как от аппаратной платформы, так и от конфигурационных параметров, которые были указаны на этапе компиляции. Исторически размер стека ядра был равен двум страницам памяти для каждого процесса. Это соответствует 8 Кбайт для 32-разрядных аппаратных платформ и 16 Кбайт для 64-разрядных аппаратных платформ.</p>
     <p>В первых версиях ядер серии 2.6 была введена возможность конфигурации, для которой размер стека ядра равен одной странице памяти. Когда устанавливается такая конфигурация, то процесс получает стек, по размеру равный всего одной странице памяти: 4 Кбайт на 32-разрядных аппаратных платформах и 8 Кбайт — на 64-разрядных. Это сделано по двум причинам. Во-первых это уменьшает затраты памяти на одну страницу для каждого процесса. Во-вторых, что наиболее важно, при увеличении времени работы системы (uptime) становится все тяжелее искать две физически смежные страницы памяти. Физическая память становится все более фрагментированной, и нагрузка на систему управления виртуальной памятью при создании новых процессов становится все более существенной.</p>
     <p>Существует еще одна сложность (оставайтесь с нами, и Вы узнаете все о стеках ядра). Вся последовательность вложенных вызовов функций в режиме ядра должна поместиться в стеке. Исторически обработчики прерываний используют стек того процесса, выполнение которого они прервали. Это означает, что в худшем случае 8 Кбайт стека должно использоваться совместно всеми вложенными вызовами функций и еще парой обработчиков прерываний. Все это эффективно и просто, но это накладывает еще больше ограничений на использование стека ядра. Когда размер стека сократился до одной страницы памяти, обработчики прерываний туда перестали помещаться.</p>
     <p>Для решения указанной проблемы была реализована еще одна возможность — стеки обработчиков прерываний. Стеки прерываний представляют собой один стек на каждый процессор, которые используются для обработки прерываний. При такой конфигурации обработчики прерываний больше не используют стеки ядра тех процессов, которые этими обработчиками прерываются. Вместо этого они используют свои собственные стеки. Это требует только одну страницу памяти на процессор.</p>
     <p>Подведем итоги. Стек ядра занимает одну или две страницы памяти, в зависимости от конфигурации, которая выполняется перед компиляцией ядра. Следовательно, размер стека ядра может иметь диапазон от 4 до 16 Кбайт. Исторически обработчики прерываний совместно использовали стек прерванного ими процесса. При появлении стеков ядра размером в одну страницу памяти обработчикам прерываний были назначены свои стеки. В любом случае неограниченная рекурсия и использование функций вроде <code>alloca()</code> явно не допустимы.</p>
    </section>
    <section>
     <title>
      <p>Честная игра со стеком</p>
     </title>
     <p>В любой функции необходимо сокращать использование стека до минимума. Хотя не существует твердых правил, тем не менее следует поддерживать максимальный суммарный объем всех локальных переменных (также известных как автоматические переменные или переменные, выделенные в стеке) не больше нескольких сотен байтов. Опасно статически выделять большие объекты в стеке, такие как большие массивы структур. В противном случае выделение памяти в стеке будет выполняться так же, как и в пространстве пользователя. Переполнение стека происходит незаметно и обычно приводит к проблемам. Так как ядро не выполняет никакого управления стеком, то данные стека просто перепишут все, что находится за стеком. В первую очередь пострадает структура <code>thread_info</code>, которая расположена в самом конце стека процесса (вспомните главу 3). За пределами стека все данные ядра могут пропасть. В лучшем случае при переполнении стека произойдет сбой в работе машины. В худших случаях может произойти повреждение данных.</p>
     <p>В связи с этим, для выделения больших объемов памяти необходимо использовать одну из динамических схем выделения памяти, которые были рассмотрены раньше в этой главе.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Отображение верхней памяти</p>
    </title>
    <section>
     <p>По определению, страницы верхней памяти не могут постоянно отображаться в адресное пространство ядра. Поэтому страницы памяти, которые были выделены с помощью функции <code>alloc_pages()</code>, при использовании флага <code>__GFP__HIGHMEM</code> могут не иметь логического адреса.</p>
     <p>Для аппаратной платформы x86 вся физическая память свыше 896 Мбайт помечается как верхняя память, и она не может автоматически или постоянно отображаться в адресное пространство ядра, несмотря на то что процессоры платформы x86 могут адресовать до 4 Гбайт физической памяти (до 64 Гбайт при наличии расширения РАЕ<a l:href="#n66" type="note">[66]</a>). После выделения эти страницы должны быть отображены в логическое адресное пространство ядра. Для платформы x86 страницы верхней памяти отображаются где-то между отметками 3 и 4 Гбайт.</p>
    </section>
    <section>
     <title>
      <p>Постоянное отображение</p>
     </title>
     <p>Для того чтобы отобразить заданную структуру <code>page</code> в адресное пространство ядра, необходимо использовать следующую функцию.</p>
     <p><code>void *kmap(struct page *page);</code></p>
     <p>Эта функция работает как со страницами нижней, так и верхней памяти. Если структура <code>page</code> соответствует странице нижней памяти, то просто возвращается виртуальный адрес. Если страница расположена в верхней памяти, то создается постоянное отображение этой страницы памяти и возвращается полученный логический адрес. Функция <code>kmap()</code> может переводить процесс в состояние ожидания, поэтому ее можно вызывать только в контексте процесса.</p>
     <p>Поскольку количество постоянных отображений ограничено (если бы это было не так, то мы бы не мучились, а просто отобразили всю необходимую память), то отображение страниц верхней памяти должно быть отменено, если оно больше не нужно. Это можно сделать с помощью вызова следующей функции.</p>
     <p><code>void kunmap(struct page *page);</code></p>
     <p>Данная функция отменяет отображение страницы памяти, связанной с параметром <code>page</code>.</p>
    </section>
    <section>
     <title>
      <p>Временное отображение</p>
     </title>
     <p>В случаях, когда необходимо создать отображение страниц памяти в адресное пространство, а текущий контекст не может переходить в состояние ожидания, ядро предоставляет функцию <emphasis>временного отображении</emphasis> (которое также называется <emphasis>атомарным отображением</emphasis>). Существует некоторое количество зарезервированных постоянных отображений, которые могут временно выполнять отображение "на лету". Ядро может автоматически отображать страницу верхней памяти в одно из зарезервированных отображений. Временное отображение может использоваться в коде, который не может переходить в состояние ожидания, как, например, контекст прерывания, потому что полученное отображение никогда не блокируется.</p>
     <p>Установка временного отображения выполняется с помощью следующей функции.</p>
     <p><code>void *kmap_atomic(struct page *page, enum km_type type);</code></p>
     <p>Параметр <code>type</code> — это одно из значений показанного ниже перечисления, определенного в файле <code>&lt;asm/kmap_types.h&gt;</code>, которое описывает цель временного отображения.</p>
     <p><code>enum km_type {</code></p>
     <p><code> KM_BOUNCE_READ,</code></p>
     <p><code> KM_SKB_SUNRPC_DATA,</code></p>
     <p><code> KM_SKB_DATA_SOFTIRQ,</code></p>
     <p><code> KM_USER0,</code></p>
     <p><code> KM_USER1,</code></p>
     <p><code> KM_BIO_SRC_IRQ,</code></p>
     <p><code> KM_BIO_DST_IRQ,</code></p>
     <p><code> KM_PTE0,</code></p>
     <p><code> KM_PTE1,</code></p>
     <p><code> KM_PTE2,</code></p>
     <p><code> KM_IRQ0,</code></p>
     <p><code> KM_IRQ1,</code></p>
     <p><code> KM_SOFTIRQ0,</code></p>
     <p><code> KM_SOFTIRQ1,</code></p>
     <p><code> KM_TYPE_NR</code></p>
     <p><code>};</code></p>
     <p>Данная функция не блокируется и поэтому может использоваться в контексте прерывания и в других случаях, когда нельзя перепланировать выполнение. Эта функция также запрещает преемптивность ядра, что необходимо потому, что отображения являются уникальными для каждого процессора (а перепланирование может переместить задание для выполнения на другом процессоре).</p>
     <p>Отменить отображение можно с помощью следующей функции.</p>
     <p><code>void kunmap_atomic(void *kvaddr, enum km_type type);</code></p>
     <p>Эта функция также не блокирующая. На самом деле для большинства аппаратных платформ она ничего не делает, за исключением разрешения преемптивности ядра, потому что временное отображение действует только до тех пор, пока не создано новое временное отображение. Поэтому ядро просто "забывает" о вызове функции <code>kmap_atomic()</code>, и функции <code>kunmap_atomic()</code> практически ничего не нужно делать. Следующее атомарное отображение просто заменяет предыдущее.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Выделение памяти, связанной с определенным процессором</p>
    </title>
    <p>В современных операционных системах широко используются данные, связанные с определенными процессорами (per-CPU data). Это данные, которые являются уникальными для каждого процессора. Данные, связанные с процессорами, хранятся в массиве. Каждый элемент массива соответствует своему процессору системы. Номер процессора является индексом в этом массиве. Таким образом была реализована работа с данными, связанными с определенным процессором, в ядрах серии 2.4. В таком подходе нет ничего плохого, поэтому значительная часть кода ядра в серии 2.6 все еще использует этот интерфейс. Данные объявляются следующим образом,</p>
    <p><code>unsigned long my_percpu[NR_CPUS];</code></p>
    <p>Доступ к этим данным выполняется, как показано ниже.</p>
    <p><code>int cpu;</code></p>
    <empty-line/>
    <p><code>cpu = get_cpu(); /* получить номер текущего процессора и запретить</code></p>
    <p><code>                    вытеснение в режиме ядра */</code></p>
    <p><code>my_percpu[cpu]++;</code></p>
    <p><code>printk("значение данных процессора cpu=%d равно %ld\n",</code></p>
    <p><code> cpu, my_percpu[cpu]);</code></p>
    <p><code>put_cpu(); /* разрешить вытеснение в режиме ядра */</code></p>
    <p>Обратите внимание, что не нужно использовать никаких блокировок, потому что данные уникальны для каждого процессора. Поскольку никакой процессор, кроме текущего, не может обратиться к соответствующему элементу данных, то не может возникнуть и никаких проблем с конкурентным доступом, а следовательно, текущий процессор может безопасно обращаться к данным без блокировок.</p>
    <p>Возможность вытеснения процессов в режиме ядра— единственное, из-за чего могут возникнуть проблемы. В преемптивном ядре могут возникнуть следующие две проблемы.</p>
    <p>• Если выполняющийся код вытесняется и позже планируется для выполнения на другом процессоре, то значение переменной <code>cpu</code> больше не будет действительным, потому что эта переменная будет содержать номер другого процессоpa. (По той же причине, после получения номера текущего процессора, нельзя переходить в состояние ожидания.)</p>
    <p>• Если некоторый другой код вытеснит текущий, то он может параллельно обратиться к переменной <code>my_percpu</code> на том же процессоре, что соответствует состоянию гонок за ресурс.</p>
    <p>Однако все опасения напрасны, потому что вызов функции <code>get_cpu()</code>, которая возвращает номер текущего процессора, также запрещает вытеснение в режиме ядра. Соответствующий вызов функции <code>put_cpu()</code> разрешает вытеснение кода в режиме ядра. Обратите внимание, что функция <code>smp_processor_id()</code>, которая также позволяет получить номер текущего процессора, не запрещает вытеснения кода в режиме ядра, поэтому для безопасной работы следует использовать указанный выше метод.</p>
   </section>
   <section>
    <title>
     <p>Новый интерфейс <code>percpu</code></p>
    </title>
    <section>
     <p>В ядрах серии 2.6 предложен новый интерфейс, именуемый <emphasis>percpu</emphasis>, который служит для создания данных и работы с данными, связанными с определенным процессором. Этот интерфейс обобщает предыдущий пример. При использовании нового подхода работа с per-CPU-данными упрощается.</p>
     <p>Рассмотренный ранее метод работы с данными, которые связаны с определенным процессором, является вполне законным. Новый интерфейс возник из необходимости иметь более простой и мощный метод работы с данными, связанными с процессорами, на больших компьютерах с симметричной мультипроцессорностью.</p>
     <p>Все подпрограммы объявлены в файле <code>&lt;linux/percpu.h&gt;</code>. Описания же находятся в файлах <code>mm/slab.с</code> и <code>&lt;asm/percpu.h&gt;</code>.</p>
    </section>
    <section>
     <title>
      <p>Работа с данными, связанными с процессорами, на этапе компиляции</p>
     </title>
     <p>Описать переменную, которая связана с определенным процессором, на этапе компиляции можно достаточно просто следующим образом.</p>
     <p><code>DEFINE_PER_CPU(type, name);</code></p>
     <p>Это описание создает переменную типа <code>type</code> с именем <code>name</code>, которая имеет интерфейс связи с каждым процессором в системе. Если необходимо объявить соответствующую переменную с целью избежания предупреждений компилятора, то необходимо использовать следующий макрос.</p>
     <p><code>DECLARE_PER_CPU(type, name);</code></p>
     <p>Работать с этими переменными можно с помощью функций <code>get_cpu_var()</code> и <code>put_cpu_var()</code>. Вызов функции <code>get_cpu_var()</code> возвращает l-значение (левый операнд, l-value) указанной переменной на текущем процессоре. Этот вызов также запрещает вытеснение кода в режиме ядра, а соответственный вызов функции <code>put_cpu_var()</code> разрешает вытеснение.</p>
     <p><code>get_cpu_var(name)++; /* увеличить на единицу значение переменной</code></p>
     <p><code>                        name, связанное с текущим процессором */</code></p>
     <p><code>put_cpu_var(); /* разрешить вытеснение кода в режиме ядра */</code></p>
     <p>Можно также получить доступ к переменной, связанной с другим процессором.</p>
     <p><code>per_cpu(name, cpu)++; /* увеличить значение переменной name</code></p>
     <p><code>                         на указанном процессоре */</code></p>
     <p>Использовать функцию <code>per_cpu()</code> необходимо осторожно, так как этот вызов не запрещает вытеснение кода и не обеспечивает никаких блокировок. Необходимость использования блокировок при работе с данными, связанными с определенным процессором, отпадает, только если к этим данным может обращаться один процессор. Если процессоры обращаются к данным других процессоров, то необходимо использовать блокировки. Будьте осторожны! Применение блокировок рассматривается в главе 8, "Введение в синхронизацию выполнения кода ядра", и главе 9, "Средства синхронизации в ядре".</p>
     <p>Необходимо сделать еще одно важное замечание относительно создания данных. связанных с процессорами, на этапе компиляции. Загружаемые модули не могут использовать те из них, которые объявлены не в самом модуле, потому что компоновщик создает эти данные в специальных сегментах кода (а именно, <code>.data.percpu</code>). Если необходимо использовать данные, связанные с процессорами, в загружаемых модулях ядра, то нужно создать эти данные для каждого модуля отдельно или использовать динамически создаваемые данные.</p>
    </section>
    <section>
     <title>
      <p>Работа с данными процессоров на этапе выполнения</p>
     </title>
     <p>Для динамического создания данных, связанных с процессорами, в ядре реализован специальный распределитель памяти, который имеет интерфейс, аналогичный <code>kmalloc()</code>. Эти функции позволяют создать экземпляр участка памяти для каждого процессора в системе. Прототипы этих функций объявлены в файле <code>&lt;linux/percpu.h&gt;</code> следующим образом.</p>
     <p><code>void *alloc_percpu(type); / * макрос */</code></p>
     <p><code>void *__alloc_percpu(size_t size, size_t align);</code></p>
     <p><code>void free_percpu(const void*);</code></p>
     <p>Функция <code>alloc_percpu()</code> создает экземпляр объекта заданного типа (выделяет память) для каждого процессора в системе. Эта функция является оболочкой вокруг функции <code>__alloc_percpu()</code>. Последняя функция принимает в качестве аргументов количество байтов памяти, которые необходимо выделить, и количество байтов, но которому необходимо выполнить выравнивание этой области памяти. Функция <code>alloc_percpu()</code> выполняет выравнивание по той границе, которая используется для указанного типа данных. Такое выравнивание соответствует обычному поведению, как показано в следующем примере.</p>
     <p><code>struct rabid_cheetah = alloc_percpu(struct rabid_cheetah);</code></p>
     <p>что аналогично следующему вызову.</p>
     <p><code>struct rabid_cheetah = __alloc_percpu(sizeof(struct rabid_cheetah),</code></p>
     <p><code> __alignof__(struct rabid_cheetah));</code></p>
     <p>Оператор <code>__alignof__</code> — это расширение, предоставляемое компилятором gcc, который возвращает количество байтов, по границе которого необходимо выполнять выравнивание (или рекомендуется выполнять для тех аппаратных платформ, у которых нет жестких требований к выравниванию данных в памяти). Синтаксис этого вызова такой же как и у оператора <code>sizeof()</code>. В примере, показанном ниже, для аппаратной платформы x86 будет возвращено значение 4.</p>
     <p><code>__alignof__(unsigned long)</code></p>
     <p>При передаче l-значения (левое значение, lvalue) возвращается максимально возможное выравнивание, которое может потребоваться для этого l-значения. Например, l-значение внутри структуры может иметь большее значение выравнивания, чем это необходимо для хранения того же типа данных за пределами структуры, что связано с особенностями выравнивания структур данных в памяти. Проблемы выравнивания более подробно рассмотрены в главе 19, "Переносимость".</p>
     <p>Соответствующий вызов функции <code>free_percpu()</code> освобождает память, которую занимают соответствующие данные на всех процессорах.</p>
     <p>Функции <code>alloc_percpu()</code> и <code>__alloc_percpu()</code> возвращают указатель, который используется для косвенной ссылки на динамически созданные данные, связанные с каждым процессором в системе. Для простого доступа к данным ядро предоставляет два следующих макроса.</p>
     <p><code>get_cpu_ptr(ptr); /* возвращает указатель типа void на данные,</code></p>
     <p><code>        соответствующие параметру ptr, связанные с текущим процессом */</code></p>
     <p><code>put_cpu_ptr(ptr); /* готово, разрешаем вытеснение кода в режиме ядра */</code></p>
     <p>Макрос <code>get_cpu_ptr()</code> возвращает указатель на экземпляр данных, связанных с текущим процессором. Этот вызов также запрещает вытеснение кода в режиме ядра, которое снова разрешается вызовом функции <code>put_cpu_ptr()</code>.</p>
     <p>Рассмотрим пример использования этих функций. Конечно, этот пример не совсем логичный, потому что память обычно необходимо выделять один раз (например, в некоторой функции инициализации), использовать ее в разных необходимых местах, а затем освободить также один раз (например, в некоторой функции, которая вызывается при завершении работы). Тем не менее этот пример позволяет пояснить особенности использования.</p>
     <p><code>void *percpu_ptr;</code></p>
     <p><code>unsigned long *foo;</code></p>
     <empty-line/>
     <p><code>percpu_ptr = alloc_percpu(unsigned long);</code></p>
     <p><code>if (!ptr)</code></p>
     <p><code> /* ошибка выделения памяти ... */</code></p>
     <empty-line/>
     <p><code>foo = get_cpu_ptr(percpu_ptr);</code></p>
     <p><code>/* работаем с данными foo ... */</code></p>
     <p><code>put_cpu_ptr(percpu_ptr);</code></p>
     <p>Еще одна функция — <code>per_cpu_ptr()</code> — возвращает экземпляр данных, связанных с указанным процессором.</p>
     <p><code>per_cpu_ptr(ptr, cpu);</code></p>
     <p>Эта функция не запрещает вытеснение в режиме ядра. Если вы "трогаете" данные, связанные с другим процессором, то, вероятно, необходимо применить блокировки.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Когда лучше использовать данные, связанные с процессорами</p>
    </title>
    <p>Использование данных, связанных с процессорами, позволяет получить ряд преимуществ. Во-первых, это ослабление требований по использованию блокировок. В зависимости от семантики доступа к данным, которые связаны с процессорами, может оказаться, что блокировки вообще не нужны. Следует помнить, что правило "<emphasis>только один процессор может обращаться к этим данным</emphasis>" является всего лишь рекомендацией для программиста. Необходимо специально гарантировать, что каждый процессор работает только со своими данными. Ничто не может помешать нарушению этого правила.</p>
    <p>Во-вторых, данные, связанные с процессорами, позволяют существенно уменьшить недостоверность данных, хранящихся в кэше. Это происходит потому, что процессоры поддерживают свои кэши в синхронизированном состоянии. Если один процессор начинает работать с данными, которые находятся в кэше другого процессора, то первый процессор должен обновить содержимое своего кэша. Постоянное аннулирование находящихся в кэше данных, именуемое перегрузкой кэша (cash thrashing), существенно снижает производительность системы. Использование данных, связанных с процессорами, позволяет приблизить эффективность работы с кэшем к максимально возможной, потому что в идеале каждый процессор работает только со своими данными.</p>
    <p>Следовательно, использование данных, которые связаны с процессорами, часто избавляет от необходимости использования блокировок (или снижает требования, связанные с блокировками). Единственное требование, предъявляемое к этим данным для безопасной работы, — это запрещение вытеснения кода, который работает в режиме ядра. Запрещение вытеснения — значительно более эффективная операция по сравнению с использованием блокировок, а существующие интерфейсы выполняют запрещение и разрешение вытеснения автоматически. Данные, связанные с процессорами, можно легко использовать как в контексте прерывания, так и в контексте процесса. Тем не менее следует обратить внимание, что при использовании данных, которые связаны с текущим процессором, нельзя переходить в состояние ожидания (в противном случае выполнение может быть продолжено на другом процессоре).</p>
    <p>Сейчас нет строгой необходимости где-либо использовать новый интерфейс работы с данными, которые связаны с процессорами. Вполне можно организовать такую работу вручную (на основании массива, как было рассказано ранее), если при этом запрещается вытеснение кода в режиме ядра. Тем не менее новый интерфейс более простой в использовании и, возможно, позволит в будущем выполнять дополнительные оптимизации. Если вы собираетесь использовать в своем коде данные, связанные с процессорами, то лучше использовать новый интерфейс. Единственный недостаток нового интерфейса — он не совместим с более ранними версиями ядер.</p>
   </section>
   <section>
    <title>
     <p>Какой способ выделения памяти необходимо использовать</p>
    </title>
    <p>Если необходимы смежные страницы физической памяти, то нужно использовать один из низкоуровневых интерфейсов выделения памяти, или функцию <code>kmalloc()</code>. Это стандартный способ выделения памяти в ядре, и, скорее всего, в большинстве случаев следует использовать именно его. Необходимо вспомнить, что два наиболее часто встречающихся флага, которые передаются этой функции, это флаги <code>GFP_ATOMIC</code> и <code>GFP_KERNEL</code>. Для высокоприоритетных операций выделения памяти, которые не переводят процесс в состояние ожидания, необходимо указывать флаг <code>GFP_ATOMIC</code>. Это обязательно для обработчиков прерываний и других случаев, когда нельзя переходить в состояние ожидания. В коде, который может переходить в состояние ожидания, как, например код, выполняющийся в контексте процесса и не удерживающий спин-блокировку, необходимо использовать флаг <code>GFP_KERNEL</code>. Такой флаг указывает, что должна выполняться операция выделения памяти, которая при необходимости может перейти в состояние ожидания для получения необходимой памяти.</p>
    <p>Если есть необходимость выделить страницы верхней памяти, то следует использовать функцию <code>alloc_pages()</code>. Функция <code>alloc_pages()</code> возвращает структуру <code>struct page</code>, а не логический адрес. Поскольку страницы верхней памяти могут не отображаться в адресное пространство ядра, единственный способ доступа к этой памяти — через структуру <code>struct page</code>. Для получения "настоящего" указателя на область памяти необходимо использовать функцию <code>kmap()</code>, которая позволяет отобразить верхнюю память в логическое адресное пространство ядра.</p>
    <p>Если нет необходимости в физически смежных страницах памяти, а необходима только виртуально непрерывная область памяти, то следует использовать функцию <code>vmalloc()</code> (также следует помнить о небольшой потере производительности при использовании функции <code>vmalloc()</code> по сравнению с функцией <code>kmalloc()</code>). Функция <code>vmalloc()</code> выделяет область памяти, которая содержит только виртуально смежные страницы, но не обязательно физически смежные. Это выполняется почти так же, как и в программах пользователя путем отображения физически несмежных участков памяти в логически непрерывную область памяти.</p>
    <p>Если необходимо создавать и освобождать много больших структур данных, то следует рассмотреть возможность построения слябового кэша. Уровень слябового распределения памяти позволяет поддерживать кэш объектов (список свободных объектов), уникальный для каждого процессора, который может значительно улучшить производительность операций выделения и освобождения объектов. Вместо того чтобы часто выделять и освобождать память, слябовый распределитель сохраняет кэш уже выделенных объектов. При необходимости получения нового участка памяти для хранения структуры данных, уровню слябового распределения часто нет необходимости выделять новые страницы памяти, вместо этого можно просто возвращать объект из кэша.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 12</p>
    <p>Виртуальная файловая система</p>
   </title>
   <section>
    <p>Виртуальная файловая система (Virtual File System), иногда называемая виртуальным файловым коммутатором (<emphasis>Virtual File Switch</emphasis>) или просто <emphasis>VFS</emphasis>, — это подсистема ядра, которая реализует интерфейс пользовательских программ к файловой системе. Все файловые системы зависят от подсистемы VFS, что позволяет не только сосуществовать разным файловым системам, но и совместно функционировать. Это также дает возможность использовать стандартные системные вызовы для чтения и записи данных на различные файловые системы, которые находятся на различных физических носителях, как показано на рис. 12.1.</p>
    <image l:href="#img_17.jpeg"/>
    <p><strong>Рис. 12.1</strong>. Подсистема VFS в действии: использование команды <code>cp(1)</code> для копирования данных с жесткого диска, на котором монтируется файловая система, ext3, на гибкий диск, на котором монтируется файловая система ext2</p>
   </section>
   <section>
    <title>
     <p>Общий интерфейс к файловым системам</p>
    </title>
    <p>Подсистема VFS — это связующее звено, которое позволяет таким системным вызовам, как <code>open()</code>, <code>read()</code> и <code>write()</code>, работать независимо от файловой системы и физической среды носителя информации. Сегодня это может не впечатлять, поскольку такая возможность принимается как должное. Тем не менее сделать так, чтобы общие системные вызовы работали для всех поддерживаемых файловых систем и физических сред хранения данных, — задача не тривиальная. Более того, эти системные вызовы позволяют выполнять операции <emphasis>между</emphasis> различными файловыми системами и различными физическими носителями — мы можем копировать и перемещать данные с одной файловой системы на другую с помощью стандартных системных вызовов. В старых операционных системах (например, DOS) таких возможностей не было. Любые операции доступа к "неродным" файловым системам требовали использования специальных утилит. Сейчас такие возможности существуют, потому что все современные операционные системы, включая Linux, абстрагируют доступ к файловым системам с помощью виртуального интерфейса, который дает возможность совместной работы с данными и обобщенного доступа к данным. В операционной системе Linux может появиться поддержка новых типов файловых систем или новых физических средств хранения данных, при этом нет необходимости переписывать или перекомпилировать существующие программы.</p>
   </section>
   <section>
    <title>
     <p>Уровень обобщенной файловой системы</p>
    </title>
    <p>Общий интерфейс для всех типов файловых систем возможен только благодаря тому, что в ядре реализован обобщающий уровень, который скрывает низкоуровневый интерфейс файловых систем. Данный обобщающий уровень позволяет операционной системе Linux поддерживать различные файловые системы, даже если эти файловые системы существенно отличаются друг от друга своими функциями и особенностями работы. Это в свою очередь становится возможным благодаря тому, что подсистема VFS реализует общую файловую модель, которая в состоянии представить общие функции и особенности работы потенциально возможных файловых систем. Конечно, эта модель имеет уклон в сторону файловых систем в стиле Unix (что представляют собой файловые системы в стиле Unix, будет рассказано в следующем разделе). Несмотря на это в ОС Linux поддерживается довольно большой диапазон различных файловых систем.</p>
    <p>Обобщенный уровень работает путем определения базовых интерфейсов и структур данных, которые нужны для поддержки всех файловых систем. Код поддержки каждой файловой системы должен формировать все концепции своей работы в соответствии с шаблонными требованиями подсистемы VFS, например "<emphasis>так открываем файл</emphasis>", а "<emphasis>так представляем каталог</emphasis>". Код файловой системы скрывает все детали реализации. По отношению к уровню VFS и остальным частям ядра все файловые системы выглядят одинаково, т.е. все файловые системы начинают поддерживать такие объекты, как файлы и каталоги, и такие операции, как создание и удаление файла.</p>
    <p>В результате получается общий уровень абстракции, который позволяет ядру легко и просто поддерживать множество типов файловых систем. Код файловых систем программируется таким образом, чтобы поддерживать общие интерфейсы и структуры данных, которые нужны для работы с виртуальной файловой системой. В свою очередь, ядро легко может работать со всеми файловыми системами, и соответственно, экспортируемый ядром интерфейс пользователя также позволяет аналогично работать со всеми файловыми системами.</p>
    <p>В ядре нет необходимости поддерживать низкоуровневые детали реализации файловых систем нигде, кроме кода самих файловых систем. Например, рассмотрим следующую простую программу, работающую в пространстве пользователя.</p>
    <p><code>write(f, &amp;buf, len);</code></p>
    <p>Этот системный вызов записывает <code>len</code> байт из области памяти по адресу <code>&amp;buf</code> в файл, представленный с помощью дескриптора <code>f</code>, начиная с текущей позиции файла. Этот системный вызов вначале обрабатывается общей функцией ядра <code>sys_write()</code>, которая определяет функцию записи в файл для той файловой системы, на которой находится файл, представленный дескриптором <code>f</code>. Далее общий системный вызов вызывает найденную функцию, которая является частью реализации файловой системы и служит для записи данных на физический носитель (или для других действий, которые файловая система выполняет при записи файла). На рис. 12.2 показана диаграмма выполнения операции записи, начиная от пользовательской функции <code>write()</code> и заканчивая поступлением данных на физический носитель. Далее в этой главе будет показано, как подсистема VFS позволяет достичь необходимой абстракции и какие для этого обеспечиваются интерфейсы.</p>
    <image l:href="#img_18.jpeg"/>
    <p><strong>Рис. 12.2</strong>. Схема прохождения данных из пространства пользователя, где вызывается функция <code>write()</code>, через общий системный вызов VFS, к специфическому методу записи файловой системы и, наконец, поступление на физический носитель</p>
   </section>
   <section>
    <title>
     <p>Файловые системы Unix</p>
    </title>
    <p>Исторически так сложилось, что ОС Unix обеспечивает четыре абстракции, связанные с файловыми системами: файлы, элементы каталогов (directory entry), индексы (inode) и точки монтирования (mount point).</p>
    <p>Файловая система — это иерархическое хранилище данных определенной структуры. Файловые системы содержат файлы, каталоги и соответствующую управляющую информацию. Обычные операции, которые выполняются с файловыми системами, — это создание (create), удаление (delete) и монтирование (mount). В ОС Unix файловые системы монтируются на определенную точку монтирования в общей иерархии<a l:href="#n67" type="note">[67]</a>, которая называется <emphasis>пространством имен</emphasis> (<emphasis>namespace</emphasis>). Это позволяет все файловые системы сделать элементами одной древовидной структуры<a l:href="#n68" type="note">[68]</a>.</p>
    <p>Файл (file) — это упорядоченный поток байтов. Первый байт соответствует началу файла, а последний байт — концу файла. Каждому файлу присваивается удобочитаемое имя, по которому файл идентифицируется как пользователями, так и системой. Обычные файловые операции— это чтение (read), запись (write), создание (create) и удаление (delete).</p>
    <p>Файлы помещаются в каталогах (directory). Каталог — это аналог папки, которая обычно содержит связанные между собой файлы. Каталоги могут содержать подкаталоги. В этой связи каталоги могут быть вложены друг в друга и образуют пути (path). Каждый компонент пути называется элементом каталога (directory entry). Пример пути — "<code>/home/wolfman/foo</code>". Корневой каталог "<code>/</code>", каталоги <code>home</code> и <code>wolfman</code>, a также файл <code>fоо</code> — это элементы каталогов, которые называются <emphasis>dentry</emphasis>. В операционной системе Unix каталоги представляют собой обычные файлы, которые просто содержат список файлов каталога. Так как каталог по отношению к виртуальной файловой системе — это файл, то с каталогами можно выполнять те же операции, что и с файлами.</p>
    <p>Unix-подобные операционные системы отличают концепцию файла от любой информации об этом файле (права доступа, размер, владелец, время создания и т.д.). Последняя информация иногда называется <emphasis>метаданнымм файла</emphasis> (<emphasis>file metadata</emphasis>), т.е. данные о данных, и хранится отдельно от файлов в специальных структурах, которые называются <emphasis>индексами</emphasis> (<emphasis>inode</emphasis>). Это сокращенное название от <emphasis>index node</emphasis> (индексный узел), хотя в наши дни термин "inode" используется значительно чаще.</p>
    <p>Вся указанная информация, а также связанная с ней информация о самой файловой системе хранится в суперблоке (superblock). Суперблок — это структура данных, которая содержит информацию о файловой системе в целом. Иногда эти общие данные называются <emphasis>метаданными файловой системы</emphasis>. Метаданные файловой системы содержат информацию об индивидуальных файлах и о файловой системе в целом.</p>
    <p>Традиционно файловые системы ОС Unix реализуют эти понятия как структуры данных, которые определенным образом расположены на физических дисках. Например, информация о файлах хранится в индексе, в отдельном блоке диска, каталоги являются файлами, информация по управлению файловой системой хранится централизованно в суперблоке и т.д. Подсистема VFS операционной системы Linux рассчитана на работу с файловыми системами, в которых поддерживаются аналогичные концепции. Не Unix-подобные файловые системы, такие как FAT или NTFS, также работают в ОС Linux, однако их программный код должен обеспечить наличие аналогичных концепций. Например, если файловая система не поддерживает отдельные индексы файлов, то код должен построить в оперативной памяти структуры данных таким образом, чтобы казалось, что такая поддержка работает. Если файловая система рассматривает каталоги как объекты специальных типов, для VFS каталоги должны представляться как обычные файлы. Часто код не файловых систем не в стиле Unix требует выполнять некоторую дополнительную обработку чтобы уложиться в парадигму Unix и требования VFS. Такие файловые системы также поддерживаются, и обычно качество не особенно страдает.</p>
   </section>
   <section>
    <title>
     <p>Объекты VFS и их структуры данных</p>
    </title>
    <section>
     <p>Виртуальная файловая система (VFS) объектно-ориентированна<a l:href="#n69" type="note">[69]</a>. Общая файловая модель представлена набором структур данных. Эти структуры данных очень похожи на объекты. Так как ядро программируется строго на языке С, то, при отсутствии возможностей прямой поддержки парадигм ООП в языке программирования, структуры данных представляются структурами языка С. Структуры содержат как указатели на элементы данных, так и указатели на функции, которые работают с этими данными.</p>
     <p>Существуют следующие четыре основных типа объектов VFS.</p>
     <p>• Объект <emphasis>суперблок</emphasis> (<emphasis>superblock</emphasis>), который представляет определенную смонтированную файловую систему.</p>
     <p>• Объект <emphasis>файловый индекс</emphasis> (<emphasis>inode</emphasis>), который представляет определенный файл.</p>
     <p>• Объект <emphasis>элемент каталога</emphasis> (<emphasis>dentry</emphasis>), который представляет определенный элемент каталога.</p>
     <p>• Объект <emphasis>файл</emphasis> (<emphasis>file</emphasis>), который представляет открытый файл, связанный с процессом.</p>
     <p>Следует обратить внимание, что поскольку подсистема VFS рассматривает каталоги как обычные файлы, то не существует специальных объектов для каталогов. Как рассказывалось ранее, объект dentry представляет компонент пути, который может содержать обычный файл. Другими словами, dentry — это не то же самое, что каталог, а каталог — это то же, что и файл. Все понятно?</p>
     <p>Каждый из рассмотренных основных объектов содержит объект <emphasis>operations</emphasis> (<emphasis>операции</emphasis>). Эти объекты описывают методы, которые ядро может применять для основных объектов.</p>
     <p>В частности, существуют следующие объекты операций.</p>
     <p>• Объект <code>super_operations</code> (операции с суперблоком файловой системы) содержит методы, которые ядро может вызывать для определенной файловой системы, как, например, <code>read_inode()</code> или <code>sync_fs()</code>.</p>
     <p>• Объект <code>inode_operations</code> (операции с файловыми индексами) содержит методы, которые ядро может вызывать для определенного файла, как, например, <code>create()</code> или <code>link()</code>.</p>
     <p>• Объект <code>dentry_operations</code> (операции с элементами каталогов) содержит методы, которые ядро может вызывать для определенного элемента каталога, как, например, <code>d_compare()</code> или <code>d_delete()</code>.</p>
     <p>• Объект <code>file_operations</code> (операции с файлами) содержит методы, которые процесс может вызывать для открытого файла, как например, <code>read()</code> и <code>write()</code>.</p>
     <p>Объекты операций реализованы в виде структур, содержащих указатели на функции. Эти функции оперируют объектом, которому принадлежит объект операций. Для многих методов объект может унаследовать общую функцию, если для работы достаточно базовой функциональности. В противном случае каждая файловая система присваивает указателям адреса своих специальных методов.</p>
     <p>И еще раз повторимся, что под <emphasis>объектами</emphasis> мы будем понимать структуры, которые явно не являются объектными типами (в отличие от языков программирования C++ и Java). Однако эти структуры представляют определенные экземпляры объектов, данные связанные с объектами, и методы, которые ими оперируют. Это практически то же, что и объектные типы.</p>
    </section>
    <section>
     <title>
      <p>Другие объекты подсистемы VFS</p>
     </title>
     <p>Структуры для VFS — это самая "любимая" вещь, и в этой подсистеме существуют не только рассмотренные структуры, но и еще некоторые. Каждая зарегистрированная файловая система представлена структурой <code>file_system_type</code>. Объекты этого типа описывают файловую систему и ее свойства. Более того, каждая точка монтирования представлена в виде структуры <code>vfsmount</code>. Эта структура содержит информацию о точке монтирования, такую как ее положение и флаги, с которыми выполнена операция монтирования.</p>
     <p>И наконец, каждый процесс имеет три структуры, которые описывают файловую систему и файлы, связанные с процессом. Это структуры <code>file_struct</code>, <code>fs_struct</code> и <code>namespace</code>.</p>
     <p>Далее в этой главе будут рассматриваться эти объекты и их роль в функционировании уровня VFS.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Объект superblock</p>
    </title>
    <section>
     <p>Объект <emphasis>суперблок</emphasis> должен быть реализован для каждой файловой системы. Он используется для хранения информации, которая описывает определенную файловую систему. Этот объект обычно соответствует <emphasis>суперблоку</emphasis> (<emphasis>superblock</emphasis>) или <emphasis>управляющему блоку</emphasis> (<emphasis>control block</emphasis>) файловой системы, который хранится в специальном секторе диска (отсюда и имя объекта). Файловые системы, которые не располагаются на дисках (например, файловые системы в виртуальной памяти, как <emphasis>sysfs</emphasis>), генерируют информацию суперблока "на лету" и хранят в памяти.</p>
     <p>Объект <emphasis>суперблок</emphasis> представляется с помощью структуры <code>struct super_block</code>, которая определена в файле <code>&lt;linux/fs.h&gt;</code>. Она выглядит следующим образом (комментарии описывают назначение каждого поля).</p>
     <p><code>struct super_block {</code></p>
     <p><code> struct list_head         s_list;           /* список всех суперблоков */</code></p>
     <p><code> dev_t                    s_dev;            /* идентификатор */</code></p>
     <p><code> unsigned long            s_blocksize;      /* размер блока в байтах */</code></p>
     <p><code> unsigned long            s_old_blocksize;  /* старый размер блока</code></p>
     <p><code>                                               в байтах */</code></p>
     <p><code> unsigned char            s_blocksize_bits; /* размер блока в битах */</code></p>
     <p><code> unsigned char            s_dirt;           /* флаг того,</code></p>
     <p><code>                                               что суперблок изменен */</code></p>
     <p><code> unsigned long long       s_maxbytes; /* максимальный размер файла */</code></p>
     <p><code> struct file_system_type  *s_type;          /* тип файловой системы */</code></p>
     <p><code> struct super_operations  *s_op;            /* операции суперблока */</code></p>
     <p><code> struct dquot_operations  *dq_op;           /* операции с квотами */</code></p>
     <p><code> struct quotactl_ops      *s_qcop; /* операции управления квотами */</code></p>
     <p><code> struct export_operations *s_export_op;     /* операции экспортирования */</code></p>
     <p><code> unsigned long            s_flags;          /* флаги монтирования */</code></p>
     <p><code> unsigned long            s_magic; /* магический номер файловой системы */</code></p>
     <p><code> struct dentry            *s_root; /* каталог, точка монтирования */</code></p>
     <p><code> struct rw_semaphore      s_umount;         /* семафор размонтирования */</code></p>
     <p><code> struct semaphore         s_lock;           /* семафор суперблока */</code></p>
     <p><code> int                      s_count; /* счетчик ссылок на суперблок */</code></p>
     <p><code> int                      s_syncing;        /* флаг синхронизации</code></p>
     <p><code>                                               файловой системы */</code></p>
     <p><code> int                      s_need_sync_fs; /* флаг того, что файловая</code></p>
     <p><code>                                       система еще не синхронизирована */</code></p>
     <p><code> atomic_t                 s_active;         /* счетчик активных ссылок */</code></p>
     <p><code> void                     *s_security;      /* модуль безопасности */</code></p>
     <p><code> struct list_head         s_dirty; /* список измененных индексов */</code></p>
     <p><code> struct list_head         s_io;             /* список обратной записи */</code></p>
     <p><code> struct hlist_head        s_anon;           /* анонимные элементы каталога</code></p>
     <p><code>                                               для экспортирования */</code></p>
     <p><code> struct list_head         s_files;          /* список связанных файлов */</code></p>
     <p><code> struct block_device      *s_bdev;          /* соответствующий драйвер</code></p>
     <p><code>                                               блочного устройства */</code></p>
     <p><code> struct list_head         s_instances;      /* список файловых систем</code></p>
     <p><code>                                               данного типа */</code></p>
     <p><code> struct quota_info        s_dquot;          /* параметры квот */</code></p>
     <p><code> char                     s_id[32];         /* текстовое имя */</code></p>
     <p><code> void                     *s_fs_info;       /* специфическая информация</code></p>
     <p><code>                                               файловой системы */</code></p>
     <p><code> struct semaphore         s_vfs_rename_sem; /* семафор переименования */</code></p>
     <p><code>};</code></p>
     <p>Код для создания, управления и ликвидации объектов суперблок находится в файле <code>fs/super.c</code>. Объект <emphasis>суперблок</emphasis> создается и инициализируется в функции <code>alloc_super()</code>. Эта функция вызывается при монтировании файловой системы, которая считывает суперблок файловой системы с диска и заполняет поля объекта <emphasis>суперблок</emphasis>.</p>
    </section>
    <section>
     <title>
      <p>Операции суперблока</p>
     </title>
     <p>Наиболее важный элемент суперблока — это поле <code>s_op</code>, которое является указателем на таблицу операций суперблока. Таблица операций суперблока представлена с помощью структуры <code>struct super_operations</code>, которая определена в файле <code>&lt;linux/fs.h&gt;</code>. Она выглядит следующим образом.</p>
     <p><code>struct super_operations {</code></p>
     <p><code> struct inode *(*alloc_inode)(struct super_block *sb);</code></p>
     <p><code> void (*destroy_inode)(struct inode*);</code></p>
     <p><code> void (*read_inode)(struct inode*);</code></p>
     <p><code> void (*dirty_inode)(struct inode*);</code></p>
     <p><code> void (*write_inode)(struct inode*, int);</code></p>
     <p><code> void (*put inode)(struct inode*);</code></p>
     <p><code> void (*drop_inode)(struct inode*);</code></p>
     <p><code> void (*delete_inode)(struct inode*);</code></p>
     <p><code> void (*put_super)(struct super_block*);</code></p>
     <p><code> void (*write_super)(struct super block*);</code></p>
     <p><code> int (*sync_fs)(struct super_block*, int);</code></p>
     <p><code> void (*write_super_lockfs)(struct super_block*);</code></p>
     <p><code> void (*unlockfs)(struct super_block*);</code></p>
     <p><code> int (*statfs)(struct super_block*, struct statfs*);</code></p>
     <p><code> int (*remount_fs)(struct super_block*, int*, char*);</code></p>
     <p><code> void (*clear_inode)(struct inode*);</code></p>
     <p><code> void (*umount_begin)(struct super block*);</code></p>
     <p><code> int (*show_options)(struct seq_file*, struct vfsmount*);</code></p>
     <p><code>};</code></p>
     <p>Каждое поле этой структуры представляет собой указатель на функцию, которая работает с объектом <emphasis>суперблок</emphasis>. Операции суперблока выполняют низкоуровневые действия с файловой системой и ее файловыми индексами.</p>
     <p>Когда для файловой системы необходимо выполнить операции с суперблоком, то выполняется разыменование указателя на суперблок, и далее получается указатель на необходимый метод. Например, если файловой системе необходимо записать суперблок, то вызывается следующая функция.</p>
     <p><code>sb-&gt;s_op-&gt;write_super(sb);</code></p>
     <p>где параметр sb — это указатель на суперблок файловой системы. Следуя по указателю <code>s_op</code>, получаем таблицу операций суперблока и, наконец, необходимую функцию <code>write_super()</code>, которая вызывается непосредственно. Следует обратить внимание на то, что вызову функции <code>write_super()</code> необходимо передать указатель на суперблок в качестве параметра, несмотря на то что метод связан с суперблоком. Это происходит от того, что язык программирования С не объектно-ориентирован. В C++ аналогичный вызов может быть выполнен следующим образом.</p>
     <p><code>sb.write_super();</code></p>
     <p>В языке С нет простого способа получить указатель на объект, для которого вызван метод, поэтому его необходимо передавать явно.</p>
     <p>Рассмотрим операции суперблока, которые описаны в структуре <code>super_operations</code>.</p>
     <p>• <code>struct inode* alloc_inode(struct super_block *sb)</code> — эта функция создает и инициализирует новый объект файлового индекса, связанного с данным суперблоком.</p>
     <p>• <code>void destroy_inode(struct inode *inode)</code> — эта функция уничтожает данный объект индекса файла.</p>
     <p>• <code>void read_inode(struct inode *inode)</code> — эта функция считывает с диска файловый индекс с номером <code>inode-&gt;i_ino</code> и заполняет все остальные поля структуры данных индекса.</p>
     <p>• <code>void dirty_inode(struct inode *inode)</code> — эта функция вызывается подсистемой VFS, когда в индекс вносятся изменения (dirty). Журналируемые файловые системы (как, например, ext3) используют эту функцию для обновления журнала.</p>
     <p>• <code>void write_inode(struct inode inode*, int wait)</code> — эта функция записывает указанный индекс на диск. Параметр <code>wait</code> указывает, должна ли данная операция выполняться синхронно.</p>
     <p>• <code>void put_inode(struct inode *inode)</code> — эта функция освобождает указанный индекс.</p>
     <p>• <code>void drop_inode(struct inode *inode)</code> — эта функция вызывается подсистемой VFS, когда исчезает последняя ссылка на индекс. Обычные файловые системы Unix никогда не определяют эту функцию, в таком случае подсистема VFS просто удаляет индекс. Вызывающий код должен удерживать блокировку <code>inode_lock</code>.</p>
     <p>• <code>void delete_inode(struct inode *inode)</code> — эта функция удаляет индекс файла с диска.</p>
     <p>• <code>void put_super(struct super_block *sb)</code> — эта функция вызывается подсистемой VFS при размонтировании файловой системы, чтобы освободить указанный суперблок.</p>
     <p>• <code>void write_super(struct super_block *sb)</code> — эта функция обновляет суперблок на диске данными из указанного суперблока. Подсистема VFS вызывает эту функцию для синхронизации измененного суперблока в памяти с данными суперблока на диске.</p>
     <p>• <code>int sync_fs(struct super_block *sb, int wait)</code> — эта функция синхронизирует метаданные файловой системы с данными на диске. Параметр <code>wait</code> указывает, должна ли операция быть синхронной или асинхронной.</p>
     <p>• <code>void write_super_lockfs(struct super_block *sb)</code> — эта функция предотвращает изменения файловой системы и затем обновляет данные суперблока на диске данными из указанного суперблока. Сейчас она используется диспетчером логических томов (LVM, Logical Volume Manager).</p>
     <p>• <code>void unlockfs(struct super_block *sb)</code> — эта функция разблокирует файловую систему после выполнения функции <code>write_super_lockfs()</code>.</p>
     <p>• <code>int statfs(struct super_block *sb, struct statfs *statfs)</code> — эта функция вызывается подсистемой VFS для получения статистики файловой системы, Статистика указанной файловой системы записывается в структуру <code>statfs</code>.</p>
     <p>• <code>int remount_fs(struct super_block *sb, int *flags, char *data)</code> — эта функция вызывается подсистемой VFS, когда файловая система монтируется с другими параметрами монтирования.</p>
     <p>• <code>void clear_inode(struct inode*)</code> — эта функция вызывается подсистемой VFS для освобождения индекса и очистки всех страниц памяти, связанных с индексом.</p>
     <p>• <code>void umount_begin(struct super_block *sb)</code> — эта функция вызывается подсистемой VFS для прерывания операции монтирования. Она используется сетевыми файловыми системами, такими как NFS.</p>
     <p>Все рассмотренные функции вызываются подсистемой VFS в контексте процесса. Все они при необходимости могут блокироваться.</p>
     <p>Некоторые из этих функций являются необязательными. Файловая система может установить их значения в структуре операций суперблока равными <code>NULL</code>. Если соответствующий указатель равен <code>NULL</code>, то подсистема VFS или вызывает общий вариант функции, или не происходит ничего, в зависимости от операции.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Объект inode</p>
    </title>
    <section>
     <p>Объект <code>inode</code> содержит всю информацию, которая необходима ядру для манипуляций с файлами и каталогами. В файловых системах в стиле Unix вся информация просто считывается из дисковых индексов и помещается в объект <code>inode</code> подсистемы VFS. Если файловые системы не имеют индексов, то эту информацию необходимо получить из других дисковых структур<a l:href="#n70" type="note">[70]</a>.</p>
     <p>Объект индекса файла представляется с помощью структуры <code>struct inode</code>, которая определена в файле <code>&lt;linux/fs.h&gt;</code>. Эта структура с комментариями, описывающими назначение каждого поля, имеет следующий вид.</p>
     <p><code>struct inode {</code></p>
     <p><code> struct hlist_node       i_hash;         /* хешированный список */</code></p>
     <p><code> struct list_head        i_list;         /* связанный список индексов */</code></p>
     <p><code> struct list_head        i_dentry; /* связанный список объектов dentry */</code></p>
     <p><code> unsigned long           i_ino;          /* номер индекса */</code></p>
     <p><code> atomic_t                i_count;        /* счетчик ссылок */</code></p>
     <p><code> umode_t                 i_mode;         /* права доступа */</code></p>
     <p><code> unsigned int            i_nlink;        /* количество жестких ссылок */</code></p>
     <p><code> uid_t                   i_uid; /* идентификатор пользователя-владельца */</code></p>
     <p><code> gid_t                   i_gid; /* идентификатор группы-владельца */</code></p>
     <p><code> kdev_t                  i_rdev;         /* связанное устройство */</code></p>
     <p><code> loff_t                  i_size;         /* размер файла в байтах */</code></p>
     <p><code> struct timespec         i_atime; /* время последнего доступа к файлу */</code></p>
     <p><code> struct timespec         i_mtime; /* время последнего изменения файла */</code></p>
     <p><code> struct timespec         i_ctime;        /* время изменения индекса */</code></p>
     <p><code> unsigned int            i_blkbits;      /* размер блока в битах */</code></p>
     <p><code> unsigned long           i_blksize;      /* размер блока в байтах */</code></p>
     <p><code> unsigned long           i_version;      /* номер версии */</code></p>
     <p><code> unsigned long           i_blocks;       /* размер файла в блоках */</code></p>
     <p><code> unsigned short          i_bytes; /* количество использованных байтов */</code></p>
     <p><code> spinlock_t              i_lock;         /* блокировка для защиты полей */</code></p>
     <p><code> struct rw_semaphore     i_alloc_sem     /* вложенные блокировки при</code></p>
     <p><code>                                            захваченной i_sem */</code></p>
     <p><code> struct semaphore        i_sem;          /* семафор индекса */</code></p>
     <p><code> struct inode_operations *i_op;          /* таблица операций с индексом */</code></p>
     <p><code> struct file_operations  *i_fop;         /* файловые операции */</code></p>
     <p><code> struct super_block      *i_sb;          /* связанный суперблок */</code></p>
     <p><code> struct file_lock        *i_flock;       /* список блокировок файлов */</code></p>
     <p><code> struct address_space    *i_mapping;     /* соответствующее адресное</code></p>
     <p><code>                                            пространство */</code></p>
     <p><code> struct address_space    i_data; /* адресное пространство устройства */</code></p>
     <p><code> struct dquot            *i_dquot[MAXQUOTAS]; /* дисковые квоты</code></p>
     <p><code>                                                 для индекса */</code></p>
     <p><code> struct list_head        i_devices;      /* список блочных устройств */</code></p>
     <p><code> struct pipe_inode_info  *i_pipe;        /* информация конвейера */</code></p>
     <p><code> struct block_device     *i_bdev;        /* драйвер блочного устройства */</code></p>
     <p><code> unsigned long           i_dnotify_mask; /* события каталога */</code></p>
     <p><code> struct dnotify_struct   *i_dnotify; /* информация о событиях каталога */</code></p>
     <p><code> unsigned long           i_state;        /* флаги состояния */</code></p>
     <p><code> unsigned long           dirtied_when    /* время первого изменения */</code></p>
     <p><code> unsigned int            i_flags;        /* флаги файловой системы */</code></p>
     <p><code> unsigned char           i_sock;         /* сокет или нет? */</code></p>
     <p><code> atomic_t                i_writecount;   /* счетчик использования</code></p>
     <p><code>                                            для записи */</code></p>
     <p><code> void                    *i_security;    /* модуль безопасности */</code></p>
     <p><code> __u32                   i_generation;   /* номер версии индекса */</code></p>
     <p><code> union {</code></p>
     <p><code>  void *generic_ip; /* специфическая информация</code></p>
     <p><code>                       файловой системы */</code></p>
     <p><code> } u;</code></p>
     <p><code>};</code></p>
     <p>Для каждого файла в системе существует представляющий его индекс (хотя объект файлового индекса создается в памяти только тогда, когда к файлу осуществляется доступ). Это справедливо и для специальных файлов, таких как файлы устройств или конвейеры. Следовательно, некоторые из полей структуры <code>struct inode</code> относятся к этим специальным файлам. Например, поле <code>i_pipe</code> указывает на структуру данных именованного конвейера. Если индекс не относится к именованному конвейеру, то это поле просто содержит значение <code>NULL</code> Другие поля, связанные со специальными файлами, — это <code>i_devices</code>, <code>i_bdev</code>, <code>i_cdev</code>.</p>
     <p>Может оказаться, что та или иная файловая система не поддерживает тех свойств, которые присутствуют в объекте <code>inode</code>. Например, некоторые файловые системы не поддерживают такого атрибута, как время создания файла. В этом случае файловая система может реализовать это свойство как угодно. Например, поле <code>i_ctime</code> можно сделать нулевым или равным значению поля <code>i_mtime</code>.</p>
    </section>
    <section>
     <title>
      <p>Операции с файловыми индексами</p>
     </title>
     <p>Так же как и в случае операций суперблока, важным является поле <code>inode_operations</code>, в котором описаны функции файловой системы, которые могут быть вызваны подсистемой VFS для объекта файлового индекса. Как и для суперблока, операции с файловыми индексами могут быть вызваны следующим образом.</p>
     <p><code>i-&gt;i_op-&gt;truncate(i);</code></p>
     <p>где переменная <code>i</code> содержит указатель на определенный объект файлового индекса. В данном случае для индекса <code>i</code> выполняется операция <code>truncate()</code>, которая определена для файловой системы, в которой находится указанный файловый индекс <code>i</code>. Структура <code>inode_operations</code> определена в файле <code>&lt;linux/fs.h&gt;</code>, как показано ниже.</p>
     <p><code>struct inode_operations {</code></p>
     <p><code> int (*create)(struct inode*, struct dentry*, int);</code></p>
     <p><code> struct dentry* (*lookup)(struct inode*, struct dentry*);</code></p>
     <p><code> int (*link)(struct dentry*, struct inode*, struct dentry*);</code></p>
     <p><code> int (*unlink)(struct inode*, struct dentry*);</code></p>
     <p><code> int (*symlink)(struct inode*, struct dentry*, const char*);</code></p>
     <p><code> int (*mkdir)(struct inode*, struct dentry*, int);</code></p>
     <p><code> int (*rmdir)(struct inode*, struct dentry*);</code></p>
     <p><code> int (*mknod)(struct inode*, struct dentry*, int, dev_t);</code></p>
     <p><code> int (*rename)(struct inode*, struct dentry*,</code></p>
     <p><code>  struct inode*, struct dentry*);</code></p>
     <p><code> int (*readlink)(struct dentry*, char*, int);</code></p>
     <p><code> int (*follow_link)(struct dentry*, struct nameidata*);</code></p>
     <p><code> int (*put_link)(struct dentry*, struct nameidata*);</code></p>
     <p><code> void (*truncate)(struct inode*);</code></p>
     <p><code> int (*permission)(struct inode*, int);</code></p>
     <p><code> int (*setattr)(struct dentry*, struct iattr*);</code></p>
     <p><code> int (*getattr)(struct vfsmount*, struct dentry*, struct kstat*);</code></p>
     <p><code> int (*setxattr)(struct dentry*, const char*,</code></p>
     <p><code> const void*, size_t, int);</code></p>
     <p><code> ssize_t (*getxattr)(struct dentry*, const char*, void*, size_t);</code></p>
     <p><code> ssize_t (*listxattr)(struct dentry*, char*, size_t);</code></p>
     <p><code> int (*removexattr)(struct dentry*, const char*);</code></p>
     <p><code>};</code></p>
     <p>Рассмотрим указанные операции более подробно.</p>
     <p>• <code>int create(struct inode *dir, struct dentry *dentry, int mode);</code></p>
     <p>Эта функция вызывается подсистемой VFS из системных вызовов <code>creat()</code> и <code>open()</code> для создания нового файлового индекса, который имеет указанный режим доступа (<code>mode</code>) и связан с указанным элементом каталога (<code>dentry</code>).</p>
     <p>• <code>struct dentry* lookup(struct inode *dir, struct dentry *dentry);</code></p>
     <p>Эта функция производит поиск файлового индекса в указанном каталоге. Файловый индекс должен соответствовать имени файла, хранящемуся в указанном объекте элемента каталога.</p>
     <p>• <code>int link(struct dentry *old_dentry, struct inode *dir,</code></p>
     <p><code>  struct dentry *dentry);</code></p>
     <p>Эта функция вызывается из системного вызова <code>link()</code> для создания жесткой ссылки (hard link) на файл, соответствующий элементу каталога <code>old_dentry</code> в каталоге <code>dir</code>. Новая ссылка должна иметь имя, которое хранится в указанном элементе каталога <code>dentry</code>.</p>
     <p>• <code>int unlink(struct inode *dir, struct dentry *dentry);</code></p>
     <p>Эта функция вызывается из системного вызова <code>unlink()</code> для удаления файлового индекса, соответствующего элементу каталога <code>dentry</code> в каталоге <code>dir</code>.</p>
     <p>• <code>int symlink(struct inode *dir, struct dentry *dentry,</code></p>
     <p><code>  const char *symname);</code></p>
     <p>Эта функция вызывается из системного вызова <code>symlink()</code> для создания символьной ссылки с именем <code>symname</code> на файл, которому соответствует элемент каталога <code>dentry</code> в каталоге <code>dir</code>.</p>
     <p>• <code>int mkdir(struct inode *dir, struct dentry *dentry, int mode);</code></p>
     <p>Эта функция вызывается из системного вызова <code>mkdir()</code> для создания нового каталога с указанным режимом доступа (<code>mode</code>).</p>
     <p>• <code>int rmdir(struct inode *dir, struct dentry *dentry);</code></p>
     <p>Эта функция вызывается из системного вызова <code>rmdir()</code> для удаления каталога на который указывает элемент каталога <code>dentry</code> из каталога <code>dir</code>.</p>
     <p>• <code>int mknod(struct inode *dir, struct dentry *dentry,</code></p>
     <p><code>  int mode, dev_t rdev);</code></p>
     <p>Эта функция вызывается из системного вызова <code>mknod()</code> для создания специального файла (файла устройства, именованного конвейера или сокета), информация о котором хранится в параметре <code>rdev</code>. Файл должен быть создан в каталоге <code>dir</code> с именем, указанным в параметре <code>dentry</code>, и режимом доступа <code>mode</code>.</p>
     <p>• <code>int rename(struct inode *old_dir, struct dentry *old_dentry,</code></p>
     <p><code>  struct inode *new_dir, struct dentry *new_dentry);</code></p>
     <p>Эта функция вызывается подсистемой VFS для перемещения указанного элемента каталога <code>old_dentry</code> из каталога <code>old_dir</code> в каталог <code>new_dir</code> с новым именем, указанным в параметре <code>new_dentry</code>.</p>
     <p>• <code>int readlink(struct dentry *dentry, char *buffer, int buflen);</code></p>
     <p>Эта функция вызывается из системного вызова <code>readlink()</code> для копирования не более <code>buflen</code> байт полного пути, связанного с символьной ссылкой, соответствующей указанному элементу каталога, в указанный буфер.</p>
     <p>• <code>int follow_link(struct dentry *dentry, struct nameidata *nd);</code></p>
     <p>Эта функция вызывается подсистемой VFS для трансляции символьной ссылки в индекс файла, на который эта ссылка указывает. На ссылку указывает указатель <code>dentry</code>, а результат сохраняется в структуру <code>nameidata</code>, на которую указывает параметр <code>nd</code>.</p>
     <p>• <code>int put_link(struct dentry *dentry, struct nameidata* nd);</code></p>
     <p>Эта функция вызывается подсистемой VFS после вызова функции <code>followlink()</code>.</p>
     <p>• <code>void truncate(struct inode *inode);</code></p>
     <p>Эта функция вызывается подсистемой VFS для изменения размера заданного файла. Перед вызовом поле <code>i_size</code> указанного индекса файла должно быть установлено в желаемое значение размера.</p>
     <p>• <code>int permission(struct inode *inode, int mask);</code></p>
     <p>Эта функция проверяет, разрешен ли указанный режим доступа к файлу, на который ссылается объект <code>inode</code>. Функция должна возвращать нулевое значение, если доступ разрешен, и отрицательное значение кода ошибки в противном случае. Для большинства файловых систем данное поле устанавливается в значение <code>NULL</code>, и при этом используется общий метод VFS, который просто сравнивает биты поля режима доступа файлового индекса с указанной маской. Более сложные файловые системы, которые поддерживают списки контроля доступа (ACL), реализуют свой метод <code>permission()</code>.</p>
     <p>• <code>int setattr(struct dentry *dentry, struct iattr *attr);</code></p>
     <p>Эта функция вызывается функцией <code>notify_change()</code> для уведомления о том, что произошло "событие изменения" ("change event") после модификации индекса.</p>
     <p>• <code>int getattr(struct vfsmount *mnt, struct dentry *dentry,</code></p>
     <p><code>  struct kstat *stat);</code></p>
     <p>Эта функция вызывается подсистемой VFS при уведомлении, что индекс должен быть обновлен с диска.</p>
     <p>• <code>int setxattr(struct dentry *dentry, const char *name,</code></p>
     <p><code>  const void *value, size_t size, int flags);</code></p>
     <p>Эта функция вызывается подсистемой VFS для установки одного из расширенных атрибутов (extended attributes)<a l:href="#n71" type="note">[71]</a> с именем <code>name</code> в значение <code>value</code> для файла, соответствующего элементу каталога <code>dentry</code>.</p>
     <p>• <code>int getxattr(struct dentry *dentry, const char *name,</code></p>
     <p><code>  void *value, size_t size);</code></p>
     <p>Эта функция вызывается подсистемой VFS для копирования значения одного из расширенных атрибутов (extended attributes) с именем <code>name</code> в область памяти с указателем <code>value</code>.</p>
     <p>• <code>ssize_t listxattr(struct dentry *dentry, char *list, size_t size);</code></p>
     <p>Эта функция должна копировать список всех атрибутов для указанного файла в буфер, соответствующий параметру <code>list</code>.</p>
     <p>• <code>int removexattr(struct dentry *dentry, const char *name);</code></p>
     <p>Эта функция удаляет указанный атрибут для указанного файла.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Объект dentry</p>
    </title>
    <section>
     <p>Как уже рассказывалось, подсистема VFS представляет каталоги так же, как и файлы. В имени пути <code>/bin/vi</code>, и элемент <code>bin</code>, и элемент <code>vi</code> — это файлы, только <code>bin</code> — это специальный файл, который является каталогом, a <code>vi</code> — это обычный файл. Объекты файловых индексов служат для представления обоих этих компонентов. Несмотря на такую полезную унификацию, подсистеме VFS также необходимо выполнять операции, специфичные для каталогов, такие как поиск компонента пути по его имени, проверка того, что указанный элемент пути существует, и переход на следующий компонент пути.</p>
     <p>Для решения этой задачи в подсистеме VFS реализована концепция элемента каталога (directory entry или dentry). Объект dentry — это определенный компонент пути. В предыдущем примере компоненты <code>/</code>, <code>bin</code> и <code>vi</code> — это объекты элементов каталога. Первые два — это каталоги, а последний — обычный файл. Важным моментом является то, что все объекты dentry — это компоненты пути, включая и обычные файлы.</p>
     <p>Элементы пути также могут включать в себя точки монтирования. В имени пути <code>/mnt/cdrom/foo</code>, компоненты <code>/</code>, <code>mnt</code>, <code>cdrom</code> и <code>foo</code> — это все объекты типа dentry. Подсистема VFS при выполнении операций с каталогами по необходимости конструирует объекты элементов каталога на лету.</p>
     <p>Объекты типа dentry представлены с помощью структуры <code>struct dentry</code> и определены в файле <code>&lt;linux/dcache.h&gt;</code>. Эта структура с комментариями, которые определяют назначение каждого поля, имеет следующий вид.</p>
     <p><code>struct dentry {</code></p>
     <p><code> atomic_t                 d_count;     /* счетчик использования */</code></p>
     <p><code> unsigned long            d_vfs_flags; /* флаги кэша объектов dentry */</code></p>
     <p><code> spinlock_t               d_lock; /* блокировка данного объекта dentry */</code></p>
     <p><code> struct inode             *d_inode; /* соответствующий файловый индекс */</code></p>
     <p><code> struct list_head         d_lru; /* список неиспользованных объектов */</code></p>
     <p><code> struct list_head         d_child;     /* список объектов у родительского</code></p>
     <p><code>                                          экземпляра */</code></p>
     <p><code> struct list_head         d_subdirs;   /* подкаталоги */</code></p>
     <p><code> struct list_head         d_alias;     /* список альтернативных (alias)</code></p>
     <p><code>                                          индексов */</code></p>
     <p><code> unsigned long            d_time;      /* время проверки правильности */</code></p>
     <p><code> struct dentry_operations *d_op;       /* таблица операций с элементом</code></p>
     <p><code>                                          каталога */</code></p>
     <p><code> struct super_block       *d_sb;       /* связанный суперблок */</code></p>
     <p><code> unsigned int             d_flags;     /* флаги элемента каталога */</code></p>
     <p><code> int                      d_mounted;   /* является ли объект точкой</code></p>
     <p><code>                                           монтирования */</code></p>
     <p><code> void                     *d_fsdata;   /* специфические данные</code></p>
     <p><code>                                          файловой системы */</code></p>
     <p><code> struct rcu_head          d_rcu; /* блокировки RCU (read-copy update) */</code></p>
     <p><code> struct dcookie_struct    *d_cookie;    /* cookie-идентификатор */</code></p>
     <p><code> struct dentry            *d_parent;    /* объект dentry</code></p>
     <p><code>                                           родительского каталога */</code></p>
     <p><code> struct qstr              d_name;       /* имя dentry */</code></p>
     <p><code> struct hlist_node        d_hash;       /* список хеширования */</code></p>
     <p><code> struct hlist_head        *d_bucket;    /* сегмент хеш-таблицы */</code></p>
     <p><code> unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* короткое имя файла */</code></p>
     <p><code>};</code></p>
     <p>В отличие от предыдущих двух объектов, объект dentry не соответствует какой бы то ни было структуре данных на жестком диске. Подсистема VSF создает эти объекты на лету на основании строкового представления имени пути. Поскольку объекты элементов каталога не хранятся физически на дисках, то в структуре <code>struct dentry</code> нет никаких флагов, которые указывают на то, изменен ли объект (т.е. должен ли он быть записан назад на диск).</p>
    </section>
    <section>
     <title>
      <p>Состояние элементов каталога</p>
     </title>
     <p>Действительный объект элемента каталога, может быть в одном из трех состояний: используемый (used), неиспользуемый (unused) и негативный (negative).</p>
     <p>Используемый объект соответствует существующему файловому индексу (т.е. поле <code>d_inode</code> указывает на связанный объект типа mode) и используется один или более раз (т.е. значение поля <code>d_count</code> — положительное число). Используемый элемент каталога используется подсистемой VFS, а также указывает на существующие данные, поэтому не может быть удален.</p>
     <p>Неиспользуемый объект типа dentry соответствует существующему объекту inode (поле <code>d_inode</code> указывает на объект файлового индекса), но подсистема VFS в данный момент не использует этот элемент каталога (поле <code>d_count</code> содержит нулевое значение). Так как элемент каталога указывает на существующий объект, то он сохраняется на случай, если вдруг окажется нужным. Если объект не ликвидировать преждевременно, то его и не нужно будет создавать заново, если вдруг он понадобится в будущем, и поиск по имени пути пройдет быстрее. Когда же появляется необходимость освободить память, то такой объект элемента каталога может быть удален, потому что он никем не используется.</p>
     <p>Негативный объект dentry<a l:href="#n72" type="note">[72]</a> не связан с существующим файловым индексом (поле <code>d_inode</code> равно значению <code>NULL</code>), потому что или файловый индекс был удален, или соответствующий элемент пути никогда не существовал. Такие объекты элементов каталогов сохраняются, чтобы в будущем поиск по имени пути проходил быстрее. Хотя такие объекты dentry и полезны, но они при необходимости могут уничтожаться, поскольку никто их на самом деле не использует.</p>
     <p id="___temp_view_cursor_for_clear_format__2">Объект dentry может быть освобожден, оставаясь в слябовом кэше объектов, как обсуждалось в предыдущей главе. В таком случае на этот объект нет ссылок ни в коде VFS, ни в коде файловых систем.</p>
    </section>
    <section>
     <title>
      <p>Кэш объектов dentry</p>
     </title>
     <p>После того как подсистема VFS преодолела все трудности, связанные с переводом всех элементов пути в объекты элементов каталогов, и был достигнут конец пути, то было бы достаточно расточительным выбрасывать на ветер всю проделанную работу. Ядро кэширует объекты в кэше элементов каталога, который называют <emphasis>dcache</emphasis>.</p>
     <p>Кэш объектов dentry состоит из трех частей.</p>
     <p>• Список "используемых" объектов dentry, которые связаны с определенным файловым индексом (поле <code>i_dentry</code> объекта inode). Поскольку указанный файловый индекс может иметь несколько ссылок, то ему может соответствовать несколько объектов dentry, а следовательно используется связанный список.</p>
     <p>• Двухсвязный список неиспользуемых и негативных объектов dentry "с наиболее поздним использованием" (last recently used, LRU). Вставки элементов в этот список отсортированы по времени, поэтому элементы, которые находятся в начале списка, — самые новые. Когда ядро должно удалить элементы каталогов для освобождения памяти, то эти элементы берутся из конца списка, потому что там находятся элементы, которые использовались наиболее давно и для которых меньше шансов, что они понадобятся в ближайшем будущем.</p>
     <p>• Хеш-таблица и хеш-функция, которые позволяют быстро преобразовать заданный путь в объект dentry.</p>
     <p>Указанная хеш-таблица представлена с помощью массива <code>dentry_hashtable</code>. Каждый элемент массива — это указатель на список тех объектов dentry, которые соответствуют одному ключу. Размер этого массива зависит от объема физической памяти в системе.</p>
     <p>Значение ключа определяется функцией <code>d_hash()</code>, что позволяет для каждой файловой системы реализовать свою хеш-функцию.</p>
     <p>Поиск в хеш-таблице выполняется с помощью функции <code>d_lookup()</code>. Если в кэше dcache найден соответствующий объект, то это значение возвращается. В случае ошибки возвращается значение <code>NULL</code>.</p>
     <p>В качестве примера рассмотрим редактирование файла исходного кода в вашем домашнем каталоге, <code>/home/dracula/src/fоо.с</code>. Каждый раз, когда производится доступ к этому файлу (например, при первом открытии, при последующей записи, при компиляции и так далее), подсистема VFS должна пройти через псе элементы каталогов в соответствии с путем к файлу: <code>/</code>, <code>home</code>, <code>dracula</code>, <code>src</code> и, наконец, <code>foo.c</code>. Для того чтобы каждый раз при доступе к этому (и любому другому) имени пути избежать выполнения данной операции, которая требует довольно больших затрат времени, подсистема VFS вначале может попытаться найти это имя пути в dentry-кэше. Если поиск проходит успешно, то необходимый конечный элемент каталога нужного пути получается без особых усилий. Если же данного элемента каталога нет в dentry-кэше, то подсистема VFS должна самостоятельно отследить путь. После завершения поиска найденные объекты dentry помещаются в кэш dcache, чтобы ускорить поиск в будущем.</p>
     <p>Кэш dcache также является интерфейсом к кэшу файловых индексов <emphasis>icache</emphasis>. Объекты inode связаны с объектами dentry, поскольку объект dentry поддерживает положительное значение счетчика использования для связанного с ним индекса. Это в свою очередь позволяет объектам dentry удерживать связанные с ними объекты mode в памяти. Иными словами, если закэширован элемент каталога, то соответственно оказывается закэшированным и соответствующий ему файловый индекс. Следовательно, если поиск в кэше для некоторого имени пути прошел успешно, то соответствующие файловые индексы уже закэшированы в памяти.</p>
    </section>
    <section>
     <title>
      <p>Операции с элементами каталогов</p>
     </title>
     <p>Структура <code>dentry_operations</code> содержит методы, которые подсистема VFS может вызывать для элементов каталогов определенной файловой системы. Эта структура определена в файле <code>&lt;linux/dcache.h&gt;</code> следующим образом.</p>
     <p><code>struct dentry_operations {</code></p>
     <p><code> int (*d_revalidate)(struct dentry*, int);</code></p>
     <p><code> int (*d_hash)(struct dentry*, struct qstr*);</code></p>
     <p><code> int (*d_compare)(struct dentry*, struct qstr*, struct qstr*);</code></p>
     <p><code> int (*d_delete)(struct dentry*);</code></p>
     <p><code> void (*d_release)(struct dentry*);</code></p>
     <p><code> void (*d_iput)(struct dentry*, struct inode*);</code></p>
     <p><code>};</code></p>
     <p>Методы служат для следующих целей</p>
     <p>• <code>int d_revalidate(struct dentry *dentry, int flags);</code></p>
     <p>Эта функция определяет, является ли указанный объект элемента каталога действительным. Подсистема VFS вызывает эту функцию, когда она пытается использовать объект dentry из кэша dcache. Для большинства файловых систем этот метод установлен в значение <code>NULL</code>, потому что объекты dentry, которые находятся в кэше, всегда действительны.</p>
     <p>• <code>int d_hash(struct dentry *dentry, struct qstr *name);</code></p>
     <p>Эта функция создает значение хеш-ключа на основании указанного объекта dentry. Подсистема VFS вызывает эту функцию всякий раз, когда добавляет объект элемента каталога в хеш-таблицу.</p>
     <p>• <code>int d_compare(struct dentry *dentry,</code></p>
     <p><code>  struct qstr *name1, struct qstr *name2);</code></p>
     <p>Эта функция вызывается подсистемой VFS для сравнения двух имен файлов <code>name1</code> и <code>name2</code>. Большинство файловых систем используют умолчание VFS, которое соответствует простому сравнению двух строк. Для некоторых файловых систем, таких как FAT, не достаточно простого сравнения строк. Файловая система FAT не чувствительна к регистру символов в именах файлов, поэтому появляется необходимость в реализации функции, которая при сравнении не учитывает регистр символов. Эта функция вызывается при захваченной блокировке <code>dcache_lock</code><a l:href="#n73" type="note">[73]</a>.</p>
     <p>• <code>int d_delete(struct dentry *dentry);</code></p>
     <p>Эта функция вызывается подсистемой VFS, когда количество ссылок d_count указанного объекта dentry становится равным пулю. Функция вызывается при захваченной блокировке <code>dcache_lock</code>.</p>
     <p>• <code>void d_release(struct dentry *dentry);</code></p>
     <p>Эта функция вызывается подсистемой VFS, когда она собирается освободить указанный объект dentry. По умолчанию данная функция не выполняет никаких действий.</p>
     <p>• <code>void d_iput(struct dentry *dentry, struct inode *inode);</code></p>
     <p>Эта функция вызывается подсистемой VFS, когда элемент каталога теряет связь со своим файловым индексом (например, когда этот элемент каталога удаляется с диска). По умолчанию подсистема VFS просто вызывает функцию <code>iput()</code>, чтобы освободить соответствующий объект inode. Если файловая система переопределяет эту функцию, то она также должна вызывать функцию <code>iput()</code> в дополнение к специфичной для файловой системы работе.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Объект file</p>
    </title>
    <section>
     <p>Последним из основных объектов подсистемы VFS рассмотрим объект файла. Объект File используется для представления файлов, которые открыты процессом. Когда мы думаем о подсистеме VFS с точки зрения пространства пользователя, то объект файла — это то, что первое приходит в голову. Процессы непосредственно работают с файлами, а не с суперблоками, индексами или элементами каталогов. Не удивительно, что информация, которая содержится в объекте file, наиболее привычна (такие данные, как режим доступа или текущее смещение), а файловые операции очень похожи на знакомые системные вызовы, такие как <code>read()</code> и <code>write()</code>.</p>
     <p>Объект файла — это представление открытого файла, которое хранится в оперативной памяти. Объект (а не сам файл) создается в ответ на системный вызов <code>open()</code> и уничтожается в результате системного вызова <code>close()</code>. Все вызовы, связанные с файлом, на самом деле являются методами, которые определены в таблице операций с файлом. Так как несколько процессов могут одновременно открыть и использовать один и тот же файл, то для одного файла может существовать несколько объектов file. Файловый объект просто представляет открытый файл с точки зрения процесса. Этот объект содержит указатель на соответствующий элемент каталога (который, в свою очередь, указывает на файловый индекс), представляющий открытый файл. Соответствующие объекты inode и dentry, конечно, являются уникальными.</p>
     <p>Файловый объект представляется с помощью структуры <code>struct file</code>, которая определена в файле <code>&lt;linux/fs.h&gt;</code>. Рассмотрим поля этой структуры с комментариями, которые описывают назначение каждого поля.</p>
     <p><code>struct file {</code></p>
     <p><code> struct list_head       f_list;      /* список объектов file */</code></p>
     <p><code> struct dentry          *f_dentry;   /* связанный объект dentry */</code></p>
     <p><code> struct vfsmount        *f_vfsmnt;   /* связанная смонтированная</code></p>
     <p><code>                                        файловая система */</code></p>
     <p><code> struct file_operations *f_op;       /* таблица файловых операций */</code></p>
     <p><code> atomic_t               f_count;     /* счетчик ссылок на этот объект */</code></p>
     <p><code> unsigned int           f_flags;     /* флаги, указанные</code></p>
     <p><code>                                        при вызове функции open */</code></p>
     <p><code> mode_t                 f_mode;      /* режим доступа к файлу */</code></p>
     <p><code> loff_t                 f_pos;       /* смещение в файле</code></p>
     <p><code>                                        (file pointer, offset) */</code></p>
     <p><code> struct fown_struct     f_owner; /* информация о владельце для обработки</code></p>
     <p><code>                                    сигналов */</code></p>
     <p><code> unsigned int           f_uid; /* идентификатор пользователя владельца, UID */</code></p>
     <p><code> unsigned int           f_gid; /* идентификатор группы владельца, GID */</code></p>
     <p><code> int                    f_error;     /* код ошибки */</code></p>
     <p><code> struct file_ra_state   f_ra; /* состояние предварительного считывания */</code></p>
     <p><code> unsigned long          f_version;   /* номер версии */</code></p>
     <p><code> void                   *f_security; /* модуль безопасности */</code></p>
     <p><code> void                   *private_data; /* привязка для</code></p>
     <p><code>                                          драйвера терминала */</code></p>
     <p><code> struct list_head       f_ep_links;  /* список ссылок eventpoll</code></p>
     <p><code>                                        (опрос событий) */</code></p>
     <p><code> spinlock_t             f_ep_lock;   /* блокировка eventpoll */</code></p>
     <p><code> struct address_space   *f_mapping;  /* отображение в страничном кэше */</code></p>
     <p><code>};</code></p>
     <p>По аналогии с объектом элемента каталога объект файла на самом деле не соответствует никакой структуре, которая хранится на жестком диске. Поэтому в этой структуре нет никакого флага, который бы указывал, что объект изменен (dirty) и требует обратной записи на диск. Объект file указывает на связанный с ним объект dentry с помощью указателя <code>f_dentry</code>. Объект dentry в свою очередь содержит указатель на связанный с ним индекс файла, который содержит информацию о том, изменен ли файл.</p>
    </section>
    <section>
     <title>
      <p>Файловые операции</p>
     </title>
     <p>Как и для других объектов подсистемы VFS, таблица файловых операций является важной структурой. Операции, связанные со структурой <code>struct file</code>, — это знакомые системные вызовы, составляющие основу системных вызовов ОС Unix.</p>
     <p>Методы работы с файловым объектом хранятся в структуре <code>file_operations</code> и определены в файле <code>&lt;linux/fs.h&gt;</code> следующим образом.</p>
     <p><code>struct file_operations {</code></p>
     <p><code> struct module *owner;</code></p>
     <p><code> loff_t (*llseek)(struct file*, loff_t, int);</code></p>
     <p><code> ssize_t (*read)(struct file*, char*, size_t, loff_t*);</code></p>
     <p><code> ssize_t (*aio_read)(struct kiocb*, char*, size_t, loff_t);</code></p>
     <p><code> ssize_t (*write)(struct file*, const char*, size_t, loff_t*);</code></p>
     <p><code> ssize_t (*aio_write)(struct kiocb*, const char*, size_t, loff_t);</code></p>
     <p><code> int (*readdir)(struct file*, void*, filldir_t);</code></p>
     <p><code> unsigned int (*poll)(struct file*, struct poll_table_struct*);</code></p>
     <p><code> int (*ioctl)(struct inode*, struct file*, unsigned int, unsigned long);</code></p>
     <p><code> int (*mmap)(struct file*, struct vm_area_struct*);</code></p>
     <p><code> int (*open)(struct inode*, struct file*);</code></p>
     <p><code> int (*flush)(struct file*);</code></p>
     <p><code> int (*release)(struct inode*, struct file*);</code></p>
     <p><code> int (*fsync)(struct file*, struct dentry*, int);</code></p>
     <p><code> int (*aio_fsync)(struct kiocb*, int);</code></p>
     <p><code> int (*fasync)(int, struct file*, int);</code></p>
     <p><code> int (*lock)(struct file*, int, struct file_lock*);</code></p>
     <p><code> ssize_t (*readv)(struct file*, const struct iovec*,</code></p>
     <p><code>  unsigned long, loff_t*);</code></p>
     <p><code> ssize_t (*writev)(struct file*, const struct iovec*,</code></p>
     <p><code>  unsigned long, loff_t*);</code></p>
     <p><code> ssize_t (*sendfile)(struct file*, loff_t*, size_t,</code></p>
     <p><code>  read_actor_t, void*);</code></p>
     <p><code> ssize_t (*sendpage)(struct file*, struct page*, int,</code></p>
     <p><code>  size_t, loff_t*, int);</code></p>
     <p><code> unsigned long (*get_unmapped_area)(struct file*, unsigned long,</code></p>
     <p><code>  unsigned long, unsigned long, unsigned long);</code></p>
     <p><code> int (*check_flags)(int flags);</code></p>
     <p><code> int (*dir_notify)(struct file *filp, unsigned long arg);</code></p>
     <p><code> int (*flock)(struct file *filp, int cmd, struct file_lock *fl);</code></p>
     <p><code>};</code></p>
     <p>Файловые системы могут реализовать уникальную функцию для каждой из этих операций или использовать общий существующий метод. Общие методы нормально работают для обычных Unix-подобных файловых систем. Разработчики файловых систем не обязаны реализовать все эти функции, хотя основные методы должны быть реализованы. Если какой-либо метод не представляет интереса, то его можно установить в значение <code>NULL</code>.</p>
     <p>Рассмотрим каждую операцию подробнее.</p>
     <p>• <code>loff_t llseek(struct file *file, loff_t offset, int origin);</code></p>
     <p>Эта функция устанавливает значения указателя текущей позиции в файле (file pointer) в заданное значение параметра <code>offset</code>. Функция вызывается из системного вызова <code>lseek()</code>.</p>
     <p>• <code>ssize_t read(struct file *file,</code></p>
     <p><code>  char *buf, size_t count, loff_t* offset);</code></p>
     <p>Эта функция считывает <code>count</code> байт данных из указанного файла, начиная с позиции, заданной параметром <code>offset</code>, в буфер памяти, на который указывает параметр <code>buf</code>. После этого значение указателя текущей позиции в файле должно быть обновлено. Данная функция вызывается из системного вызова <code>read()</code>.</p>
     <p>• <code>ssize_t aio_read(struct kiocb *iocb,</code></p>
     <p><code>  char *buf, size_t count, loff_t offset);</code></p>
     <p>Эта функция запускает асинхронную операцию считывания <code>count</code> байт данных из файла, который описывается параметром <code>iocb</code>, в буфер памяти, описанный параметром <code>buf</code>. Эта функция вызывается из системного вызова <code>aio_read()</code>.</p>
     <p>• <code>ssize_t write(struct file *file,</code></p>
     <p><code>  const char *buf, size_t count, loff_t* offset);</code></p>
     <p>Эта функция записывает <code>count</code> байт данных в указанный файл, начиная с позиции <code>offset</code>. Данная функция вызывается из системного вызова <code>write()</code>.</p>
     <p>• <code>ssize_t aio_write(struct kiocb *iocb,</code></p>
     <p><code>  const char *buf, size_t count, loff_t offset);</code></p>
     <p>Эта функция запускает асинхронную операцию записи <code>count</code> байт данных в файл, описываемый параметром <code>iocb</code>, из буфера памяти, на который указывает параметр <code>buf</code>. Данная функция вызывается из системного вызова <code>aio_write()</code>.</p>
     <p>• <code>int readdir(struct file *file, void *dirent, filldir_t filldir);</code></p>
     <p>Эта функция возвращает следующий элемент из списка содержимого каталога. Данная функция вызывается из системного вызова <code>readdir()</code>.</p>
     <p>• <code>unsigned int poll(struct file *file,</code></p>
     <p><code>  struct poll_table_struct *poll_table);</code></p>
     <p>Эта функция переводит вызывающий процесс в состояние ожидания для ожидания действий, которые производятся с указанным файлом. Она вызывается из системного вызова <code>poll()</code>.</p>
     <p>• <code>int ioctl(struct inode *inode,</code></p>
     <p><code>  struct file *file, unsigned int cmd, signed long arg);</code></p>
     <p>Эта функция используется для того, чтобы отправлять устройствам пары значений команда/аргумент. Функция используется, когда открытый файл — это специальный файл устройства. Данная функция вызывается из системного вызова <code>ioctl()</code>.</p>
     <p>• <code>int mmap(struct file *file, struct vm_area_struct *vma);</code></p>
     <p>Эта функция отображает указанный файл на область памяти в указанном адресном пространстве и вызывается из системного вызова <code>mmap()</code>.</p>
     <p>• <code>int open(struct inode *inode, struct file *file);</code></p>
     <p>Эта функция создает новый файловый объект и связывает его с указанным файловым индексом. Она вызывается из системного вызова <code>open()</code>.</p>
     <p>• <code>int flush(struct file *file);</code></p>
     <p>Эта функция вызывается подсистемой VFS, когда уменьшается счетчик ссылок на открытый файл. Назначение данной функции зависит от файловой системы.</p>
     <p>• <code>int release(struct inode *inode, struct file *file);</code></p>
     <p>Эта функция вызывается подсистемой VFS, когда исчезает последняя ссылка на файл, например, когда последний процесс, который использовал соответствующий файловый дескриптор, вызывает функцию <code>close()</code> или завершается. Назначение этой функции также зависит от файловой системы.</p>
     <p>• <code>int fsync(struct file *file,</code></p>
     <p><code>  struct dentry *dentry, int datasync);</code></p>
     <p>Эта функция вызывается из системного вызова <code>fsync()</code> для записи на диск всех закэшированных данных файла.</p>
     <p>• <code>int aio_fsync(struct kiocb *iocb, int datasync);</code></p>
     <p>Эта функция вызывается из системного вызова <code>aio_fsync()</code> для записи на диск всех закэшированных данных файла, связанного с параметром <code>iocb</code>.</p>
     <p>• <code>int fasync(int fd, struct file *file, int on);</code></p>
     <p>Эта функция разрешает или запрещает отправку сигнала для уведомлении о событиях при асинхронном вводе-выводе.</p>
     <p>• <code>int lock(struct file *file, int cmd, struct file_lock *lock);</code></p>
     <p>Эта функция управляет файловыми блокировками для данного файла.</p>
     <p>• <code>ssize_t readv(struct file *file,</code></p>
     <p><code>  const struct iovec *vector, unsigned long count, loff_t* offset);</code></p>
     <p>Эта функция вызывается из системного вызова <code>readv()</code> для считывания данных из указанного файла в count буферов, которые описываются параметром <code>vector</code>. После этого указатель текущей позиции файла должен быть соответственным образом увеличен.</p>
     <p>• <code>ssize_t writev(struct file *file,</code></p>
     <p><code>  const struct iovec *vector, unsigned long count, loff_t *offset);</code></p>
     <p>Эта функция вызывается из системного вызова <code>writev()</code> для записи в указанный файл буферов, описанных параметром <code>vector</code>; количество буферов равно <code>count</code>. После этого должно быть соответственным образом увеличено значение текущей позиции в файле.</p>
     <p>• <code>ssize_t sendfile(struct file *file,</code></p>
     <p><code>  loff_t *of fset, size_t size, read_actor_t actor, void *target);</code></p>
     <p>Эта функция вызывается из системного вызова <code>sendfile()</code> для копирования данных из одного файла в другой. Она выполняет операцию копирования исключительно в режиме ядра и позволяет избежать дополнительного копирования данных в пространство пользователя.</p>
     <p>• <code>ssize_t sendpage(struct file *file,</code></p>
     <p><code>  struct page *page, int offset, size_t size,</code></p>
     <p><code>  loff_t *pos, int more);</code></p>
     <p>Эта функция используется для отправки данных из одного файла в другой.</p>
     <p>• <code>unsigned long get_unmapped_area(struct file*file,</code></p>
     <p><code>  unsigned long addr, unsigned long len, unsigned long offset,</code></p>
     <p><code>  unsigned long flags);</code></p>
     <p>Эта функция получает неиспользуемое пространство адресов для отображения данного файла.</p>
     <p>• <code>int check_flags(int flags);</code></p>
     <p>Эта функция используется для проверки корректности флагов, которые передаются в системный вызов <code>fcntl()</code>, при использовании команды <code>SETFL</code>. Как и в случае многих операций подсистемы VFS, для файловой системы нет необходимости реализовать функцию <code>check_flags()</code>. Сейчас это сделано только для файловой системы NFS. Эта функция позволяет файловой системе ограничить некорректные значения флагов команды <code>SETFL</code> в обобщенном системном вызове <code>fcntl()</code>. Для файловой системы NFS не разрешается использовать комбинацию флагов <code>O_APPEND</code> и <code>O_DIRECT</code>.</p>
     <p>• <code>int flock(struct file *filp, int cmd, struct file_lock *fl);</code></p>
     <p>Эта функция используется для реализации системного вызова <code>flock()</code>, который служит для выполнения рекомендованных блокировок.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Структуры данных, связанные с файловыми системами</p>
    </title>
    <p>В дополнение к фундаментальным объектам подсистемы VFS, ядро использует и другие стандартные структуры данных для управления данными, связанными с файловыми системами. Первый объект используется для описания конкретного типа файловой системы, как, например, ext3 или XFS. Вторая структура данных используется для описания каждого экземпляра смонтированной файловой системы.</p>
    <p>Поскольку операционная система Linux поддерживает множество файловых систем, то ядро должно иметь специальную структуру для описания возможностей и поведения каждой файловой системы.</p>
    <p><code>struct file_system_type {</code></p>
    <p><code> const char      *name;     /* название файловой системы */</code></p>
    <p><code> struct subsystem subsys;   /* объект подсистемы sysfs */</code></p>
    <p><code> int              fs_flags; /* флаги типа файловой системы */</code></p>
    <empty-line/>
    <p><code> /* следующая функция используется для считывания суперблока с диска */</code></p>
    <p><code> struct super_block*(*get_sb)(</code></p>
    <p><code>  struct file_system_type*, int, char*, void*);</code></p>
    <empty-line/>
    <p><code> /* эта функция используется для прекращения доступа к суперблоку */</code></p>
    <p><code> void (*kill_sb)(struct super_block*);</code></p>
    <empty-line/>
    <p><code> struct module     *owner;      /* соответствующий модуль (если есть) */</code></p>
    <p><code> struct file_system_type *next; /* следующая файловая система в списке */</code></p>
    <p><code> struct list_head   fs_supers;  /* список объектов типа суперблок */</code></p>
    <p><code>};</code></p>
    <p>Функция <code>get_sb()</code> служит для считывания суперблока с диска и заполнения объекта суперблока соответствующими данными при монтировании файловой системы. Остальные параметры описывают свойства файловой системы.</p>
    <p>Для каждого типа файловой системы существует только одна структура <code>file_system_type</code>, независимо от того, сколько таких файловых систем смонтировано и смонтирован ли хотя бы один экземпляр соответствующей файловой системы.</p>
    <p>Значительно интереснее становится, когда файловая система монтируется, при этом создается структура <code>vfsmount</code>. Эта структура используется для представления конкретного экземпляра файловой системы, или, другими словами, точки монтирования.</p>
    <p>Структура <code>vfsmount</code> определена в файле <code>&lt;linux/mount.h&gt;</code> следующим образом.</p>
    <p><code>struct vfsmount {</code></p>
    <p><code> struct list_head   mnt_hash;       /* список хеш-таблицы */</code></p>
    <p><code> struct vfsmount   *mnt_parent;     /* родительская файловая система */</code></p>
    <p><code> struct dentry     *mnt_mountpoint; /* объект элемента каталога</code></p>
    <p><code>                                       точки монтирования */</code></p>
    <p><code> struct dentry     *mnt_root;       /* объект элемента каталога корня</code></p>
    <p><code>                                        данной файловой системы */</code></p>
    <p><code> struct super_block *mnt_sb; /* суперблок данной файловой системы */</code></p>
    <p><code> struct list_head   mnt_mounts;     /* список файловых систем,</code></p>
    <p><code>                                       смонтированных к данной */</code></p>
    <p><code> struct list_head   mnt_child;      /* потомки, связанные с родителем */</code></p>
    <p><code> atomic_t           mnt_count;      /* счетчик использования */</code></p>
    <p><code> int                mnt_flags;      /* флаги монтирования */</code></p>
    <p><code> char               *mnt_devname;   /* имя смонтированного устройства */</code></p>
    <p><code> struct list_head   mnt_list;       /* список дескрипторов */</code></p>
    <p><code> struct list_head   mnt_fslinkk;    /* истекший список, специфичный</code></p>
    <p><code>                                       для файловой системы */</code></p>
    <p><code> struct namespace   *mnt_namespace; /* связанное пространство имен */</code></p>
    <p><code>};</code></p>
    <p>Самая сложная задача — это поддержание списка всех точек монтирования и взаимоотношений между данной файловой системой и другими точками монтирования. Эта информация хранится в различных связанных списках структуры <code>vfsmount</code>.</p>
    <p>Структура <code>vfsmount</code> также содержит поле <code>mnt_flags</code>. В табл. 12.1 приведен список стандартных флагов монтирования.</p>
    <empty-line/>
    <p><strong>Таблица 12.1</strong>. Список стандартных флагов монтирования</p>
    <table>
     <tr align="left">
      <th align="left" valign="top">Флаг</th>
      <th align="left" valign="top">Описание</th>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>MNT_NOSUID</code></td>
      <td align="left" valign="top">Запрещает использование флагов setuid и setgid для бинарных файлов на файловой системе</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>MNT_NODEV</code></td>
      <td align="left" valign="top">Запрещает доступ к файлам устройств на файловой системе</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>MNT_NOEXEC</code></td>
      <td align="left" valign="top">Запрещает выполнение программ на файловой системе</td>
     </tr>
    </table>
    <p>Эти флаги полезны, в основном, для сменных носителей, которым администратор не доверяет.</p>
   </section>
   <section>
    <title>
     <p>Структуры данных, связанные с процессом</p>
    </title>
    <p>Каждый процесс в системе имеет свои открытые файлы, корневую файловую систем); текущий рабочий каталог, точки монтирования и т.д. Следующие три структуры данных связывают вместе подсистему VFS и процессы, которые выполняются в системе. Это структуры <code>files_struct</code>, <code>fs_struct</code> и <code>namespace</code>.</p>
    <p>Структура <code>files_struct</code> определена в файле <code>&lt;linux/file.h&gt;</code>. Адрес этой структуры хранится в поле files дескриптора процесса. В данной структуре хранится вся информация процесса об открытых файлах и файловых дескрипторах. Эта структура, с комментариями, имеет следующий вид.</p>
    <p><code>struct files_struct {</code></p>
    <p><code> atomic_t    count;              /* счетчик ссылок на данную структуру */</code></p>
    <p><code> spinlock_t  file_lock; /* блокировка для защиты данной структуры */</code></p>
    <p><code> int         max_fds; /* максимальное количество файловых объектов */</code></p>
    <p><code> int         max_fdset;          /* максимальное количество</code></p>
    <p><code>                                    файловых дескрипторов */</code></p>
    <p><code> int         next_fd; /* номер следующего файлового дескриптора */</code></p>
    <p><code> struct file **fd;               /* массив всех файловых объектов */</code></p>
    <p><code> fd_set      *close on exec;     /* файловые дескрипторы, которые должны</code></p>
    <p><code>                                    закрываться при вызове exec() */</code></p>
    <p><code> fd_set      *open_fds; /* указатель на дескрипторы открытых файлов */</code></p>
    <p><code> fd_set      close_on_exec init; /* первоначальные файлы для закрытия</code></p>
    <p><code>                                    при вызове exec() */</code></p>
    <p><code> fd_set       open_fds_init;     /* первоначальный набор</code></p>
    <p><code>                                    файловых дескрипторов */</code></p>
    <p><code> struct file *fd_array[NR_OPEN_DEFAULT]; /* массив файловых объектов */</code></p>
    <p><code>};</code></p>
    <p>Массив <code>fd</code> указывает на список открытых файловых объектов. По умолчанию это массив <code>fd_array</code>. Так как по умолчанию значение константы <code>NR_OPEN_DEFAULT</code> равно 32, то это соответствует 32 файловым объектам. Если процесс открывает больше 32 файловых объектов, то ядро выделяет новый массив и присваивает полю <code>fd</code> указатель на него. При таком подходе доступ к небольшому количеству файловых объектов осуществляется быстро, потому что они хранятся в статическом массиве. В случае, когда процесс открывает аномально большое количество файлов, ядро может создать новый массив. Если большинство процессов в системе открывает больше 32 файлов, то для получения оптимальной производительности администратор может увеличить значение константы <code>NR_OPEN_DEFAULT</code> с помощью директивы препроцессора. Следующая структура данных, связанная с процессом, — это структура <code>fs_struct</code>, которая содержит информацию, связанную с процессом, и на которую указывает поле <code>fs</code> дескриптора процесса. Эта структура определена в файле <code>&lt;linux/fs_struct.h&gt;</code> и имеет следующий вид с поясняющими комментариями.</p>
    <p><code>struct fs_struct {</code></p>
    <p><code> atomic_t       count;        /* счетчик ссылок на структуру */</code></p>
    <p><code> rwlock_t       lock;         /* блокировка для защиты структуры */</code></p>
    <p><code> int             umask;       /* права доступа к файлу, используемые</code></p>
    <p><code>                                 по умолчанию */</code></p>
    <p><code> struct dentry   *root;       /* объект dentry корневого каталога */</code></p>
    <p><code> struct dentry   *pwd;        /* объект dentry</code></p>
    <p><code>                                 текущего рабочего каталога */</code></p>
    <p><code> struct dentry   *allroot;    /* объект dentry альтернативного корня */</code></p>
    <p><code> struct vfsmount *rootmnt;    /* объект монтирования корневого каталога */</code></p>
    <p><code> struct vfsmount *pwdmnt;     /* объект монтирования</code></p>
    <p><code>                                 текущего рабочего каталога */</code></p>
    <p><code> struct vfsmount *altrootmnt; /* объект монтирования</code></p>
    <p><code>                                 альтернативного корня */</code></p>
    <p><code>};</code></p>
    <p>Эта структура содержит текущий рабочий каталог и корневой каталог данного процесса.</p>
    <p>Третья, и последняя, структура — это структура <code>namespace</code>, которая определена в файле <code>&lt;linux/namespace.h&gt;</code> и на экземпляр которой указывает поле <code>namespace</code> дескриптора процесса. Пространства имен, индивидуальные для каждого процесса, были введены в ядрах Linux серии 2.4. Это позволило создать для каждого процесса уникальное представление о смонтированных файловых системах. Иными словами, процесс может иметь не только уникальный корневой каталог, но и полностью уникальную иерархию смонтированных файловых систем, если это необходимо. Как обычно, ниже приведена соответствующая структура данных с комментариями.</p>
    <p><code>struct namespace {</code></p>
    <p><code> atomic_t            count; /* счетчик ссылок на структуру */</code></p>
    <p><code> struct vfsmount     *root; /* объект монтирования корневого каталога */</code></p>
    <p><code> struct list_head    list;  /* список точек монтирования */</code></p>
    <p><code> struct rw_semaphore sem;   /* семафор для защиты пространства имен */</code></p>
    <p><code>};</code></p>
    <p>Поле <code>list</code> представляет собой двухсвязный список смонтированных файловых систем, которые составляют пространство имен.</p>
    <p>Каждый дескриптор процесса имеет связанные с ним рассмотренные структуры данных. Для большинства процессов их дескриптор процесса указывает на уникальную структуру <code>files_struct</code> и структуру <code>fs_struct</code>. Однако для процессов, созданных с флагами <code>CLONE_FILES</code> и <code>CLONE_FS</code>, эти структуры являются совместно используемыми<a l:href="#n74" type="note">[74]</a>. Отсюда следует, что несколько дескрипторов процессов могут указывать на одну и ту же структуру <code>files_struct</code>, или структуру <code>fs_struct</code>. Поле <code>count</code> каждой структуры содержит счетчик использования, что предотвращает уничтожение структуры данных, когда ее использует хотя бы один процесс.</p>
    <p>Структура <code>namespace</code> используется несколько по-другому. По умолчанию вес процессы совместно используют одно пространство имен (и соответственно одну иерархию файловых систем). Только когда для системного вызова <code>clone()</code> указан флаг <code>CLONE_NEWNS</code>, для процесса создается уникальная копия пространства имен. Поскольку для большинства процессов этот флаг не указывается, процессы обычно наследуют пространство имен родительского процесса. Следовательно, для большинства систем существует только одно пространство имен.</p>
   </section>
   <section>
    <title>
     <p>Файловые системы в операционной системе Linux</p>
    </title>
    <p>Операционная система Linux поддерживает большой набор файловых систем, от "родных" ext2 и ext3 до сетевых файловых систем, таких как NFS или Coda. Сейчас в официальном ядре ОС Linux поддерживается более 50 файловых систем. Уровень VFS обеспечивает все эти разнообразные файловые системы общей базой для их реализации и общим интерфейсом для работы со стандартными системными вызовами. Следовательно, уровень виртуальной файловой системы позволяет четким образом реализовать поддержку новых файловых систем в операционной системе Linux, a также дает возможность работать с этими файловыми системами с помощью стандартных системных вызовов Unix.</p>
    <p>В этой главе было описано назначение подсистемы VFS и рассмотрены соответствующие структуры данных, включая такие важные объекты, как inode, dentry и superblock. В главе 12, "Виртуальная файловая система", будет рассказано о том, как данные физически поступают на файловые системы.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 13</p>
    <p>Уровень блочного ввода-вывода</p>
   </title>
   <section>
    <p>Устройства блочного ввода-вывода (блочные устройства, устройства ввода-вывода блоками, block devices) — это аппаратные устройства, которые позволяют случайным образом (т.е. не обязательно последовательно) осуществлять доступ к фрагментам данных фиксированного размера, называемых блоками. Наиболее часто встречающееся устройство блочного ввода-вывода — это жесткий диск, но существуют и другие блочные устройства, например устройства работы с гибкими дисками, оптическими компакт-дисками (CD-ROM) и флеш-памятью. Следует обратить внимание, что файловые системы монтируются с таких устройств. Именно таким образом обычно и осуществляется доступ к устройствам блочного ввода-вывода.</p>
    <p>Другой фундаментальный тип устройства — это устройство посимвольного ввода-вывода (символьное устройство, character device, char device). Это — устройство, к которому можно обращаться, только как к последовательному потоку данных, т.е. байт за байтом. Пример символьных устройств — это последовательный порт и клавиатура. Если же устройство позволяет обращаться к данным случайным образом (не последовательно), то это блочное устройство.</p>
    <p>Существенное различие между этими типами устройств проявляется, в основном, в возможности случайного доступа к данным, т.е. в возможности производить <emphasis>поиск</emphasis> (<emphasis>seek</emphasis>) по устройству, перемещаясь из одной позиции в другую. Как пример, рассмотрим клавиатуру. Драйвер устройства клавиатуры на выходе выдает поток данных. Когда печатают слово "fox", то драйвер клавиатуры возвращает поток данных, в котором три символа идут строго в указанном порядке. Считывание символов в другом порядке или считывание какого-нибудь другого символа, кроме следующего символа в потоке, имеет немного смысла. Поэтому драйвер клавиатуры — это устройство посимвольного ввода-вывода, он позволяет на выходе получить поток символов, которые пользователь вводит на клавиатуре. Операция чтения данных с устройства возвращает сначала символ "f", затем символ "о" и в конце символ "x". Когда нажатий клавиш нет, то поток — пустой. Жесткий диск же работает по-другому. Драйвер жесткого диска может потребовать чтения содержимого определенного блока, а затем прочитать содержимое другого блока, и эти блоки не обязательно должны следовать друг за другом. Поэтому доступ к данным жесткого диска может выполняться случайным образом, а не как к потоку данных, и поэтому жесткий диск — блочное устройство.</p>
    <p>Управление блочными устройствами в ядре требует большего внимания, подготовки и работы, чем управление устройствами посимвольного ввода-вывода. Все это потому, что символьные устройства имеют всего одну позицию — текущую, в то время как блочные устройства должны иметь возможность перемещаться туда и обратно между любыми позициями на физическом носителе информации. Оказывается, что нет необходимости создавать в ядре целую подсистему для обслуживания символьных устройств, а для блочных устройств это необходимо. Такая подсистема необходима отчасти из-за сложности блочных устройств. Однако основная причина такой мощной поддержки в том, что блочные устройства достаточно чувствительны к производительности. Выжать максимум производительности из жесткого диска значительно важнее, чем получить некоторый прирост скорости при работе с клавиатурой. Более того, как будет видно дальше, сложность блочных устройств обеспечивает большой простор для таких оптимизаций. Предмет данной главы — как ядро управляет работой блочных устройств и запросами к этим устройствам. Рассматриваемая часть ядра называется <emphasis>уровнем, блочного ввода-вывода</emphasis> (<emphasis>block I/O layer</emphasis>). Интересно, что усовершенствование подсистемы блочного ввода-вывода было одной из целей разрабатываемой серии ядра 2.5. В этой главе рассматриваются все новые особенности уровня блочного ввода-вывода, которые появились в ядрах серии 2.6.</p>
   </section>
   <section>
    <title>
     <p>Анатомия блочного устройства</p>
    </title>
    <p>Наименьший адресуемый элемент блочного устройства называется сектором. Размеры секторов — это числа, которые являются целыми степенями двойки, однако наиболее часто встречающийся размер — 512 байт. Размер сектора — это физическая характеристика устройства, а сектор — фундаментальный элемент блочного устройства. Устройства не могут адресовать или другим образом работать с элементами данных, размер которых меньше, чем один сектор, тем не менее многие блочные устройства могут передавать несколько секторов за один раз. Хотя большинство блочных устройств и имеет размер сектора, равный 512 байт, все же существуют и другие стандартные размеры сектора (например, большинство компакт-дисков CD-ROM имеют размер сектора, равный 2 Кбайт).</p>
    <p>У программного обеспечения несколько другие цели, и поэтому там существует другая минимально адресуемая единица, которая называется блок. Блок— это абстракция файловой системы, т.е. все обращения к файловым системам могут выполняться только с данными, кратными размеру блока. Хотя физические устройства сами по себе адресуются на уровне секторов, ядро выполняет все дисковые операции в терминах блоков. Так как наименьший возможный адресуемый элемент —- это сектор, то размер блока не может быть меньше размера одного сектора и должен быть кратен размеру сектора. Более того, для ядра (так же как и для аппаратного обеспечения в случае секторов) необходимо, чтобы размер блока был целой степенью двойки. Ядро также требует, чтобы блок имел размер, не больший, чем размер страницы памяти (см. главу 11, "Управление памятью" и главу 12, "Виртуальная файловая система")<a l:href="#n75" type="note">[75]</a>.</p>
    <p>Поэтому размер блока равен размеру сектора, умноженному на число, которое является целой степенью двойки. Наиболее часто встречающиеся размеры блока — это 512 байт, один килобайт и четыре килобайта.</p>
    <p>Часто сбивает с толку то, что некоторые люди называют секторы и блоки по- разному. Секторы, наименьшие адресуемые элементы устройства, иногда называют "аппаратными секторами" (hardware sector) или "блоками аппаратного устройства" (device block). В то время как блоки, наименьшие адресуемые единицы файловых систем, иногда называются "блоками файловой системы" (filesystem block) или "блоками ввода-вывода" (I/O block). В этой главе будут использованы термины "сектор" (sector) и "блок" (block), однако следует помнить и о других возможных названиях. На рис. 13.1 показана диаграмма соответствия между секторами и блоками.</p>
    <image l:href="#img_19.jpeg"/>
    <p><strong>Рис. 13.1</strong>. Связь между секторами и бликами</p>
    <p>Существует еще одна часто встречающаяся терминология. По крайней мере в отношении к жестким дискам, используются термины <emphasis>кластеры</emphasis> (clusters), <emphasis>цилиндры</emphasis> (cylinder, дорожка) и <emphasis>головки</emphasis> (head). Такие обозначения являются специфическими только для некоторых типов блочных устройств, они, в основном, невидимы для пользовательских программ и в этой главе рассматриваться не будут. Причина, по которой секторы являются важными для ядра, состоит в том, что все операции ввода-вывода должны выполняться в единицах секторов. Поэтому более высокоуровневые концепции ядра, которые основаны на блоках, являются надстройками над секторами.</p>
   </section>
   <section>
    <title>
     <p>Буферы и заголовки буферов</p>
    </title>
    <p>Когда блок хранится в памяти (скажем, после считывания или в ожидании записи), то он хранится в структуре данных, называемой <emphasis>буфером</emphasis> (<emphasis>buffer</emphasis>). Каждый буфер связан строго с одним блоком. Буфер играет роль объекта, который представляет блок в оперативной памяти. Вспомним, что блок состоит из одного или больше секторов, но по размеру не может быть больше одной страницы памяти. Поэтому одна страница памяти может содержать один или больше блоков. Поскольку для ядра требуется некоторая управляющая информация, связанная с данными (например, какому блочному устройству и какому блоку соответствует буфер), то каждый буфер связан со своим дескриптором. Этот дескриптор называется <emphasis>заголовком буфера</emphasis> (<emphasis>buffer head</emphasis>) и представляется с помощью структуры <code>struct buffer_head</code>.</p>
    <p>Структура <code>buffer_head</code> содержит информацию, которая необходима ядру для управления буферами и определена в файле <code>&lt;linux/buffer_head.h&gt;</code>.</p>
    <p>Рассмотрим эту структуру с комментариями, которые описывают назначение каждого поля.</p>
    <p><code>struct buffer_head {</code></p>
    <p><code> unsigned long       b_state;         /* флаги состояния буфера */</code></p>
    <p><code> atomic_t            b_count;         /* счетчик использования буфера */</code></p>
    <p><code> struct buffer_head  *b_this_page;    /* список буферов в текущей</code></p>
    <p><code>                                         странице памяти */</code></p>
    <p><code> struct page         *b_page;  /* соответствующая страница памяти */</code></p>
    <p><code> sector_t            b_blocknr;       /* логический номер блока */</code></p>
    <p><code> u32                 b_size;          /* размер блока (в байтах) */</code></p>
    <p><code> char                *b_data;  /* указатель на буфер в странице памяти */</code></p>
    <p><code> struct block_device *b_bdev;  /* соответствующее блочное устройство */</code></p>
    <p><code> bh_end_io_t         *b_end_io;       /* метод завершения ввода-вывода */</code></p>
    <p><code> void                *b_private;      /* данные метода завершения */</code></p>
    <p><code> struct list_head    b_assoc_buffers; /* список связанных отображений */</code></p>
    <p><code>};</code></p>
    <p>Поле b_<code>state</code> содержит состояние определенного буфера. Это значение может содержать один или несколько флагов, которые перечислены в табл. 13.1. Возможные значения флагов описаны в виде перечисления <code>bh_state_bits</code>, которое описано в файле <code>&lt;linux/buffer_head.h&gt;</code>.</p>
    <empty-line/>
    <p><strong>Таблица 13.1</strong>. Значения флагов поля <code>bh_state</code></p>
    <table>
     <tr align="left">
      <th align="left" valign="top">Флаг состояния</th>
      <th align="left" valign="top">Назначение</th>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>BH_Uptodate</code></td>
      <td align="left" valign="top">Буфер содержит действительные данные</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>BH_Dirty</code></td>
      <td align="left" valign="top">Буфер изменен (содержимое буфера новее соответствующих данных на диске, и поэтому буфер должен быть записан на диск)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>BH_Lock</code></td>
      <td align="left" valign="top">Для буфера выполняется операция чтения-записи дисковых данных, и буфер заблокирован, чтобы предотвратить конкурентный доступ</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>BH_Req</code></td>
      <td align="left" valign="top">Буфер включен в запрос</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>BH_Mapped</code></td>
      <td align="left" valign="top">Буфер является действительным и отображается на дисковый блок</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>BH_New</code></td>
      <td align="left" valign="top">Буфер только что выделен и к нему еще не было доступа</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>BH_Async_Read</code></td>
      <td align="left" valign="top">Для буфера выполняется асинхронная операция чтения</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>BH_Async_Write</code></td>
      <td align="left" valign="top">Для буфера выполняется асинхронная операция записи</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>BH_Delay</code></td>
      <td align="left" valign="top">С буфером еще не связан дисковый блок</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>BH_Boundary</code></td>
      <td align="left" valign="top">Буфер является последним в последовательности смежных блоков — следующий за ним блок не является смежным с этой серией</td>
     </tr>
    </table>
    <p>Перечисление <code>bh_state_bits</code> также содержит в качестве последнего элемента флаг <code>BH_PrivateStart</code>. Этот флаг не является разрешенным значением флага, а соответствует первому биту, который можно использовать по усмотрению разработчиков кода. Все биты, номер которых больше или равен значению <code>BH_PrivateStart</code>, не используются подсистемой блочного ввода-вывода и безопасно могут использоваться драйверами, которым необходимо хранить информацию в поле <code>b_state</code>.</p>
    <p>Флаги, которые используются драйверами, могут быть определены на основании значения этого флага, что позволяет гарантированно избежать перекрытия с битами, которые официально используются уровнем блочного ввода-вывода.</p>
    <p>Поле <code>b_count</code> — это счетчик использования буфера. Значение этого поля увеличивается и уменьшается двумя функциями, которые определены в файле <code>&lt;linux/buffer_head.h&gt;</code> следующим образом.</p>
    <p><code>static inline void get_bh(struct buffer_head *bh) {</code></p>
    <p><code> atomic_inc{&amp;bh-&gt;b_count);</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>static inline void put_bh(struct buffer_head *bh) {</code></p>
    <p><code> atomic_dec(&amp;bh-&gt;b_count);</code></p>
    <p><code>}</code></p>
    <p>Перед тем как манипулировать заголовком буфера, необходимо увеличить значение счетчика использования с помощью функции <code>get_bh()</code>, что гарантирует, что во время работы буфер не будет освобожден. Когда работа с заголовком буфера закончена, необходимо уменьшить значение счетчика, ссылок с помощью функции <code>put_bh()</code>.</p>
    <p>Физический блок на жестком диске, которому соответствует буфер, — это блок с логическим номером <code>b_blocknr</code>, который находится на блочном устройстве <code>b_bdev</code>.</p>
    <p>Физическая страница памяти, в которой хранятся данные буфера, соответствует значению поля <code>b_page</code>. Поле <code>b_data</code> — это указатель прямо на данные блока (которые хранятся где-то в странице памяти <code>b_page</code>), размер блока хранится в поле <code>b_size</code>. Следовательно, блок хранится в памяти, начиная с адреса <code>b_data</code> и заканчивая адресом (<code>b_data + b_size</code>).</p>
    <p>Назначение заголовка буфера — это описание отображения между блоком на диске и буфером в физической памяти (т.е. последовательностью байтов, которые хранятся в указанной странице памяти). Выполнение роли дескриптора отображения буфер-блок — единственное назначение этой структуры данных ядра.</p>
    <p>В ядрах до серии 2.6 заголовок буфера был значительно более важной структурой данных. По существу, это была единица ввода-вывода данных в ядре. Он не только выполнял роль дескриптора для отображения буфер-блок-страница физической памяти, но и выступал контейнером для всех операций блочного ввода-вывода. Это приводило к двум проблемам. Первая проблема заключалась в том, что заголовок буфера был большой и громоздкой структурой данных (сегодня он несколько уменьшился в размерах), а кроме того, выполнение операций блочного ввода-вывода в терминах заголовков буферов было непростой и довольно непонятной задачей. Вместо этого, ядру лучше работать со страницами памяти, что одновременно и проще и позволяет получить большую производительность. Использовать большой заголовок буфера, описывающий отдельный буфер (который может быть размером со страницу памяти), — неэффективно. В связи с этим в ядрах серии 2.6 было сделано много работы, чтобы дать возможность ядру выполнять операции непосредственно со страницами памяти и пространствами адресов, вместо операций с буферами. Некоторые из этих операций обсуждаются в главе 15, "Страничный кэш и обратная запись страниц", где также рассматривается структура <code>address_space</code> и демоны <code>pdflush</code>.</p>
    <p>Вторая проблема, связанная с заголовками буферов, — это то, что они описывают только один буфер. Когда заголовок буфера используется в качестве контейнера для операций ввода-вывода, то это требует, чтобы ядро разбивало потенциально большую операцию блочного ввода-вывода на множество мелких структур <code>buffer_head</code>, что в свою очередь приводит к ненужным затратам памяти для храпения структур данных. В результате, основной целью при создании серии ядра 2.5 была разработка нового гибкого и быстрого контейнера для операций блочного ввода-вывода. В результат появилась структура <code>bio</code>, которая будет рассмотрена в следующем разделе.</p>
   </section>
   <section>
    <title>
     <p>Структура <code>bio</code></p>
    </title>
    <section>
     <p>Основным контейнером для операций ввода-вывода в ядре является структура bio, которая определена в файле <code>&lt;linux/bio.h&gt;</code>. Эта структура представляет активные операции блочного ввода-вывода в виде списка <emphasis>сегментов</emphasis> (<emphasis>segment</emphasis>). Сегмент — это участок буфера, который является непрерывным в физической памяти, т.е. отдельные буферы не обязательно должны быть непрерывными в физической памяти. Благодаря тому, что буфер может представляться в виде нескольких участков, структура <code>bio</code> даст возможность выполнять операции блочного ввода-вывода, даже если данные одного буфера хранятся в разных местах памяти. Ниже показана структура <code>bio</code> с комментариями, описывающими назначение каждого поля.</p>
     <p><code>struct bio {</code></p>
     <p><code> sector_t            bi_sector; /* соответствующий сектор на диске */</code></p>
     <p><code> struct bio          *bi_next;         /* список запросов */</code></p>
     <p><code> struct block_device *bi_bdev;  /* соответствующее блочное устройство */</code></p>
     <p><code> unsigned long       bi_flags;         /* состояние и флаги команды */</code></p>
     <p><code> unsigned long       bi_rw;            /* чтение или запись? */</code></p>
     <p><code> unsigned short      bi_vcnt;          /* количество структур bio vec</code></p>
     <p><code>                                          в массиве bi_io_vec */</code></p>
     <p><code> unsigned short      bi_idx;    /* текущий индекс в массиве bi_io_vec */</code></p>
     <p><code> unsigned short      bi_phys_segments; /* количество сегментов</code></p>
     <p><code>                                          после объединения */</code></p>
     <p><code> unsigned short      bi_hw_segments;   /* количество сегментов после</code></p>
     <p><code>                                          перестройки отображения */</code></p>
     <p><code> unsigned int        bi_size;          /* объем данных для ввода-вывода */</code></p>
     <p><code> unsigned int        bi_hw_front_size; /* размер первого</code></p>
     <p><code>                                          объединяемого сегмента */</code></p>
     <p><code> unsigned int        bi_hw_front_size; /* размер последнего объединяемого</code></p>
     <p><code>                                          сегмента */</code></p>
     <p><code> unsigned int        bi_max_vecs;      /* максимально возможное количество</code></p>
     <p><code>                                          структур bio_vecs */</code></p>
     <p><code> struct bio_vec      *bi_io_vec;       /* массив структур bio_vec */</code></p>
     <p><code> bio_end_io_t        *bi_end_io;       /* метод завершения ввода-вывода */</code></p>
     <p><code> atomic_t            bi_cnb;           /* счетчик использования */</code></p>
     <p><code> void                *bi_private;      /* поле для информации создателя */</code></p>
     <p><code> bio_destructor_t    *bi_destructor;   /* деструктор */</code></p>
     <p><code>};</code></p>
     <p>Главное назначение структуры <code>bio</code> — это представление активной (выполняющейся) операции блочного ввода-вывода. В связи с этим большинство полей этой структуры являются служебными. Наиболее важные поля — это <code>bi_io_vecs</code>, <code>bi_vcnt</code> и <code>bi_idx</code>.</p>
     <p>Поле <code>bi_io_vecs</code> указывает на начало массива структур <code>bio_vec</code>. Эти структуры используются в качестве списка отдельных сегментов в соответствующей операции блочного ввода-вывода. Каждый экземпляр структуры <code>bio_vec</code> представляет собой вектор следующего вида: <code>&lt;страница памяти, смещение, размер&gt;</code>, который описывает определенный сегмент, соответственно страницу памяти, где этот сегмент хранится, положение блока — смещение внутри страницы — и размер блока. Массив рассмотренных векторов описывает весь буфер полностью. Структура <code>bio_vec</code> определена в файле <code>&lt;linux/bio.h&gt;</code> следующим образом.</p>
     <p><code>struct bio_vec {</code></p>
     <p><code> /* указатель на страницу физической памяти, где находится этот буфер */</code></p>
     <p><code> struct page *bv_page;</code></p>
     <empty-line/>
     <p><code> /* размер буфера в байтах */</code></p>
     <p><code> unsigned int bv_len;</code></p>
     <empty-line/>
     <p><code> /* смещение в байтах внутри страницы памяти, где находится буфер */</code></p>
     <p><code> unsigned int bv_offset;</code></p>
     <p><code>};</code></p>
     <p>Для каждой операции блочного ввода-вывода создается массив из <code>bi_vcnt</code> элементов типа <code>bio_vec</code>, начало которого содержится в поле <code>bi_io_vecs</code>. В процессе выполнения операции блочного ввода-вывода поле <code>bi_idx</code> используется для указания на текущий элемент массива.</p>
     <p>В общем, каждый запрос на выполнение блочного ввода-вывода представляется с помощью структуры <code>bio</code>. Каждый такой запрос состоит из одного или более блоков, которые хранятся в массиве структур <code>bio_vec</code>. Каждая из этих структур представляет собой вектор, который описывает положение в физической памяти каждого сегмента запроса. На первый сегмент для операции ввода-вывода указывает поле <code>bi_io_vec</code>. Каждый следующий сегмент следует сразу за предыдущим. Всего в массиве <code>bi_vcnt</code> сегментов. В процессе того, как уровень блочного ввода-вывода обрабатывает сегменты запроса, обновляется значение поля <code>bi_idx</code>, чтобы его значение соответствовало номеру текущего сегмента. На рис. 13.2 показана связь между структурами <code>bio</code>, <code>bio_vec</code> и <code>page</code>.</p>
     <image l:href="#img_20.jpeg"/>
     <p><strong>Рис. 13.2</strong>. Связь между структурами <code>struct bio</code>, <code>struct bio_vec</code> и <code>struct page</code></p>
     <p>Поле <code>bi_idx</code> указывает на текущую структуру <code>bio_vec</code> в массиве, что позволяет уровню блочного ввода-вывода поддерживать частично выполненные операции блочного ввода-вывода. Однако более важное использование состоит в том, что драйверы таких устройств, как RAID (Redundant Array of Inexpensive/Independent Disks, массив недорогих/независимых дисковых устройств с избыточностью — специальный способ использования жестких дисков, при котором один логический том может быть распределен но нескольким физическим дискам для увеличения надежности или производительности), могут одну структуру <code>bio</code>, которая изначально была адресована одному устройству, разбивать на несколько частей, которые предназначаются различным дискам RAID массива. Все, что необходимо сделать драйверу RAID, это создать необходимое количество копий структуры <code>bio</code>, которая предназначалась одному устройству, и изменить для каждой копии значение поля <code>bi_idx</code>, чтобы оно указывало на ту часть массива, откуда каждый диск должен начать свою операцию ввода-вывода.</p>
     <p>Структура <code>bio</code> содержит счетчик использования, который хранится в поле <code>bi_cnt</code>. Когда значение этого поля становится равным нулю, структура удаляется, и занятая память освобождается. Следующие две функции позволяют управлять счетчиком использования.</p>
     <p><code>void bio_get(struct bio *bio);</code></p>
     <p><code>void bio_put(struct bio *bio);</code></p>
     <p>Первая увеличивает на единицу значение счетчика использования, а вторая — уменьшает значение этого счетчика на единицу и, если это значение становится равным нулю, уничтожает соответствующую структуру <code>bio</code>. Перед тем как работать с активной структурой <code>bio</code>, необходимо увеличить счетчик использования, чтобы гарантировать, что экземпляр структуры не будет удален во время работы. После окончания работы необходимо уменьшить счетчик использования.</p>
     <p>И наконец, поле <code>bio_private</code> — это поле данных создателя (владельца) структуры. Как правило, это поле необходимо считывать или записывать только тому, кто создал данный экземпляр структуры <code>bio</code>.</p>
    </section>
    <section>
     <title>
      <p>Сравнение старой и новой реализаций</p>
     </title>
     <p>Между заголовками буферов и новой структурой <code>bio</code> существуют важные отличия. Структура <code>bio</code> представляет операцию ввода-вывода, которая может включать одну или больше страниц в физической памяти. С другой стороны, заголовок буфера связан с одним дисковым блоком, который занимает не более одной страницы памяти. Поэтому использование заголовков буферов приводит к ненужному делению запроса ввода-вывода на части, размером в один блок, только для того, чтобы их потом снова объединить. Работа со структурами bio выполняется быстрее, эта структура может описывать несмежные блоки и не требует без необходимости разбивать операции ввода-вывода на части.</p>
     <p>Переход от структуры <code>struct buffer_head</code> к структурам <code>struct bio</code> позволяет получить также и другие преимущества.</p>
     <p>• Структура <code>bio</code> может легко представлять верхнюю память (см. главу 11), так как структура <code>struct bio</code> работает только со страницами физической памяти, а не с указателями.</p>
     <p>• Структура <code>bio</code> может представлять как обычные страничные операции ввода- вывода, так и операции непосредственного (direct) ввода-вывода (т.е. те, которые не проходят через страничный кэш; страничный кэш обсуждается в главе 15).</p>
     <p>• Структура <code>bio</code> позволяет легко выполнять операции блочного ввода-вывода типа распределения-аккумуляции (scatter-gather), в которых данные находятся в нескольких страницах физической памяти.</p>
     <p>• Структура <code>bio</code> значительно проще заголовка буфера, потому что она содержит только минимум информации, необходимой для представления операции блочного ввода-вывода, а не информацию, которая связана с самим буфером.</p>
     <p>Тем не менее заголовки буферов все еще необходимы для функций, которые выполняют отображение дисковых блоков на страницы физической памяти. Структура <code>bio</code> не содержит никакой информации о состоянии буфера, это просто массив векторов, которые описывают один или более сегментов данных одной операции блочного ввода-вывода, плюс соответствующая дополнительная информация. Структура <code>buffer_head</code> необходима для хранения информации о буферах. Применение двух отдельных структур позволяет сделать размер обеих этих структур минимальным.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Очереди запросов</p>
    </title>
    <section>
     <p>Для блочных устройств поддерживаются <emphasis>очереди запросов</emphasis> (<emphasis>request queue</emphasis>), в которых хранятся ожидающие запросы на выполнение операций блочного ввода-вывода. Очередь запросов представляется с помощью структуры request_queue, которая определена в файле <code>&lt;linux/blkdev.h&gt;</code>. Очередь запросов содержит двухсвязный список запросов и соответствующую управляющую информацию. Запросы добавляются в очередь кодом ядра более высокого уровня, таким как файловые системы. Пока очередь запросов не пуста, драйвер блочного устройства, связанный с очередью, извлекает запросы из головы очереди и отправляет их на соответствующее блочное устройство. Каждый элемент списка запросов очереди— это один запрос, представленный с помощью структуры <code>struct request</code>.</p>
    </section>
    <section>
     <title>
      <p>Запросы</p>
     </title>
     <p>Отдельные запросы представляются с помощью структуры <code>struct request</code>, которая тоже определена в файле <code>&lt;linux/blkdev.h&gt;</code>. Каждый запрос может состоять из более чем одной структуры <code>bio</code>, потому что один запрос может содержать обращение к нескольким смежным дисковым блокам. Обратите внимание, что хотя блоки на диске и должны быть смежными, данные этих блоков не обязательно должны быть смежными в физической памяти — каждая структура <code>bio</code> может содержать несколько сегментов (вспомните, сегменты — это непрерывные участки памяти, в которых хранятся данные блока), а запрос может состоять из нескольких структур <code>bio</code>.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Планировщики ввода-вывода</p>
    </title>
    <section>
     <p>Простая отправка запросов на устройство ввода-вывода в том же порядке, в котором эти запросы направляет ядро, приводит к очень плохой производительности. Одна из наиболее медленных операций, которые <emphasis>вообще</emphasis> могут быть в компьютере,— это поиск по жесткому диску. Операция поиска — это позиционирование головки жесткого диска на определенный блок, которое может запять много миллисекунд. Минимизация количества операций поиска чрезвычайно критична для производительности всей системы.</p>
     <p>Поэтому ядро не отправляет все запросы на выполнение операций блочного ввода-вывода жесткому диску в том же порядке, в котором они были получены, или сразу же, как только они были получены. Вместо этого, оно выполняет так называемые операции <emphasis>слияния</emphasis> (<emphasis>объединения</emphasis>, <emphasis>merging</emphasis>) и <emphasis>сортировка</emphasis> (<emphasis>sorting</emphasis>), которые позволяют значительно увеличить производительность всей системы<a l:href="#n76" type="note">[76]</a>. Подсистема ядра, которая выполняет эти операции называется <emphasis>планировщиком ввода-вывода</emphasis> (<emphasis>I/O scheduler</emphasis>).</p>
     <p>Планировщик ввода-вывода разделяет дисковые ресурсы между ожидающими в очереди запросами ввода-вывода. Это выполняется путем объединения и сортировки запросов ввода-вывода, которые находятся в очереди. Планировщик ввода-вывода не нужно путать с планировщиком выполнения процессов (см. главу 4, "Планирование выполнения процессов"), который делит ресурсы процессора между процессами системы. Эти две подсистемы похожи, но это — не одно и то же. Как планировщик выполнения процессов, так и планировщик ввода-вывода выполняют виртуализацию ресурсов для нескольких объектов. В случае планировщика процессов выполняется виртуализация процессора, который совместно используется процессами системы. Это обеспечивает иллюзию того, что процессы выполняются одновременно, и является основой многозадачных операционных систем с разделением времени, таких как Unix. С другой стороны, планировщики ввода-вывода выполняют виртуализацию блочных устройств для ожидающих выполнения запросов блочного ввода-вывода. Это делается для минимизации количества операций поиска по жесткому диску и для получения оптимальной производительности дисковых операций.</p>
    </section>
    <section>
     <title>
      <p>Задачи планировщика ввода-вывода</p>
     </title>
     <p>Планировщик ввода-вывода работает управляя очередью запросов блочного устройства. Он определяет, в каком порядке должны следовать запросы в очереди и в какое время каждый запрос должен передаваться на блочное устройство. Управление производится с целью уменьшения количества операций поиска по жесткому диску, что значительно повышает <emphasis>общее быстродействие</emphasis>. Определение "общее" здесь существенно. Планировщик ввода-вывода ведет себя "нечестно" по отношению к некоторым запросам и за счет этого повышает производительность системы в целом.</p>
     <p>Планировщики ввода-вывода выполняют две основные задачи: объединение и сортировку. Объединение — это слияние нескольких запросов в один. В качестве примера рассмотрим запрос, который поставлен в очередь кодом файловой системы, например чтобы прочитать порцию данных из файла (конечно, на данном этапе все уже выполняется на уровне секторов и блоков, а не на уровне файлов). Если в очереди уже есть запрос на чтение из соседнего сектора диска (например, другой участок того же файла), то два запроса могут быть объединены в один запрос для работы с одним или несколькими, расположенными рядом, секторами диска. Путем слияния запросов планировщик ввода-вывода уменьшает затраты ресурсов, связанные с обработкой нескольких запросов, до уровня необходимого на обработку одного запроса. Наиболее важно здесь то, что диск выполняет всего одну команду и обслуживание нескольких запросов может быть выполнено вообще без применения поиска. Следовательно, слияние запросов уменьшает накладные расходы и минимизирует количество операций поиска.</p>
     <p>Теперь предположим, что наш запрос на чтение помещается в очередь запросов, но там нет других запросов на чтение соседних секторов. Поэтому нет возможности выполнить объединение этого запроса с другими запросами, находящимися в очереди. Можно просто поместить запрос в конец очереди. Но что если в очереди есть запросы к близкорасположенным секторам диска? Не лучше ли будет поместить новый запрос в очередь где-то рядом с запросами к физически близко расположенным секторам диска. На самом деле планировщики ввода-вывода именно так и делают, Вся очередь запросов поддерживается в отсортированном состоянии по секторам, чтобы последовательность запросов в очереди (насколько это возможно) соответствовала линейному движению по секторам жесткого диска. Цель состоит в том, чтобы не только уменьшить количество перемещений в каждом индивидуальном случае, но и минимизировать общее количество операций поиска таким образом, чтобы головка двигалась по прямой линии. Это аналогично алгоритму, который используется в лифте (elevator) — лифт не прыгает между этажами. Вместо этого он плавно пытается двигаться в одном направлении. Когда лифт доходит до последнего этажа в одном направлении, он начинает двигаться в другую сторону. Из-за такой аналогии планировщик ввода-вывода (а иногда только алгоритм сортировки) называют <emphasis>лифтовым планировщиком</emphasis> (<emphasis>алгоритмом лифта</emphasis>, <emphasis>elevator</emphasis>).</p>
    </section>
   </section>
   <section>
    <title>
     <p>Лифтовой алгоритм Линуса</p>
    </title>
    <section>
     <p>Рассмотрим некоторые планировщики ввода-вывода, применяемые в реальной жизни. Первый планировщик ввода-вывода, который мы рассмотрим, называется <emphasis>Linus Elevator</emphasis> (<emphasis>лифтовой алгоритм Линуса</emphasis>). Это не опечатка, действительно существует лифтовой планировщик, разработанный Линусом Торвальдсом и названный в его честь! Это основной планировщик ввода-вывода в ядре 2.4. В ядре 2.6 его заменили другими планировщиками, которые мы ниже рассмотрим. Однако поскольку этот алгоритм значительно проще новых и в то же время позволяет выполнять почти те же функции, то он заслуживает внимания.</p>
     <p>Лифтовой алгоритм Линуса позволяет выполнять как объединение, так и сортировку запросов. Когда запрос добавляется в очередь, вначале он сравнивается со всеми ожидающими запросами, чтобы обнаружить все возможные кандидаты на объединение. Алгоритм Линуса выполняет два типа объединения: <emphasis>добавление в начало запроса</emphasis> (<emphasis>front merging</emphasis>) и <emphasis>добавление в конец запроса</emphasis> (<emphasis>back merging</emphasis>). Тип объединения соответствует тому, с какой стороны найдено соседство. Если новый запрос следует перед существующим, то выполняется вставка в начало запроса. Если новый запрос следует сразу за существующим — добавление выполняется в конец очереди. В связи с тем, что секторы, в которых хранится файл, расположены по мере увеличения номера сектора и операции ввода-вывода чаще всего выполняются от начала файла до конца, а не наоборот, то при обычной работе вставка в начало запроса встречается значительно реже, чем вставка в конец. Тем не менее алгоритм Линуса проверяет и выполняет оба типа объединения.</p>
     <p>Если попытка объединения была неудачной, то определяется возможное место вставки запроса в очередь (положение в очереди, в котором новый запрос наилучшим образом вписывается по номеру сектора между окружающими запросами). Если такое положение находится, то новый запрос помещается туда. Если подходящего места не найдено, то запрос помещается в конец очереди. В дополнение к этому, если в очереди найден запрос, который является достаточно старым, то новый запрос также добавляется в конец очереди. Это предотвращает ситуацию, в которой наличие большого количества запросов к близко расположенным секторам приводит к недостатку обслуживания других запросов. К сожалению, такая проверка "на старость" не очень эффективна. В рассмотренном алгоритме не предпринимается никаких попыток обслуживания запросов в заданных временных рамках, а просто прекращается процесс сортировки-вставки при наличии определенной задержки. Это в свою очередь приводит к задержке в обслуживании, что было веской причиной для доработки планировщика ввода-вывода ядра 2.4.</p>
     <p>Итак, когда запрос добавляется в очередь возможны четыре типа действий. Вот эти действия в необходимой последовательности.</p>
     <p>• Если запрос к соседнему сектору находится в очереди, то существующий запрос и новый объединяются в один.</p>
     <p>• Если в очереди существует достаточно старый запрос, то новый запрос помещается в конец очереди, чтобы предотвратить отказ обслуживания для других запросов, которые долгое время находятся в очереди.</p>
     <p>• Если для секторов данного запроса в очереди существует позиция, которая соответствует рациональному перемещению между секторами, то данный запрос помещается в эту позицию, что позволяет поддерживать очередь в отсортированном состоянии.</p>
     <p>• И наконец, если такая позиция не найдена, то запрос помещается в конец очереди.</p>
    </section>
    <section>
     <title>
      <p>Планировщик ввода-вывода с лимитом по времени</p>
     </title>
     <p>Планировщик ввода-вывода с лимитом по времени (Deadline I/O scheduler, deadline-планировщик ввода-вывода) разработан с целью предотвращения задержек обслуживания, которые могут возникать для алгоритма Линуса. Если задаться целью только минимизировать количество операций поиска, то при большом количестве операций ввода-вывода из одной области диска могут возникать задержки обслуживания для операций с другими областями диска, причем на неопределенное время. Более того, поток запросов к одной и той же области диска может привести к тому, что запросы к области диска, которая находится далеко от первой, никогда не будут обработаны. Такой алгоритм не может обеспечить равнодоступность ресурсов.</p>
     <p>Хуже того, общая проблема задержки обслуживания запросов приводит к частной проблеме з<emphasis>адержки обслуживания чтения при обслуживании записи</emphasis> (<emphasis>writes-starving-reads</emphasis>). Обычно операции записи могут быть отправлены на обработку диском в любой момент, когда ядру это необходимо, причем это выполняется полностью асинхронно по отношению к пользовательской программе, которая сгенерировала запрос записи. Обработка же операций чтения достаточно сильно отличается. Обычно, когда пользовательское приложение отправляет запрос на чтение, это приложение блокируется до тех пор, пока запрос не будет выполнен, т.е. запросы чтения возникают синхронно по отношению к приложению, которое эти запросы генерирует. В связи с этим время реакции системы, в основном, не зависит от латентности записи (времени задержки, которое необходимо на выполнение запроса записи), а задержки чтения (время, которое необходимо на выполнение операции чтения) очень важно минимизировать. Латентность записи мало влияет на производительность пользовательских программ<a l:href="#n77" type="note">[77]</a>, но эти программы должны "с дрожащими руками" ждать завершение каждого запроса чтения. Следовательно, задержки чтения очень важны для производительности системы.</p>
     <p>Проблему усугубляет то, что запросы чтения обычно зависят друг от друга. Например, рассмотрим чтение большого количества файлов. Каждая операция чтения выполняется небольшими порциями, которые соответствуют размеру буфера. Приложение не станет считывать следующую порцию данных (или следующий файл), пока предыдущая порция данных не будет считана с диска и возвращена приложению. Следовательно, если существует задержка в обслуживании одного запроса чтения, то для программы эти задержки складываются, и общая задержка может стать недопустимой. Принимая во внимание, что синхронность и взаимозависимость запросов чтения приводят к большим задержкам обработки этих запросов (что в свою очередь сильно влияет на производительность системы), в планировщике ввода-вывода с лимитом по времени были добавлены некоторые функции, которые позволяют гарантированно минимизировать задержки в обработке запросов вообще и в обработке запросов чтения в частности.</p>
     <p>Следует обратить внимание, что уменьшение времени задержки в обслуживании может быть выполнено только за счет уменьшения общего быстродействия системы. Даже для алгоритма Линуса такой компромисс существует, хотя и в более мягкой форме. Алгоритм Линуса мог бы обеспечить и большую общую пропускную способность (путем уменьшения количества операций поиска), если бы запросы всегда помещались в очередь в соответствии с номерами секторов и не выполнялась проверка на наличие старых запросов и вставка в конец очереди. Хотя минимизация количества операций поиска и важна, тем не менее неопределенное время задержки тоже не очень хорошая вещь. Поэтому deadline-планировщик и выполняет много работы для уменьшения задержек в обслуживании. Достаточно сложно одновременно обеспечить равнодоступность и максимизировать общую пропускную способность.</p>
     <p>В планировщике ввода-вывода, с лимитом по времени с запросом связано предельное время ожидания (expiration time). По умолчанию этот момент времени равен 500 миллисекунд в будущем для запросов чтения и 5 секунд в будущем для запросов записи. Планировщик ввода-вывода с лимитом по времени работает аналогично планировщику Линуса — он также поддерживает очередь запросов в отсортированном состоянии в соответствии с физическим расположением сектора на диске. Эта очередь называется отсортированной (sorted queue). Когда запрос помещается в отсортированную очередь, то deadline-планировщик ввода-вывода выполняет объединение и вставку запросов так же, как это делается в лифтовом алгоритме Линуса<a l:href="#n78" type="note">[78]</a>. Кроме того, планировщик с лимитом по времени помещает каждый запрос и во вторую очередь, в зависимости от типа запроса. Запросы чтения помещаются в специальную очередь FIFO запросов чтения, а запросы записи— в очередь FIFO запросов записи. В то время как обычная очередь отсортирована по номеру сектора на диске, две очереди FIFO (first-in first-out— первым поступил, первым обслужен) сортируются по времени поступления запроса, так как новые запросы всегда добавляются в конец очереди. При нормальной работе deadline-планировщик ввода-вывода получает запросы из головы отсортированной очереди и помещает их в очередь диспетчеризации. Очередь диспетчеризации отправляет запросы жесткому диску. Это приводит к минимизации количества операций поиска.</p>
     <p>Если же для запроса, который находится в голове FIFO-очереди записи или FIFO-очереди чтения, истекает период ожидания (т.е. текущий момент времени становится большим, чем момент времени, когда истекает период ожидания, связанный с запросом), то deadline-планировщик начинает обрабатывать запросы из соответствующей очереди FIFO. Таким образом планировщик с лимитом по времени пытается гарантировать, что запросы не будут ожидать дольше максимального периода ожидания (рис. 13.3).</p>
     <image l:href="#img_21.jpeg"/>
     <p><strong>Рис. 13.3</strong>. Три очереди планировщика ввода-вывода с лимитом по времени</p>
     <p>Следует заметить, что deadline-плаиировщик ввода-вывода не дает строгой гарантии времени задержки запроса. Однако он, в общем, позволяет отправлять на обработку запросы до или вскоре после того, как истек их период ожидания. Это позволяет предотвратить ситуацию недостатка обслуживания запросов. Так как для запросов чтения максимальное время ожидания значительно больше, чем для запросов записи, то планировщик с лимитом по времени также позволяет гарантировать, что обслуживание запросов записи не приведет к недостатку обслуживания запросов чтения. Больший приоритет запросов чтения позволяет минимизировать время задержки при операциях чтения.</p>
     <p>Код планировщика ввода-вывода с лимитом по времени находится в файле <code>drivers/block/deadline-iosched.с</code>.</p>
    </section>
    <section>
     <title>
      <p>Прогнозирующий планировщик ввода-вывода</p>
     </title>
     <p>Хотя планировщик с лимитом по времени ввода-вывода и выполняет работу по минимизации задержек чтения, это делается ценой уменьшения глобального быстродействия. Рассмотрим систему с большой активностью записи. Каждый раз, когда приходит запрос на чтение, планировщик сразу же начинает его выполнять. Это приводит к тому, что сразу же запускается операция поиска того места на диске, где будет выполнено чтение и сразу после выполнения чтения снова осуществляется поиск того места, где будет выполнена запись, и так повторяется при каждом запросе чтения. Большой приоритет запросов чтения вещь хорошая, но две операции поиска на каждый запрос чтения (перемещение в точку чтения и обратно в точку записи) очень плохо сказываются на общей дисковой производительности. Цель прогнозирующего планировщика ввода-вывода (anticipatory I/O scheduler) — обеспечение хороших характеристик по задержкам чтения и в то же время обеспечение отличной общей производительности.</p>
     <p>Прогнозирующий планировщик построен на базе планировщика ввода-вывода с лимитом но времени. Поэтому он не особо отличается. В прогнозирующем планировщике реализованы три очереди (плюс очередь диспетчеризации) и обработка времени ожидания для каждого запроса, так же как и В случае deadline-планировщика. Главное отличие состоит в наличии дополнительного <emphasis>эвристического прогнозирования</emphasis> (<emphasis>anticipation heuristic</emphasis>).</p>
     <p>Прогнозирующий планировщик ввода-вывода пытается минимизировать "шторм операций поиска", который следует за каждым запросом чтения во время выполнения других дисковых операций. Когда поступает запрос на чтение, он обрабатывается в рамках своего времени ожидания как обычно. После того как запрос отправлен жесткому диску, прогнозирующий планировщик сразу не возвращается к выполнению предыдущих запросов и не запускает операцию поиска сразу же. Он абсолютно ничего не делает в течение нескольких миллисекунд (значение этого периода времени можно настраивать, а по умолчанию оно равно 6 миллисекунд). Существует большой шанс, что за эти несколько миллисекунд приложение отправит еще один запрос на чтение. Все запросы к соседним секторам диска выполняются немедленно. После .того как время ожидания истекает, прогнозирующий планировщик возвращается к выполнению ранее оставленных запросов и выполняет поиск соответствующего места на диске.</p>
     <p>Важно обратить внимание, что те несколько миллисекунд, в течение которых планировщик ожидает на новые запросы (т.е. время, которое планировщик тратит в предвещании нового запроса), полностью окупаются, даже если это позволяет минимизировать всего лишь небольшой процент операций поиска при выполнении запросов чтения в случае большого количества других запросов. Если во время ожидания приходит запрос к соседней области диска, то это позволяет избежать двух операций поиска. Чем больше за это время приходит запросов к соседним областям диска, тем большего количества операций поиска можно избежать.</p>
     <p>Конечно, если в течение периода ожидания не было никакой активности, то прогнозирующий планировщик зря потратит эти несколько миллисекунд.</p>
     <p>Ключевой момент для получения максимальной производительности от прогнозирующего планировщика — правильно предсказать действия приложений и файловых систем. Это выполняется на основе эвристических алгоритмов и сбора статики. Прогнозирующий планировщик ведет статистику операций блочного ввода-вывода по каждому процессу в надежде предсказать действия приложений. При достаточно высоком проценте точных предсказаний прогнозирующий планировщик способен значительно снизить затраты на поиск при выполнении операций чтения и в то же время уделить внимание тем запросам, которые критичны для производительности системы. Это позволяет прогнозирующему планировщику минимизировать задержки чтения и в то же время уменьшить количество и продолжительность операций поиска, что в свою очередь проявляется в уменьшении времени реакции системы и в увеличении ее производительности.</p>
     <p>Код прогнозирующего планировщика находится в файле <code>drivers/block/as-iosched.c</code> дерева исходных кодов ядра.</p>
     <p>Этот планировщик используется в ядре Linux по умолчанию и хорошо работает для большинства типов нагрузки на систему. Он идеальный для серверов, однако работает очень плохо в случае определенных типов загрузки, которые встречаются не очень часто, как, например, в случае баз данных, рассчитанных на большое количество операций поиска но диску.</p>
    </section>
    <section>
     <title>
      <p>Планировщик ввода-вывода с полностью равноправными очередями</p>
     </title>
     <p>Планировщик ввода-вывода с полностью равноправными очередями (Complete Fair Queuing, CFQ) был разработан для определенного типа нагрузок на систему, по на практике он позволяет получить хорошую производительность для широкого диапазона типов нагрузки. Он фундаментальным образом отличается от всех ранее рассмотренных планировщиков ввода-вывода.</p>
     <p>Планировщик CFQ распределяет все приходящие запросы ввода-вывода по определенным очередям на основании того, какой процесс прислал этот запрос. Например, запросы от процесса foo идут в очередь foo, запросы от процесса bar — в очередь bar. В пределах каждой очереди запросы объединяются со смежными и сортируются. Таким образом очереди поддерживаются в отсортированном состоянии, так же как и в случае других планировщиков ввода-вывода. Отличие планировщика CFQ состоит в том, что он поддерживает отдельную очередь для каждого процесса, который выполняет операции ввода-вывода.</p>
     <p>После этого планировщик CFQ выполняет запросы из разных очередей по круговому алгоритму, выполняя конфигурируемое количество запросов (по умолчанию 4) из каждой очереди, перед тем как перейти к следующей. Это позволяет получить равномерное распределение пропускной способности диска для каждого процесса в системе. Предполагаемое использование такого планировщика — мультимедийные приложения, для которых он позволяет гарантировать, что, например, аудио-проигрыватель всегда будет успевать вовремя заполнять аудиобуферы с диска. Тем не менее планировщик CFQ на практике хорошо работает для многих сценариев загруженности системы.</p>
     <p>Код CFQ планировщика находится в файле <code>drivers/block/cfq-iosched.с</code>. Этот планировщик рекомендуется для офисных компьютеров, хотя хорошо работает практически для всех типов нагрузок, за исключением, может быть, уж очень экстремальных типов загруженности.</p>
    </section>
    <section>
     <title>
      <p>Планировщик ввода-вывода noop</p>
     </title>
     <p>Четвертый, и последний, тип планировщика ввода-вывода — это планировщик noop (no operation, с отсутствием операций). Он назван так потому, что практически ничего не делает. Этот планировщик не выполняет никакой сортировки или других операций для предотвращения поиска по устройству. Ему нет необходимости выполнять ничего, включая алгоритмы, которые минимизируют задержки и были рассмотрены для предыдущих планировщиков.</p>
     <p>Планировщик ввода-вывода noop выполняет только объединение приходящих запросов со смежными, которые находятся в очереди. Кроме этого, больше никаких функций у данного планировщика нет. Он просто обслуживает очередь запросов, которые передаются драйверу блочного устройства, в режиме FIFO.</p>
     <p>Планировщик noop не является полностью бесполезным. В том, что он ничего не делает, есть большой смысл. Он рассчитан на блочные устройства, которые позволяют выполнять истинно произвольный доступ, такие как платы флеш-памяти. Если для блочного устройства нет накладных затрат, связанных с поиском по устройству, то нет и необходимости выполнять сортировку и вставку вновь приходящих запросов, и планировщик noop — идеальный вариант.</p>
     <p>Код планировщика noop находится в файле <code>drivers/block/noop-iosched.с</code>. Он предназначен только для устройств с произвольным доступом.</p>
    </section>
    <section>
     <title>
      <p>Выбор планировщика ввода-вывода</p>
     </title>
     <p>В ядрах серии 2.6 есть четыре планировщика ввода-вывода. Каждый из этих планировщиков может быть активизирован. По умолчанию все блочные устройства используют прогнозирующий планировщик ввода-вывода. Планировщик можно изменить, указав параметр ядра <code>elevator=&lt;плaниpoвщик&gt;</code> в командной строке при загрузке системы, где <code>&lt;планировщик&gt;</code> — это один из поддерживаемых типов планировщика, которые показаны в табл. 13.2.</p>
     <empty-line/>
     <p><strong>Таблица 13.2</strong>. Возможные значения параметра elevator</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Значение</th>
       <th align="left" valign="top">Тип планировщика</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>as</code></td>
       <td align="left" valign="top">Прогнозирующий</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>cfq</code></td>
       <td align="left" valign="top">С полностью равноправными очередями</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>deadline</code></td>
       <td align="left" valign="top">С лимитом по времени</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>noop</code></td>
       <td align="left" valign="top">С отсутствием операций (noop)</td>
      </tr>
     </table>
     <p>Например, указание параметра <code>elevator=cfq</code> в командной строке ядра при загрузке системы означает, что для всех блочных устройств будет использоваться планировщик с полностью равноправными очередями.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Резюме</p>
    </title>
    <p>В этой главе были рассмотрены основы работы устройств блочного ввода-вывода, а также структуры данных, используемые для работы уровня ввода-вывода блоками: структура <code>bio</code>, которая представляет выполняемую операцию ввода-вывода; структура <code>buffer_head</code>, которая представляет отображение блоков на страницы памяти; структура <code>request</code>, которая представляет собой отдельный запрос ввода-вывода. После рассмотрения запросов ввода-вывода был описан их короткий, но важный путь, кульминацией которого является прохождение через планировщик ввода-вывода. Были рассмотрены дилеммы, возникающие при планировании операций ввода-вывода, и четыре типа планировщика, которые на данный момент существуют в ядре Linux, а также планировщик ввода вывода из ядра 2.4 — лифтовой алгоритм Линуса.</p>
    <p>Далее мы рассмотрим адресное пространство процесса.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 14</p>
    <p>Адресное пространство процесса</p>
   </title>
   <section>
    <p>В главе 11, "Управление памятью", было рассказано о том, как ядро управляет физической памятью. В дополнение к тому, что ядро должно управлять своей памятью, оно также должно, управлять и адресным пространством процессов — тем, как память видится для каждого процесса в системе. Операционная система Linux — это операционная система с виртуальной памятью (virtual memory operating system), т.е. для всех процессов выполняется виртуализация ресурсов памяти. Для каждого процесса создается иллюзия того, что он один использует всю физическую память в системе. Еще более важно, что адресное пространство процессов может быть даже значительно больше объема физической памяти. В этой главе рассказывается о том, как ядро управляет адресным пространством процесса.</p>
    <p>Адресное пространство процесса состоит из диапазона адресов, которые выделены процессу, и, что более важно, в этом диапазоне выделяются адреса, которые процесс может так или иначе использовать. Каждому процессу выделяется "плоское" 32- или 64-битовое адресное пространство. Термин "плоское" обозначает, что адресное пространство состоит из одного диапазона адресов (например, 32-разрядное адресное пространство занимает диапазон адресов от 0 до 429496729). Некоторые операционные системы предоставляют <strong>сегментированное адресное пространство</strong> — адресное пространство состоит больше чем из одного диапазона адресов, т.е. состоит из сегментов. Современные операционные системы обычно предоставляют плоское адресное пространство. Размер адресного пространства зависит от аппаратной платформы. Обычно для каждого процесса существует свое адресное пространство. Адрес памяти в адресном пространстве одного процесса не имеет никакого отношения к такому же адресу памяти в адресном пространстве другого процесса. Тем не менее несколько процессов могут совместно использовать одно общее адресное пространство. Такие процессы называются потоками.</p>
    <p>Значение адреса памяти — это заданное значение из диапазона адресов адресного пространства, как, например, 41021f000. Это значение идентифицирует определенный байт в 32-битовом адресном пространстве. Важной частью адресного пространства являются интервалы адресов памяти, к которым процесс имеет право доступа, как, например, 08048000–0804c000. Такие интервалы разрешенных адресов называются <emphasis>областями памяти</emphasis> (<emphasis>memory area</emphasis>). С помощью ядра процесс может динамически добавлять и удалять области памяти своего адресного пространства.</p>
    <p>Процесс имеет право доступа только к действительным областям памяти. Более того, на область памяти могут быть установлены права только для чтения или запрет на выполнение. Если процесс обращается к адресу памяти, который не находится в действительной области памяти, или доступ к действительной области выполняется запрещенным образом, то ядро уничтожает процесс с ужасным сообщением "Segmentation Fault" (ошибка сегментации).</p>
    <p>Области памяти могут содержать следующую нужную информацию.</p>
    <p>• Отображение выполняемого кода из выполняемого файла в область памяти процесса, которая называется <emphasis>сегментом кода</emphasis> (<emphasis>text section</emphasis>).</p>
    <p>• Отображение инициализированных переменных из выполняемого файла в область памяти процесса, которая называется <emphasis>сегментом данных</emphasis> (<emphasis>data section</emphasis>).</p>
    <p>• Отображение страницы памяти, заполненной нулями, в область памяти процесса, которая содержит неинициализированные глобальные переменные и называется <emphasis>сегментом bss</emphasis><a l:href="#n79" type="note">[79]</a> (<emphasis>bss section</emphasis>). Нулевая страница памяти (zero page, страница памяти, заполненная нулями) — это страница памяти, которая полностью заполнена нулевыми значениями и используется, например, для указанной выше цели.</p>
    <p>• Отображение страницы памяти, заполненной нулями, в память процесса, которая используется в качестве стека процесса пространства пользователя (не нужно путать со стеком процесса в пространстве ядра, который является отдельной структурой данных и управляется и используется ядром).</p>
    <p>• Дополнительные сегменты кода, данных и BSS каждой совместно используемой библиотеки, таких как библиотека libc и динамический компоновщик, которые загружаются в адресное пространство процесса.</p>
    <p>• Все файлы, содержимое которых отображено в память.</p>
    <p>• Все области совместно используемой памяти.</p>
    <p>• Все анонимные отображения в память, как, например, связанные с функцией <code>malloc()</code><a l:href="#n80" type="note">[80]</a>.</p>
    <p>Каждое действительное значение адреса памяти в адресном пространстве процесса принадлежит только и только одной области памяти (области памяти не перекрываются). Как будет показано, для каждого отдельного участка памяти в выполняющемся процессе существует своя область: стек, объектный код, глобальные переменные, отображенный в память файл и т.д.</p>
   </section>
   <section>
    <title>
     <p>Дескриптор памяти</p>
    </title>
    <section>
     <p>Ядро представляет адресное пространство процесса в виде структуры данных, которая называется <emphasis>дескриптором памяти</emphasis>. Эта структура содержит всю информацию, которая относится к адресному пространству процесса. Дескриптор памяти представляется с помощью структуры <code>struct mm_struct</code>, которая определена в файле <code>&lt;linux/sched.h&gt;</code><a l:href="#n81" type="note">[81]</a>.</p>
     <p>Рассмотрим эту структуру с комментариями, поясняющими назначение каждого поля.</p>
     <p><code>struct mm_struct {</code></p>
     <p><code> struct vm_area_struct *mmap;        /* список областей памяти */</code></p>
     <p><code> struct rb_root        mm_rb;        /* красно-черное дерево</code></p>
     <p><code>                                        областей памяти */</code></p>
     <p><code> struct vm_area_struct *mmap_cache;  /* последняя использованная</code></p>
     <p><code>                                        область памяти */</code></p>
     <p><code> unsigned long         free_area_cache; /* первый незанятый участок</code></p>
     <p><code>                                           адресного пространства */</code></p>
     <p><code> pgd_t                 *pgd;         /* глобальный каталог страниц */</code></p>
     <p><code> atomic_t              mm_users;     /* счетчик пользователей адресного</code></p>
     <p><code>                                        пространства */</code></p>
     <p><code> atomic_t              mm_count;     /* основной счетчик использования */</code></p>
     <p><code> int                   map_count;    /* количество областей памяти */</code></p>
     <p><code> struct rw_semaphore   mmap_sem;     /* семафор для областей памяти */</code></p>
     <p><code> spinlock_t            page_table_lock; /* спин-блокировка</code></p>
     <p><code>                                           таблиц страниц */</code></p>
     <p><code> struct list_head      mmlist;       /* список всех структур mm_struct */</code></p>
     <p><code> unsigned long         start_code;   /* начальный адрес сегмента кода */</code></p>
     <p><code> unsigned long         end code;     /* конечный адрес сегмента кода */</code></p>
     <p><code> unsigned long         start_data;   /* начальный адрес сегмента данных */</code></p>
     <p><code> unsigned long         end_data;     /* конечный адрес сегмента данных */</code></p>
     <p><code> unsigned long         start_brk;    /* начальный адрес сегмента "кучи" */</code></p>
     <p><code> unsigned long         brk;          /* конечный адрес сегмента "кучи" */</code></p>
     <p><code> unsigned long         start_stack;  /* начало стека процесса */</code></p>
     <p><code> unsigned long         arg_start;    /* начальный адрес</code></p>
     <p><code>                                        области аргументов */</code></p>
     <p><code> unsigned long         arg_end;      /* конечный адрес</code></p>
     <p><code>                                        области аргументов */</code></p>
     <p><code> unsigned long         env_start;    /* начальный адрес</code></p>
     <p><code>                                        области переменных среды */</code></p>
     <p><code> unsigned long         env_end;      /* конечный адрес</code></p>
     <p><code>                                        области переменных среды */</code></p>
     <p><code> unsigned long         rss;          /* количество физических страниц памяти */</code></p>
     <p><code> unsigned long         total_vm;     /* общее количество страниц памяти */</code></p>
     <p><code> unsigned long         locked_vm;    /* количество заблокированных страниц</code></p>
     <p><code>                                        памяти */</code></p>
     <p><code> unsigned long         def_flags;    /* флаги доступа, используемые</code></p>
     <p><code>                                        по умолчанию */</code></p>
     <p><code> unsigned long         cpu_vm_mask;  /* маска отложенного переключения</code></p>
     <p><code>                                        буфера TLB */</code></p>
     <p><code> unsigned long         swap_address; /* последний сканированный адрес */</code></p>
     <p><code> unsigned              dumpable:1;   /* можно ли создавать файл core? */</code></p>
     <p><code> int                   used_hugetlb; /* используются ли гигантские</code></p>
     <p><code>                                        страницы памяти (hugetlb)? */</code></p>
     <p><code> mm_context_t          context;      /* данные, специфичные для аппаратной</code></p>
     <p><code>                                        платформы */</code></p>
     <p><code> int                   core_waiters; /* количество потоков, ожидающих на</code></p>
     <p><code>                                        создание файла core */</code></p>
     <p><code> struct completion     *core_startup_done; /* условная переменная начала</code></p>
     <p><code>                                              создания файла core */</code></p>
     <p><code> struct completion     core_done;    /* условная переменная завершения</code></p>
     <p><code>                                        создания файла core */</code></p>
     <p><code> rwlock_t              ioctx_list_lock; /* блокировка списка асинхронного</code></p>
     <p><code>                                           ввода-вывода (AIO) */</code></p>
     <p><code> struct kioctx         *ioctx_list;  /* список асинхронного ввода-вывода (AIO) */</code></p>
     <p><code> struct kioctx         default_kioctx; /* контекст асинхронного ввода-</code></p>
     <p><code>                        вывода, используемый по умолчанию */</code></p>
     <p><code>};</code></p>
     <p>Поле <code>mm_users</code> — это количество процессов, которые используют данное адресное пространство. Например, если одно и то же адресное пространство совместно используется двумя потоками, то значение поля <code>mm_users</code> равно двум. Поле <code>mm_count</code> — это основной счетчик использования структуры <code>mm_struct</code>. Наличие пользователей структуры, которым соответствует поле <code>mm_users</code>, приводит к увеличению счетчика <code>mm_count</code> на единицу. В предыдущем примере значение поля <code>mm_count</code> равно единице. Когда значение поля <code>mm_users</code> становится равным нулю (т.е. когда два потока завершатся), только тогда значение поля <code>mm_count</code> уменьшается на единицу. Когда значение поля mm_count становится равным нулю, то на соответствующую структуру <code>mm_struct</code> больше нет ссылок, и она освобождается, Поддержка двух счетчиков позволяет ядру отличать главный счетчик использования (<code>mm_count</code>) от количества процессов, которые используют данную структуру (<code>mm_users</code>).</p>
     <p>Поля <code>mmap</code> и <code>mm_rb</code> — это два различных контейнера данных, которые содержат одну и ту же информацию: информацию обо всех областях памяти в соответствующем адресном пространстве. В первом контейнере эта информация хранится в виде связанного списка, а во втором — в виде красно-черного бинарного дерева. Поскольку красно-черное дерево — это разновидность бинарного дерева, то, как и для всех типов бинарного дерева, количество операций поиска заданного элемента в нем равно <emphasis>О(log(n)</emphasis>). Более детальное рассмотрение красно-черных деревьев найдете в разделе "Списки и деревья областей памяти".</p>
     <p>Хотя обычно в ядре избегают избыточности, связанной с введением нескольких структур для хранения одних и тех же данных, тем не менее в данном случае эта избыточность очень кстати. Контейнер <code>mmap</code> — это связанный список, который позволяет очень быстро проходить по всем элементам. С другой стороны, контейнер <code>mm_rb</code> — это красно-черное дерево, которое очень хорошо подходит для поиска заданного элемента. Области памяти будут рассмотрены в этой главе несколько ниже,</p>
     <p>Все структуры <code>mm_struct</code> объединены в двухсвязный список с помощью нолей <code>mmlist</code>. Первым элементом этого списка является дескриптор памяти <code>init_mm</code>, который является дескриптором памяти процесса <strong>init</strong>. Этот список защищен от конкурентного доступа с помощью блокировки <code>mmlist_lock</code>, которая определена в файле <code>kernel/fork.с</code>. Общее количество дескрипторов памяти хранится в глобальной целочисленной переменной <code>mmlist_nr</code>, которая определена в том же файле.</p>
    </section>
    <section>
     <title>
      <p>Выделение дескриптора памяти</p>
     </title>
     <p>Указатель на дескриптор памяти, выделенный для какой-либо задачи, хранится в поле <code>mm</code> дескриптора процесса этой задачи. Следовательно, выражение <code>current-&gt;mm</code> позволяет получить дескриптор памяти текущего процесса. Функция <code>copy_mm()</code> используется для копирования дескриптора родительского процесса в дескриптор порожденного процесса во время выполнения вызова <code>fork()</code>. Структура <code>mm_struct</code> выделяется из слябового кэша <code>mm_cachep</code> с помощью макроса <code>allocate_mm()</code>. Это реализовано в файле <code>kernel/fork.c</code>. Обычно каждый процесс получает уникальный экземпляр структуры <code>mm_struct</code> и соответственно уникальное адресное пространство.</p>
     <p>Процесс может использовать одно и то же адресное пространство совместно со своими порожденными процессами, путем указания флага <code>CLONE_VM</code> при выполнении вызова <code>clone()</code>. Такие процессы называются потоками. Вспомните из материала главы 3, "Управление процессами", что в операционной системе Linux в этом и состоит <emphasis>единственное</emphasis> существенное отличие между обычными процессами и потоками. Ядро Linux больше никаким другим образом их не различает. Потоки с точки зрения ядра — это обычные процессы, которые просто совместно используют некоторые общие ресурсы.</p>
     <p>В случае, когда указан флаг <code>CLONE_VM</code>, макрос <code>allocate_mm()</code> не вызывается, а в поле mm дескриптора порожденного процесса записывается значение указателя на дескриптор памяти родительского процесса. Это реализовано с. помощью следующего оператора ветвления в функции <code>сору_mm()</code>.</p>
     <p><code>if (clone_flags &amp; CLONE_VM) {</code></p>
     <p><code> /*</code></p>
     <p><code> * current — это родительский процесс</code></p>
     <p><code> * tsk — это процесс, порожденный в вызове fork()</code></p>
     <p><code> */</code></p>
     <p><code> atomic_inc(&amp;current-&gt;mm-&gt;mm_users);</code></p>
     <p><code> tsk-&gt;mm = current-&gt;mm;</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>Удаление дескриптора памяти</p>
     </title>
     <p>Когда процесс, связанный с определенным адресным пространством, завершается, то вызывается функция <code>exit_mm()</code>. Эта функция выполняет некоторые служебные действия и обновляет некоторую статистическую информацию. Далее вызывается функция <code>mput()</code>, которая уменьшает на единицу значение счетчика количества пользователей <code>mm_users</code> для дескриптора памяти. Когда значение счетчика количества пользователей становится равным нулю, то вызывается функция <code>mmdrop()</code>, которая уменьшает значение основного счетчика использования <code>mm_count</code>. Когда и этот счетчик использования наконец достигает нулевого значения, то вызывается функция <code>free_mm()</code>, которая возвращает экземпляр структуры <code>mm_struct</code> в слябовый кэш <code>mm_cachep</code> с помощью вызова функции <code>kmem_cache_free()</code>, поскольку дескриптор памяти больше не используется.</p>
    </section>
    <section>
     <title>
      <p>Структура <code>mm_struct</code> и потоки пространства ядра</p>
     </title>
     <p>Потоки пространства ядра не имеют своего адресного пространства процесса и, следовательно, связанного с ним дескриптора памяти. Значение поля <code>mm</code> для потока пространства ядра равно <code>NULL</code>. Еще одно <emphasis>определение</emphasis> потока ядра — это процесс, который не имеет пользовательского контекста.</p>
     <p>Отсутствие адресного пространства— хорошее свойство, поскольку потоки ядра вообще не обращаются к памяти в пространстве пользователя (действительно, к какому адресному пространству им обращаться?). Поскольку потоки ядра не обращаются к страницам памяти в пространстве пользователя, им вообще не нужен дескриптор памяти и таблицы страниц (таблицы страниц обсуждаются дальше в этой главе). Несмотря на это, потокам пространства ядра все же нужны некоторые структуры данных, такие как таблицы страниц, чтобы обращаться к памяти ядра. Чтобы обеспечить потоки ядра всеми данными без необходимости тратить память на дескриптор памяти и таблицы страниц, а также процессорное время на переключение на новое адресное пространство и так далее, каждый поток ядра использует дескриптор памяти задания, которое выполнялось перед ним.</p>
     <p>Когда процесс запланирован на выполнение, то загружается адресное пространство, на которое указывает поле <code>mm</code> этого процесса. Поле <code>active</code>_mm дескриптора процесса обновляется таким образом, чтобы указывать на новое адресное пространство. Потоки ядра не имеют своего адресного пространства, поэтому значение поля mm для них равно <code>NULL</code>. Поэтому, когда поток ядра планируется на выполнение, ядро определяет, что значение ноля <code>mm</code> равно <code>NULL</code>, и оставляет загруженным предыдущее адресное пространство. После этого ядро обновляет поле <code>active_mm</code> дескриптора процесса для потока ядра, чтобы он указывал на дескриптор памяти предыдущего процесса. При необходимости поток ядра может использовать таблицы страниц предыдущего процесса. Так как потоки ядра не обращаются к памяти в пространстве пользователя, то они используют только ту информацию об адресном пространстве ядра, которая связана с памятью ядра и является общей для всех процессов.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Области памяти</p>
    </title>
    <section>
     <p>Области памяти (memory areas) представляются с помощью объектов областей памяти, которые хранятся в структурах типа <code>vm_area_struct</code>. Эта структура определена в файле <code>&lt;linux/mm.h&gt;</code>. Области памяти часто называются <emphasis>областями виртуальной памяти</emphasis> (<emphasis>virtual memory area</emphasis>, или VMA).</p>
     <p>Структура <code>vm_area_struct</code> описывает одну непрерывную область памяти в данном адресном пространстве. Ядро рассматривает каждую область памяти, как уникальный объект. Для каждой области памяти определены некоторые общие свойства, такие как права доступа и набор соответствующих операций. Таким образом, одна структура VMA может представлять различные типы областей памяти, например файлы, отображаемые в память, или стек пространства пользователя. Это аналогично объектно-ориентированному подходу, который используется в подсистеме VFS (см. главу 12, "Виртуальная файловая система").</p>
     <p>Ниже показана эта структура данных с комментариями, описывающими назначение каждого поля.</p>
     <p><code>struct vm_area_struct {</code></p>
     <p><code> struct mm_struct      *vm_mm;       /* соответствующая структура mm_struct */</code></p>
     <p><code> unsigned long         vm_start;     /* начало диапазона адресов */</code></p>
     <p><code> unsigned long         vm_end;       /* конец диапазона адресов */</code></p>
     <p><code> struct vm_area_struct *vm_next;     /* список областей VMA */</code></p>
     <p><code> pgprot_t              vm_page_prot; /* права доступа */</code></p>
     <p><code> unsigned long         vm_flags;     /* флаги */</code></p>
     <p><code> struct rb_node        vm_rb;        /* узел текущей области VMA */</code></p>
     <p><code> union { /* связь с address_space-&gt;i_mmap, или i_mmap_nonlinear */</code></p>
     <p><code>  struct {</code></p>
     <p><code>   struct list_head      list;</code></p>
     <p><code>   void                  *parent;</code></p>
     <p><code>   struct vm_area_struct *head;</code></p>
     <p><code>  } vm_set;</code></p>
     <p><code>  struct prio_tree_node prio_tree_node;</code></p>
     <p><code> } shared;</code></p>
     <p><code> struct list_head            anon_vma_node;    /* анонимные области */</code></p>
     <p><code> struct anon_vma             *anon_vma;        /* объект анонимной VMA */</code></p>
     <p><code> struct vm_operations_struct *vm_ops;          /* операции */</code></p>
     <p><code> unsigned long               vm_pgoff;         /* смещение в файле */</code></p>
     <p><code> struct file                 *vm_file;         /* отображенный файл (если есть) */</code></p>
     <p><code> void                        *vm_private_data; /* приватные данные */</code></p>
     <p><code>};</code></p>
     <p>Как уже было рассказано, каждый дескриптор памяти связан с уникальным диапазоном (интервалом) адресов в адресном пространстве процесса. Поле vm_<code>start</code> — это начальный (минимальный) адрес, а поле <code>vm_end</code> — конечный (максимальный) адрес данного интервала. Следовательно, значение (<code>vm_end - vm_start</code>) — это размер (длина) интервала адресов в байтах. Интервалы адресов разных областей памяти одного адресного пространства не могут перекрываться.</p>
     <p>Поле <code>vm_mm</code> указывает на структуру <code>mm_struct</code>, связанную с данной областью VMA. Заметим, что каждая область VMA уникальна для той структуры <code>mm_struct</code>, с которой эта область связана. Поэтому, даже если два разных процесса отображают один и тот же файл на свои адресные пространства, то для каждого процесса создается своя структура <code>vm_area_struct</code>, чтобы идентифицировать уникальные области памяти каждого процесса. Следовательно, два потока, которые совместно используют адресное пространство, также совместно используют и все структуры <code>vm_area_struct</code> в этом адресном пространстве.</p>
    </section>
    <section>
     <title>
      <p>Флаги областей VMA</p>
     </title>
     <p>Поле флагов <code>vm_flags</code> содержит битовые флаги, которые определены в файле <code>&lt;linux/mm.h&gt;</code>. Они указывают особенности поведения и содержат описательную информацию о страницах памяти, которые входят в данную область памяти. В отличие от прав доступа, которые связаны с определенной физической страницей памяти, флаги областей VMA указывают особенности поведения, за которые отвечает ядро, а не аппаратное обеспечение. Более того, поле <code>vm_flags</code> содержит информацию, которая относится к каждой странице в области памяти или, что то же самое, ко всей области памяти в целом. В табл. 14.1 приведен список возможных значений флагов <code>vm_flags</code>.</p>
     <empty-line/>
     <p><strong>Таблица 14.1</strong>. Флаги областей VMA</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Флаг</th>
       <th align="left" valign="top">Влияние на область VMA и на ее страницы памяти</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_READ</code></td>
       <td align="left" valign="top">Из страниц памяти можно считывать информацию</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_WRITE</code></td>
       <td align="left" valign="top">В страницы памяти можно записывать информацию</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_EXEC</code></td>
       <td align="left" valign="top">Можно выполнять код, хранящийся в страницах памяти</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_SHARED</code></td>
       <td align="left" valign="top">Страницы памяти являются совместно используемыми</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_MAYREAD</code></td>
       <td align="left" valign="top">Можно устанавливать флаг <code>VM_READ</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_MAYWRITE</code></td>
       <td align="left" valign="top">Можно устанавливать флаг <code>VM_WRITE</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_MAYEXEC</code></td>
       <td align="left" valign="top">Можно устанавливать флаг <code>VM_EXEC</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_MAYSHARE</code></td>
       <td align="left" valign="top">Можно устанавливать флаг <code>VM_SHARED</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_GROWSDOWN</code></td>
       <td align="left" valign="top">Область памяти может расширяться "вниз"</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_GROWSUP</code></td>
       <td align="left" valign="top">Область памяти может расширяться "вверх"</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_SHM</code></td>
       <td align="left" valign="top">Область используется для разделяемой (совместно используемой) памяти</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_DENYWRITE</code></td>
       <td align="left" valign="top">В область отображается файл, в который нельзя выполнять запись</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_EXECUTABLE</code></td>
       <td align="left" valign="top">В область отображается выполняемый файл</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_LOCKED</code></td>
       <td align="left" valign="top">Страницы памяти в области являются заблокированными</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_IQ</code></td>
       <td align="left" valign="top">В область памяти отображается пространство ввода-вывода аппаратного устройства</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_SEQ_READ</code></td>
       <td align="left" valign="top">К страницам памяти, вероятнее всего, осуществляется последовательный доступ</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_RAND_READ</code></td>
       <td align="left" valign="top">К страницам памяти, вероятнее всего, осуществляется случайный доступ</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_DONTCOPY</code></td>
       <td align="left" valign="top">Область памяти не должна копироваться при вызове <code>fork()</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_DONTEXPAND</code></td>
       <td align="left" valign="top">Область памяти не может быть увеличена с помощью вызова <code>remap()</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_RESERVED</code></td>
       <td align="left" valign="top">Область памяти не должна откачиваться на диск</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_ACCOUNT</code></td>
       <td align="left" valign="top">Область памяти является объектом, по которому выполняется учет ресурсов</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_HUGETLB</code></td>
       <td align="left" valign="top">В области памяти используются гигантские (<code>hugetlb</code>) страницы памяти</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>VM_NONLINEAR</code></td>
       <td align="left" valign="top">Область памяти содержит нелинейное отображение</td>
      </tr>
     </table>
     <p>Рассмотрим подробнее назначение наиболее интересных и важных флагов. Флаги <code>VM_READ</code>, <code>VM_WRITE</code> и <code>VM_EXEC</code> указывают обычные права на чтение-запись и выполнение для страниц памяти, которые принадлежат данной области памяти. При необходимости их можно комбинировать для формирования соответствующих прав доступа. Например, отображение выполняемого кода процесса может быть выполнено с указанием флагов <code>VM_READ</code> и <code>VM_EXEC</code>, но никак не с указанием флага <code>VM_WRITE</code>. С другой стороны, сегмент данных из выполняемого файла может отображаться с указанием флагов <code>VM_READ</code> и <code>VM_WRITE</code>, указывать при этом флаг <code>VM_EXEC</code> не имеет смысла. Файл данных, который отображается только для чтения, должен отображаться с указанием только флага <code>VM_READ</code>.</p>
     <p>Флаг <code>VM_SHARED</code> указывает на то, что область памяти содержит отображение, которое может совместно использоваться несколькими процессами. Если этот флаг установлен, то такое отображение называют совместно используемым (<emphasis>shared mapping</emphasis>), что интуитивно понятно. Если этот флаг не установлен, то такое отображение доступно только одному процессу и оно называется <emphasis>частным отображением</emphasis>, (<emphasis>private mapping</emphasis>).</p>
     <p>Флаг <code>VM_IO</code> указывает, что область памяти содержит отображение области ввода-вывода аппаратного устройства. Этот флаг обычно устанавливается драйверами устройств при выполнении вызова <code>mmap()</code> для отображения в память области ввода-вывода аппаратного устройства. Кроме всего прочего, этот флаг указывает, что область памяти не должна включаться в файл core процесса. Флаг <code>VM_RESERVED</code> указывает, что область памяти не должна откачиваться на диск. Этот флаг также укалывается при отображении на память областей ввода-вывода аппаратных устройств.</p>
     <p>Флаг <code>VM_SBQ_READ</code> является подсказкой ядру, что приложение выполняет последовательное (т.е. линейное и непрерывное) чтение из соответствующего отображения. При этом ядро может повысить производительность чтения за счет выполнения упреждающего чтения (read-ahead) из отображаемого файла. Флаг <code>VM_RAND_READ</code> указывает обратное, т.е. приложение выполняет операции чтения из случайно выбранных мест отображения (т.е. не последовательно). При этом ядро может уменьшить или совсем отключить выполнение упреждающего чтения из отображаемого файла. Эти флаги устанавливаются с помощью системного вызова <code>madvice()</code> путем указания соответственно флагов <code>MADV_SEQUENTIAL</code> и <code>MADV_RANDOM</code> для этого вызова. Упреждающее чтение — это последовательное чтение несколько большего количества данных, чем было запрошено, в надежде на то, что дополнительно считанные данные могут скоро понадобиться. Такой режим полезен для приложений, которые считывают данные последовательно. Однако если считывание данных выполняется случайным образом, то режим упреждающего чтения не эффективен.</p>
    </section>
    <section>
     <title>
      <p>Операции с областями VMA</p>
     </title>
     <p>Поле <code>vm_ops</code> структуры <code>vm_area_struct</code> содержит указатель на таблицу операций, которые связаны с данной областью памяти и которые ядро может вызывать для манипуляций с областью VMA. Структура <code>vm_area_struct</code> служит общим объектом для представления всех типов областей виртуальной памяти, а в таблице операций описаны конкретные методы, которые могут быть применены к каждому конкретному экземпляру объекта.</p>
     <p>Таблица операций представлена с помощью структуры <code>vm_operations_struct</code>, которая определена в файле <code>&lt;linux/mm.h&gt;</code> следующим образом.</p>
     <p><code>struct vm_operations_struct {</code></p>
     <p><code> void (*open)(struct vm_area_struct*);</code></p>
     <p><code> void (*close)(struct vm_area_struct*);</code></p>
     <p><code> struct page* (*nopage)(struct vm_area_struct*, unsigned long, int);</code></p>
     <p><code> int (*populate)(struct vm_area struct*, unsigned long,</code></p>
     <p><code>  unsigned long, pgprot_t, unsigned long, int);</code></p>
     <p><code>};</code></p>
     <p>Рассмотрим каждый метод в отдельности.</p>
     <p>• <code>void open(struct vm_area_struct *area);</code></p>
     <p>Эта функция вызывается, когда соответствующая область памяти добавляется в адресное пространство.</p>
     <p>• <code>void close(struct vm_area_struct *area);</code></p>
     <p>Эта функция вызывается, когда соответствующая область памяти удаляется из адресного пространства.</p>
     <p>• <code>struct page* nopage(struct vm_area_struct *area,</code></p>
     <p><code>  unsigned long address, int unused);</code></p>
     <p>Эта функция вызывается обработчиком прерывания из-за отсутствия страницы (page fault), когда производится доступ к странице, которая отсутствует в физической памяти.</p>
     <p>• <code>int populate(struct vm_area_struct *area,</code></p>
     <p><code>  unsigned long address, unsigned long len, pgprot_t prot,</code></p>
     <p><code>  unsigned long pgoff, int nonblock);</code></p>
     <p>Эта функция вызывается из системного вызова <code>remap_pages()</code> для предварительного заполнения таблиц страниц области памяти (prefault) при создании нового отображения.</p>
    </section>
    <section>
     <title>
      <p>Списки и деревья областей памяти</p>
     </title>
     <p>Как уже рассказывалось, к областям памяти осуществляется доступ с помощью двух структур данных дескриптора памяти: полей <code>mmap</code> и <code>mm_rb</code>. Эти две структуры данных независимо друг от друга указывают на все области памяти, связанные с данным дескриптором памяти. Они содержат указатели на одни и те же структуры <code>vm_area_struct</code>, просто эти указатели связаны друг с другом по-разному.</p>
     <p>Первый контейнер, поле <code>mmap</code>, объединяет все объекты областей памяти в односвязный список. Структуры <code>vm_area_struct</code> объединяются в список с помощью своих полей <code>vm_next</code>. Области памяти отсортированы в порядке увеличения адресов (от наименьшего и до наибольшего). Первой области памяти соответствует структура <code>vm_area_struct</code>, на которую указывает само поле <code>mmap</code>. Указатель на самую последнюю структуру равен значению <code>NULL</code>.</p>
     <p>Второе поле, <code>mm_rb</code>, объединяет все объекты областей памяти в красно-черное (red-black) дерево. На корень дерева указывает поле <code>mm_rb</code>, а каждая структура <code>vm_area_struct</code> присоединяется к дереву с помощью поля <code>vm_rb</code>.</p>
     <p>Красно-черное дерево — это один из типов бинарного дерева. Каждый элемент красно-черного дерева называется узлом. Начальный узел является корнем дерева. Большинство узлов имеет два дочерних узла: левый дочерний узел и правый дочерний узел. Некоторые узлы имеют всего один дочерний узел, и, наконец, узлы, которые не имеют дочерних, называются листьями. Для любого узла все элементы дерева, которые находятся слева от данного узла, всегда меньше по своему значению, чем значение данного узла, а все элементы дерева, которые находятся справа от некоторого узла, всегда больше по значению, чем значение этого узла. Более того, каждому узлу присвоен цвет (красный или черный, отсюда и название этого типа деревьев) в соответствии со следующими двумя правилами: дочерние элементы красного узла являются черными и любой путь по дереву от узла к листьям должен содержать одинаковое количество черных узлов. Корень дерева всегда красный. Поиск, вставка и удаление элементов из такого дерева требуют количество операций порядка <emphasis>О(log(n)</emphasis>).</p>
     <p>Связанный список используется, когда необходимо пройти по всем узлам. Красно- черное дерево используется, когда необходимо найти определенную область памяти адресного пространства. Таким образом, ядро использует избыточные структуры данных для обеспечения оптимальной производительности независимо от того, какие операции выполняются с областями памяти.</p>
    </section>
    <section>
     <title>
      <p>Области памяти в реальной жизни</p>
     </title>
     <p>Рассмотрим пример адресного пространства процесса и области памяти в этом адресном пространстве. Для этой цели можно воспользоваться полезной файловой системой <code>/proc</code> и утилитой <code>pmар(1)</code>. В качестве примера рассмотрим следующую простую прикладную программу, которая работает в пространстве пользователя. Эта программа не делает абсолютно ничего, кроме того, что служит примером.</p>
     <p><code>int main(int argc, char *argv[]) {</code></p>
     <p><code> return 0;</code></p>
     <p><code>}</code></p>
     <p>Рассмотрим список областей памяти из адресного пространства этого процесса. Этих областей немного. Мы уже знаем, что среди них есть сегмент кода, сегмент данных сегмент bss. Если учесть, что эта программа динамически скомпонована с библиотекой функций языка С, то соответствующие области существуют также для модуля <code>libc.so</code> и для модуля <code>ld.so</code>. И наконец, среди областей памяти также есть стек процесса.</p>
     <p>Результат вывода списка областей адресного пространства этого процесса из файла <code>/proc/&lt;pid&gt;/maps</code> имеет следующий вид.</p>
     <p><code>rml@phantasy:~$ cat /proc/1426/maps</code></p>
     <p><code>00e80000-00faf000 r-xp 00000000 03:01 208530 /lib/tls/libc-2.3.2.so</code></p>
     <p><code>00faf000-00fb2000 rw-p 0012f000 03:01 208530 /lib/tls/libc-2.3.2.so</code></p>
     <p><code>00fb2000-00fb4000 rw-p 00000000 00:00 0</code></p>
     <p><code>08048000-08049000 r-xp 00000000 03:03 439029 /home/rml/src/example</code></p>
     <p><code>08049000-0804a000 rw-p 00000000 03:03 439029 /home/rml/src/example</code></p>
     <p><code>40000000-40015000 r-xp 00000000 03:01 80276  /lib/ld-2.3.2.so</code></p>
     <p><code>40015000-40016000 rw-p 00015000 03:01 80276  /lib/ld-2.3.2.so</code></p>
     <p><code>4001e000-4001f000 rw-p 00000000 00:00 0</code></p>
     <p><code>bfffe000-c0000000 rwxp fffff000 00:00 0</code></p>
     <p>Информация об областях памяти выдается в следующем формате.</p>
     <p><code>начало-конец права доступа смещение старший:младший номера устройства файловый индекс файл</code></p>
     <p>Утилита <code>pmap(1)</code><a l:href="#n82" type="note">[82]</a> форматирует эту информацию в следующем, более удобочитаемом виде.</p>
     <p><code>rml@phantasy:~$ pmap 142 6 example[142 6]</code></p>
     <p><code>00e80000 (1212 KB) r-xp (03:01 208530) /lib/tls/libc-2.3.2.so</code></p>
     <p><code>00faf000 (12 KB)   rw-p (03:01 208530) /lib/tls/libc-2.3.2.so 00fb2000 (8 KB) rw-p (00:00 0)</code></p>
     <p><code>08048000 (4 KB)    r-xp (03:03 439029) /home/rml/src/example</code></p>
     <p><code>08049000 (4 KB)    rw-p (03:03 439029) /home/rml/src/example</code></p>
     <p><code>40000000 (84 KB)   r-xp (03:01 80276)  /lib/ld-2.3.2.so</code></p>
     <p><code>40015000 (4 KB)    rw-p (03:01 80276)  /lib/ld-2.3.2.so</code></p>
     <p><code>4001e000 (4 KB)    rw-p (00:00 0)</code></p>
     <p><code>bfffe000 (8 KB)    rwxp (00:00 0)</code></p>
     <p><code>mapped: 1340 KB writable/private: 40 KB shared: 0 KB</code></p>
     <p>Первые три строчки соответствуют сегменту кода, сегменту данных и сегменту bss модуля <code>libc.so</code> (библиотека функций языка С). Следующие две строчки описывают соответственно сегмент кода и сегмент данных выполняемого образа. Далее три строчки — описание сегментов кода, данных и bss модуля <code>ld.so</code> (динамический компоновщик). Последняя строчка описывает стек процесса.</p>
     <p>Обратите внимание, что все сегменты кода имеют права на чтение и выполнение, что и должно быть для выполняемых образов. С другой стороны, сегменты данных и bss, которые содержат глобальные переменные, помечаются как имеющие права на запись и чтение, а не на выполнение.</p>
     <p>Все адресное пространство составляет порядка 1340 Кбайт, но только 40 Кбайт из них имеют право на запись и соответствуют частному отображению. Если область памяти является совместно используемой и не имеет прав на запись, то ядро хранит в памяти всего одну копию отображаемого файла. Это может показаться обычным для совместно используемых отображений; однако, случай, когда при этом еще и отсутствуют права на запись, проявляется несколько неожиданно. Если учесть факт, что когда на отображение нет прав записи, то соответствующая информация никогда не может быть изменена (из отображения возможно только чтение), становится ясно, что можно совершенно безопасно загрузить выполняемый образ в память всего один раз. Поэтому динамически загружаемая библиотека функций языка С и занимает в памяти всего 1212 Кбайт, а не 1212 Кбайт, умноженное на количество процессов, которые эту библиотеку используют. В связи с этим, процесс, код и данные которого имеют объем порядка 1340 Кбайт, на самом деле занимает всего 40 Кбайт физической памяти. Экономия памяти из-за такого совместного использования получается существенной.</p>
     <p>Обратите внимание на области памяти, которые не имеют отображаемого файла, находятся на устройстве с номерами <code>00:00</code> и номер файлового индекса для которых равен нулю. Это отображение страницы, заполненной нулями (zero page, пулевая страница). Если отобразить страницу, заполненную нулями, на область памяти, которая имеет права на запись, то побочным эффектом является инициализация всех переменных в нулевые значения. Это важно, поскольку в таком случае получается область памяти, заполненная нулями, которая нужна для сегмента bss.</p>
     <p>Каждой области памяти, связанной с процессом, соответствует структура <code>vm_area_struct</code>. Так как процесс не является потоком (thread), то для него существует отдельная структура <code>min_struct</code>, на которую есть ссылка из структуры <code>task_struct</code>.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Работа с областями памяти</p>
    </title>
    <section>
     <p>Ядру часто необходимо определять, соответствует ли та или иная область памяти в адресном пространстве процесса заданному критерию, например, существует ли заданный адрес в области памяти. Эти операции являются основой работы функции <code>mmap()</code>, которая будет рассмотрена в следующем разделе, и выполнять их приходится часто. Несколько полезных для этого функций объявлены в файле <code>&lt;linux/mm.h&gt;</code>.</p>
    </section>
    <section>
     <title>
      <p>Функция <code>find_vma()</code></p>
     </title>
     <p>Функция <code>find_vma()</code> определена в файле <code>mm/mmap.c</code>.</p>
     <p>Эта функция позволяет найти в заданном адресном пространстве ту первую область памяти, для которой значение поля <code>vm_end</code> больше заданного адреса <code>addr</code>. Другими словами, эта функция позволяет найти первую область памяти, которая содержит адрес <code>addr</code> или начинается с адреса, большего адреса <code>addr</code>. Если такой области памяти не существует, то функция возвращает значение <code>NULL</code>.</p>
     <p>В противном случае возвращается указатель на соответствующую структуру <code>vm_area_struct</code>. Обратите внимание, что найденная область VMA может начинаться с адреса, большего адреса <code>addr</code>, и этот адрес не обязательно <emphasis>принадлежит</emphasis> найденной области памяти. Результат выполнения функции <code>find_vma()</code> кэшируется в поле <code>map_cache</code> дескриптора памяти. Поскольку очень велика вероятность того, что после одной операции с областью памяти последуют еще операции с ней же, то процент попаданий в кэш получается достаточно большим (на практике получаются значения порядка 30-40%). Проверка кэшированных результатов выполняется очень быстро. Если нужный адрес в кэше не найден, то выполняется поиск по всем областям памяти, связанным с заданным дескриптором. Этот поиск выполняется с помощью красно-черного дерева следующим образом.</p>
     <p><code>struct vm_area_struct* find_vma(struct mm_struct *mm,</code></p>
     <p><code> unsigned long addr) {</code></p>
     <p><code> struct vm_area_struct *vma = NULL;</code></p>
     <empty-line/>
     <p><code> if (mm) {</code></p>
     <p><code>  vma = mm-&gt;mmap_cache;</code></p>
     <p><code>  if (!(vma &amp;&amp; vma-&gt;vm_end &gt; addr &amp;&amp; vma-&gt;vm_start &lt;= addr)) {</code></p>
     <p><code>   struct rb_node* rb_node;</code></p>
     <empty-line/>
     <p><code>   rb_node = mm-&gt;mm_rb.rb_node;</code></p>
     <p><code>   vma = NULL;</code></p>
     <p><code>   while (rb_node) {</code></p>
     <p><code>    struct vm_area_struct* vma_tmp;</code></p>
     <empty-line/>
     <p><code>    vma_tmp =</code></p>
     <p><code>     rb_entry(rb_node, struct vm_area_struct, vm_rb);</code></p>
     <p><code>    if (vma_tmp-&gt;vm_end &gt; addr) {</code></p>
     <p><code>     vma = vma_tmp;</code></p>
     <p><code>     if (vma_tmp-&gt;vm_start &lt;= addr)</code></p>
     <p><code>      break;</code></p>
     <p><code>     rb_node = rb_node-&gt;rb_left;</code></p>
     <p><code>    } else</code></p>
     <p><code>     rb_node = rb_node-&gt;rb_right;</code></p>
     <p><code>   }</code></p>
     <p><code>   if (vma)</code></p>
     <p><code>    mm-&gt;mmap_cache = vma;</code></p>
     <p><code>  }</code></p>
     <p><code> }</code></p>
     <p><code> return vma;</code></p>
     <p><code>}</code></p>
     <p>Вначале выполняется проверка поля <code>vma_cache</code> на предмет того, содержит ли кэшированная область VMA необходимый адрес. Обратите внимание, что простая проверка того, является ли значение поля <code>vm_end</code> большим <code>addr</code>, не гарантирует что проверяемая область памяти является первой, в которой есть адреса, большие <code>addr</code>. Поэтому, для того чтобы кэш в этой ситуации оказался полезным, проверяемый адрес должен принадлежать кэшированной области памяти. К счастью, это как раз и соответствует случаю выполнения последовательных операций с одной и той же областью VMA.</p>
     <p>Если кэш не содержит нужную область VMA, то функция должна выполнять поиск по красно-черному дереву. Это выполняется путем проверки узлов дерева. Если значение поля <code>vma_end</code> для области памяти текущего узла больше <code>addr</code>, то текущим становится левый дочерний узел, в противном случае — правый. Функция завершает свою работу, как только находится область памяти, которая содержит адрес <code>addr</code>. Если такая область VMA не найдена, то функция продолжает поиск по дереву и возвращает ту область памяти, которая начинается после адреса <code>addr</code>. Если вообще не найдена ни одна область памяти, то возвращается значение <code>NULL</code>.</p>
    </section>
    <section>
     <title>
      <p>Функция <code>find_vma_prev()</code></p>
     </title>
     <p>Функция <code>find_vma_prev()</code> работает аналогично функции <code>find_vma()</code>, но дополнительно она еще возвращает последнюю область VMA, которая заканчивается перед адресом <code>addr</code>. Эта функция также определена в файле <code>mma/mmap.c</code> и объявлена в файле <code>&lt;linux/ram.h&gt;</code> следующим образом.</p>
     <p><code>struct vm_area_struct* find vma_prev(struct mm_struct *mm,</code></p>
     <p><code> unsigned long addr, struct vm_area_struct **pprev);</code></p>
     <p>Параметр <code>pprev</code> после возвращения из функции содержит указатель на предыдущую область VMA.</p>
    </section>
    <section>
     <title>
      <p>Функция <code>find_vma_intersection()</code></p>
     </title>
     <p>Функция <code>find_vma_intersection()</code> возвращает первую область памяти, которая перекрывается с указанным интервалом адресов. Эта функция определена в файле <code>&lt;linux/mm.h&gt;</code> следующим образом. Это функция с подстановкой тела.</p>
     <p><code>static inline struct vm_area_struct* find_vma_intersection(</code></p>
     <p><code> struct mm_struct *mm, unsigned long start_addr,</code></p>
     <p><code> unsigned long end_addr) {</code></p>
     <p><code> struct vm_area_struct *vma;</code></p>
     <p><code> vma = find_vma(mm, start_addr);</code></p>
     <p><code> if (vma &amp;&amp; end_addr &lt;= vma-&gt;vm_start)</code></p>
     <p><code>  vma = NULL;</code></p>
     <p><code> return vma;</code></p>
     <p><code>}</code></p>
     <p>Первый параметр — адресное пространство, в котором выполняется поиск, параметр <code>start_addr</code> — это первый адрес интервала адресов, а параметр <code>end_addr</code> — последний адрес интервала.</p>
     <p>Очевидно, что если функция <code>find_vma()</code> возвращает значение <code>NULL</code>, то это же значение будет возвращать и функция <code>find_vma_intersection()</code>. Если функция <code>find_vma()</code> возвращает существующую область VMA, то функция <code>find_vma_intersection()</code> возвратит ту же область только тогда, когда эта область <emphasis>не</emphasis> начинается после конца данного диапазона адресов. Если область памяти, которая возвращается функцией <code>find_vma()</code>, начинается после последнего адреса из указанного диапазона, то функция <code>find_vma_intersection()</code> возвращает значение <code>NULL</code>.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Функции <code>mmap()</code> и <code>do_mmap()</code>: создание интервала адресов</p>
    </title>
    <section>
     <p>Функция <code>do_mmap()</code> используется ядром для создания нового линейного интервала адресов. Говорить, что эта функция создает новую область VMA, — технически не корректно, поскольку если создаваемый интервал адресов является смежным с существующим интервалом адресов и у этих интервалов одинаковые права доступа, то два интервала объединяются в один. Если это невозможно, то создается новая область VMA В любом случае функция <code>do_mmap()</code> — это функция, которая добавляет интервал адресов к адресному пространству процесса, независимо от того, создается ли при этом новая область VMA или расширяется существующая.</p>
     <p>Функция <code>do_mmap()</code> объявлена в файле <code>&lt;linux/mm.h&gt;</code> следующим образом.</p>
     <p><code>unsigned long do_mmap(struct file *file,</code></p>
     <p><code> unsigned long addr, unsigned long len,</code></p>
     <p><code> unsigned long prot,</code> <code>unsigned long flag,</code></p>
     <p><code> unsigned long offset);</code></p>
     <p>Эта функция выполняет отображение на память содержимого файла <code>file</code> начиная с позиции в файле <code>offset</code>; размер отображаемого участка равен <code>len</code> байт. Значения параметров <code>file</code> и <code>offset</code> могут быть нулевыми, в этом случае отображение не будет резервироваться (сохраняться) в файле. Такое отображение называется <emphasis>анонимным</emphasis> (<emphasis>anonymous mapping</emphasis>). Если указан файл и смещение, то отображение называется <emphasis>отображением файла</emphasis> в память (<emphasis>file-backed mapping</emphasis>).</p>
     <p>Параметр <code>addr</code> указывает (точнее, всего лишь подсказывает), откуда начинать поиск свободного интервала адресов.</p>
     <p>Параметр <code>prot</code> указывает права доступа для страниц памяти в данной области. Возможные значение флагов зависят от аппаратной платформы и описаны в файле <code>&lt;asm/mman.h&gt;</code>. Хотя на практике для всех аппаратных платформ определены флаги, приведенные в табл. 14.2.</p>
     <empty-line/>
     <p><strong>Таблица 14.2</strong>. Флаги защиты страниц памяти</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Флаг</th>
       <th align="left" valign="top">Влияние на страницы памяти в созданном интервале адресов</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>PROT_READ</code></td>
       <td align="left" valign="top">Соответствует флагу <code>VM_READ</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>PROT_WRITE</code></td>
       <td align="left" valign="top">Соответствует флагу <code>VM_WRITE</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>PROT_EXEC</code></td>
       <td align="left" valign="top">Соответствует флагу <code>VM_EXEC</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>PROT_NONE</code></td>
       <td align="left" valign="top">К страницам памяти нет доступа</td>
      </tr>
     </table>
     <p>Параметр <code>flags</code> позволяет указать все остальные флаги области VMA Эти флаги также определены в <code>&lt;asm/mman.h&gt;</code> и приведены в табл. 14.3.</p>
     <empty-line/>
     <p><strong>Таблица 14.3</strong>. Флаги защиты страниц памяти</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Флаг</th>
       <th align="left" valign="top">Влияние на созданный интервал адресов</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>MAP_SHARED</code></td>
       <td align="left" valign="top">Отображение может быть совместно используемым</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>MAP_PRIVATE</code></td>
       <td align="left" valign="top">Отображение не может быть совместно используемым</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>MAP_FIXED</code></td>
       <td align="left" valign="top">Создаваемый интервал адресов должен начинаться с указанного адреса <code>addr</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>MAP_ANONYMOUS</code></td>
       <td align="left" valign="top">Отображение является анонимным, а не отображением файла</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>MAP_GROWSDOWN</code></td>
       <td align="left" valign="top">Соответствует флагу <code>VM_GROWSDOWN</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>MAP_DENYWRITE</code></td>
       <td align="left" valign="top">Соответствует флагу <code>VM_DENYWRITE</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>MAP_EXECUTABLE</code></td>
       <td align="left" valign="top">Соответствует флагу <code>VM_EXECUTABLE</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>MAP_LOCKED</code></td>
       <td align="left" valign="top">Соответствует флагу <code>VM_LOCKED</code></td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>MAP_NORESERVE</code></td>
       <td align="left" valign="top">Нет необходимости резервировать память для отображения</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>MAP_POPULATE</code></td>
       <td align="left" valign="top">Предварительно заполнить (prefault) таблицы страниц</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>MAP_NONBLOCK</code></td>
       <td align="left" valign="top">Не блокировать при операциях ввода-вывода</td>
      </tr>
     </table>
     <p>Если какой-либо из параметров имеет недопустимое значение, то функция <code>do_mmap()</code> возвращает отрицательное число. В противном случае создастся необходимый интервал адресов. Если это возможно, то этот интервал объединяется с соседней областью памяти. Если это невозможно, то создается новая структура <code>vm_area_struct</code>, которая выделяется в слябовом кэше <code>vm_area_cachep</code>. После этого новая область памяти добавляется в связанный список и красно-черное дерево областей памяти адресного пространства с помощью функции <code>vma_link()</code>. Затем обновляется значение поля <code>total_vm</code> в дескрипторе памяти. В конце концов, функция возвращает начальный адрес вновь созданного интервала адресов.</p>
    </section>
    <section>
     <title>
      <p>Системный вызов <code>mmap()</code></p>
     </title>
     <p>Возможности функции <code>do_mmap()</code> экспортируются в пространство пользователя с помощью системного вызова <code>mmap()</code>, который определен следующим образом.</p>
     <p><code>void *mmap2(void *start,</code></p>
     <p><code> size_t length, int prot, int flags, int fd, off_t pgoff);</code></p>
     <p>Этот системный вызов имеет имя <code>mmap2()</code>, т.е. второй вариант функции <code>mmap()</code>. Первоначальный вариант <code>mmap()</code> требовал в качестве последнего параметра смещение в байтах, а текущий вариант, <code>mmap2()</code>, — смещение в единицах размера страницы памяти. Это позволяет отображать файлы большего размера с большим значением смещения. Первоначальный вариант функции <code>mmap()</code>, который соответствует стандарту POSIX, доступен через библиотеку функций языка С, как функция <code>mmap()</code>, но в ядре уже не реализован. Новый вариант библиотечной функции называется <code>mmap2()</code>. Обе эти библиотечные функции используют системный вызов <code>mmap2()</code>. При этом библиотечная функция <code>mmap()</code> переводит значение смещения из байтов в количество страниц памяти.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Функции <code>munmap()</code> и <code>do_munmap()</code>: удаление интервала адресов</p>
    </title>
    <section>
     <p>Функция <code>do_munmap()</code> удаляет интервал адресов из указанного адресного пространства процесса. Эта функция объявлена в файле <code>&lt;asm/mman.h&gt;</code> следующим образом.</p>
     <p><code>int do_munmap(struct mm_struct *mm,</code></p>
     <p><code> unsigned long start, size_t len);</code></p>
     <p>Первый параметр указывает адресное пространство, из которого удаляется интервал адресов, начинающийся с адреса <code>start</code> и имеющий длину <code>len</code> байт. В случае успеха возвращается нуль, а в случае ошибки — отрицательное значение.</p>
    </section>
    <section>
     <title>
      <p>Системный вызов <code>munmap()</code></p>
     </title>
     <p>Системный вызов <code>munmap()</code> экспортируется в адресное пространство пользователя, чтобы иметь возможность удалять интервалы адресов из адресного пространства. Эта функция является комплиментарной к системному вызову <code>mmap()</code> и имеет следующий прототип.</p>
     <p><code>int munmap(void*start, size_t length);</code></p>
     <p>Данный системный вызов реализован в виде очень простой интерфейсной оболочки (wrapper) функции <code>do_munmap()</code>.</p>
     <p><code>asmlinkage long sys_munmap(unsigned long addr, size_t len) {</code></p>
     <p><code> int ret;</code></p>
     <p><code> struct mm_struct *mm; mm = current-&gt;mm;</code></p>
     <p><code> down_write(&amp;mm-&gt;mmap_sem);</code></p>
     <p><code> ret = do_munmap(mm, addr, len);</code></p>
     <p><code> p_write(&amp;mm-&gt;mmap_sem);</code></p>
     <p><code> return ret;</code></p>
     <p><code>}</code></p>
    </section>
   </section>
   <section>
    <title>
     <p>Таблицы страниц</p>
    </title>
    <p>Хотя пользовательские программы и работают с виртуальной памятью, которая отображается на физические адреса, процессоры работают непосредственно с этими физическими адресами. Следовательно, когда приложение обращается к адресу виртуальной памяти, этот адрес должен быть конвертирован в физический адрес, чтобы процессор смог выполнить запрос. Соответствующий поиск выполняется с помощью таблиц страниц. Таблицы страниц работают путем разбиения виртуального адреса на части. Каждая часть используется в качестве индекса (номера) записи в таблице. Таблица содержит или указатель на другую таблицу, или указатель на соответствующую страницу физической памяти.</p>
    <p>В операционной системе Linux таблицы страниц состоят из трех уровней<a l:href="#n83" type="note">[83]</a>. Несколько уровней позволяют эффективно поддерживать неравномерно заполненные адресные пространства даже для 64-разрядных машин. Если бы таблицы страниц были выполнены в виде одного статического массива, то их размер, даже для 32-разрядных аппаратных платформ, был бы чрезвычайно большим. В операционной системе Linux трехуровневые таблицы страниц используются даже для тех аппаратных платформ, которые аппаратно не поддерживают трехуровневых таблиц (например, для некоторых аппаратных платформ поддерживается только два уровня или аппаратно реализовано хеширование). Три уровня соответствуют своего рода "наибольшему общему знаменателю". Для аппаратных платформ с менее сложной реализацией работа с таблицами страниц в ядре при необходимости может быть упрощена с помощью оптимизаций компилятора.</p>
    <p>Таблица страниц самого верхнего уровня называется глобальным каталогом страниц (page global directory, PGD). Таблица PGD представляет собой массив элементов типа <code>pgd_t</code>. Для большинства аппаратных платформ тип <code>pgd_t</code> соответствует типу <code>unsigned long</code>. Записи в таблице PGD содержат указатели на каталоги страниц более низкого уровня, PMD.</p>
    <p>Каталоги страниц второго уровня еще называются каталогами страниц; среднего уровня (page middle directory, PMD). Каждый каталог PMD — это массив элементов типа <code>pmd_t</code>. Записи таблиц PMD укалывают на таблицы РТЕ (page table entry, запись таблицы страниц).</p>
    <p>Таблицы страниц последнего уровня называются просто таблицами страниц и содержат элементы типа <code>pte_t</code>. Записи таблиц страниц указывают на страницы памяти.</p>
    <p>Для большинства аппаратных платформ поиск в таблицах страниц выполняется аппаратным обеспечением (по крайней мере частично). При нормальной работе аппаратное обеспечение берет на себя большую часть ответственности по использованию таблиц страниц. Однако для этого ядро должно все настроить так, чтобы аппаратное обеспечение могло нормально работать. На рис. 14.1 показана диаграмма того, как происходит перевод виртуального адреса в физический с помощью таблицы страниц.</p>
    <image l:href="#img_22.jpeg"/>
    <p><strong>Рис. 14.1</strong>. Таблицы страниц</p>
    <p>Каждый процесс имеет свои таблицы страниц (разумеется, потоки эти таблицы используют совместно). Поле <code>pgd</code> дескриптора памяти указывает на глобальный каталог страниц. Манипуляции с таблицами и прохождение по ним требуют захвата блокировки <code>page_table_lock</code>, которая также находится в соответствующем дескрипторе памяти.</p>
    <p>Структуры данных, связанные с таблицами страниц, сильно зависят от аппаратной платформы и определены в файле <code>&lt;asm/page.h&gt;</code>.</p>
    <p>Поскольку практически каждое обращение к страницам виртуальной памяти требует определения соответствующего адреса физической памяти, производительность операций с таблицами страниц является очень критичной. Поиск всех этих адресов в памяти должен всегда выполняться очень быстро. Чтобы посодействовать этому, большинство процессоров имеют <emphasis>буфер быстрого преобразования адреса</emphasis> (<emphasis>translation lookaside buffer</emphasis>, или <emphasis>TLB</emphasis>), который работает, как аппаратный кэш отображения виртуальных адресов на физические. При обращении к виртуальному адресу процессор вначале проверяет, не кэшировано ли это отображение в TLB. Если обращение в кэш было удачным, то сразу же возвращается физический адрес. В противном случае поиск физического адреса выполняется с помощью таблиц страниц.</p>
    <p>Несмотря на это, управление таблицами страниц все же остается критичной и развивающейся частью ядра. Изменения в ядре 2.6 включают выделение частей таблиц страниц не в области верхней памяти. В будущем, вероятно, появится возможность совместного использования таблиц страниц с копированием при записи. В такой схеме таблицы страниц будут совместно использоваться родительским и порожденным процессами даже после выполнения вызова <code>fork()</code>. Если же родительский или порожденный процесс изменит некоторую запись таблицы страниц, то будет создана копия этой записи, и эти процессы больше не будут совместно использовать данную запись. Совместное использование таблиц страниц позволит устранить затраты, связанные с копированием таблиц страниц при вызове <code>fork()</code>.</p>
   </section>
   <section>
    <title>
     <p>Заключение</p>
    </title>
    <p>В этой главе была рассмотрена абстракция виртуальной памяти, которая предоставляется каждому процессу. Было рассказано, как ядро представляет адресное пространство процесса (с помощью структуры <code>struct mm_struct</code>) и каким образом ядро представляет области памяти внутри этого адресного пространства (<code>struct vm_area_struct</code>). Также рассказывалось о том, как ядро создает (с помощью функции <code>mmap()</code>) и удаляет (с помощью функции <code>munmap()</code>) области памяти. Б конце были рассмотрены таблицы страниц. Так как операционная система Linux — это система с виртуальной памятью, то все эти понятия очень важны для понимания работы системы и используемой модели процессов.</p>
    <p>В следующей главе рассматривается страничный кэш - общий кэш данных, который используется для выполнения страничных операций ввода-вывода и обратной записи страниц. Оставайтесь с нами!</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 15</p>
    <p>Страничный кэш и обратная запись страниц</p>
   </title>
   <section>
    <p>В ядре операционной системы Linux реализован один главный дисковый кэш, который называется страничным (page cache). Назначение этого кэша — минимизировать количество дисковых операций ввода-вывода путем хранения в памяти тех данных, для обращения к которым необходимо выполнять дисковые операции, Эта глава посвящена рассмотрению страничного кэша.</p>
    <p>Кэширование дисковых данных важно по двум причинам. Во-первых, доступ к диску выполняется значительно медленнее, чем доступ к памяти. Доступ к данным в памяти выполняется значительно быстрее, чем к данным на диске. Во-вторых, если к некоторым данным осуществлялся доступ, то с достаточно большой вероятностью к этим же данным в ближайшем будущем потребуется обратиться снова. Принцип, согласно которому операции обращения к некоторым данным имеют тенденцию группироваться друг с другом во времени, называется сосредоточенностью во времени (temporal locality). Сосредоточенность во времени гарантирует, что если данные кэшируются при первом доступе к ним, то существует большая вероятность удачного обращения в кэш к этим данным в ближайшем будущем.</p>
    <p>Страничный кэш состоит из физических страниц, которые находятся в оперативной памяти. Каждая страница памяти в кэше соответствует нескольким дисковым блокам. Когда ядро начинает некоторую операцию страничного ввода-вывода (дисковые, обычно файловые, операции ввода-вывода, которые выполняются порциями, равными размеру страницы памяти), то оно вначале проверяет, нет ли соответствующих данных в страничном кэше. Если эти данные есть в кэше, то ядро может не обращаться к диску и использовать данные прямо из страничного кэша.</p>
    <p>Отдельные дисковые блоки также могут быть привязаны к страничному кэшу с помощью буферов блочного ввода-вывода. Вспомните из материала главы 13, "Уровень блочного ввода-вывода", что буфер — это представление в памяти одного физического дискового блока. Буферы играют роль дескрипторов, которые отображают страницы памяти на дисковые блоки. Поэтому страничный кэш также позволяет сократить количество обращений к диску при выполнении операций блочного ввода-вывода как за счет кэширования, так и за счет буферизации операций блочного ввода-вывода для выполнения в будущем. Такой тип кэширования часто называют "буферным кэшем", хотя на самом деле это не отдельный кэш, а часть страничного кэша.</p>
    <p>Рассмотрим те типы операций и данных, которые связаны со страничным кэшем. Страничный кэш в основном пополняется при выполнении страничных операций ввода-вывода, таких как <code>read()</code> и <code>write()</code>. Страничные операции ввода-вывода выполняются с целыми страницами памяти, в которых хранятся данные, что соответствует операциям с более, чем одним дисковым блоком. В страничном кэше данные файлов хранятся порциями. Размер одной порции равен одной странице памяти.</p>
    <p>Операции блочного ввода-вывода работают в каждый отдельный момент времени с одним дисковым блоком. Часто встречающаяся операция блочного ввода-вывода — это чтение и запись файловых индексов. Ядро предоставляет функцию <code>bread()</code>, которая выполняет низкоуровневое чтение одного блока с диска. С помощью буферов дисковые блоки отображаются на связанные с ними страницы памяти и благодаря этому сохраняются в страничном кэше.</p>
    <p>Например, при первом открытии в текстовом редакторе дискового файла с исходным кодом, данные считываются с диска и записываются в память. При редактировании файла считывается вес больше данных в страницы памяти. Когда этот файл позже начинают компилировать, то ядро может считывать соответствующие страницы памяти из дискового кэша. Нет необходимости снова считывать данные с диска. Поскольку пользователи склонны к тому, чтобы периодически работать с одними и теми же файлами, страничный кэш уменьшает необходимость выполнения большого количества дисковых операций.</p>
   </section>
   <section>
    <title>
     <p>Страничный кэш</p>
    </title>
    <section>
     <p>Как следует из названия, страничный кэш (page cache) — это кэш страниц; памяти. Соответствующие страницы памяти получаются в результате чтения и записи обычных файлов на файловых системах, специальных файлов блочных устройств и файлов, отображаемых в память. Таким образом, в страничном кэше содержатся страницы памяти, полностью заполненные данными из файлов, к которым только что производился доступ. Перед выполнением операции страничного ввода-вывода, как, например, <code>read()</code><a l:href="#n84" type="note">[84]</a>, ядро проверяет, есть ли те данные, которые нужно считать, в страничном кэше. Если данные находятся в кэше, то ядро может быстро возвратить требуемую страницу памяти.</p>
    </section>
    <section>
     <title>
      <p>Объект <code>address_space</code></p>
     </title>
     <p>Физическая страница памяти может содержать данные из нескольких несмежных физических дисковых блоков<a l:href="#n85" type="note">[85]</a>.</p>
     <p>Проверка наличия определенных данных в страничном кэше может быть затруднена, если смежные блоки принадлежат совершенно разным страницам памяти. Невозможно проиндексировать данные в страничном кэше, используя только имя устройства и номер блока, что было бы наиболее простым решением.</p>
     <p>Более того, страничный кэш ядра Linux является хранилищем данных достаточно общего характера в отношении того, какие страницы памяти в нем могут кэшироваться. Первоначально страничный кэш был предложен в операционной системе System V (SVR 4) для кэширования только данных из файловых систем. Следовательно, для управления страничным кэшем операционной системы SVR 4 использовался эквивалент файлового объекта, который назывался <code>struct vnode</code>. Кэш операционной системы Linux разрабатывался с целью кэширования <emphasis>любых</emphasis> объектов, основанных на страницах памяти, что включает множество типов файлов и отображений в память.</p>
     <p>Для получения необходимой общности в страничном кэше операционной системы Linux используется структура <code>address_space</code> (адресное пространство), которая позволяет идентифицировать страницы памяти, находящиеся в кэше. Эта структура определена в файле <code>&lt;linux/fs.h&gt;</code> следующим образом.</p>
     <p><code>struct address_space {</code></p>
     <p><code> struct inode                    *host;     /* файловый индекс, которому</code></p>
     <p><code>                                               принадлежит объект */</code></p>
     <p><code> struct radix_tree_root          page_tree; /* базисное дерево</code></p>
     <p><code>                                               всех страниц */</code></p>
     <p><code> spinlock_t                      tree_lock; /* блокировка для защиты</code></p>
     <p><code>                                               поля page_tree */</code></p>
     <p><code> unsigned int                    i_mmap_wrltable; /* количество областей</code></p>
     <p><code>                                             памяти</code><code> с флагом VM_SHARED */</code></p>
     <p><code> struct prio_tree_root           i_mmap;    /* список всех отображений */</code></p>
     <p><code> struct list_head                i_mmap_nonlinear; /* список областей</code></p>
     <p><code>                                         памяти с флагом VM_NONLINEAR */</code></p>
     <p><code> spinlock_t                      i_mmap_lock; /* блокировка поля i_mmap */</code></p>
     <p><code> atomic_t                        truncate_count; /* счетчик запросов</code></p>
     <p><code>                                                    truncate */</code></p>
     <p><code> unsigned long                   nrpages; /* общее количество страниц */</code></p>
     <p><code> pgoff_t                         writeback_index; /* смещения начала</code></p>
     <p><code>                                                     обратной записи */</code></p>
     <p><code> struct address_space_operations *a_ops;          /* таблица операций */</code></p>
     <p><code> unsigned long                   flags;           /* маска gfp_mask</code></p>
     <p><code>                                                     и флаги ошибок */</code></p>
     <p><code> struct backing_dev_info         *backing_dev_info; /* информация</code></p>
     <p><code>                                           упреждающего чтения */</code></p>
     <p><code> spinlock_t                      private_lock; /* блокировка</code></p>
     <p><code>                                     для частных отображений */</code></p>
     <p><code> struct list_head                private_list; /* список</code></p>
     <p><code>                                                  частных отображений */</code></p>
     <p><code> struct address_spacs            *assoc_mapping; /* соответствующие</code></p>
     <p><code>                                                    буферы */</code></p>
     <p><code>};</code></p>
     <p>Поле <code>i_mmap</code> — это дерево поиска по приоритетам для всех совместно используемых и частных отображений. Дерево поиска по приоритетам— это хитрая смесь базисных и частично упорядоченных бинарных деревьев<a l:href="#n86" type="note">[86]</a>.</p>
     <p>Всего в адресном пространстве nrpages страниц памяти.</p>
     <p>Объект <code>address_space</code> связан с некоторым другим объектом ядра, обычно с файловым индексом. Если это так, то поле <code>host</code> указывает на соответствующий файловый индекс. Если значение поля <code>host</code> равно <code>NULL</code>, то соответствующий объект не является файловым индексом; например, объект <code>address_space</code> может быть связан с процессом подкачки страниц (swapper).</p>
     <p>Поле <code>a_ops</code> указывает на таблицу операций с адресным пространством так же, как и в случае объектов подсистемы VFS. Таблица операций представлена с помощью структуры <code>struct address_space_operations</code>, которая определена в файле <code>&lt;linux/fs.h&gt;</code> следующим образом.</p>
     <p><code>struct address_space_operations {</code></p>
     <p><code> int (*writepage)(struct page*, struct writeback_control*);</code></p>
     <p><code> int (*readpage)(struct file*, struct page*);</code></p>
     <p><code> int (*sync_page)(struct page*);</code></p>
     <p><code> int (*writepages)(struct address_space*,</code></p>
     <p><code>  struct writeback_control*);</code></p>
     <p><code> int (*set_page_dirty)(struct page*);</code></p>
     <p><code> int (*readpages)(struct file*, struct address_space*,</code></p>
     <p><code>  struct list_head*, unsigned);</code></p>
     <p><code> int (*prepare_write)(struct file*, struct page*,</code></p>
     <p><code>  unsigned, unsigned);</code></p>
     <p><code> int (*commit_write)(struct file*, struct page*,</code></p>
     <p><code>  unsigned, unsigned);</code></p>
     <p><code> sector_t (*bmap)(struct address_space*, sector_t);</code></p>
     <p><code> int (*invalidatepage)(struct page*, unsigned long);</code></p>
     <p><code> int (*releasepage)(struct page*, int);</code></p>
     <p><code> int (*direct_IO)(int, struct kiocb*, const struct iovec*,</code></p>
     <p><code>  loff_t, unsigned long);</code></p>
     <p><code>};</code></p>
     <p>Методы <code>read_page</code> и <code>write_page</code> являются наиболее важными. Рассмотрим шаги, которые выполняются при страничной операции чтения.</p>
     <p>Методу чтения в качестве параметров передается пара значений: объект <code>address_space</code> и смещение. Эти значения используются следующим образом для поиска необходимых данных в страничном кэше.</p>
     <p><code>page = find_get_page(mapping, index);</code></p>
     <p>где параметр <code>mapping</code> — это заданное адресное пространство, a <code>index</code> — заданная позиция в файле.</p>
     <p>Если в кэше нет необходимой страницы памяти, то новая страница памяти выделяется и добавляется в кэш следующим образом.</p>
     <p><code>struct page *cached_page;</code></p>
     <p><code>int error;</code></p>
     <empty-line/>
     <p><code>cached_page = page_cache_alloc_cold(mapping);</code></p>
     <p><code>if (!cached_page)</code></p>
     <p><code> /* ошибка выделения памяти */</code></p>
     <p><code>error =</code></p>
     <p><code> add_to_page_cache_lru(cached_page, mapping, index, GFP_KERNEL);</code></p>
     <p><code>if (error)</code></p>
     <p><code> /* ошибка добавления страницы памяти в страничный кэш */</code></p>
     <p>Наконец, необходимые данные могут быть считаны с диска, добавлены в страничный кэш и возвращены пользователю. Это делается следующим образом.</p>
     <p><code>error = mapping-&gt;a_ops-&gt;readpage(file, page);</code></p>
     <p>Операции записи несколько отличаются. Для отображаемых в память файлов при изменении страницы памяти система управления виртуальной памятью просто вызывает следующую функцию.</p>
     <p><code>SetPageDirty(page);</code></p>
     <p>Ядро выполняет запись этой страницы памяти позже с помощью вызова метода <code>writepage()</code>. Операции записи для файлов, открытых обычным образом (без отображения в память), выполняются более сложным путем. В основном, общая операция записи, которая реализована в файле <code>mm/filemap.с</code>, включает следующие шаги.</p>
     <p><code>page =</code></p>
     <p><code> __grab_cache_page(mapping, index, &amp;cached_page, &amp;lru_pvec);</code></p>
     <p><code>status =</code></p>
     <p><code> a_ops-&gt;prepare_write(file, page, offset, offset+bytes);</code></p>
     <p><code>page_fault =</code></p>
     <p><code> filemap_copy_from_user(page, offset, buf, bytes);</code></p>
     <p><code>status =</code></p>
     <p><code> a_ops-&gt;commit_write(file, page, offset, offset+bytes);</code></p>
     <p>Выполняется поиск необходимой страницы памяти в кэше. Если такая страница в кэше не найдена, то создается соответствующий элемент кэша. Затем вызывается метод <code>prepare_write()</code>, чтобы подготовить запрос на запись. После этого данные копируются из пространства пользователя в буфер памяти в пространстве ядра. И наконец данные записываются на диск с помощью функции <code>commit_write()</code>.</p>
     <p>Поскольку все описанные шаги выполняются при всех операциях страничного ввода-вывода, то все операции страничного ввода-вывода выполняются только через страничный каш. Ядро пытается выполнить все запросы чтения из страничного кэша. Если этого сделать не удается, то страница считывается с диска и добавляется в страничный кэш. Для операций записи страничный кэш выполняет роль "стартовой площадки". Следовательно, все записанные страницы также добавляются в страничный кэш.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Базисное дерево</p>
    </title>
    <section>
     <p>Так как ядро должно проверять наличие страниц в страничном кэше перед тем, как запускать любую операцию страничного ввода-вывода, то этот поиск должен выполняться быстро. В противном случае затраты на поиск могут свести на нет все выгоды кэширования (по крайней мере, в случае незначительного количества удачных обращений в кэш, эти затраты времени будут сводить на нет все преимущества считывания данных из памяти по сравнению со считыванием напрямую с диска).</p>
     <p>Как было показано в предыдущем разделе, поиск в страничном кэше выполняется на основании информации объекта <code>address_space</code> и значения смещения. Каждый объект <code>address_space</code> имеет свое уникальное базисное дерево (radix tree), которое хранится в поле <code>page_tree</code>. Базисное дерево — это один из типов бинарных деревьев. Базисное дерево позволяет выполнять очень быстрый поиск необходимой страницы только на основании значения смещения в файле. Функции поиска в страничном кэше, такие как <code>find_get_page()</code> и <code>radix_tree_lookup()</code>, выполняют поиск с использованием заданного дерева и заданного объекта.</p>
     <p>Основной код для работы с базисными деревьями находится в файле <code>lib/radix-tree.c</code>. Для использования базисных деревьев необходимо подключить заголовочный файл <code>&lt;linux/radix-tree.h&gt;</code>.</p>
    </section>
    <section>
     <title>
      <p>Старая хеш-таблица страниц</p>
     </title>
     <p>Для ядер до серии 2.6 поиск в страничном кэше не выполнялся с помощью базисных деревьев. Вместо этого поддерживалась глобальная хеш-таблица всех страниц памяти в системе. Специальная хеш-функция возвращала двухсвязный список значений, связанных с одним значением ключа. Если нужная страница находится в кэше, то один из элементов этого списка соответствует этой нужной странице. Если страница в кэше отсутствует, то хеш-функция возвращает значение <code>NULL</code>.</p>
     <p>Использование глобальной хеш-таблицы приводило к четырем основным проблемам.</p>
     <p>• Хеш-таблица защищалась одной глобальной блокировкой. Количество конфликтов при захвате этой блокировки было достаточно большим даже для не очень больших машин. В результате страдала производительность.</p>
     <p>• Размер хеш-таблицы был большим, потому что в ней содержалась информация обо всех страницах памяти в страничном кэше, в то время как важными являются лишь страницы, связанные с одним конкретным файлом.</p>
     <p>• Производительность в случае неудачного обращения в кэш (когда искомая страница памяти не находится в кэше) падала из-за необходимости просматривать все элементы списка, связанного с заданным ключом.</p>
     <p>• Хеш-таблица требовала больше памяти, чем другие возможные решения.</p>
     <p>Применение в ядрах серии 2.6 страничного кэша на основании базисных деревьев позволило решить эти проблемы.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Буферный кэш</p>
    </title>
    <p>В операционной системе Linux больше нет отдельного буферного кэша. В ядрах серии 2.2 существовало два отдельных кэша: страничный и буферный. В первом кэшировались: страницы памяти, а в другом — буферы. Эти два кэша не были объединены между собой. Дисковый блок мог находиться в обоих кэшах одновременно. Это требовало больших усилий по синхронизации двух кэшированных копий, не говоря уже о напрасной трате памяти.</p>
    <p>Так было в ядрах серии 2.2 и более ранних, но начиная с ядер Linux серии 2.4 оба кэша объединили вместе. Сегодня существует только один дисковый кэш — страничный кэш.</p>
    <p>Ядру все еще необходимо использовать буферы для того, чтобы представлять дисковые блоки в памяти. К счастью, буферы описывают отображение блоков на страницы памяти, которые в свою очередь находятся в страничном кэше.</p>
   </section>
   <section>
    <title>
     <p>Демон <code>pdflush</code></p>
    </title>
    <section>
     <p>Измененные (dirty, "грязные") страницы памяти когда-нибудь должны быть записаны на диск. Обратная запись страниц памяти выполняется в следующих двух случаях.</p>
     <p>• Когда объем свободной памяти становится меньше определенного порога, ядро должно записать измененные данные обратно на диск, чтобы освободить память.</p>
     <p>• Когда несохраненные данные хранятся в памяти достаточно долго, то ядро должно их записать на диск, чтобы гарантировать, что эти данные не будут находиться в несохраненном состоянии неопределенное время.</p>
     <p>Эти два типа записи имеют разные цели. В более старых ядрах они выполнялись двумя разными потоками пространства ядра (см. следующий раздел). Однако в ядре 2.6 эту работу выполняет группа (gang<a l:href="#n87" type="note">[87]</a>) потоков ядра <code>pdflush</code>, которые называются демонами фоновой обратной записи (или просто потоками <code>pdflush</code>). Ходят слухи, что название <code>pdflush</code> — это сокращение от "dirty page flush" ("очистка грязных страниц"). Не обращайте внимание на это сомнительное название, давайте лучше более детально рассмотрим, для чего нужны эти процессы.</p>
     <p>Во-первых, потоки <code>pdflush</code> служат для записи измененных страниц на диск, когда объем свободной памяти в системе уменьшается до определенного уровня. Цель такой фоновой записи — освобождение памяти, которую занимают незаписанные страницы, в случае недостатка физических страниц памяти. Уровень, когда начинается обратная запись, может быть сконфигурирован с помощью параметра <code>dirty_background_ratio</code> утилиты <code>sysctl</code>. Когда объем свободной памяти становится меньше этого порога, ядро вызывает функцию <code>wakeup_bdflush()</code><a l:href="#n88" type="note">[88]</a> для перевода в состояние выполнения потока <code>pdflush</code>, который выполняет функцию обратной записи измененных страниц памяти <code>background_writeout()</code>. Эта функция получает один параметр, равный количеству страниц, которые функция должна попытаться записать на диск.</p>
     <p>Функция продолжает запись до тех пор, пока не выполнятся два следующих условия.</p>
     <p>• Указанное минимальное количество страниц записано на диск.</p>
     <p>• Объем свободной памяти превышает соответствующее значение параметра <code>dirty_background_ratio</code>.</p>
     <p>Выполнение этих условий гарантирует, что демон <code>pdflush</code> выполнил свою работу по предотвращению нехватки памяти. Если эти условия не выполняются, то обратная запись может остановиться только тогда, когда демон <code>pdflush</code> запишет на диск все несохраненные страницы и для него больше не будет работы.</p>
     <p>Во-вторых, назначение демона <code>pdflush</code> — периодически переходить в состояние выполнения (независимо от состояния нехватки памяти) и записывать на диск очень давно измененные страницы памяти. Это гарантирует, что измененные страницы не будут находиться в памяти неопределенное время. При сбоях системы будут потеряны те страницы памяти, которые не были сохранены на диске, так как содержимое памяти после перегрузки не сохраняется. Следовательно, периодическая синхронизация страничного кэша с данными на диске является важным делом. При загрузке системы инициализируется таймер, периодически возвращающий к выполнению поток <code>pdflush</code>, который выполняет функцию <code>wb_kupdate()</code>. Эта функция выполняет обратную запись данных, которые были изменены более чем <code>dirty_expire_centisecs</code> сотых секунды тому назад. После этого таймер снова инициализируется, чтобы сработать через <code>dirty_expire_centisecs</code> сотых секунды. Таким образом потоки <code>pdflush</code> периодически возвращаются к выполнению и записывают на диск все измененные страницы, данные в которых старше, чем указанный лимит.</p>
     <p>Системный администратор может установить эти значения с помощью каталога <code>/proc/sys/vm</code> и утилиты <code>sysctl</code>. В табл. 15.1 приведен список всех соответствующих переменных.</p>
     <empty-line/>
     <p><strong>Таблица 15.1</strong>. Параметры для настройки демона <code>pdflush</code></p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Переменная</th>
       <th align="left" valign="top">Описание</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>dirty_background_ratio</code></td>
       <td align="left" valign="top">Объем свободной оперативной памяти, при котором демон <code>pdflush</code> начинает обратную запись незаписанных данных</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>dirty_expire_centisecs</code></td>
       <td align="left" valign="top">Время, в сотых долях секунды, в течение которого незаписанные данные могут оставаться в памяти, перед тем как демон <code>pdflush</code> не запишет их на диск при следующем периоде обратной записи</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>dirty_ratio</code></td>
       <td align="left" valign="top">Процент от общей оперативной памяти, соответствующий страницам памяти одного процесса, при котором начинается обратная запись незаписанных данных</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>dirty_writeback_centisecs</code></td>
       <td align="left" valign="top">Насколько часто, в сотых долях секунды, процесс <code>bdflush</code> возвращается к выполнению для обратной записи данных</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>laptop_mode</code></td>
       <td align="left" valign="top">Переменная булевого типа, которая включает или выключает режим ноутбука (см. следующий раздел)</td>
      </tr>
     </table>
     <p>Код потока <code>pdflush</code> находится в файлах <code>mm/page-writeback.c</code> и <code>fs/fs-writeback.c</code>.</p>
     <subtitle>Режим ноутбука</subtitle>
     <p>Режим ноутбука — это специальная политика обратной записи страниц с целью оптимизации использования батареи и продления срока ее работы. Это делается путем минимизации активности жестких дисков, чтобы они оставались в остановленном состоянии по возможности долго. Конфигурировать этот режим можно с помощью файла <code>/proc/sys/vm/laptop_mode</code>. По умолчанию в этом файле записано значение 0 и режим ноутбука выключен. Запись значения 1 в этот файл позволяет включить режим ноутбука.</p>
     <p>В режиме ноутбука существует всего одно изменение в выполнении обратной записи страниц. В дополнение к обратной записи измененных страниц; памяти, когда они становятся достаточно старыми, демон <code>pdflush</code> также выполняет и все остальные операции дискового ввода-вывода, записывая все дисковые буферы на диск. Таким образом демон <code>pdflush</code> пользуется тем преимуществом, что диск уже запущен, а также он гарантирует, что в ближайшем будущем диск снова запущен не будет.</p>
     <p>Такое поведение имеет смысл, когда параметры <code>dirty_expire_centisecs</code> и <code>dirty_writeback_centisecs</code> установлены в большие значения, скажем 10 минут. При таких задержках обратной записи диск запускается не часто, а когда он все-таки запускается, то работа в режиме ноутбука гарантирует, что этот момент будет использован с максимальной эффективностью.</p>
     <p>Во многих поставках ОС Linux режим ноутбука автоматически включается и выключается, при этом также могут изменяться и другие параметры демона <code>pbflush</code>, когда заряд батареи уменьшается. Такое поведение позволяет получать преимущества от режима ноутбука при работе от батареи и автоматически возвращаться к нормальному поведению, когда машина включается в электрическую сеть.</p>
    </section>
    <section>
     <title>
      <p>Демоны <code>bdflush</code> и <code>kupdated</code></p>
     </title>
     <p>В ядрах серий до 2.6 работа потоков <code>pdflush</code> выполнялась двумя другими потоками ядра <code>bdflush</code> и <code>kupdated</code>.</p>
     <p>Поток пространства ядра <code>bdflush</code> выполнял фоновую обратную запись измененных страниц, когда количество доступной памяти становилось достаточно малым. Также был определен ряд пороговых значений, аналогично тому как это делается для демона <code>pdflush</code>. Демон <code>bdflush</code> возвращался к выполнению с помощью функции <code>wakeup_bdflush()</code>, когда количество свободной памяти становилось меньше этих пороговых значений.</p>
     <p>Между демонами <code>bdflush</code> и <code>pdflush</code> существует два главных отличия. Первое состоит в том, что демон <code>bdflush</code> был всего один, а количество потоков <code>pdflush</code> может меняться динамически. Об этом более подробно будет рассказано в следующем разделе. Второе отличие состоит в том, что демон <code>bdflush</code> работал с буферами, он записывал на диск измененные буферы. Демон <code>pdflush</code> работает со страницами, он записывает на диск целые измененные страницы памяти. Конечно, страницы памяти могут соответствовать буферам, но единицей ввода-вывода является целая страница памяти, а не один буфер. Это дает преимущество, поскольку работать со страницами памяти проще, чем с буферами, так как страница памяти — более общий и более часто используемый объект.</p>
     <p>Так как демон <code>bdflush</code> выполнял обратную запись, только когда количество свободной памяти очень сильно уменьшалось или количество буферов было очень большим, то был введен поток ядра <code>kupdated</code>, который периодически выполнял обратную запись измененных страниц памяти. Он использовался для целей, аналогичных функции <code>wb_kupdate()</code> демона <code>pdflush</code>.</p>
     <p>Потоки <code>bdflush</code> и <code>kupdated</code> и их функциональность сейчас заменены потоками <code>pdflush</code>.</p>
    </section>
    <section>
     <title>
      <p>Предотвращение перегруженности: для чего нужны несколько потоков</p>
     </title>
     <p>Один из главных недостатков решения на основе демона <code>bdflush</code> состоит в том, что демон <code>bdflush</code> имел всего один поток выполнения. Это приводило к возможности зависания демона при большом количестве операций обратной записи, когда один поток демона <code>bdflush</code> блокировался на очереди запросов ввода-вывода перегруженного устройства, в то время как очереди запросов других устройств могли быть в этот момент сравнительно свободными. Если система имеет несколько дисков и соответствующую процессорную мощность, то ядро должно иметь возможность загрузить работой все диски. К сожалению, даже при большом количестве данных, для которых необходима обратная запись, демон <code>bdflush</code> может оказаться загруженным работой с одной очередью и не сможет поддерживать все диски в нагруженном состоянии. Это происходит потому, что пропускная способность диском конечна и, к несчастью, очень низкая. Если только один поток выполняет обратную запись страниц, то он может проводить много времени в ожидании одного диска, так как пропускная способность диска ограничена. Для облегчения этой ситуации ядру необходима многопоточная обратная запись. В таком случае ни одна очередь запросов не может стать узким местом.</p>
     <p>В ядрах серии 2.6 эта проблема решается путем введения нескольких потоков <code>pdflush</code>. Каждый поток самостоятельно выполняет обратную запись страниц памяти на диск, что позволяет различным потокам <code>pdflush</code> работать с разными очередями запросов устройств.</p>
     <p>Количество потоков изменяется в процессе работы системы в соответствии с простым алгоритмом. Если все существующие потоки <code>pdflush</code> оказываются занятыми в течение одной секунды, то создается новый поток <code>pdflush</code>. Общее количество потоков не может превышать значения константы <code>MAX_PDFLUSH_THREADS</code>, которая по умолчанию равна 8. И наоборот, если поток pdflush находился в состоянии ожидания больше одной секунды, то он уничтожается. Минимальное количество потоков равно, по крайней мере, значению константы <code>MIN_PDFLUSH_THREADS,</code> что по умолчанию соответствует 2. Таким образом, количество потоков <code>pdflush</code> изменяется динамически в зависимости от количества страниц, для которых необходима обратная запись, и загруженности этих потоков. Если все потоки <code>pdflush</code> заняты обратной записью, то создается новый поток. Это гарантирует, что ни одна из очередей запросов устройств не будет перегружена, в то время как другие очереди устройств не так загружены и в них тоже можно выполнять обратную запись. Если перегрузка предотвращается, то количество потоков <code>pdflush</code> уменьшается, чтобы освободить память.</p>
     <p>Всё это хорошо, но что если все потоки <code>pdflush</code> зависнут в ожидании записи в одну и ту же перегруженную очередь? В этом случае производительность нескольких потоков <code>pdflush</code> не будет выше производительности одного потока, а количество занятой памяти станет значительно большим. Чтобы уменьшить такой эффект, для потоков <code>pdflush</code> реализован алгоритм предотвращения зависания (congestion avoidance). Потоки активно начинают обратную запись страниц для тех очередей, которые не перегружены. В результате потоки <code>pdflush</code> распределяют свою работу по разным очередям и воздерживаются от записи в перегруженную очередь. Когда все потоки <code>pdflush</code> заняты работой и запускается новый поток, то это означает, что они действительно заняты.</p>
     <p>В связи с усовершенствованием алгоритмов обратной записи страниц, включая введение демона <code>bdflush</code>, ядро серии 2.6 позволяет поддерживать в загруженном состоянии значительно большее количество дисков, чем в более старых версиях ядер. При активной работе потоки <code>pdflush</code> могут обеспечить большую пропускную способность сразу для большого количества дисковых устройств.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Коротко о главном</p>
    </title>
    <p>В этой главе был рассмотрен страничный кэш и обратная запись страниц. Было показано, как ядро выполняет все операции страничного ввода-вывода, как операции записи откладываются с помощью дискового кэша и как данные записываются на диск с помощью группы потоков пространства ядра <code>pdflush</code>.</p>
    <p>На основании материала последних нескольких глав вы получили устойчивое представление о том, как выполняется управление памятью и файловыми системами. Теперь давайте перейдем к теме модулей и посмотрим, ядро Linux обеспечивает модульную и динамическую инфраструктуру для загрузки кода ядра во время работы системы.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 16</p>
    <p>Модули</p>
   </title>
   <section>
    <p>Несмотря на то что ядро является монолитным, в том смысле что все ядро выполняется в общем защищенном адресном домене, ядро Linux также является модульным, что позволяет выполнять динамическую вставку и удаление кода ядра в процессе работы системы. Соответствующие подпрограммы, данные, а также точки входа и выхода группируются в общий бинарный образ, загружаемый объект ядра, который называется модулем. Поддержка модулей позволяет системам иметь минимальное базовое ядро с опциональными возможностями и драйверами, которые компилируются в качестве модулей. Модули также позволяют просто удалять и перегружать код ядра, что помогает при отладке, а также дает возможность загружать драйверы по необходимости в ответ на появление новых устройств с функциями горячего подключения.</p>
    <p>В этой главе рассказывается о хитростях, которые стоят за поддержкой модулей в ядре, и о том, как написать свой собственный модуль.</p>
   </section>
   <section>
    <title>
     <p>Модуль "Hello, World!"</p>
    </title>
    <p>В отличие от разработки основных подсистем ядра, большинство из которых были уже рассмотрено, разработка модулей подобна созданию новой прикладной программы, по крайней мере в том, что модули имеют точку входа, точку выхода и находятся каждый в своем бинарном файле.</p>
    <p>Может показаться банальным, но иметь возможность написать программу, которая выводит сообщение "Hello World!", и не сделать этого- просто смешно. Итак, леди и джентльмены, модуль "Hello, World!".</p>
    <p><code>/*</code></p>
    <p><code>* hello.c - модуль ядра Hello, World!</code></p>
    <p><code>*/</code></p>
    <p><code>#include &lt;linux/init.h&gt;</code></p>
    <p><code>#include &lt;linux/module.h&gt;</code></p>
    <p><code>#include &lt;linux/kernel.h&gt;</code></p>
    <empty-line/>
    <p><code>/*</code></p>
    <p><code>* hello_init - функция инициализации, вызывается при загрузке модуля,</code></p>
    <p><code>* В случае успешной загрузки модуля возвращает значение нуль,</code></p>
    <p><code>* и ненулевое значение в противном случае.</code></p>
    <p><code>*/</code></p>
    <p><code>static int hello_init(void) {</code></p>
    <p><code> printk(KERN_ALERT "I bear a charmed life.\n");</code></p>
    <p><code> return 0;</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>/*</code></p>
    <p><code>* hello_exit - функция завершения, вызывается при выгрузке модуля.</code></p>
    <p><code>*/</code></p>
    <p><code>static void hello_exit(void) {</code></p>
    <p><code> printk(KERN_ALERT "Out, out, brief candle!\n");</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>module_init(hello_init);</code></p>
    <p><code>module_exit(hello_exit);</code></p>
    <empty-line/>
    <p><code>MODULE_LICENSE{"GPL");</code></p>
    <p><code>MODULE_AUTHOR("Shakespeare");</code></p>
    <p>Это самый простой модуль ядра, который только может быть. Функция <code>hello_init()</code> регистрируется с помощью макроса <code>module_init()</code> в качестве точки входа в модуль. Она вызывается ядром при загрузке модуля. Вызов <code>module_init()</code> — это не вызов функции, а макрос, который устанавливает значение своего параметра в качестве функции инициализации. Все функции инициализации должны соответствовать следующему прототипу.</p>
    <p><code>int my_init(void);</code></p>
    <p>Так как функция инициализации редко вызывается за пределами модуля, ее обычно не нужно экспортировать и можно объявить с ключевым словом <code>static</code>.</p>
    <p>Функции инициализации возвращают значение тина <code>int</code>. Если инициализация (или то, что делает функция инициализации) прошла успешно, то функция должна возвратить значение нуль. В случае ошибки возвращается ненулевое значение.</p>
    <p>В данном случае эта функция просто печатает сообщение и возвращает значение нуль. В настоящих модулях функция инициализации регистрирует ресурсы, выделяет структуры данных и т.д. Даже если рассматриваемый файл будет статически скомпилирован с ядром, то функция инициализации останется и будет вызвана при загрузке ядра.</p>
    <p>Функция <code>hello_exit()</code> регистрируется в качестве точки выхода из модуля с помощью макроса <code>module_exit()</code>. Ядро вызывает функцию <code>hello_exit()</code>, когда модуль удаляется из памяти. Завершающая функция должна выполнить очистку ресурсов, гарантировать, что аппаратное обеспечение находится в непротиворечивом состоянии, и т.д. После того как эта функция завершается, модуль выгружается.</p>
    <p>Завершающая функция должна соответствовать следующему прототипу.</p>
    <p><code>void my_exit(void);</code></p>
    <p>Так же как и в случае функции инициализации, ее можно объявить как <code>static</code>.</p>
    <p>Если этот файл будет статически скомпилирован с образом ядра, то данная функция не будет включена в образ и никогда не будет вызвана (так как если нет модуля, то код никогда не может быть удален из памяти).</p>
    <p>Макрос <code>MODULE_LICENSE()</code> позволяет указать лицензию на право копирования модуля. Загрузка в память модуля, для которого лицензия не соответствует GPL, приведет к установке в ядре флага <code>tainted</code> (буквально, испорченное). Этот флаг служит для информационных целей, кроме того, многие разработчики уделяют меньше внимания сообщениям об ошибках, в которых указан этот флаг. Более того, модули, у которых лицензия не соответствует GPL, не могут использовать символы, которые служат "только для GPL" (см. раздел "Экспортируемые символы" ниже в этой главе).</p>
    <p>Наконец, макрос <code>MODULE_AUTHOR()</code> позволяет указать автора модуля. Значение этого макроса служит только для информационных целей.</p>
   </section>
   <section>
    <title>
     <p>Сборка модулей</p>
    </title>
    <section>
     <p>Благодаря новой системе сборки "kbuild", в ядрах серии 2.6 сборка модулей выполняется значительно проще, чем в старых сериях. Первое, что нужно сделать при сборке модулей, — это решить, где будет находиться исходный код модуля. Исходный код модуля необходимо правильно объединить с деревом исходных кодов ядра. Это можно сделать в виде заплаты или путем добавления в официальное дерево исходного кода ядра. Кроме этого, можно компилировать исходный код модуля отдельно от исходных кодов ядра.</p>
    </section>
    <section>
     <title>
      <p>Использование дерева каталогов исходных кодов ядра</p>
     </title>
     <p>В идеале модуль является частью официального ядра и находится в каталоге исходных кодов ядра. Введение вашей разработки непосредственно в ядро может вначале потребовать больше работы, но обычно такое решение более предпочтительно.</p>
     <p>На первом этапе необходимо решить, <emphasis>где</emphasis> именно будет находиться модуль в дереве исходных кодов ядра. Драйверы необходимо хранить в подкаталогах каталога <code>drivers/</code>, который находится в корне дерева исходных кодов ядра. Внутри этого каталога драйверы делятся на классы, типы и собственно на отдельные драйверы. Символьные устройства находятся в каталоге <code>drivers/char/</code>, блочные — в каталоге <code>drivers/block/</code>, устройства USB — в каталоге <code>drivers/usb/</code>. Эти правила не есть жесткими, так как многие устройства USB также являются и символьными устройствами. Но такая организация является понятной и четкой.</p>
     <p>Допустим, что вы хотите создать свой подкаталог и ваш воображаемый драйвер разработан для удочки с числовым программным управлением, которая имеет интерфейс Fish Master XL 2000 Titanium для подключения к компьютеру. Следовательно, необходимо создать подкаталог <code>fishing</code> внутри каталога <code>drivers/char/</code>.</p>
     <p>После этого необходимо добавить новую строку в файл <code>Makefile</code>, который находится в каталоге <code>drivers/char/</code>. Для этого отредактируйте файл <code>drivers/char/Makefile</code> и добавьте в него следующую запись.</p>
     <p><code>obj-m += fishing/</code></p>
     <p>Эта строка указывает системе компиляции, что необходимо войти в подкаталог <code>fishing/</code> при компиляции модулей. Скорее всего, компиляция драйвера определяется отдельным конфигурационным параметром, например, <code>CONFIG_FISHING_POLE</code> (как создавать новые конфигурационные параметры, рассмотрено ниже в этой главе в разделе "Управление конфигурационными параметрами"). В этом случае необходимо добавить строку следующего вида.</p>
     <p><code>obj-$(CONFIG_FISHING_POLE) += fishing/</code></p>
     <p>И наконец, в каталоге <code>drivers/char/fishing</code> необходимо добавить новый файл Makefile, содержащий следующую строку.</p>
     <p><code>obj-m += fishing.o</code></p>
     <p>При таких настройках система компиляции перейдет в каталог <code>fishing/</code> и скомпилирует модуль <code>fishing.ko</code> из исходного файла <code>fishing.c</code>. Да, расширение объектного файла указано как <code>.o</code>, но в результате будет создан модуль с расширением <code>.ko</code>.</p>
     <p>И снова, скорее всего, факт компиляции модуля будет зависеть от конфигурационного параметра, в таком случае в <code>Makefile</code> необходимо добавить следующую строку.</p>
     <p><code>obj-$(CONFIG_FISHING_POLE) += fishing.o</code></p>
     <p>Однажды драйвер удочки может стать очень сложным. Введение функции автодетектирования наличия лески может привести к тому, что модуль станет очень большим и теперь будет занимать больше одного файла исходного кода. Никаких проблем! Просто нужно внести в <code>Makefile</code> следующую запись.</p>
     <p><code>obj-$(CONFIG_FISHING_POLE) += fishing.o</code></p>
     <p><code>fishing-objs := fishing-main.o fishing-line.o</code></p>
     <p>В последнем случае будут скомпилированы файлы <code>fishing-main.c</code> и <code>fishing-line.c</code> и скомпонованы в файл модуля <code>fishing.ko</code>.</p>
     <p>Наконец, может потребоваться передать компилятору gcc дополнительные конфигурационные параметры. Для этого в файле <code>Makefile</code> необходимо добавить следующую строку.</p>
     <p><code>EXTRA_CFLAGS += -DTITANIUM_POLE</code></p>
     <p>Если вы желаете поместить ваши файлы в каталог <code>drivers/char/</code>, вместо того чтобы создавать новый подкаталог, то необходимо просто прописать указанные строки (те, что должны быть прописаны в файле <code>Makefile</code> подкаталога <code>drivers/char/fishing/</code>) в файле <code>drivers/char/Makefile</code>.</p>
     <p>Для компиляции просто запустите процесс сборки ядра, как обычно. Если компиляция модуля зависит от конфигурационного параметра, как в данном случае она зависит от параметра <code>CONFIG_FISHING_POLE</code>, то необходимо включить этот конфигурационный параметр перед компиляцией.</p>
    </section>
    <section>
     <title>
      <p>Компиляция вне дерева исходных кодов ядра</p>
     </title>
     <p>Если вы предпочитаете разрабатывать и поддерживать ваш модуль отдельно от дерева исходных кодов ядра и жить жизнью аутсайдера, просто создайте файл <code>Makefile</code> следующего вида в том каталоге, где находится модуль.</p>
     <p><code>obj-m := fishing.o</code></p>
     <p>Такая конфигурация позволяет скомпилировать файл <code>fishing.c</code> в файл <code>fishing.ko</code>. Если ваш исходный код занимает несколько файлов, то необходимо добавить две строки.</p>
     <p><code>obj-m := fishing.o</code></p>
     <p><code>fishing-objs := fishing-main.o fishing-line.o</code></p>
     <p>Такая конфигурация позволяет скомпилировать файлы <code>fishing-main.c</code> и <code>fishing-line.c</code> и создать модуль <code>fishing.ko</code>.</p>
     <p>Главное отличие от случая, когда модуль находится внутри дерева исходного кода, состоит в процессе сборки. Так как модуль находится за пределами дерева исходных кодов ядра, необходимо указать утилите <code>make</code> местонахождение исходных файлов ядра и файл <code>Makefile</code> ядра. Это также делается просто с помощью следующей команды!.</p>
     <p><code>make -С /kernel/source/location SUBDTRS=$PWD modules</code></p>
     <p>В этом примере <code>/kernel/source/location</code> — путь к сконфигурированному дереву исходных кодов ядра. Вспомните, что не нужно хранить копию дерева исходных кодов ядра, с которой вы работаете, в каталоге <code>/usr/src/linux</code>, эта копия должна быть где-то в другом месте, скажем где-нибудь в вашем домашнем каталоге.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Инсталляция модулей</p>
    </title>
    <p>Скомпилированные модули должны быть инсталлированы в каталог <code>/lib/modules/<emphasis>version</emphasis>/kernel</code>. Например, для ядра 2.6.10 скомпилированный модуль управления удочкой будет находиться в файле <code>/lib/modules/2.6.10/kernel/drivers/char/fishing.ko</code>, если исходный код находился непосредственно в каталоге drivers/char/.</p>
    <p>Для инсталляции скомпилированных модулей в правильные каталоги используется следующая команда.</p>
    <p><code>make modules_install</code></p>
    <p>Разумеется, эту команду необходимо выполнять от пользователя root.</p>
   </section>
   <section>
    <title>
     <p>Генерация зависимостей между модулями</p>
    </title>
    <p>Утилиты работы с модулями ОС Linux поддерживают зависимости между модулями. Это означает, что если модуль <code>chum</code> зависит от модуля <code>bait</code>, то при загрузке модуля <code>chum</code> модуль <code>bait</code> будет загружен автоматически. Информация о зависимостях между модулями должна быть сгенерирована администратором. В большинстве поставок ОС Linux эта информация генерируется автоматически и обновляется при загрузке системы. Для генерации информации о зависимостях между модулями необходимо от пользователя root выполнить следующую команду.</p>
    <p><code>depmod</code></p>
    <p>Для быстрого обновления и генерации информации только о более новых модулях, чем сам файл информации, необходимо от пользователя root выполнить другую команду.</p>
    <p><code>depmod -A</code></p>
    <p>Информация о зависимостях между модулями хранится в файле <code>/lib/modules/version/modules.dep</code>.</p>
   </section>
   <section>
    <title>
     <p>Загрузка модулей</p>
    </title>
    <p>Наиболее простой способ загрузки модуля — это воспользоваться утилитой <code>insmod</code>. Эта утилита выполняет самые общие действия. Она просто загружает тот модуль, который ей указан в качестве параметра. Утилита <code>insmod</code> не отслеживает зависимости и не выполняет никакой интеллектуальной обработки ошибок. Использовать ее очень просто. От пользователя root необходимо просто выполнить команду</p>
    <p><code>insmod module</code></p>
    <p>где <code>module</code> — это имя модуля, который необходимо загрузить. Для загрузки модуля управления удочкой необходимо выполнить команду.</p>
    <p><code>insmod fishing</code></p>
    <p>Удалить модуль можно аналогичным образом с помощью утилиты <code>rmmod</code>. Для этого от пользователя root нужно просто выполнить команду.</p>
    <p><code>rmmod module</code></p>
    <p>Например, удалить модуль управления удочкой можно следующим образом.</p>
    <p><code>rmmod fishing</code></p>
    <p>Тем не менее, эти утилиты тривиальные и не обладают интеллектуальным поведением. Утилита <code>modprobe</code> позволяет обеспечить удовлетворение зависимостей, оповещение об ошибках, интеллектуальную обработку ошибок, а также выполняет множество других расширенных функций. Её настоятельно рекомендуется использовать.</p>
    <p>Для загрузки модуля в ядро с помощью утилиты <code>modprobe</code> необходимо от пользователя root выполнить команду</p>
    <p><code>modprobe module [ module parameters ]</code></p>
    <p>где параметр <code>module</code> — это имя модуля, который необходимо загрузить. Все следующие аргументы интерпретируются как параметры, которые передаются модулю при загрузке. Параметры модулей обсуждаются ниже в одноименном разделе.</p>
    <p>Утилита <code>modprobe</code> пытается загрузить не только указанный модуль, но и все модули, от которых он зависит. Следовательно, это наиболее предпочтительный механизм загрузки модулей ядра.</p>
    <p>Команда <code>modprobe</code> также может использоваться для удаления модулей из ядра. Для этого с правами пользователя root необходимо выполнить ее следующим образом.</p>
    <p><code>modprobe Pr modules</code></p>
    <p>где параметр <code>modules</code> — имя одного или нескольких модулей, которые необходимо удалить. В отличие от команды <code>rmmod</code>, утилита <code>modprobe</code> также удаляет и все модули, от которых указанный модуль зависит, если последние не используются.</p>
    <p>В восьмом разделе страниц руководства операционной системы Linux приведен список других, менее используемых ключей этой команды.</p>
   </section>
   <section>
    <title>
     <p>Управление конфигурационными параметрами</p>
    </title>
    <p>В предыдущих разделах рассматривалась компиляция модуля управления удочкой при условии, что установлен конфигурационный параметр <code>CONFIG_FISHING_POLE</code>. Конфигурационные параметры рассматривались в предыдущих главах, а теперь давайте рассмотрим добавление нового параметра в продолжение примера модуля управления удочкой.</p>
    <p>Благодаря новой системе компиляции ядра "kbuild", которая появилась в серии ядер 2.6, добавление нового конфигурационного параметра является очень простым делом. Все, что необходимо сделать, — это добавить новую запись в файл <code>Kconfig</code>, который отвечает за конфигурацию дерева исходных кодов ядра. Для драйверов этот файл обычно находится в том же каталоге, в котором находится и исходный код. Если код драйвера удочки находится в каталоге <code>drivers/char/</code>, то необходимо использовать файл <code>drivers/char/Kconfig</code>.</p>
    <p>Если был создан новый каталог и есть желание, чтобы файл конфигурации находился в этом новом каталоге, то необходимо на него сослаться из существующего файла <code>Kconfig</code>. Это можно сделать путем добавления строки</p>
    <p><code>source drivers/char/fishing/Kconfig</code></p>
    <p>в существующий файл <code>Kconfig</code>, скажем в файл <code>drivers/char/Kconfig</code>.</p>
    <p>Конфигурационные записи в файле <code>Kconfig</code> добавляются очень просто. Для модуля управления удочкой эта запись может выглядеть следующим образом.</p>
    <p><code>config FISHING_POLE</code></p>
    <p><code> tristate "Fish Master XL support"</code></p>
    <p><code> default n</code></p>
    <p><code> help</code></p>
    <p><code>  If you say Y here, support for the Fish Master XL 2000 Titanium</code></p>
    <p><code>  with computer interface will be compiled into the kernel</code></p>
    <p><code>  and accessible via</code></p>
    <p><code>  device node. You can also say M here and the driver</code></p>
    <p><code>  will be built as a</code></p>
    <p><code>  module named fishing.ko.</code></p>
    <empty-line/>
    <p><code>  If unsure, say N.</code></p>
    <p>Первая строка определяет, какой конфигурационный параметр создается. Обратите внимание, что префикс <code>CONFIG_</code> указывать не нужно, он добавляется автоматически.</p>
    <p>Вторая строка указывает на то, что параметр может иметь три состояния (<emphasis>tristate</emphasis>), которые соответствуют следующим значениям: статическая компиляция в ядро (<code>Y</code>), компиляция в качестве модуля (<code>M</code>) или не компилировать драйвер вообще (<code>N</code>). Для того чтобы запретить компиляцию кода, который соответствует конфигурационному параметру, в качестве модуля (допустим, что этот параметр определяет не драйвер. а просто некоторую дополнительную функцию) необходимо указать тип параметра <code>bool</code> вместо <code>tristate</code>. Текст в кавычках, который следует после этой директивы, определяет название конфигурационного параметра и будет отображаться различными утилитами конфигурации.</p>
    <p>Третья строка позволяет указать значение этого параметра по умолчанию, который соответствует в данном случае запрещению компиляции.</p>
    <p>Директива <code>help</code> указывает на то, что остальная часть текста будет интерпретироваться как описание данного модуля. Различные конфигурационные утилиты могут при необходимости отображать этот текст. Так как этот текст предназначен для пользователей и разработчиков, которые будут компилировать ядро, то он должен быть коротким и ясным. Обычные пользователя, скорее всего, не будут компилировать ядро, а сели будут, то тогда они должны понимать, что в этом описании сказано.</p>
    <p>Существуют также и другие директивы файла конфигурации. Директива <code>depends</code> указывает на конфигурационные параметры, которые должны быть установлены перед тем, как может быть установлен текущий параметр. Если зависимости не будут удовлетворены, то текущий параметр будет запрещен. Например, можно указать следующую директиву.</p>
    <p><code>depends on FISH_TANK</code></p>
    <p>При этом текущий модуль не будет разрешен, пока не будет разрешен модуль, соответствующий конфигурационному параметру <code>CONFIG_FISH_TANK</code>.</p>
    <p>Директива <code>select</code> аналогична директиве <code>depends</code>, за исключением того, что она принудительно включает указанный конфигурационный параметр, если включается текущая конфигурационная опция. Ее не нужно использовать так же часто, как директиву <code>depends</code>, потому что она включает другие конфигурационные опции. Использовать ее так же просто.</p>
    <p><code>select BAIT</code></p>
    <p>В этом случае конфигурационный параметр <code>CONFIG_BAIT</code> автоматически активизируется при включении конфигурационного параметра <code>CONFIG_FISHING_POLE</code>.</p>
    <p>Как для директивы <code>select</code>, так и для директивы <code>depends</code> можно указывать несколько параметров с помощью оператора <code>&amp;&amp;</code>. В директиве <code>depends</code> с помощью восклицательного знака перед параметром можно указать требование, что некоторый конфигурационный параметр <emphasis>не</emphasis> должен быть установлен. Например, следующая запись указывает, что для компиляции текущего драйвера необходимо, чтобы был установлен конфигурационный параметр <code>CONFIG_DUMB_DRIVERS</code> и не был установлен параметр <code>CONFIG_NO_FISHING_ALLOWED</code>.</p>
    <p><code>depends on DUMB_DRIVERS &amp;&amp; !NO_FISHING_ALLOWED</code></p>
    <p>После директив <code>tristate</code> и <code>bool</code> можно указать директиву <code>if</code>, что позволяет сделать соответствующий параметр зависимым от другого конфигурационного параметра. Если условие не выполняется, то конфигурационный параметр не только запрещается, но и не будет отображаться утилитами конфигурации. Например, следующая строка указывает, что функция "<code>Deep Sea Mode</code>" будет доступна, только если разрешен конфигурационный параметр <code>CONFIG_OCEAN</code>.</p>
    <p><code>bool "Deep Sea Mode" if OCEAN</code></p>
    <p>Директива <code>if</code> также может быть указана после директивы <code>default</code>, что означает, что значение по умолчанию будет установлено, только если выполняется условие, указанное в директиве <code>if</code>.</p>
    <p>Система конфигурации экспортирует несколько метапараметров, чтобы упростить процесс конфигурации. Параметр <code>CONFIG_EMBEDDED</code> устанавливается только тогда, когда пользователь указывает, что он хочет видеть вес параметры, отвечающие за запрещение некоторых ключевых возможностей ядра (обычно с целью сохранения памяти на встраиваемых системах). Параметр <code>CONFIG_BROKEN_ON_SMP</code> используется, чтобы указать, что драйвер не рассчитан на системы с симметричной многопроцессорностью. Обычно этот параметр не устанавливается, при этом от пользователя требуется, чтобы он сам убедился в возможности компиляции драйвера для SMP. Новые драйверы этот флаг использовать не должны.</p>
    <p>Параметр <code>CONFIG_EXPERIMENTAL</code> используется для указания экспериментальных или не очень хорошо оттестированных возможностей. По умолчанию этот параметр отключен, что требует от пользователя лично убедиться в степени риска при разрешении компиляции того или иного драйвера.</p>
   </section>
   <section>
    <title>
     <p>Параметры модулей</p>
    </title>
    <p>Ядро Linux предоставляет возможность драйверам определять параметры, которые пользователь будет указывать при загрузке ядра или модуля. Эти параметры будут доступны коду модуля в качестве глобальных переменных. Указанные параметры модулей также будут отображаться в файловой системе sysfs (см. главу 17, "Объекты kobject и файловая система sysfs"). Определять параметры модуля и управлять ими просто.</p>
    <p>Параметр модуля определяется с помощью макроса <code>module_param()</code> следующим образом.</p>
    <p><code>module_param(name, type, perm);</code></p>
    <p>где аргумент <code>name</code> — это имя неременной, которая появляется в модуле, и имя параметра, который может указать пользователь. Аргумент <code>type</code> — это тип данных параметра. Значения типа могут быть следующими: <code>byte</code>, <code>short</code>, <code>ushort</code>, <code>int</code>, <code>uint</code>, <code>long</code>, <code>ulong</code>, <code>charp</code>, <code>bool</code> или <code>invbool</code>. Эти значения соответствуют следующим типам данных: байт; короткое целое число; короткое целое число без знака; целое число; целое число без знака; длинное целое; длинное целое число без знака; указатель на строку символов; булев тип; булев тип, значение которого инвертируется по сравнению с тем, которое указывает пользователь. Данные типа <code>byte</code> хранятся в переменной типа <code>char</code>, а данные булевых типов — в переменных типа <code>int</code>. Остальные- типы соответствуют аналогичным типам языка С. Наконец, аргумент <code>perm</code> указывает права доступа к соответствующему файлу в файловой системе sysfs. Права доступа можно указать как в обычном восьмеричном формате, например 0644 (владелец имеет права на чтение и запись, группа имеет права на чтение и запись, остальные пользователи имеют право только на чтение), так и в виде определений препроцессора, объединенных с помощью оператора "<code>|</code>", например <code>S_IRUGO | S_IWUSR</code> (все могут считывать данные, а владелец также и записывать). Нулевое значение этого параметра приводит к тому, что соответствующий файл в файловой системе sysfs не появляется.</p>
    <p>Этот макрос не определяет переменную. <emphasis>Перед</emphasis> тем как использовать макрос, соответствующую переменную нужно определить. В связи с этим типичный пример использования может быть следующим.</p>
    <p><code>/* параметр модуля, который управляет переменной bait */</code></p>
    <p><code>static int allow live bait = 1;            /* по умолчанию включено */</code></p>
    <p><code>module_param(allow_live_bait, bool, 0644); /* булев тип */</code></p>
    <p>Это определение должно быть в глобальной области видимости, т.е. неременная <code>allow_live_bait</code> должна быть глобальной.</p>
    <p>Существует возможность дать внешнему параметру модуля имя, отличное от имени переменной. Это можно сделать с помощью макроса <code>module_param_named()</code>.</p>
    <p><code>module_param_named(name, variable, type, perm);</code></p>
    <p>где <code>name</code> — это имя внешнего параметра модуля, a <code>variable</code> — имя внутренней глобальной переменной модуля, как показано ниже.</p>
    <p><code>static unsigned int max_test = DEFAULT_МАХ_LINE_TEST;</code></p>
    <p><code>module_param_named(maximum_line_test, max_test, int, 0);</code></p>
    <p>Для того чтобы определить параметр модуля, значением которого является строка символов, необходимо использовать тип <code>charp</code>. Ядро копирует переданную пользователем строку символов в память и присваивает переменной указатель на эту строку, как в следующем примере.</p>
    <p><code>static char *name;</code></p>
    <p><code>module_param(name, charp, 0);</code></p>
    <p>При необходимости ядро может скопировать строку в заранее определенный массив символов, который указывает разработчик. Это делается с помощью макроса <code>module_param_string()</code>.</p>
    <p><code>module_param_string(name, string, len, perm);</code></p>
    <p>где <code>name</code> — это имя внешнего параметра, <code>string</code> — имя внутренней переменной, которая содержит указатель на область памяти массива, <code>len</code> — размер буфера <code>string</code> (или некоторое меньшее число, чем размер буфера, что, однако, обычно не имеет смысла), <code>perm</code> — права доступа к файлу на файловой системе sysfs (нулевое значение запрещает доступ к параметру через sysfs). Пример показан ниже.</p>
    <p><code>static char species[BUF_LEN];</code></p>
    <p><code>module_param_string(specifies, species, BUF_LEN, 0);</code></p>
    <p>В качестве параметров модуля также можно передавать список значений, которые разделены запятой и в коде модуля будут записаны в массив данных. Эти параметры модуля можно обработать с помощью макроса module_param_array() следующим образом.</p>
    <p><code>module_param_array(name, type, nump, perm);</code></p>
    <p>В данном случае аргумент <code>name</code> — это имя внешнего параметра и внутренней переменной, <code>type</code> — это тип данных одного значения, a perm — это права доступа к файлу на файловой системе sysfs. Новый аргумент <code>nump</code> — это указатель на целочисленное значение, где ядро сохраняет количество элементов, записанных в массив. Обратите внимание, что массив, который передается в качестве параметра name, должен быть выделен статически. Ядро определяет размер массива на этапе компиляции и гарантирует, что он не будет переполнен. Как использовать данный макрос, показано в следующем примере.</p>
    <p><code>static int fish[MAX_FISH];</code></p>
    <p><code>static int nr_fish;</code></p>
    <p><code>module_param_array(fish, int, &amp;nr_fish, 0444);</code></p>
    <p>Внутренний массив может иметь имя, отличное от имени внешнего параметра, в этом случае следует использовать макрос <code>module_param_array_named()</code>.</p>
    <p><code>module_param_array_named(name, array, type, nump, perm);</code></p>
    <p>Параметры идентичны аналогичным параметрам других макросов.</p>
    <p>Наконец, параметры модуля можно документировать, используя макрос <code>MODULE_PARM_DESC()</code>.</p>
    <p><code>static unsigned short size = 1;</code></p>
    <p><code>module_param(size, ushort, 0644);</code></p>
    <p><code>MODULE_PARM_DESC(size, "The size in inches of the fishing pole " \</code></p>
    <p><code> "connected to this computer.");</code></p>
    <p>Вес описанные в этом разделе макросы требуют включения заголовочного файла <code>&lt;linux/moduleparam.h&gt;</code>.</p>
   </section>
   <section>
    <title>
     <p>Экспортируемые символы</p>
    </title>
    <p>При загрузке модули динамически компонуются с ядром. Так же как и в случае динамически загружаемых бинарных файлов пространства пользователя, в коде модулей могут вызываться только те функции ядра (основного образа или других модулей), которые явно <emphasis>экспортируются</emphasis> для использования. В ядре экспортирование осуществляется с помощью специальных директив <code>EXPORT_SYMBOL()</code> и <code>EXPORT_SYMBOL_GPL()</code>.</p>
    <p>Функции, которые экспортируются, доступны для использования модулями. Функции, которые не экспортируются, не могут быть вызваны из модулей. Правила компоновки и вызова функций для модулей значительно более строгие, чем для основного образа ядра. Код ядра может использовать любые интерфейсы ядра (кроме тех, которые определены с ключевым словом <code>static</code>), потому что код ядра компонуется в один выполняемый образ. Экспортируемые символы, конечно, тоже не должны определяться как <code>static</code>.</p>
    <p>Набор символов ядра, которые экспортируются, называется экспортируемым интерфейсом ядра или даже (здесь не нужно удивляться) <emphasis>API ядра</emphasis>.</p>
    <p>Экспортировать символы просто. После того как функция определена, необходимо вызвать директиву <code>EXPORT_SYMBOL()</code>.</p>
    <p><code>/*</code></p>
    <p><code>* get_pirate_beard_color — возвратить значение цвета бороды текущего</code></p>
    <p><code>* пирата pirate — это глобальная переменная, доступная из данной</code></p>
    <p><code>* функции цвета определены в файле &lt;linux/beard_colors.h&gt;</code></p>
    <p><code>*/</code></p>
    <p><code>int get_pirate_beard_color(void) {</code></p>
    <p><code> return pirate-&gt;beard-&gt;color;</code></p>
    <p><code>}</code></p>
    <p><code>EXPORT_SYMBOL(get_pirate_beard_color);</code></p>
    <p>Допустим, что функция <code>get_pirate_beard_color()</code> объявлена в заголовочном файле и ее может использовать любой модуль.</p>
    <p>Некоторые разработчики хотят, чтобы их интерфейсы были доступны только для модулей с лицензией GPL. Такая возможность обеспечивается компоновщиком ядра с помощью макроса <code>MODULE_LICENSE()</code>. Если есть желание, чтобы рассматриваемая функция была доступна только для модулей, которые помеченные как соответствующие лицензии GPL, то экспортировать функцию можно следующим образом.</p>
    <p><code>EXPORT_SYMBOL_GPL(get_pirate_beard_color);</code></p>
    <p>Если код ядра конфигурируется для компиляции в виде модуля, то необходимо гарантировать, что все используемые интерфейсы экспортируются. В противном случае будут возникать ошибки компоновщика и загружаемый модуль не будет работать.</p>
   </section>
   <section>
    <title>
     <p>Вокруг модулей</p>
    </title>
    <p>В этой главе были рассмотрены особенности написания, сборки, загрузки и выгрузки модулей ядра. Мы обсудили, что такое модули и каким образом ядро операционной системы Linux, несмотря на то что оно является монолитным, может загружать код динамически. Были также рассмотрены параметры модулей и экспортируемые символы. На примере воображаемого модуля ядра (драйвера устройства) управления удочкой был показан процесс написания модуля и процесс добавления к нему различных возможностей, таких как внешние параметры.</p>
    <p>В следующей главе будут рассмотрены объекты <code>kobject</code> и файловая система sysfs, которые являются основным интерфейсом к драйверам устройств и, следовательно, к модулям ядра.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 17</p>
    <p>Объекты kobject и файловая система sysfs</p>
   </title>
   <section>
    <p>Унифицированная <emphasis>модель представления устройств</emphasis> — это существенно новая особенность, которая появилась в ядрах серии 2.6. Модель устройств — это единый механизм для представления устройств и описания их топологии в системе. Использование единого представления устройств позволяет получить следующие преимущества.</p>
    <p>• Уменьшается дублирование кода.</p>
    <p>• Используется механизм для выполнения общих, часто встречающихся функций, таких как счетчики использования.</p>
    <p>• Появляется возможность систематизации всех устройств в системе, возможность просмотра состояний устройств и определения, к какой шине то или другое устройство подключено.</p>
    <p>• Появляется возможность генерации полной и корректной информации о древовидной структуре всех устройств в системе, включая все шины и соединения.</p>
    <p>• Обеспечивается возможность связывания устройств с их драйверами и наоборот.</p>
    <p>• Появляется возможность разделения устройств на категории в соответствии с различными классификациями, таких как устройства ввода, без знания физической топологии устройств.</p>
    <p>• Обеспечивается возможность просмотра иерархии устройств от листьев к корню и выключения питания устройств в правильном порядке.</p>
    <p>Последний пункт был самой первой мотивацией необходимости создания общей модели представления устройств. Для того чтобы реализовать интеллектуальное управление электропитанием в ядре, необходимо построить дерево, которое представляет топологию устройств в системе. Для выключения питания устройств, которые организованы в виде древовидной топологии, ориентированной сверху вниз, ядро должно выключить питание нижних узлов (листьев) перед выключением питания верхних узлов. Например, ядро должно выключить питание USB-мыши перед тем, как выключать питание контроллера шины USB, а питание контроллера шины USB должно быть выключено перед выключением питания шины PCI. Чтобы делать это эффективно и правильно для всей системы, ядру необходимо отслеживать топологию дерева всех устройств в системе.</p>
   </section>
   <section>
    <title>
     <p>Объекты <code>kobject</code></p>
    </title>
    <p>Сердцем модели представления устройств являются объекты <emphasis>kobject</emphasis>, которые представляются с помощью структуры <code>struct kobject</code>, определенной в файле <code>&lt;linux/kobject.h&gt;</code>. Тип <code>kobject</code> аналогичен классу <code>Object</code> таких объектно-ориентированных языков программирования, как С# и Java. Этот тип определяет общую функциональность, такую как счетчик ссылок, имя, указатель на родительский объект, что позволяет создавать объектную иерархию.</p>
    <p>Структура, с помощью которой реализованы объекты <code>kobject</code>, имеет следующий вид.</p>
    <p><code>struct kobject {</code></p>
    <p><code> char             *k_name;</code></p>
    <p><code> char             name[KOBJ_NAME_LEN];</code></p>
    <p><code> struct kref      kref;</code></p>
    <p><code> struct list_head entry;</code></p>
    <p><code> struct kobject   *parent;</code></p>
    <p><code> struct kset      *kset</code></p>
    <p><code> struct kobj_type *ktype;</code></p>
    <p><code> struct dentry    *dentry;</code></p>
    <p><code>};</code></p>
    <p>Поле <code>k_name</code> содержит указатель на имя объекта. Если длина имени меньше <code>KOBJ_NAME_LEN</code>, что сейчас составляет 20 байт, то имя хранится в массиве <code>name</code>, a поле <code>kname</code> указывает на первый элемент этого массива. Если длина имени больше <code>KOBJ_NAME_LEN</code> байт, то динамически выделяется буфер, размер которого достаточен для хранения строки символов имени, имя записывается в этот буфер, а поле <code>k_name</code> указывает на него.</p>
    <p>Указатель <code>parent</code> указывает на родительский объект данного объекта <code>kobject</code>. Таким образом, с помощью структур <code>kobject</code> может быть создана иерархия объектов в ядре, которая позволяет устанавливать соотношения родства между различными объектами. Как будет видно дальше, с помощью файловой системы sysfs осуществляется представление в пространстве пользователя той иерархии объектов <code>kobject</code>, которая существует в ядре.</p>
    <p>Указатель <code>dentry</code> содержит адрес структуры <code>struct dentry</code>, которая представляет этот объект в файловой системе sysfs.</p>
    <p>Поля <code>kref</code>, <code>ktype</code> и <code>kset</code> указывают на экземпляры структур, которые используются для поддержки объектов <code>kobject</code>. Поле <code>entry</code> используется совместно с полем <code>kset</code>. Сами эти структуры и их использование будут обсуждаться ниже.</p>
    <p>Обычно структуры <code>kobject</code> встраиваются в другие структуры данных и сами по себе не используются. Например, такая важная структура, как <code>struct cdev</code>, имеет поле <code>kobj</code>.</p>
    <p><code>/* структура cdev - объект для представления символьных устройств */</code></p>
    <p><code>struct cdev {</code></p>
    <p><code> struct kobject         kobj;</code></p>
    <p><code> struct module          *owner;</code></p>
    <p><code> struct file_operations *ops;</code></p>
    <p><code> struct list_head       list;</code></p>
    <p><code> dev_t                  dev;</code></p>
    <p><code> unsigned int           count;</code></p>
    <p><code>};</code></p>
    <p>Когда структуры <code>kobject</code> встраиваются в другие структуры данных, то последние получают те стандартизированные возможности, которые обеспечиваются структурами <code>kobject</code>. Еще более важно, что структуры, которые содержат в себе объекты <code>kobject</code>, становятся частью объектной иерархии. Например, структура <code>cdev</code> представляется в объектной иерархии с помощью указателя на родительский объект <code>cdev-&gt;kobj-&gt;parent</code> и списка <code>cdev-&gt;kobj-&gt;entry</code>.</p>
   </section>
   <section>
    <title>
     <p>Типы <code>ktype</code></p>
    </title>
    <p>Объекты <code>kobject</code> могут быть связаны с определенным типом, который называется <code>ktype</code>. Типы <code>ktype</code> представляются с помощью структуры s<code>truct kobj_type</code>, определенной в файле <code>&lt;linux/kobject.h&gt;</code> следующим образом.</p>
    <p><code>struct kobj_type {</code></p>
    <p><code> void (*release)(struct kobject*);</code></p>
    <p><code> struct sysfs_ops *sysfs_ops;</code></p>
    <p><code> struct attribute **default_attrs;</code></p>
    <p><code>};</code></p>
    <p>Тип <code>ktype</code> имеет простое назначение — представлять общее поведение для некоторого семейства объектов <code>kobject</code>. Вместо того чтобы для каждого отдельного объекта задавать особенности поведения, эти особенности связываются с их полем <code>ktype</code>, и объекты одного "типа" характеризуются одинаковым поведением.</p>
    <p>Поле <code>release</code> содержит указатель на деструктор, который вызывается, когда количество ссылок на объект становится равным нулю. Эта функция отвечает за освобождение памяти, связанной с объектом, и за другие операции очистки.</p>
    <p>Поле <code>sysfs_ops</code> указывает на структуру <code>sysfs_ops</code>. Эта структура определяет поведение файлов на файловой системе <code>sysfs</code> при выполнении операций записи и чтения. Более детально она рассматривается в разделе "Добавление файлов на файловой системе sysfs".</p>
    <p>Наконец, поле <code>default_attrs</code> указывает на массив структур <code>attribute</code>. Эти структуры определяют <emphasis>атрибуты</emphasis>, которые связаны с объектом <code>kobject</code> и используются но умолчанию. Атрибуты соответствуют свойствам данного объекта. Если некоторый объект <code>kobject</code> экспортируется через файловую систему sysfs, то атрибуты экспортируются как отдельные файлы. Последний элемент этого массива должен содержать значению <code>NULL</code>.</p>
   </section>
   <section>
    <title>
     <p>Множества объектов <code>kset</code></p>
    </title>
    <p>Множества <code>kset</code> представляют собой коллекции объектов <code>kobject</code>. Множество <code>kset</code> работает как базовый контейнерный класс для объектов, например, "все блочные устройства". Множества <code>kset</code> очень похожи на типы <code>ktype</code>, и возникает вопрос: "Для чего нужны два разных обобщения?" Множество <code>kset</code> объединяет несколько объектов <code>kobject</code>, а типы <code>ktype</code> определяют общие свойства, которые связаны с объектами <code>kobject</code> одного типа. Существует возможность объединить объекты одного типа <code>ktype</code> в различные множества <code>kset</code>.</p>
    <p>Поле <code>kset</code> объекта <code>kobject</code> указывает на связанное с данным объектом множество <code>kset</code>. Множество объектов <code>kset</code> представляется с помощью структуры <code>kset</code>, которая определена в файле <code>&lt;linux/kobject.h&gt;</code> следующим образом.</p>
    <p><code>struct kset {</code></p>
    <p><code> struct subsystem        *subsys;</code></p>
    <p><code> struct kobj_type        *ktype;</code></p>
    <p><code> struct list_head        list;</code></p>
    <p><code> struct kobject          kobj;</code></p>
    <p><code> struct kset_hotplug_ops *hotplug_ops;</code></p>
    <p><code>};</code></p>
    <p>Указатель <code>ktype</code> указывает на структуру <code>ktype</code>, которая определяет тип всех объектов данного множества, поле <code>list</code> — список всех объектов <code>kobject</code> данного множества, поле <code>kobj</code> — объект <code>kobject</code>, который представляет базовый класс для всех объектов данного множества, а поле <code>hotplug_ops</code> указывает на структуру, которая определяет поведение объектов <code>kobject</code> при горячем подключении устройств, связанных с данным множеством.</p>
    <p>Наконец, поле <code>subsys</code> указывает на структуру <code>struct subsystem</code>, которая связана с данным множеством <code>kset</code>.</p>
   </section>
   <section>
    <title>
     <p>Подсистемы</p>
    </title>
    <p>Подсистемы используются для представления высокоуровневых концепций ядра и являются коллекцией одного или нескольких множеств <code>kset</code>. Множества <code>kset</code> содержат объекты <code>kobject</code>, подсистемы — множества <code>kset</code>, но связь между множествами в подсистеме значительно более слабая, чем связь между объектами <code>kobject</code> в множестве. Множества <code>kset</code> одной подсистемы могут иметь только наиболее общие объединяющие факторы.</p>
    <p>Несмотря на их важную роль, подсистемы представляются с помощью очень простой структуры данных — <code>struct subsystem</code>.</p>
    <p><code>struct subsystem {</code></p>
    <p><code> struct kset kset;</code></p>
    <p><code> struct rw_semaphore rwsem;</code></p>
    <p><code>};</code></p>
    <p>Структура <code>subsystem</code> содержит только одно множество <code>kset</code>, тем не менее несколько множеств <code>kset</code> могут указывать на общую структуру <code>subsystem</code> с помощью поля <code>subsys</code>. Такие однонаправленные взаимоотношения означают, что нет возможности определить все множества подсистемы, только имея ее структуру <code>subsystem</code>.</p>
    <p>Поле <code>kset</code>, которое содержится в структуре <code>subsystem</code>, — это множество <code>kset</code> подсистемы, которое используется по умолчанию, чтобы зафиксировать положение этой подсистемы в иерархии объектов.</p>
    <p>Поле <code>rwsem</code> структуры <code>subsystem</code> — это семафор чтения-записи (см. главу 9, "Средства синхронизации в ядре"), который используется для защиты подсистемы и ее множеств <code>kset</code> от конкурентного доступа. Все множества <code>kset</code> должны принадлежать какой-нибудь подсистеме, поскольку они используют семафор подсистемы для защиты своих данных от конкурентного доступа.</p>
   </section>
   <section>
    <title>
     <p>Путаница со структурами</p>
    </title>
    <p>Те несколько структур, которые только что были описаны, приводят к путанице не потому, что их много (только четыре) или они сложные (все они достаточно просты), а потому что они сильно друг с другом переплетаются. При использовании объектов <code>kobject</code> достаточно сложно рассказать об одной структуре, не упоминая другие. Тем не менее, на основании рассмотренных особенностей этих структур можно построить прочное понимание их взаимоотношений.</p>
    <p>Самым важным является объект <code>kobject</code>, который представляется с помощью структуры <code>struct kobject</code>. Структура <code>kobject</code> используется для представления наиболее общих объектных свойств структур данных ядра, таких как счетчик ссылок, взаимоотношения родитель-порожденный и имя объекта. С помощью структуры <code>kobject</code> эти свойства можно обеспечить одинаковым для всех стандартным способом. Сами по себе структуры <code>kobject</code> не очень полезны, они обычно встраиваются в другие структуры данных.</p>
    <p>С каждым объектом <code>kobject</code> связан один определенный тип данных — <code>ktype</code>, который представляется с помощью структуры <code>struct kobj_type</code>. На экземпляр такой структуры указывает поле <code>ktype</code> каждого объекта <code>kobject</code>. С помощью типов <code>ktype</code> определяются некоторые общие свойства объектов: поведение при удалении объекта, поведение, связанное с файловой системой sysfs, а также атрибуты объекта.</p>
    <p>Объекты <code>kobject</code> группируются в множества, которые называются <code>kset</code>. Множества <code>kset</code> представляются с помощью структур данных <code>struct kset</code>. Эти множества предназначены для двух целей. Во-первых, они позволяют использовать встроенный в них объект <code>kobject</code> в качестве базового класса для группы других объектов <code>kobject</code>. Во-вторых, они позволяют объединять вместе несколько связанных между собой объектов <code>kobject</code>. На файловой системе sysfs объекты <code>kobject</code> представляются отдельными каталогами файловой системы. Связанные между собой каталоги, например все подкаталоги одного каталога, могут быть включены в одно множество <code>kset</code>.</p>
    <p>Подсистемы соответствуют большим участкам ядра и являются набором множеств kset. Подсистемы представляются с помощью структур <code>struct subsystem</code>. Все каталоги, которые находятся в корне файловой системы sysfs, соответствуют подсистемам ядра.</p>
    <p>На рис. 17.1 показаны взаимоотношения между этими структурами данных.</p>
    <image l:href="#img_23.jpeg"/>
    <p><strong>Рис. 17.1</strong>. Взаимоотношения между объектами <code>kobject</code>, множествами <code>kset</code> и подсистемами</p>
   </section>
   <section>
    <title>
     <p>Управление и манипуляции с объектами <code>kobject</code></p>
    </title>
    <p>Теперь, когда у нас уже есть представление о внутреннем устройстве объектов <code>kobject</code> и связанных с ними структурах данных, самое время рассмотреть экспортируемые интерфейсы, которые дают возможность управлять объектами <code>kobject</code> и выполнять с ними другие манипуляции. В основном, разработчикам драйверов непосредственно не приходится иметь дело с объектами <code>kobject</code>. Структуры <code>kobject</code> встраиваются в некоторые специальные структуры данных (как это было в примере структуры устройства посимвольного ввода-вывода) и управляются "за кадром" с помощью соответствующей подсистемы драйверов. Тем не менее, объекты <code>kobject</code> не всегда могут оставаться невидимыми, иногда с ними приходится иметь дело, как при разработке кода драйверов, так и при разработке кода управления подсистемами ядра.</p>
    <p>Первый шаг при работе с объектами <code>kobject</code> — это их декларация и инициализация. Инициализируются объекты <code>kobject</code> с помощью функции <code>kobject_init()</code>, которая определена в файле <code>&lt;linux/kobject.h&gt;</code> следующим образом.</p>
    <p><code>void kobject_init(struct kobject *kobj);</code></p>
    <p>Единственным параметром этой функции является объект <code>kobject</code>, который необходимо проинициализировать. Перед вызовом этой функции область памяти, в которой хранится объект, должна быть заполнена нулевыми значениями. Обычно это делается при инициализации большой структуры данных, в которую встраивается объект <code>kobject</code>. В других случаях просто необходимо вызвать функцию <code>memset()</code>.</p>
    <p><code>memset(kobj, 0, sizeof(*kobj));</code></p>
    <p>После заполнения нулями безопасным будет инициализация полей <code>parent</code> и <code>kset</code>, как показано в следующем примере.</p>
    <p><code>kobj = kmalloc(sizeof(*kobj), GFP_KERNEL);</code></p>
    <p><code>if (!kobj)</code></p>
    <p><code> return -ENOMEM;</code></p>
    <p><code>memset(kobj, 0, sizeof(*kobj));</code></p>
    <p><code>kobj-&gt;kset = kset;</code></p>
    <p><code>kobj-&gt;parent = parent_kobj;</code></p>
    <p><code>kobject_init(kobj);</code></p>
    <p>После инициализации необходимо установить имя объекта с помощью функции <code>kobject_set_name()</code>, которая имеет следующий прототип.</p>
    <p><code>int kobject_set_name(struct kobject* kobj,</code></p>
    <p><code> const char* fmt, ...);</code></p>
    <p>Эта функция принимает переменное количество параметров, по аналогии с функциями <code>printf()</code> и <code>printk()</code>. Как уже было сказано, на имя объекта указывает поле <code>k_name</code> структуры <code>kobject</code>. Если это имя достаточно короткое, то оно хранится в статически выделенном массиве <code>name</code>, поэтому есть смысл без необходимости не указывать длинные имена.</p>
    <p>После того как для объекта выделена память и объекту присвоено имя, нужно установить значение его поля <code>kset</code>, а также опционально поле <code>ktype</code>. Последнее необходимо делать только в том случае, если множество <code>kset</code> не предоставляет типа <code>ktype</code> для данного объекта, в противном случае значение поля <code>ktype</code>, которое указано в структуре <code>kset</code>, имеет преимущество. Если интересно, почему объекты <code>kobject</code> имеют свое поле <code>ktype</code>, то добро пожаловать в клуб!</p>
   </section>
   <section>
    <title>
     <p>Счетчики ссылок</p>
    </title>
    <section>
     <p>Одно из главных свойств, которое реализуется с помощью объектов <code>kobject</code>, — это унифицированная система поддержки счетчиков ссылок. После инициализации количество ссылок на объект устанавливается равным единице. Пока значение счетчика ссылок на объект не равно нулю, объект существует в памяти, и говорят, что он <emphasis>захвачен</emphasis> (<emphasis>pinned</emphasis>, буквально, пришпилен). Любой код, который работает с объектом, вначале должен увеличить значение счетчика ссылок. После того как код закончил работу с объектом, он должен уменьшить значение счетчика ссылок. Увеличение значения счетчика называют <emphasis>захватом</emphasis> (<emphasis>getting</emphasis>), уменьшение — <emphasis>освобождением</emphasis> (<emphasis>putting</emphasis>) ссылки на объект. Когда значение счетчика становится равным нулю, объект может быть уничтожен, а занимаемая им память освобождена.</p>
     <p>Увеличение значения счетчика ссылок выполняется с помощью функции <code>kobject_get()</code>.</p>
     <p><code>struct kobject* kobject_get(struct kobject *kobj);</code></p>
     <p>Эта функция возвращает указатель на объект <code>kobject</code> в случае успеха и значение <code>NULL</code> в случае ошибки.</p>
     <p>Уменьшение значения счетчика ссылок выполняется с помощью функции <code>kobject_put()</code>.</p>
     <p><code>void kobject put(struct kobject *kobj);</code></p>
     <p>Если значение счетчика ссылок объекта, который передается в качестве параметра, становится равным нулю, то вызывается функция, на которую указывает указатель <code>release</code> поля <code>ktype</code> этого объекта.</p>
    </section>
    <section>
     <title>
      <p>Структуры <code>kref</code></p>
     </title>
     <p>Внутреннее представление счетчика ссылок выполнено с помощью структуры <code>kref</code>, которая определена в файле <code>&lt;linux/kref.h&gt;</code> следующим образом.</p>
     <p><code>struct kref {</code></p>
     <p><code> atomic_t refcount;</code></p>
     <p><code>};</code></p>
     <p>Единственное поле этой структуры — атомарная переменная, в которой хранится значение счетчика ссылок. Структура используется просто для того, чтобы выполнять проверку типов. Чтобы воспользоваться структурой <code>kref</code>, необходимо ее инициализировать с помощью функции <code>kref_init()</code>.</p>
     <p><code>void kref_init(struct kref *kref) {</code></p>
     <p><code> atomic_set(&amp;kref-&gt;refcount, 1);</code></p>
     <p><code>}</code></p>
     <p>Как видно из определения, эта функция просто инициализирует атомарную переменную тина <code>atomic_t</code> в значение, равное единице.</p>
     <p>Следовательно, структура <code>kref</code> является захваченной сразу же после инициализации, так же ведут себя и объекты <code>kobject</code>.</p>
     <p>Для того чтобы захватить ссылку на структуру <code>kref</code>, необходимо использовать функцию <code>kref_get()</code>.</p>
     <p><code>void kref_get(struct kref *kref) {</code></p>
     <p><code> WARN_ON(!atomic_read(&amp;kref-&gt;refcount));</code></p>
     <p><code> atomic_inc(&amp;kref-&gt;refcount);</code></p>
     <p><code>}</code></p>
     <p>Эта функция увеличивает значение счетчика ссылок на единицу. Она не возвращает никаких значений. Чтобы освободить ссылку на структуру <code>kref</code>, необходимо использовать функцию <code>kref_put()</code>.</p>
     <p><code>void kref_put(struct kref *kref, void (*release)(struct kref *kref)) {</code></p>
     <p><code> WARN_ON(release == NULL);</code></p>
     <p><code> WARN_ON(release == (void(*)(struct kref*))kfree);</code></p>
     <empty-line/>
     <p><code> if (atomic_dec_and_test(&amp;kref-&gt;refcount))</code></p>
     <p><code>  release (kref);</code></p>
     <p><code>}</code></p>
     <p>Эта функция уменьшает значение счетчика ссылок на единицу и вызывает функцию <code>release()</code>, которая передастся ей в качестве параметра, когда значение счетчика ссылок становится равным нулю. Как видно из использованного выражения <code>WARN_ON()</code>, функция <code>release()</code> не может просто совпадать с функцией <code>kfrее()</code>, а должна быть специальной функцией, которая принимает указатель на структуру <code>struct kref</code> в качестве своего единственного параметра и не возвращает никаких значений.</p>
     <p>Вместо того чтобы разрабатывать свои функции управления счетчиками ссылок на основании типа данных <code>atomic_t</code>, настоятельно рекомендуется использовать тип данных <code>kref</code> и соответствующие функции, которые обеспечивают общий и правильно работающий механизм поддержки счетчиков ссылок в ядре.</p>
     <p>Все эти функции определены в файле <code>lib/kref.c</code> и объявлены в файле <code>&lt;linux/kref.h&gt;</code>.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Файловая система sysfs</p>
    </title>
    <section>
     <p>Файловая система sysfs — это виртуальная файловая система, которая существует только в оперативной памяти и позволяет просматривать иерархию объектов <code>kobject</code>. Она позволяет пользователям просматривать топологию устройств операционной системы в виде простой файловой системы. Атрибуты объектов <code>kobject</code> могут экспортироваться в виде файлов, которые позволяют считывать значения переменных ядра, а также опционально записывать их.</p>
     <p>Хотя изначально целью создания модели представления устройств было описание топологии устройств системы для управления электропитанием, файловая система sysfs стала удачным продолжением этой идеи. Для того чтобы упростить отладку, разработчик унифицированной модели устройств решил экспортировать дерево устройств в виде файловой системы. Такое решение показало свою полезность вначале в качестве замены файлов, связанных с устройствами, которые раньше экспортировались через файловую систему <code>/proc</code>, а позже в качестве мощного инструмента просмотра информации о системной иерархии объектов. Вначале, до появления объектов <code>kobject</code>, файловая система sysfs называлась driverfs. Позже стало ясно — новая объектная модель была бы очень кстати, и в результате этого появилась концепция объектов <code>kobject</code>. Сегодня каждая система, на которой работает ядро 2.6, имеет поддержку файловой системы sysfs, и практически во всех случаях эта файловая система монтируется.</p>
     <p>Основная идея работы файловой системы sysfs — это привязка объектов <code>kobject</code> к структуре каталогов с помощью поля <code>dentry</code>, которое есть в структуре <code>kobject</code>. Вспомните из материала главы 12, "Виртуальная файловая система", что структура <code>dentry</code> используется для представления элементов каталогов. Связывание объектов с элементами каталогов проявляется в том, что каждый объект просто видится как каталог файловой системы. Экспортирование объектов <code>kobject</code> в виде файловой системы выполняется путем построения дерева элементов каталогов в оперативной памяти. Но обратите внимание, объекты <code>kobject</code> уже образуют древовидную структуру — нашу модель устройств! Поэтому простое назначение каждому объекту иерархии, которые уже образуют дерево в памяти, соответствующего элемента каталога позволяет легко построить файловую систему sysfs.</p>
     <p>На рис. 17.2 показан частичный вид файловой системы sysfs, которая смонтирована на каталог <code>/sys</code>.</p>
     <image l:href="#img_24.jpeg"/>
     <p><strong>Рис. 17.2</strong>. Содержимое части каталога <code>/sys</code></p>
     <p>Корневой каталог файловой системы sysfs содержит семь подкаталогов: <code>block</code>, <code>bus</code>, <code>class</code>, <code>devices</code>, <code>firmware</code>, <code>module</code> и <code>power</code>. В каталоге <code>block</code> содержатся каталоги для каждого зарегистрированного в системе устройства блочного ввода-вывода.</p>
     <p>Каждый из каталогов в свою очередь содержит подкаталоги, соответствующие разделам блочного устройства. Каталог <code>bus</code> позволяет просматривать информацию о системных шинах. В каталоге <code>class</code> представлена информация о системных устройствах, которая организована в соответствии с высокоуровневыми функциями этих устройств. Каталог devices содержит информацию о топологии устройств в системе. Она отображается непосредственно на иерархию структур устройств ядра. Каталог <code>firmware</code> содержит специфичное для данной системы дерево низкоуровневых подсистем, таких как ACPI, EDD, EFT и т.д. В каталоге <code>power</code> содержатся данные по управлению электропитанием всех устройств системы.</p>
     <p>Наиболее важным является каталог <code>devices</code>, который экспортирует модель устройств ядра во внешний мир. Структура каталога соответствует топологии устройств в системе. Большинство информации, которая содержится в других каталогах, — это просто другое представление данных каталога devices. Например, в каталоге <code>/sys/class/net/</code> информация представлена в соответствии с высокоуровневым представлением зарегистрированных сетевых устройств. В этом каталоге может содержаться подкаталог <code>eth0</code>, который содержит символьную ссылку device на соответствующее устройство каталога <code>devices</code>.</p>
     <p>Посмотрите на содержимое каталога <code>/sys</code> той системы Linux, к которой вы имеете доступ. Такое представление системных устройств является очень четким и ясным. Оно показывает взаимосвязь между высокоуровневым представлением информации в каталоге <code>class</code>, низкоуровневым представлением в каталоге devices и драйверами устройств — в каталоге <code>bus</code>. Такое представление взаимосвязи между устройствами очень информативно. Оно становится еще более ценным, если осознать, что все эти данные свободно доступны и описывают все то, что происходит внутри ядра<a l:href="#n89" type="note">[89]</a>.</p>
    </section>
    <section>
     <title>
      <p>Добавление и удаление объектов на файловой системе sysfs</p>
     </title>
     <p>Инициализированные объекты <code>kobject</code> автоматически не экспортируются через файловую систему sysfs. Для того чтобы сделать объект видимым через sysfs, необходимо использовать функцию <code>kobject_add()</code>.</p>
     <p><code>int kobject_add(struct kobject *kobj);</code></p>
     <p>Положение объекта на файловой системе sysfs зависит от его положения в объектной иерархии. Если установлен указатель <code>parent</code> объекта, то объект будет отображен внутри каталога, соответствующего объекту, на который указывает указатель parent. Если указатель <code>parent</code> не установлен, то объект будет отображен в каталоге, соответствующем значению переменной <code>kset-&gt;kobj</code>. Если для некоторого объекта не установлены ни значение поля parent, ни значение поля <code>kset</code>, то считается, что данный объект не имеет родительского и будет отображаться в корневом каталоге файловой системы <code>sysfs</code>. Такое поведение практически всегда соответствует тому, что нужно. Поэтому одно из полей parent или <code>kset</code> (или оба) должно быть установлено правильным образом перед вызовом функции <code>kobject_add()</code>. Имя каталога, который представляет объект <code>kobject</code> в файловой системе sysfs, будет определяться значением поля <code>kobj-&gt;name</code>.</p>
     <p>Вместо того чтобы последовательно вызывать функции <code>kobject_init()</code> и <code>kobject_add()</code>, можно вызвать функцию <code>kobject_register()</code>.</p>
     <p><code>int kobject_register(struct kobject *kobj);</code></p>
     <p>Удаление объекта из файловой системы sysfs выполняется с помощью функции <code>kobject_del()</code>.</p>
     <p><code>void kobject_del(struct kobject *kobj);</code></p>
     <p>Функция <code>kobject_unregister()</code> сочетает в себе выполнение функций <code>kobject_del()</code> и <code>kobject_put()</code>.</p>
     <p><code>void kobject_unregister(struct kobject* kobj);</code></p>
     <p>Все эти четыре функции определены в файле <code>lib/kobject.c</code> и объявлены в файле <code>&lt;linux/kobject.h&gt;</code>.</p>
    </section>
    <section>
     <title>
      <p>Добавление файлов на файловой системе sysfs</p>
     </title>
     <p>Объекты <code>kobject</code> отображаются на каталоги, и такое отображение выполняется естественным образом. А как насчет создания файлов? Файловая система sysfs — это не что иное, как дерево каталогов без файлов.</p>
     <subtitle>Атрибуты, используемые по умолчанию</subtitle>
     <p>Набор файлов, которые создаются в каталоге по умолчанию, определяется с помощью поля <code>ktype</code> объектов <code>kobject</code> и множеств <code>kset</code>. Следовательно, все объекты <code>kobject</code> одного типа имеют один и тот же набор файлов в каталогах, которые этим объектам соответствуют. Структура <code>kobject_type</code> содержит поле <code>default_attrs</code>, которое представляет собой массив структур <code>attribute</code>. Атрибуты отображают данные ядра на файлы в файловой системе sysfs.</p>
     <p>Структура <code>attributes</code> определена в файле <code>&lt;linux/sysfs.h&gt;</code>.</p>
     <p><code>/* структура attribute - атрибуты позволяют отобразить данные ядра</code></p>
     <p><code> на файлы файловой системы sysfs */</code></p>
     <p><code>struct attribute {</code></p>
     <p><code> char          *name;  /* имя атрибута */</code></p>
     <p><code> struct module *owner; /* модуль, если есть, которому</code></p>
     <p><code>                          принадлежат данные */</code></p>
     <p><code> mode_t        mode;   /* права доступа к файлу */</code></p>
     <p><code>};</code></p>
     <p>Поле <code>name</code> содержит имя атрибута. Такое же имя будет иметь и соответствующий файл на файловой системе sysfs. Поле <code>owner</code> — это указатель на структуру <code>module</code>, которая представляет загружаемый модуль, содержащий соответствующие данные. Если такого модуля не существует, то значение поля равно <code>NULL</code>. Поле <code>mode</code> имеет тип <code>mode_t</code> и указывает права доступа к файлу на файловой системе sysfs. Если атрибут предназначен для чтения всеми, то флаг прав доступа должен быть установлен в значение <code>S_IRUGO</code>, если атрибут имеет право на чтение только для владельца, то права доступа устанавливаются в значение <code>S_IRUSR</code>. Атрибуты с правом на запись, скорее всего, будут иметь права доступа <code>S_IRUGO | S_IWUSR</code>. Все файлы и каталоги на файловой системе sysfs принадлежат пользователю с идентификаторами пользователя и группы равными нулю.</p>
     <p>Структура <code>attribute</code> используется для представления атрибутов, а структура <code>sysfs_ops</code> описывает, как эти атрибуты использовать. Поле <code>sysfs_ops</code> — это указатель на одноименную структуру, которая определена в файле <code>&lt;linux/sysfs.h&gt;</code> следующим образом.</p>
     <p><code>struct sysfs_ops {</code></p>
     <p><code> /* метод вызывается при чтении файла на файловой системе sysfs */</code></p>
     <p><code> ssize_t (*show)(struct kobject *kobj,</code></p>
     <p><code>  struct attribute *attr, char *buffer);</code></p>
     <p><code> /* метод вызывается при записи файла на файловой системе sysfs */</code></p>
     <p><code> ssize_t (*store)(struct kobject *kobj,</code></p>
     <p><code>  struct attribute *attr, const char *buffer, size_t size);</code></p>
     <p><code>};</code></p>
     <p>Метод <code>show()</code> вызывается при чтении файла. Он должен выполнить копирование значения атрибута, который передается в качестве параметра <code>attr</code>, в буфер, на который указывает параметр <code>buffer</code>. Размер буфера равен <code>PAGE_SIZE</code> байт. Для аппаратной платформы значение <code>PAGE_SIZE</code> равно 4096 байтов. Функция должна возвратить количество байтов данных, которые записаны в буфер в случае успешного завершения, и отрицательный код ошибки, если такая ошибка возникает.</p>
     <p>Метод <code>store()</code> вызывается при записи. Он должен скопировать <code>size</code> байт данных из буфера <code>buffer</code> в атрибут <code>attr</code>. Размер буфера всегда равен <code>PAGE_SIZE</code> или меньше. Функция должна возвратить количество байтов данных, которые прочитаны из буфера при успешном выполнении, и отрицательный код ошибки в случае неудачного завершения.</p>
     <p>Так как этот набор функций должен выполнять операции ввода-вывода для всех атрибутов, то необходимо выполнить некоторые дополнительные действия, чтобы вызвать обработчик, специфичный для каждого атрибута.</p>
     <subtitle>Создание нового атрибута</subtitle>
     <p>Обычно атрибутов, которые используются по умолчанию и предоставляются типом <code>ktype</code>, связанным с объектом <code>kobject</code>, оказывается достаточно. Действительно, все объекты <code>kobject</code> одного типа должны быть чём-то похожи друг на друга или даже быть идентичными по своей природе. Например, для всех разделов жестких дисков один и тот же набор атрибутов должен подходить для всех объектов <code>kobject</code>. Это не просто упрощает жизнь, но и позволяет упорядочить код и получить одинаковый способ доступа ко всем каталогам файловой системы sysfs, связанным с родственными объектами.</p>
     <p>Тем не менее иногда требуется, чтобы определенный экземпляр объекта <code>kobject</code> имел некоторые специфические свойства. Для таких объектов может оказаться желательным (или необходимым) создать атрибут, которого нет у общего типа данного объекта. Для такого случая ядро предоставляет функцию <code>sysfs_create_file()</code> для добавления атрибута к существующему объекту.</p>
     <p><code>int sysfs_create_file(struct kobject *kobj, const struct attribute *attr);</code></p>
     <p>Эта функция позволяет привязать структуру attribute, на которую указывает параметр <code>attr</code>, к объекту <code>kobject</code>, на который указывает параметр <code>kobj</code>. Перед тем как вызвать эту функцию, необходимо установить значение атрибута (заполнить поля структуры). Эта функция возвращает значение нуль в случае успеха и отрицательное значение в случае ошибки.</p>
     <p>Обратите внимание, что для обработки указанного атрибута используется структура <code>sysfs_ops</code>, соответствующая типу <code>ktype</code> объекта. Иными словами, существующие функции <code>show()</code> и <code>store()</code>, которые используются для объекта по умолчанию, должны иметь возможность обработать вновь созданный атрибут.</p>
     <p>Кроме того, существует возможность создавать символьные ссылки. Создать символьную ссылку на файловой системе sysfs можно с помощью вызова следующей функции.</p>
     <p><code>int sysfs_create_link(struct kobject *kobj,</code></p>
     <p><code> struct kobject *target, char *name);</code></p>
     <p>Эта функция создает символьную ссылку с именем <code>name</code> в каталоге объекта, соответствующего параметру <code>kobj</code>, на каталог, соответствующий параметру <code>target</code>. Эта функция возвращает нулевое значение в случае успеха и отрицательный код ошибки в противном случае.</p>
     <subtitle>Удаление созданного атрибута</subtitle>
     <p>Удаляется атрибут с помощью вызова функции <code>sysfs_remove_file()</code>.</p>
     <p><code>void sysfs_remove_file(struct kobject *kobj,</code></p>
     <p><code> const struct attribute *attr);</code></p>
     <p>После возврата из этой функции указанный атрибут больше не отображается в каталоге объекта.</p>
     <p>Символьная ссылка, созданная с помощью функции <code>sysfs_create_link()</code>, может быть удалена с помощью функции <code>sysfs_remove_link()</code>.</p>
     <p><code>void sysfs_remove_link(struct kobject *kobj, char *name);</code></p>
     <p>После возврата из функции символьная ссылка с именем name удаляется из каталога, на который отображается объект <code>kobj</code>.</p>
     <p>Все эти четыре функции объявлены в файле <code>&lt;linux/kobject.h&gt;</code>. Функции <code>sysfs_create_file()</code> и <code>sysfs_remove_file()</code> определены в файле <code>fs/sysfs/file.c</code>, а функции <code>sysfs_create_link()</code> и <code>sysfs_remove_link()</code> — в файле <code>fs/sysfs/symlink.c</code>.</p>
     <subtitle>Соглашения по файловой системе sysfs</subtitle>
     <p>Файловая система sysfs — это место, где должна реализовываться функциональность, для которой раньше использовался системный вызов <code>ioctl()</code> для специальных файлов устройств, или файловая система procfs. Сегодня модно выполнять такие вещи через атрибуты файловой системы sysfs в соответствующем каталоге. Например, вместо того чтобы реализовать новую директиву <code>ioctl()</code> для специального файла устройства, лучше добавить соответствующий атрибут в каталоге файловой системы sysfs, который относится к этому устройству. Такой подход позволяет избежать использования небезопасных, из-за отсутствия проверки типов аргументов, директив <code>ioctl()</code>, а также файловой системы <code>/proc</code> с ее бессистемным расположением файлов и каталогов.</p>
     <p>Однако чтобы файловая система sysfs оставалась четко организованной и интуитивно понятной, разработчики должны придерживаться определенных соглашений.</p>
     <p>Во-первых, каждый атрибут sysfs должен экспортировать значение одной переменной на файл. Значения должны быть в текстовом формате и соответствовать простым типам языка программирования С. Целью такого представления является необходимость избежать чрезвычайно запутанного и плохо структурированного представления информации, которое мы сегодня имеем на файловой системе <code>/proc</code>. Использование одной переменной на файл позволяет легко считывать и записывать данные из командной строки, а также просто работать через файловую систему sysfs с данными ядра в программах, написанных на языке С. В случаях, когда одно значение на файл приводит к неэффективному представлению информации, допустимо использование файлов, в которых хранится несколько значений одного типа. Эти данные необходимо четко разделять. Наиболее предпочтительным разделителем является символ пробела. При разработке кода ядра необходимо всегда помнить, что файлы файловой системы sysfs являются представлениями переменных ядра, и ориентироваться на доступ к ним из пространства пользователя, в частности из командной строки.</p>
     <p>Во-вторых, данные файловой системы sysfs должны быть организованы в виде четкой иерархии. Для этого необходимо правильно разрабатывать связи "родитель- потомок" объектов <code>kobject</code>. Связывать атрибуты с объектами <code>kobject</code> необходимо с учетом того, что эта иерархия объектов существует не только в ядре, но и экспортируется в пространство пользователя. Структуру файловой системы sysfs необходимо поддерживать в четком виде!</p>
     <p>Наконец, необходимо помнить, что файловая система sysfs является службой ядра и в некотором роде интерфейсом ядра к прикладным программам (Application Binary Interface, ABT). Пользовательские программы должны разрабатываться в соответствии с наличием, положением, содержимым и поведением каталогов и файлов на файловой системе sysfs. Изменение положения существующих файлов крайне не рекомендуется, а изменение поведения атрибутов, без изменения их имени или положения, может привести к серьезным проблемам.</p>
     <p>Эти простые соглашения позволяют с помощью файловой системы sysfs обеспечить в пространстве пользователя интерфейс ядра с широкими возможностями. При правильном использовании файловой системы sysfs разработчики прикладных программ не будут вас ругать и будут вам благодарны за хороший код.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Уровень событий ядра</p>
    </title>
    <p>Уровень событий ядра (kernel event layer) — это подсистема, которая позволяет передавать информацию о различных событиях из ядра в пространство пользователя и реализована, как вы уже, наверное, догадываетесь, на базе объектов kobject. После выпуска ядра версии 2.6.0 стало ясно, что необходим механизм для отправления сообщений из ядра в пространство пользователя, в частности для настольных рабочих компьютеров, что позволит сделать такие системы более функциональными, а также лучше использовать асинхронную обработку. Идея состояла в том, что ядро будет помещать возникающие события в стек. Например, "Жесткий диск переполнен!", "Процессор перегрелся!", "Раздел диска смонтирован!", "На горизонте появился пиратский корабль!" (последнее, конечно, шутка).</p>
    <p>Первые реализации подсистемы событий ядра появились незадолго до того, как эта подсистема стала тесно связанной с объектами <code>kobject</code> и файловой системой sysfs. В результате такой связи реализация получилась достаточно красивой. В модели уровня событий ядра, события представляются в виде <emphasis>сигналов</emphasis>, которые посылаются объектами, в частности объектами типа <code>kobject</code>. Так как объекты отображаются на элементы каталогов файловой системы sysfs, то <emphasis>источниками</emphasis> событий являются определенные элементы пути на файловой системе sysfs. Например, если поступившее событие связано с первым жестким диском, то адресом источника события является каталог <code>/sys/block/hda</code>. Внутри же ядра источником события является соответствующий объект <code>kobject</code>.</p>
    <p>Каждому событию присваивается определенная строка символов, которая представляет сигнал и называется <emphasis>командой</emphasis> (<emphasis>verb</emphasis>) или <emphasis>действием</emphasis> (<emphasis>action</emphasis>). Эта строка символов содержит в себе информацию о том, что именно произошло, например <emphasis>изменение</emphasis> (<emphasis>modified</emphasis>) или <emphasis>размонтирование</emphasis> (<emphasis>unmounted</emphasis>).</p>
    <p>Каждое событие может нести в себе некоторую дополнительную информацию (дополнительную нагрузку, payload). Вместо того чтобы передавать в пространство пользователя строку, которая содержит эту полезную информацию, данная дополнительная информация представляется с помощью атрибутов, отображаемых на файловой системе sysfs.</p>
    <p>События ядра поступают из пространства ядра в пространство пользователя через интерфейс <code>netlink</code>. Интерфейс <code>netlink</code> — это специальный тип высокоскоростного сетевого сокета групповой передачи (multicast), который используется для передачи сообщений, связанных с сетевой подсистемой. Использование интерфейса netlink позволяет выполнить обработку событий ядра с помощью простых блокирующих вызовов функций для чтения информации из сокетов. Задача пространства пользователя — реализовать системный процесс-демон, который выполняет прослушивание сокета, считывает информацию о всех приходящих событиях, обрабатывает их и отправляет полученные сообщения в системный стек пространства пользователя. Одна из возможных реализаций такого демона, работающего в пространстве пользователя, — это D-BUS<a l:href="#n90" type="note">[90]</a>. который также реализует и системную шину сообщений. Таким образом, ядро может подавать сигналы так же, как это делают все остальные компоненты системы.</p>
    <p>Для отправки события в пространство пользователя код ядра должен вызвать функцию <code>kobject_uevent()</code>.</p>
    <p><code>int kobject_uevent(struct kobject *kobj,</code></p>
    <p><code> enum kobject_action action, struct attribute *attr);</code></p>
    <p>Первый параметр указывает объект <code>kobject</code>, который является источником сигнала. Соответствующее событие ядра будет содержать элемент пути на файловой системе sysfs, связанный с объектом, сгенерировавшим сигнал.</p>
    <p>Второй параметр позволяет указать <emphasis>команду</emphasis> или <emphasis>событие</emphasis>, которое описывает сигнал. Сгенерированное событие ядра будет содержать строку, которая соответствует номеру, передаваемому в качестве значения параметра <code>enum kobject_action</code>. Вместо того чтобы непосредственно передать строку, здесь используется ее номер, который имеет тип перечисления (<code>enum</code>). Это дает возможность более строго выполнить проверку типов, изменить соответствие между номером строки и самой строкой в будущем, а также уменьшить количество ошибок и опечаток. Перечисления определены в файле <code>&lt;linux/kobject_uevent.h&gt;</code> и имеют имена в формате <code>KOBJ_foo</code>. На момент написания книги были определены следующие события: <code>KOBJ_MOUNT</code>, <code>KOBJ_UNMOUNT</code>, <code>KOBJ_ADD</code>, <code>KOBJ_REMOVE</code> и <code>КОВJ_CHANGE</code>. Эти значения отображаются на строки "mount" (монтирование), "unmount" (размонтирование), "add" (добавление), "remove" (удаление) и "change" (изменение) соответственно. Допускается добавление новых значений событий, если существующих значений недостаточно.</p>
    <p>Последний параметр — опциональный указатель на структуру <code>attribute</code>. Этот параметр можно трактовать как дополнительную информацию (payload) о событии. Если только одного значения события недостаточно, то событие может предоставить информацию о том, в каком файле файловой системы sysfs содержатся дополнительные данные.</p>
    <p>Рассмотренная функция использует динамическое выделение памяти и поэтому может переходить в состояние ожидания. Существует атомарная версия рассмотренной функции, которая идентична ей по всем, кроме того что при выделении использует флаг <code>GFP_ATOMIC</code>.</p>
    <p><code>int kobject_uevent_atomic(struct kobject *kobj,</code></p>
    <p><code> enum kobject_action action, struct attribute *attr);</code></p>
    <p>По возможности необходимо использовать стандартный интерфейс без атомарного выделения памяти. Параметры этих функций и их смысл — идентичны.</p>
    <p>Использование объектов <code>kobject</code> и их атрибутов не только дают возможность описать события в терминах файловой системы sysfs, но и стимулируют создание новых объектов и их атрибутов, которые еще не представлены через файловую систему sysfs.</p>
    <p>Обе рассмотренные функции определены в файле <code>lib/kobject_uevent.c</code> и объявлены в файле <code>&lt;linux/kobject_uevent.h&gt;</code>.</p>
   </section>
   <section>
    <title>
     <p>Кратко об объектах <code>kobject</code> и файловой системе sysfs</p>
    </title>
    <p>В этой главе рассматривается модель представления устройств, файловая система sysfs, объекты <code>kobject</code> и уровень событий ядра. Описание материала главы было бы невозможно без рассмотрения родственных вещей: были также описаны множества <code>kset</code>, подсистемы, атрибуты, типы <code>ktype</code> и счетчики ссылок <code>kref</code>. Эти структуры предназначены для использования разными людьми в разных местах. Разработчикам драйверов необходимо только ознакомление с внешними интерфейсами. Большинство подсистем драйверов эффективно скрывают внутренние механизмы использования объектов <code>kobject</code> и других, близких к ним структур. Понимание основных принципов работы и знание основного назначения интерфейсов, таких как <code>sysfs_create_file()</code>, является достаточным для разработчиков драйверов. Однако для разработчиков, которые занимаются разработкой основного кода ядра, может потребоваться более детальное понимание принципов функционирования объектов <code>kobject</code>. Объекты <code>kobject</code> могут оказаться еще более важными, так как их могут использовать и те разработчики, которые вообще не занимаются разработкой подсистем драйверов!!!</p>
    <p>Эта глава — последняя из тех, которые посвящены подсистемам ядра. В следующих главах будут рассмотрены некоторые общие вопросы, которые также могут оказаться важными для разработчиков ядра. Например, основные рекомендации по отладке кода!</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 18</p>
    <p>Отладка</p>
   </title>
   <section>
    <p>Один из самых существенных факторов, который отличает разработку ядра от разработки пользовательских приложений, — это сложность отладки. Отлаживать код ядра сложно, но крайней мере по сравнению с кодом пространства пользователя. Еще больше усугубляет ситуацию тот факт, что ошибка в ядре может привести к катастрофическим последствиям для всей системы.</p>
    <p>Успех в освоении приемов отладки ядра и, в конце концов, в разработке ядра вообще, в основном, зависит от опыта и понимания принципов работы операционной системы в целом. Понятно также, что, для того чтобы успешно выполнять отладку ядра, необходимо понимать, как ядро работает. Тем не менее когда-то нужно начать, и в этой главе будут рассмотрены подходы к отладке ядра.</p>
   </section>
   <section>
    <title>
     <p>С чего необходимо начать</p>
    </title>
    <p>Итак, готовы ли вы начать охоту за ошибками? Этот путь может оказаться длинным и полным разочарований. Некоторые ошибки ставили в тупик все сообщество разработчиков ядра на несколько месяцев. К счастью, на каждую из таких злостных ошибок находятся простые, которые легко исправить. Если вам повезет, то все проблемы, с которыми вы столкнетесь, будут простыми и тривиальными. Однако чтобы это проверить, необходимо начать исследования. Для этого понадобится следующее.</p>
    <p>• Сама проблема. Может звучать глупо, но дефект должен быть конкретным и хорошо определенным. Очень помогает, если его хотя бы кто-нибудь может устойчиво воспроизвести. Однако, к сожалению, дефекты обычно ведут себя не так хорошо, как хотелось бы, и не всегда могут быть хорошо определены.</p>
    <p>• Версия ядра, в которой существует дефект (обычно это последняя версия, хотя кто может это гарантировать?). Еще лучше, если известна версия ядра, в которой проблема впервые появилась. Мы рассмотрим, как это установить, если нет такой информации.</p>
    <p>• Немного удачи, опыта и их комбинации.</p>
    <p>Если дефект нельзя воспроизвести, то многие из приведенных ниже подходов становятся бесполезными. Очень важно, чтобы проблему можно было повторить. Если этого не удается сделать, то исправление дефекта становится возможным только путем визуального анализа кода для того, чтобы найти в нем ошибку. На самом деле так случается достаточно часто (например, с разработчиками ядра), но очевидно, что шансы добиться успеха становятся более весомыми, если появляется возможность воспроизвести проблему.</p>
    <p>Может также показаться странным, что существуют дефекты, которые кто-то не может воспроизвести. Дело в том, что в пользовательских программах дефекты чаще всего проявляются очень просто, например <emphasis>вызов функции</emphasis> <code><emphasis>foo</emphasis></code> <emphasis>приводит к созданию файла</emphasis> <code><emphasis>core</emphasis></code>. В ядре все совсем по-другому. Взаимодействия между ядром, пространством пользователя и аппаратурой могут быть достаточно тонкими. Состояния конкуренции за ресурсы могут возникать с вероятностью одно на миллион итераций алгоритма. Плохо спроектированный или даже не правильно скомпилированный код может обеспечивать удовлетворительную производительность на одной системе, но неудовлетворительную на другой, Очень часто происходит так, что на какой-то случайной машине, при очень специфическом характере загрузке, начинают проявляться дефекты, которые больше нигде не проявляются. Чем больше доступно дополнительной информации при локализации дефекта, тем лучше. Во многих случаях, как только удалось устойчиво воспроизвести проблему, можно считать, что большая половина работы сделана.</p>
   </section>
   <section>
    <title>
     <p>Дефекты ядра</p>
    </title>
    <p>Дефекты в ядре могут быть такими же разнообразными, как и дефекты в пользовательских программах. Они возникают по различным причинам и проявляются в разнообразных формах. Дефекты занимают диапазон от явно неправильного кода (например, запись правильного значения в неправильное место) до ошибок синхронизации (например, если не правильно блокируется совместно используемая переменная). Эти дефекты проявляются в любой форме; от плохой производительности до неправильного функционирования и даже до потери данных.</p>
    <p>Часто, между тем моментом, когда в ядре возникла ошибка и тем моментом, когда пользователь ее заметил происходит большая цепь событий. Например, разделяемая структура данных, у которой нет счетчика использования может привести к возникновению состояния конкуренции за ресурс (race condition). Если не принять необходимых мер, то один процесс может освободить память, в которой хранится структура, в то время, как другой процесс может эту структуру все еще использовать. Спустя некоторое время второй процесс может обратиться к этим данным, что в свою очередь может привести к попытке разыменования указателя со значением <code>NULL</code>, если будут считаны случайные данные ("мусор"), или вообще не привести ни к чему плохому (если данные в соответствующей области памяти еще не были перезаписаны). Разыменование указателя со значением <code>NULL</code> приводит к выводу сообщения <code>"oops"</code>, в то время, как случайный "мусор" может привести к потере данных (и соответственно к неправильному функционированию, или опять же к выводу сообщения <code>"oops"</code>, но уже по другом поводу). Пользователь же заметит только неправильное функционирование или сообщение <code>"oops"</code>. Разработчик ядра при этом должен пойти но обратному пути: исходя из ошибки определить, что к данным было обращение после того, как память с этими данными была освобождена, что это произошло в результате возникновения конкуренции за ресурс и исправить ошибку путем правильного учета количества ссылок на совместно используемую структуру данных. Для этого также вероятно потребуется применение блокировок.</p>
    <p>Отладка ядра может показаться сложным делом, тем не менее, ядро не особо отличается от других больших программных проектов. У ядра есть свои уникальные особенности, такие как ограничения связанные со временем выполнения участков кода, возможности возникновения состояний конкуренции (race) — как результат параллельного выполнения множества потоков в ядре. Можно дать стопроцентную гарантию, что если приложить некоторые усилия и понимание, то проблемы ядра можно с успехом находить и решать (и даже, возможно, получать удовольствие от успешного преодоления трудностей).</p>
   </section>
   <section>
    <title>
     <p>Функция <code>printk()</code></p>
    </title>
    <section>
     <p>Функция форматированного вывода сообщений <code>printk()</code> работает аналогично библиотечной функции <code>printf()</code> языка С. Действительно в этой книге до этого момента мы не видели никаких существенных отличий в ее использовании. Для большинства задач это именно так: функция <code>printk()</code> — это просто функция ядра, выполняющая форматированный вывод сообщений. Однако, некоторые различия все же имеются.</p>
    </section>
    <section>
     <title>
      <p>Устойчивость функции <code>printk()</code></p>
     </title>
     <p>Одно из проверенных и часто используемых свойств функции <code>printk()</code> — это ее устойчивость. Функцию <code>printk()</code> можно вызывать практически <emphasis>в любое время</emphasis> и <emphasis>в любом месте</emphasis> ядра. Её можно вызывать из контекста прерывания и из контекста процесса. Её можно вызывать во время удержания блокировки. Её можно вызывать одновременно на нескольких процессорах и она не требует при этом удерживать какие-нибудь блокировки.</p>
     <p>Эта функция очень устойчива, и это очень важно, потому что полезность функции <code>printk()</code> базируется на том факте, что она всегда доступна и всегда работает.</p>
     <subtitle>Неустойчивость функции <code>printk()</code></subtitle>
     <p>Слабое место у функции <code>printk()</code> в плане устойчивости все же существует. Её нельзя использовать до некоторого момента при загрузки ядра, пока консоль еще не инициализирована. Действительно, если нет консоли, то куда будут выводится сообщения?</p>
     <p>Обычно это не проблема, если не нужно выполнять отладку кода, который выполняется на очень ранних стадиях процесса загрузки (например, функции <code>setup_arch()</code>, которая выполняет инициализацию специфичную для аппаратной платформы). Отладка такого рода — настоящая задача: отсутствие каких-либо способов вывода сообщений, а только проблема в полном составе.</p>
     <p>В таких ситуациях тоже есть некоторые обнадеживающие моменты, но их не много. Настоящие хакеры, которые работают с аппаратурой на таком низком уровне, для связи с внешним миром используют аппаратное обеспечение соответствующей платформы, которое всегда работает (например, последовательный порт). Поверьте, что у большинства людей такая работа не вызовет радости. Для одних аппаратных платформ такое решение работает, для других платформ (включая платформу i386) существуют заплаты кода, которые тоже позволяют сэкономить время.</p>
     <p>Одно из решений проблемы — вариант функции <code>printk()</code>, который может выводить информацию на консоль на очень ранних стадиях процесса загрузки — <code>early_printk()</code>. Поведение этой функции аналогично функции <code>printk()</code>, за исключением имени и возможности работать на очень ранних стадиях загрузки. Однако, такое решение не переносимо, потому что не для всех поддерживаемых аппаратных платформ этот метод работы реализован. Если же он есть, то может сослужить хорошую службу.</p>
     <p>Кроме ситуаций, когда необходимо выводить на консоль информацию на очень ранних стадиях загрузки системы, можно положиться на функцию <code>printk()</code>, которая работает практически всегда.</p>
    </section>
    <section>
     <title>
      <p>Уровни вывода сообщений ядра</p>
     </title>
     <p>Главное отличие между функциями <code>printk()</code> и <code>printf()</code> — это возможность в первой указывать <emphasis>уровень вывода сообщений ядра</emphasis> (<emphasis>loglevel</emphasis>). Ядро использует уровень вывода сообщений для принятия решения о том, выводить сообщение на консоль или нет. Ядро выводит на консоль все сообщение с уровнями меньшими, или равными, соответствующему значению для консоли (console loglevel). Уровень вывода сообщений можно указывать следующим образом.</p>
     <p><code>printk(KERN_WARNING "Это предупреждение!\n");</code></p>
     <p><code>printk(KERN_DEBUG "Это отладочное сообщение!\n");</code></p>
     <p><code>printk("Мы не указали значения loglevel!\n");</code></p>
     <p>Строки <code>KERN_WARNING</code> и <code>KERN_DEBUG</code> определены через препроцессор в заголовочном файле <code>&lt;linux/kernel.h&gt;</code>. Эти макросы раскрываются в строки, соответственно <code>"&lt;4&gt;"</code> и <code>"&lt;7&gt;"</code>, которые объединяются со строкой формата в самом начале сообщения, выводимого функцией <code>printk()</code>. После этого на основании уровня вывода сообщения и уровня вывода консоли (значение переменной <code>console_loglevel</code>) ядро принимает решение выводить информацию на консоль или нет. В табл. 18.1 приведен полный список возможных значений уровня вывода сообщений.</p>
     <empty-line/>
     <p><strong>Таблица 18.1</strong>. Доступные значения уровня вывода сообщений ядра (loglevel)</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Значение loglevel</th>
       <th align="left" valign="top">Описание</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>KERN_EMERG</code></td>
       <td align="left" valign="top">Аварийная ситуация</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>KERN_ALERT</code></td>
       <td align="left" valign="top">Проблема, на которую требуется немедленно обратить внимание</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>KERN_CRIT</code></td>
       <td align="left" valign="top">Критическая ситуация</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>KERN_ERR</code></td>
       <td align="left" valign="top">Ошибка</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>KERN_WARNING</code></td>
       <td align="left" valign="top">Предупреждение</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>KERN_NOTICE</code></td>
       <td align="left" valign="top">Обычная ситуация, но на которую следует обратить внимание</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>KERN_INFO</code></td>
       <td align="left" valign="top">Информационное сообщение</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>KERN_DEBUG</code></td>
       <td align="left" valign="top">Отладочное сообщение — обычно избыточная информация</td>
      </tr>
     </table>
     <p>Если уровень вывода сообщений ядра не указан, то его значение по умолчанию равно <code>DEFAULT_MESSAGE_LOGLEVEL</code>, который в данный момент равен <code>KERN_WARNING</code>. Так как это значение может измениться, то для своих сообщений необходимо всегда указывать уровень вывода.</p>
     <p>Наиболее важный уровень вывода — <code>KERN_EMERG</code> определен как <code>"&lt;0&gt;"</code>, а наименее важный — <code>KERN_DEBUG</code>, как <code>"&lt;7&gt;"</code>. Например, после обработки препроцессором кода из предыдущего примера получается следующее.</p>
     <p><code>printk("&lt;4&gt;Это предупреждение!\n");</code></p>
     <p><code>printk("&lt;7&gt;Это отладочное сообщение!\n");</code></p>
     <p><code>printk("&lt;4&gt;Мы не указали значения loglevel!\n");</code></p>
     <p>Как вы будете использовать функцию <code>printk()</code> зависит только от вас. Конечно, обычные сообщения, которые должны быть видимы, должны иметь соответствующий уровень вывода. Отладочные сообщения, которые в большом количестве встраиваются в самые разные места кода с целью разобраться с проблемой — "допустим ошибка здесь", "пробуем", "работает" — могут иметь любой уровень вывода. Один вариант — оставить уровень при котором сообщения выводятся на консоль равным значению этого параметра по умолчанию, а уровень вывода ваших сообщений установить в значение <code>KERN_CRIT</code>, или что-то около этого. Можно поступить и наоборот — для отладочных сообщений установить уровень <code>KERN_DEBUG</code> и поднять уровень при котором сообщения выводятся на консоль. Каждый из вариантов имеет свои положительные и отрицательные стороны — вам решать.</p>
     <p>Уровни вывода сообщений определены в файле <code>&lt;linux/kernel.h&gt;</code>.</p>
    </section>
    <section>
     <title>
      <p>Буфер сообщений ядра</p>
     </title>
     <p>Сообщения ядра хранятся в кольцевом буфере (log buffer) размером <code>LOG_BUF_LEN</code>. Этот размер можно изменять во время компиляции с помощью параметра <code>CONFIG_LOG_BUF_SHIFT</code>. Для однопроцессорной машины это значение по умолчанию равно 16 Кбайт. Другими словами в ядре может хранится до 16 Кбайт системных сообщений. Если общий размер всех сообщений ядра достигает этого максимального значения и приходит новое сообщение, то оно переписывается поверх самого старого из хранящихся в буфере сообщений. Буфер сообщений ядра называется <emphasis>кольцевым</emphasis>, потому что запись и считывание сообщений выполняется по круговой схеме.</p>
     <p>Использование кольцевого буфера предоставляет определенные преимущества. Так как одновременные операции чтения и записи в кольцевом буфере выполняются достаточно просто, то функцию <code>printk()</code> можно использовать даже из контекста прерывания. Более того, это позволяет просто организовать управление системными сообщениями. Если сообщений оказывается очень много, то новые сообщения просто затирают старые. Если возникает проблема, которая проявляется в генерации большого количества сообщений, то буфер сообщений просто начинает переписывать себя вместо того, чтобы бесконтрольно занимать память. Единственный недостаток кольцевого буфера — возможность потерять сообщения, что не такая уж и большая плата за ту устойчивость, которую такое решение предоставляет.</p>
    </section>
    <section>
     <title>
      <p>Демоны <code>syslogd</code> и <code>klogd</code></p>
     </title>
     <p>В стандартной системе Linux для извлечения сообщений ядра из буфера используется специальный демон пространства пользователя <code>klogd</code>, который направляет эти сообщения в файл журнала системных сообщений. Для чтения системных сообщений программа <code>klogd</code> может считывать данные из файла <code>/proc/kmsg</code>, или использовать системный вызов <code>syslog()</code>. По умолчанию используется подход на основе файловой системы <code>/proc</code>. Если сообщений нет, то демон <code>klogd</code> блокируется на операции чтения, пока не поступит новое сообщение. Когда приходит новое сообщение, демон возвращается к выполнению, считывает сообщения и обрабатывает их. По умолчанию сообщения отправляются демону <code>syslogd</code>.</p>
     <p>Демон <code>syslogd</code> добавляет полученные сообщения в конец файла журнала, по умолчанию — <code>/var/log/messages</code>. Имя соответствующего файла можно настроить в конфигурационном файле <code>/etc/syslog.conf</code>.</p>
     <p>Изменить уровень вывода сообщений на консоль (console loglevel) можно при старте демона <code>klogd</code> с помощью флага <code>-с</code>.</p>
    </section>
    <section>
     <title>
      <p>Замечание относительно функции <code>printk()</code> и разработки ядра</p>
     </title>
     <p>Когда впервые начинают разрабатывать код ядра, то скорее всего очень часто приходится заменять функцию <code>printf()</code> на функцию <code>printk()</code>. Это нормально, потому что нельзя не принимать во внимание многолетний опыт по написанию пользовательских программ и использовании функции <code>printf()</code>. Следует надеяться, что повторение таких ошибок не будет продолжаться долго, потому что повторяющиеся ошибки компоновщика начнут быстро надоедать.</p>
     <p>Однажды вдруг окажется, что вы поймали себя на том, что начали использовать функцию <code>printk()</code> вместо функции <code>printf()</code> в пользовательских программах. Когда для вас этот день наконец наступит, то можно сказать, что вы стали настоящим хакером и специалистом по разработке кода ядра.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Сообщения <code>Oops</code></p>
    </title>
    <section>
     <p>Сообщения <emphasis>oops</emphasis> — обычный для ядра способ сообщить пользователю, что произошло что-то нехорошее. Так как ядро управляет всей системой, то оно не может само себя исправить, или завершить, как это возможно для программ пространства пользователя, когда они делают что-то не так. Вместо этого, ядро выводит сообщение <code>oops</code>. Такое сообщение включает вывод информации об ошибке на консоль, вывод дампа содержимого всех регистров и вывод обратной трассировки вызовов функций (back trace). Сбои в работе ядра трудно обработать, поэтому ядро должно "пролезть'' через многие дыры, чтобы вывести сообщение <code>oops</code> и выполнить за собой все необходимые действия по очистке. Часто после выдачи сообщения <code>oops</code> ядро находится в несогласованном состоянии. Например, в момент возникновения ситуации, в которой выдается сообщение <code>oops</code>, ядро может находится в процессе обработки важных данных. В этот момент может удерживаться блокировка, или выполняться сеанс взаимодействия с оборудованием. Ядро должно аккуратно отойти от текущего состояния и попытаться восстановить контроль над системой. Во многих случаях это невозможно. Если ситуация, в которой выдается сообщение <code>oops</code>, возникает в контексте прерывания, то ядро не может продолжать работу и переходит в состояние паники. Состояние паники проявляется в полной остановке системы. Если <code>oops</code> возникает в холостой задаче (idle task, идентификатор <code>pid</code> равен нулю), или при выполнении процесса <code>init</code> (идентификатор <code>pid</code> равен единице), то ядро также переходит в состояние паники, потому что ядро не может продолжать выполнение без этих важных процессов. Однако, если <code>oops</code> возникает при выполнении любого другого процесса, то ядро завершает этот процесс и продолжает работу.</p>
     <p>Сообщение <code>oops</code> может выдаваться по многим причинам, включая недопустимый доступ к памяти (memory access violation) и выполнение недопустимой машинной команды. Как разработчику ядра, вам придется иметь дело с сообщениями <code>oops</code> и далее, несомненно, быть причиной их появления.</p>
     <p>Ниже показано сообщение <code>oops</code> для машины аппаратной платформы PPC, которое возникло и обработчике таймера для сетевого интерфейсного адаптера tulip.</p>
     <p><code>Oops: Exception in kernel mode, sig: 4</code></p>
     <p><code>Unable to handle kernel NULL pointer dereference at virtual address 00000001</code></p>
     <empty-line/>
     <p><code>NIP: C013A7F0 LR: C013A7F0 SP:C0685E00 REGS: c0905d10 TRAP: 0700</code></p>
     <p><code>Not tainted</code></p>
     <p><code>MSR: 00089037 EE: 1 PR: 0 FP: 0 ME: 1 IR/DR: 11</code></p>
     <p><code>TASK=c0712530[0] swapper Last syscall: 120</code></p>
     <p><code>GPR00: C013A7C0 C0295E00 C0231530 0000002F 00000001 C0380CB8 C0291B80 C02D0000</code></p>
     <p><code>GPR08: 000012AD 00000000 00000000 C0292AA0 4020A088 00000000 00000000 00000000</code></p>
     <p><code>GPR16: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000</code></p>
     <p><code>GPR24: 00000000 00000005 00000000 00001032 C3F7C000 00000032 FFFFFFFF C3F7C1C0</code></p>
     <p><code>Call trace:</code></p>
     <p><code>[c013ab30] tulip_timer+0x128/0x1c4</code></p>
     <p><code>[c0020744] run_timer_softirq+0x10c/0x164</code></p>
     <p><code>[c001b864] do_softirq+0x88/0x104</code></p>
     <p><code>[c0007e80] timer_interrupt+0x284/0x298</code></p>
     <p><code>[c00033c4] ret_from_except+0x0/0x34</code></p>
     <p><code>[c0007b84] default_idle+0x20/0x60</code></p>
     <p><code>[c0007bf8] cpu_idle+0x34/0x38</code></p>
     <p><code>[c0003ae8] rest_init+0x24/0x34</code></p>
     <p>У пользователей ПК может вызвать удивление количество регистров процессора (32 — огромное число!). Сообщение <code>oops</code> для аппаратной платформы x86, которые возможно вам более знакомы, имеют несколько более простой вид. Тем не менее, важная информация идентична для всех аппаратных платформ: содержимое всех регистров и обратная трассировка.</p>
     <p>Обратная трассировка показывает точную последовательность вызовов функций, которая привела к проблеме. В данном случае можно точно определить, что случилось: машина выполняла холостое задание — холостой цикл: вызов функции <code>cpu_idle()</code>, из которой циклически вызывается функция <code>default_idle()</code>. Поступило прерывание от системного таймера, в котором вызываются обработчики таймеров ядра. Среди них вызывается обработчик таймера — функция <code>tulip_timer()</code>, в которой выполнено разыменование указателя со значением <code>NULL</code>. Можно даже воспользоваться значением смещения (числа вроде <emphasis>0х128/0х1с4</emphasis>, которые указаны справа от имени функции) для точного нахождения команды, в которой возникла ошибка.</p>
     <p>Содержимое регистров точно также полезно, хотя и используется не так часто. Вместе с дизассемблированным кодом функции содержимое регистров может помочь восстановить точную последовательность событий, которая привела к проблеме. Если значение в некотором регистре не соответствует ожидаемому, то это может пролить некоторый свет на корень проблемы. В данном случае можно проверить, какие регистры содержат значение <code>NULL</code> (все разряды нулевые) и определить, какая из переменных функции содержит не то значение. В ситуациях, похожих на данную, скорее всего причина — конкуренция за ресурс (race) и скорее всего между таймером и другой частью сетевого адаптера. Отладка состояний конкуренции за ресурсы — всегда серьезная задача.</p>
    </section>
    <section>
     <title>
      <p>Утилита <code>ksymoops</code></p>
     </title>
     <p>Только что рассмотренное сообщение <code>oops</code> имеет так называемый декодированный вид, потому что адреса памяти транслированы в имена функций, которые им соответствуют. Не декодированный вид предыдущего сообщения выглядит следующим образом.</p>
     <p><code>NIP: C013A7F0 LR: C013A7F0 SP: C0685E00 REGS: c0905d10 TRAP: 0700</code></p>
     <p><code>Not tainted</code></p>
     <p><code>MSR: 00089037 EE: 1 PR: 0 FP: 0 ME 1 IR/DR: 11</code></p>
     <p><code>TASK = c0712530 [0] 'swapper' Last syscall: 120</code></p>
     <p><code>GPR00: C013A7C0 C0295E00 C0231530 0000002F 00000001 C0380CB8 C0291B80 C02D0000</code></p>
     <p><code>GPR08: 000012AD 00000000 00000000 C0292AA0 4020A088 00000000 00000000 00000000</code></p>
     <p><code>GPR16: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000</code></p>
     <p><code>GPR24: 00000000 00000005 00000000 00001032 C3F7C000 00000032 FFFFFFFF C3F7C1C0</code></p>
     <p><code>Call trace: [c013ab30] [c0020744] [c001b864] [c0007e80] [c00061c4]</code></p>
     <p><code>[c0007b84] [c0007bf8] [c0003ae8]</code></p>
     <p>Адреса обратной трассировки должны быть переведены в символические имена функций. Это можно сделать с помощью команды <code>ksymoops</code> при наличии файла <code>System.map</code>, который сгенерирован во время компиляции данного ядра. Если используются загружаемые модули ядра, то необходима также информация о модулях. Утилита <code>ksymoops</code> пытается самостоятельно определить всю необходимую информацию, поэтому обычно ее можно просто вызывать следующим образом.</p>
     <p><code>ksymoops saved_oops.txt</code></p>
     <p>Программа выводит декодированную информацию сообщения <code>oops</code>. Если информация, которая используется по умолчанию, недоступна, или есть необходимость указать альтернативное положение соответствующих информационных файлов, то на такой случай программа принимает различные параметры. Страницы руководства по данной программе, которые необходимо прочитать перед использованием, содержат всю необходимую информацию.</p>
     <p>Программа <code>ksymoops</code> включена в большинство поставок операционной системы Linux.</p>
    </section>
    <section>
     <title>
      <p>Функция <code>kallsyms</code></p>
     </title>
     <p>К счастью, больше нет необходимости использовать программу <code>ksymoops</code>. Это очень полезно, потому что, хотя, у разработчиков обычно нет проблем с ее использованием, пользователи часто указывают неправильный файл <code>System.map</code>, или неправильно декодируют сообщение <code>oops</code>.</p>
     <p>В разрабатываемой серии ядра 2.5 была введено новая возможность <code>kallsyms</code>, которая включается с помощью конфигурационного параметра <code>CONFIG_KALLSYMS</code>. Эта функция включает в исполняемый образ ядра информацию для отображения адресов памяти в соответствующие имена функций ядра, что дает возможность ядру самостоятельно декодировать информацию обратной трассировки. Следовательно, декодирование сообщений oops больше не требует файла <code>System.map</code>, или утилиты <code>kallsyms</code>. Как недостаток такого подхода следует отметить некоторое увеличение объема памяти, используемой ядром, так как таблица перевода адресов памяти в имена функций загружается в постоянно отображаемую память ядра. На такое увеличение объемов используемой памяти стоит пойти, по крайней мере, на этапе разработки ядра.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Конфигурационные параметры отладки ядра</p>
    </title>
    <section>
     <p>Существует несколько конфигурационных параметров, которые помогают в отладке и тестировании кода ядра и которые включаются во время компиляции. Эти параметры доступны в пункте <strong>Kernel hacking</strong> меню редактора конфигурации ядра. Все эти параметры зависят от параметра <code>CONFIG_DEBUG_KERNEL</code>. Для разработки кода ядра следует включать только те параметры, которые необходимы.</p>
     <p>Некоторые из этих параметров достаточно полезны, такие как отладка работы со слябовым распределителем памяти (slab layer debugging), отладка работы с верхней памятью (high memory debugging), отладка работы с отображаемым на память вводом-выводом (I/O mapping debugging), отладка работы со спин-блокировками (spin-lock debugging) и проверка переполнения стека (stack overflow checking). Однако, один из самых полезных параметров — это <emphasis>проверка перехода в состояние ожидания при захваченной спин-блокировке</emphasis> (<emphasis>sleep-inside-spinlock checking</emphasis>), которая на самом деле выполняет значительно больше работы.</p>
    </section>
    <section>
     <title>
      <p>Отладка атомарных операций</p>
     </title>
     <p>Начиная с серии 2.5 в ядре появилась отличная инфраструктура для определения всех типов нарушения атомарности. Вспомните из главы 8, "Введение в синхронизацию выполнения кода ядра", что атомарность означает неделимое выполнение, то есть код выполняется без перерыва до завершения, или не завершается вообще. Код, который удерживает спин-блокировку, или выполняется при запрещенной преемптивности ядра, является атомарным. Во время атомарного выполнения нельзя переходить в состояние ожидания. Ожидание при удерживаемой спин-блокировке — один из вариантов взаимоблокировки.</p>
     <p>Благодаря свойствам преемптивности, ядро имеет глобальный счетчик преемптивности. Ядро может быть настроено так, что, если выполняется переход в состояние ожидания, или даже выполняется код, который потенциально может переходить в состояние ожидания при выполнении атомарной операции, то ядро выводит предупреждающее сообщение и обратную трассировку. Потенциальные ошибки, которые детектируются таким образом, включают вызов функции schedule () при удерживаемой блокировке, выполнение блокирующего выделения памяти при удерживаемой блокировке, или переход в состояние ожидания при удерживаемой ссылке на данные, связанные с процессором. Эта отладочная инфраструктура может обнаружить очень много ошибок и ее очень рекомендуется использовать.</p>
     <p>Следующие конфигурационные параметры позволяют полностью использовать данную возможность.</p>
     <p><code>CONFIG_PREEMPT=y</code></p>
     <p><code>CONFIG_DEBUG_KERNEL=y</code></p>
     <p><code>CONFIG_KALLSYMS=y</code></p>
     <p><code>CONFIG_SPINLOCK_SLEEP=y</code></p>
    </section>
   </section>
   <section>
    <title>
     <p>Генерация ошибок и выдача информации</p>
    </title>
    <p>Существует несколько подпрограмм ядра, которые позволяют легко сигнализировать о наличии дефектов кода, обеспечивать объявления об ошибках и выводить необходимую информацию. Две наиболее часто используемые — это <code>BUG()</code> и <code>BUG_ON()</code>. При вызове эти функции создают ситуацию <code>oops</code>, которая проявляется в выводе обратной трассировки стека ядра и сообщения об ошибке. Каким образом эти вызовы генерируют ситуацию oops зависит от аппаратной платформы. Для большинства аппаратных платформ вызовы <code>BUG()</code> и <code>BUG_ON()</code> определяются как некоторая недопустимая машинная команда, которая приводит к выводу желаемого сообщения <code>oops</code>.</p>
    <p>Обычно эти вызовы используются в качестве объявления о наличие ошибки (assertion), чтобы сигнализировать о ситуации, которая не должна произойти.</p>
    <p><code>if (bad_thing)</code></p>
    <p><code> BUG();</code></p>
    <p>Или даже так.</p>
    <p><code>BUG_ON(bad_thing);</code></p>
    <p>О более критичной ошибке можно сигнализировать с помощью функции <code>panic()</code>. Функция <code>panic()</code> печатает сообщение об ошибке и останавливает ядро. Ясно, что эту функцию следует использовать только в самой плохой ситуации.</p>
    <p><code>if (terrible_thing)</code></p>
    <p><code> panic("foo is %ld!\n", foo);</code></p>
    <p>Иногда необходимо просто вывести на консоль трассировку стека, чтобы облегчить отладку. В этих случаях используется функция <code>dump_stack()</code>. Эта функция отображает на консоль содержимое регистров процессора и обратную трассировку вызовов функций.</p>
    <p><code>if (!debug_check) {</code></p>
    <p><code> printk(KERN_DEBUG "выдать некоторую информацию...\n");</code></p>
    <p><code> dump_stack();</code></p>
    <p><code>}</code></p>
   </section>
   <section>
    <title>
     <p>Магическая клавиша <code>SysRq</code></p>
    </title>
    <p>Использование магической клавиши <code>SysRq</code>, которую можно активизировать с помощью конфигурационного параметра <code>CONFIG_MAGIC_SYSRQ</code> на этапе компиляции, часто позволяет значительно облегчить жизнь. Клавиша <code>SysRq</code> является стандартной на многих клавиатурах. Для аппаратных платформ i386 и PPC ей соответствует комбинация клавиш <code>ALT-PrintScreen</code>. Если указанный конфигурационный параметр активизирован, то специальные комбинации клавиш позволяют взаимодействовать с ядром независимо от того, чем ядро в данный момент нанимается. Это в свою очередь позволяет выполнять некоторые полезные операции даже на неработоспособной системе.</p>
    <p>В дополнение к конфигурационному параметру существует вызов <code>sysctl</code> для включения и выключения этого свойства.</p>
    <p><code>echo 1 &gt; /proc/sys/kernel/sysrq</code></p>
    <p>Список возможных комбинаций клавиш можно получить с консоли путем нажатия комбинации клавиш <code>SysRq-h</code>. Комбинация клавиш <code>SysRq</code>-s выполняет синхронизацию не сохраненных буферов файловых систем на диск, комбинация <code>SysRq-u</code> размонтирует все файловые системы, a <code>SysRq-b</code> — перегружает машину. Последовательное использование этих комбинаций клавиш позволяет более безопасно перегрузить машину, которая зависла, чем простое нажатие кнопки <code>reset</code>.</p>
    <p>Если машина заблокирована очень сильно, то она может не отвечать на магические комбинации клавиш <code>SysRq</code>, или соответствующая операция не будет выполнена. Если же повезет, то эти комбинации клавиш смогут помочь при отладке, а также сохранить данные. В табл. 18.2 приведен список поддерживаемых команд <code>SysRq</code>.</p>
    <empty-line/>
    <p><strong>Таблица 18.2</strong>. Список поддерживаемых команд SysRq</p>
    <table>
     <tr align="left">
      <th align="left" valign="top">Команда</th>
      <th align="left" valign="top">Описание</th>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>SysRq-b</code></td>
      <td align="left" valign="top">Перегрузить машину (reboot)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>SysRq-e</code></td>
      <td align="left" valign="top">Послать сигнал <code>SIGTERM</code> всем процессам, кроме процесса <code>init</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>SysRq-h</code></td>
      <td align="left" valign="top">Отобразить на консоли помощь по использованию комбинаций клавиш <code>SysRq</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>SysRq-i</code></td>
      <td align="left" valign="top">Послать сигнал <code>SIGKILL</code> всем процессам, кроме процесса <code>init</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>SysRq-k</code></td>
      <td align="left" valign="top">Клавиша безопасного доступа: завершить все процессы, связанные с текущей консолью</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>SysRq-l</code></td>
      <td align="left" valign="top">Послать сигнал <code>SIGKILL</code> всем процессам, включая процесс <code>init</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>SysRq-m</code></td>
      <td align="left" valign="top">Отобразить на консоли дамп информации по использованию памяти</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>SysRq-o</code></td>
      <td align="left" valign="top">Завершить работу машины (shutdown)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>SysRq-p</code></td>
      <td align="left" valign="top">Отобразить на консоли дамп регистров памяти</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>SysRq-r</code></td>
      <td align="left" valign="top">Отключить прямой режим работы клавиатуры (raw mode)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>SysRq-s</code></td>
      <td align="left" valign="top">Синхронизировать данные смонтированных файловых систем с дисковыми устройствами</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>SysRq-t</code></td>
      <td align="left" valign="top">Отобразить на консоли дамп информации о заданиях</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>SysRq-u</code></td>
      <td align="left" valign="top">Размонтировать все смонтированные файловые системы</td>
     </tr>
    </table>
    <p>В файле <code>Documentation/sysrq.txt</code>, который находится в каталоге исходных кодов ядра, приводится более полное описание. Реализация поддержки магической комбинации клавиш находится в файле <code>drivers/char/sysrq.с</code>. Магические комбинации клавиш <code>SysRq</code> — жизненно необходимый инструмент, который помогает в отладке и сохранении "гибнущей" системы, так как предоставляет большие возможности для любого пользователя при работе с консолью. Тем не менее необходимо соблюдать осторожность при его использовании на критичных машинах. Если же машина используется для разработок, то польза от этих команд огромная.</p>
   </section>
   <section>
    <title>
     <p>Сага об отладчике ядра</p>
    </title>
    <section>
     <p>Многие разработчики ядра давно высказываются о необходимости встроенного в ядро отладчика. К сожалению, Линус не желает видеть отладчик ядра в своем дереве исходного кода, Он уверен, что использование программ-отладчиков приводит к плохому исправлению ошибок неправильно информированными разработчиками. Никто не может поспорить с его логикой — исправления ошибок, построенные на основании хорошего понимания кода скорее всего будут верными. Тем не менее большинство разработчиков ядра все же нуждаются в официальном отладчике, встроенном в ядро. Поскольку такая возможность навряд ли появится в ближайшее время, то взамен было разработано несколько заплат, которые добавляют поддержку отладчика в стандартном ядре. Не смотря на то, что это внешние и неофициальные заплаты, они являются мощными инструментами с высокой функциональностью. Перед тем, как обращаться к этим решениям, посмотрим, на сколько нам может помочь стандартный отладчик ОС Linux — gdb.</p>
    </section>
    <section>
     <title>
      <p>Использование отладчика gdb</p>
     </title>
     <p>Для того, чтобы мельком заглянуть внутрь работающего ядра можно использовать стандартный отладчик GNU. Запуск отладчика для работы с ядром почти ни чем не отличается от отладки выполняющегося процесса.</p>
     <p><code>gdb vmlinux /proc/kcore</code></p>
     <p>Файл <code>vmlinux</code> — это декомпрессированный исполняемый образ ядра, который хранится в корне каталога исходных кодов, где выполнялась сборка выполняющегося ядра. Сжатые файлы <code>zImage</code>, или <code>bzImage</code> использовать нельзя.</p>
     <p>Опциональный параметр <code>/proc/kcore</code> исполняет роль файла <code>core</code>, чтобы позволить отладчику читать из памяти выполняющегося ядра. Чтобы иметь возможность читать этот файл, необходимо иметь права пользователя root.</p>
     <p>Можно пользоваться практически всеми командами программы gdb для чтения информации. Например, чтобы напечатать значение переменной можно воспользоваться командой.</p>
     <p><code>p global_variable</code></p>
     <p>Для того, чтобы дизассемблировать код функции можно выполнить следующую команду.</p>
     <p><code>disassemble function</code></p>
     <p>Если ядро было скомпилировано с указанием флага -g (необходимо добавить <code>-g</code> к значению переменной <code>CFLAGS</code> в файле <code>Makefile</code> ядра), то отладчик gdb сможет выдавать больше информации. Например, можно выводить дампы структур данных и разыменовывать указатели. При этом также получается ядро значительно большего размера, поэтому для обычной работы не следует компилировать ядро с отладочной информацией.</p>
     <p>К сожалению, на этом заканчиваются возможности использования отладчика gdb. С его помощью никак нельзя изменять данные ядра. Нет возможности пошагово выполнять код ядра, или устанавливать точки остановки (breakpoint). Невозможность изменять структуры данных ядра — это большой недостаток. Хотя очень полезно иметь возможность дизассемблировать код функций, еще более полезной была бы возможность изменять структуры данных.</p>
    </section>
    <section>
     <title>
      <p>Отладчик kgdb</p>
     </title>
     <p>Отладчик kgdb — это заплата ядра, которая позволяет с помощью отладчика gdb отлаживать ядро по линии последовательной передачи. Для этого требуется два компьютера. На первом выполняется ядро с заплатой kgdb. Второй компьютер используется для отладки ядра по линии последовательной передачи (нуль-модемный кабель, соединяющий две машины) с помощью gdb. Благодаря отладчику kgdb полностью доступен весь набор функций gdb: чтение и запись любых переменных, установка точек остановки, установка точек слежения (watch points), пошаговое исполнение и др.. Специальные версии kgdb даже позволяют вызывать функции.</p>
     <p>Установка kgdb и линии последовательной передачи несколько сложная процедура, но если ее выполнить, то отладка ядра значительно упрощается. Заплата ядра также устанавливает большое количество документации в каталог <code>Documentation/</code>, ее следует прочитать.</p>
     <p>Несколько человек выполняют поддержку заплаты kgdb для различных аппаратных платформ и версий ядра. Поиск в Интернет — наилучший способ найти необходимую заплату для заданного ядра.</p>
    </section>
    <section>
     <title>
      <p>Отладчик kdb</p>
     </title>
     <p>Альтернативой kgdb является отладчик kdb. В отличие от kgdb отладчик kdb — не удаленный отладчик. Отладчик kdb — это заплата, которая сильно модифицирует ядро и позволяет выполнять отладку прямо на той же машине, где выполняется ядро. Кроме всего прочего поддерживается возможность изменения переменных, установки точек остановки и пошаговое выполнение. Выполнять отладку просто — необходимо нажать на консоли клавишу <code>break</code>. При выводе сообщения oops переход в отладчик выполняется автоматически. Более подробная документация доступна в каталоге <code>Documentation/kdb</code> после применения заплаты. Заплата kdb доступна в Интернет по адресу <code>http://oss.sgi.com/</code>.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Исследование и тестирование системы</p>
    </title>
    <section>
     <p>По мере того, как вы будете накапливать опыт в отладке ядра, у вас будет появляться все больше маленьких хитростей, которые помогают в исследовании и тестировании ядра для получения ответов на интересующие вопросы. Так как отладка ядра требует больших усилий, то каждый маленький совет, или хитрость может оказаться полезным. Рассмотрим несколько таких хитростей.</p>
    </section>
    <section>
     <title>
      <p>Использование идентификатора UID в качестве условия</p>
     </title>
     <p>Если разрабатываемый код связан с контекстом процесса, то иногда появляется возможность выполнить альтернативную реализацию не "ломая" существующий код. Это важно, если необходимо переписать важный системный вызов и при этом необходима полностью функционирующая система, на которой этот вызов нужно отладить. Например, допустим, что нужно переписать алгоритм работы системного вызова <code>fork()</code>, который бы использовал некоторые новые возможности, которые уже существуют в ядре. Если сразу не получится все сделать так как надо, то будет очень тяжело отлаживать ядро, так как неработающий системный вызов <code>fork()</code> скорее всего приведет к неработоспособности системы. Но как и всегда, есть надежда.</p>
     <p>Часто безопасным будет сохранить старый алгоритм, а новую реализацию выполнить в другом месте. Этого можно достичь используя идентификатор пользователя (UID) в качестве условия того, какой алгоритм использовать.</p>
     <p><code>if (current-&gt;uid != 7777) {</code></p>
     <p><code> /* старый алгоритм ... */</code></p>
     <p><code>} else {</code></p>
     <p><code> /* новый алгоритм ... */</code></p>
     <p><code>}</code></p>
     <p>Все пользователи, кроме того, у которого идентификатор UID равен 7777 будут использовать старый алгоритм. Для тестирования нового алгоритма можно создать нового пользователя с идентификатором 7777. Это позволяет более просто оттестировать критические участки кода, связанные с выполнением процессов.</p>
    </section>
    <section>
     <title>
      <p>Использование условных переменных</p>
     </title>
     <p>Если код, который необходимо протестировать, выполняется не в контексте процесса, или необходим более глобальный метод для контроля новых функций, то можно использовать условные переменные. Этот подход даже более простой, чем использование идентификатора пользователя. Необходимо просто создать глобальную переменную и использовать ее в качестве условия выполнения того, или другого участка кода. Если значение переменной равно нулю, то следует выполнить один участок кода. Если переменная не равна нулю, то выполняется другой участок. Значение переменной может быть установлено с помощью отладчика, или специального экспортируемого интерфейса.</p>
    </section>
    <section>
     <title>
      <p>Использование статистики</p>
     </title>
     <p>Иногда необходимо получить представление о том, насколько часто происходит некоторое событие. Иногда требуется сравнить несколько событий и вычислить характеристики для их сравнения. Это очень легко сделать путем введения статистки и механизма для экспортирования соответствующих параметров.</p>
     <p>Например, допустим, что необходимо выяснить на сколько часто происходит событие <emphasis>foo</emphasis> и событие <emphasis>bar</emphasis>. В файле исходного кода, в идеале там, где соответствующие события возникают, вводится две глобальные переменные.</p>
     <p><code>unsigned long foo_stat = 0;</code></p>
     <p><code>unsigned long bar_stat = 0;</code></p>
     <p>Как только наступает интересующее событие, значение соответствующей переменной увеличивается на единицу. Эти переменные могут быть экспортированы как угодно. Например, можно создать интерфейс к ним через файловую систему /proc, или написать свой системный вызов. Наиболее просто прочитать их значение с помощью отладчика.</p>
     <p>Следует обратить внимание, что такой подход принципиально не безопасен на SMP машине. В идеале необходимо использовать атомарные переменные. Однако, для временной статистики, которая необходима только для отладки, никакой защиты обычно не требуется.</p>
    </section>
    <section>
     <title>
      <p>Ограничение частоты следования событий при отладке</p>
     </title>
     <p>Часто необходимо встроить в код отладочные проверки (с соответствующими функциями вывода информации), чтобы визуально производить мониторинг проблемы. Однако, в ядре некоторые функции вызываются по много раз в секунду. Если в такую функцию будет встроен вызов функции <code>printk()</code>, то системная консоль будет перегружена выводом отладочных сообщений и ее будет невозможно использовать.</p>
     <p>Для предотвращения такой проблемы существует два сравнительно простых приема. Первый — ограничение частоты следования событий — очень полезен, когда необходимо наблюдать, как развивается событие, но частота возникновения события очень большая. Чтобы ограничить поток отладочных сообщений, эти сообщения выводятся только раз в несколько секунд, как это показано в следующем примере.</p>
     <p><code>static unsigned long prev_jiffy = jiffies; /* ограничение частоты */</code></p>
     <empty-line/>
     <p><code>if (time_after(jiffies, prev_jiffy + 2*HZ)) {</code></p>
     <p><code> prev_jiffy = jiffies;</code></p>
     <p><code> printk(KERN_ERR "blah blah blah\n");</code></p>
     <p><code>}</code></p>
     <p>В этом примере отладочные сообщения выводятся не чаще, чем один раз в две секунды. Это предотвращает перегрузку консоли сообщениями и системой можно нормально пользоваться. Частота вывода может быть большей, или меньшей, в зависимости от требований.</p>
     <p>Вторая ситуация имеет место, когда необходимо замечать любые появления события. В отличие от предыдущего примера нет необходимости выполнять мониторинг развития событий. А только получить сообщение о том, что что-то произошло. Вероятно это уведомление необходимо получить один, или два раза. Проблема возникает в том случае, если проверка, которая после того, как сработала один раз, начинает срабатывать постоянно. Решением в данном случае будет не ограничение частоты, а ограничение общего количества повторений.</p>
     <p><code>static unsigned long limit = 0;</code></p>
     <empty-line/>
     <p><code>if (limit &lt; 5) {</code></p>
     <p><code> limit++;</code></p>
     <p><code> printk(KERN_ERR "blah blah blah\n");</code></p>
     <p><code>}</code></p>
     <p>В этом примере количество отладочных сообщений ограничено числом пять. После пяти сообщений условие всегда будет ложно.</p>
     <p>В обоих примерах переменные должны быть статическими (<code>static</code>) и локальными по отношению к той функции, где используются. Это позволяет использовать одинаковые имена переменных в разных функциях.</p>
     <p>Ни один из этих примеров не рассчитан на SMP, или преемптивность, хотя очень легко перейти к атомарным операциям и сделать их безопасными для использования и в этих случаях. Однако, честно говоря, это всего лишь отладочный код, поэтому зачем нужны лишние проблемы?</p>
    </section>
   </section>
   <section>
    <title>
     <p>Нахождение исполняемых образов с изменениями приводящими к ошибкам</p>
    </title>
    <p>Обычно полезно знать, в какой версии исходных кодов ядра появился дефект. Если известно, что дефект появился в версии 2.4.18, но его не было в версии 2.4.17, то сразу появляется ясная картина изменений, которые привели к появлению ошибки. Исправление ошибки сводится к обратным изменениям, или другим исправлениям измененного кода.</p>
    <p>Однако, чаще оказывается неизвестным в какой версии появился дефект. Известно, что проблема проявляется в <emphasis>текущей</emphasis> версии ядра, и кажется, что она <emphasis>всегда</emphasis> была в текущей версии. Хотя это и требует некоторой исследовательской работы, но приложив небольшие усилия можно найти изменения, которые привели к ошибкам. Если известны эти изменения, то до исправления ошибки уже недалеко.</p>
    <p>Для того, чтобы начать, необходима четко повторяемая проблема. Желательно, чтобы проблема проявлялась сразу же после загрузки системы. Далее необходимо гарантированно работающее ядро. Вероятно, это ядро уже известно. Например, может оказаться, что пару месяцев назад ядро работало нормально, поэтому стоит взять ядро того времени. Если это не помогает, то можно воспользоваться еще более старой версией. Такой поиск ядра без дефекта должен быть не сложным, если, конечно, дефект не существовал всегда.</p>
    <p>Далее, необходимо ядро, в котором гарантированно есть дефект. Для облегчения поиска следует воспользоваться наиболее ранней версией ядра, в которой есть дефект. После этого начинается поиск исполняемого образа, в котором появилась ошибка. Рассмотрим пример. Допустим, что последнее ядро, в котором не было ошибки — 2.4.11, а последнее с ошибкой — 2.4.20. Начинаем с версии ядра, которая находится посредине — 2.4.15. Проверяем версию 2.4.15 на наличие ошибки. Если версия 2.4.15 работает, значит ошибка появилась в более поздней версии. Следует попробовать версию между 2.4.15 и 2.4.20, скажем версию 2.4.17. С другой стороны, если версия 2.4.15 не работает, то тогда ясно, что ошибка появилась в более ранней версии, и следует попробовать версию, скажем 2.4.13. И так далее.</p>
    <p>В конце концов проблема сужается до двух ядер — одно с дефектом, а другое — без. В таком случае есть ясная картина изменений, которые привели к проблеме.</p>
    <p>Такой подход избавляет от необходимости проверять ядра всех версий!</p>
   </section>
   <section>
    <title>
     <p>Если ничто не помогает — обратитесь к сообществу</p>
    </title>
    <p>Возможно, вы уже испробовали все, что знали. Вы просидели за клавиатурой несчетное количество часов, и даже дней, а решение все еще не найдено. Если проблема в основном ядре Linux, то всегда можно обратиться за помощью к людям из сообщества разработчиков ядра.</p>
    <p>Короткое, но достаточно детальное описание проблемы вместе с вашими находками, посланное в список рассылки разработчиков ядра по электронной почте, может помочь отыскать решение. В конце концов, дефектов никто не любит.</p>
    <p>Глава 20, "Заплаты, разработка и сообщество" специально посвящена сообществу разработчиков ядра и его основному форуму — списку рассылки разработчиков ядра Linux (Linux Kernel Mail List, LKML).</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 19</p>
    <p>Переносимость</p>
   </title>
   <section>
    <p>Linux — переносимая операционная система, которая поддерживает большое количество различных компьютерных аппаратных платформ. <emphasis>Переносимость</emphasis>— это свойство, которое указывает, насколько легко можно перенести код, который выполнялся на одной аппаратной платформе, для выполнения на другой аппаратной платформе (если вообще это возможно). Известно, что ОС Linux является переносимой операционной системой, поскольку ее уже <emphasis>перенесли</emphasis> (портировали) на большое количество различных аппаратных платформ. Тем не менее переносимость не возникает сама по себе, для выполнения она требует большого количества проектных решений. Сегодня процесс перенесения ОС Linux на другую аппаратную платформу достаточно прост (в относительном смысле, конечно). В этой главе рассказывается о том, как писать переносимый код, — вопрос, о котором всегда необходимо помнить при написании нового кода ядра или драйверов устройств.</p>
    <p>Некоторые операционные системы специально разрабатываются с учетом требований переносимости как главного свойства. По возможности минимальное количество кода выполняется зависимым от аппаратуры. Разработка на языке ассемблера сводится к минимуму, а интерфейсы и свойства выполняются принципиально общими и абстрактными, чтобы иметь возможность работать на различных аппаратных платформах. Очевидным преимуществом в этом случае является легкость поддержки новой аппаратной платформы. В некоторых случаях простые операционные системы с высокой переносимостью могут быть нормированы на новую аппаратную платформу только путем изменения нескольких сотен строк специфичного кода. Недостаток такого подхода состоит в том, что не используются специфические свойства аппаратной платформы и код не может быть в ручную оптимизирован под конкретную машину. Переносимость ставится выше оптимальности. Примером операционных систем с высокой переносимостью могут быть Minix, OpenBSD и многие исследовательские системы.</p>
    <p>С противоположной стороны можно поставить операционные системы, в которых оптимизация кода выполняется в ущерб переносимости. Код, по возможности, пишется на языке ассемблера или специфические свойства аппаратных платформ используются каким-либо другим образом. Свойства ядра разрабатываются на основании свойств аппаратной платформы. В этом случае перенос операционной системы на другую аппаратную платформу сводится к написанию ядра с нуля. Оптимальность выполняется в ущерб переносимости. Примером таких систем могут быть DOS и Windows 9x. Сегодня таким системам нет необходимости иметь более оптимальный код, чем переносимым операционным системам, однако они предоставляют возможность в максимальной степени использовать ручную оптимизацию кода.</p>
    <p>Операционная система Linux в плане переносимости занимает промежуточное положение. Насколько это целесообразно из практических соображений, интерфейсы и код сохраняются независимыми от аппаратной платформы и пишутся на языке С. Однако функции ядра, которые критичны к производительности, выполняются зависимыми от аппаратной платформы. Например, низкоуровневый код и код, который должен выполняться очень быстро, разрабатывается зависимым от аппаратной платформы и обычно на языке ассемблера. Такой подход позволяет сохранить переносимость ОС Linux и при этом воспользоваться оптимизациями.</p>
    <p>В случаях, когда переносимость становится помехой производительности, производительность обычно всегда побеждает. В остальных случая сохраняется переносимость кода.</p>
    <p>Обычно экспортируемые интерфейсы ядра независимы от аппаратной платформы. Если различные части одной подпрограммы должны быть разными для разных аппаратных платформ (из соображений производительности или по необходимости), то код выполняется в виде нескольких функций, которые вызываются, в нужных местах. Для каждой поддерживаемой аппаратной платформы реализуются свои функции, которые затем компонуются в общий исполняемый образ ядра.</p>
    <p>Хороший пример — планировщик. Большая часть планировщика написана независимым от аппаратной платформы образом на языке С. Реализация находится в файле <code>kernel/sched.c</code>. Некоторые из функций планировщика, такие как переключение состояния процессора или переключение адресного пространства, очень сильно зависят от аппаратной платформы. Следовательно, функция <code>context_switch()</code>, которая переключает выполнение от одного процесса к другому и написана на языке С, вызывает функции <code>switch_to()</code> и <code>switch_mm()</code> для переключения состояния процессора и переключения адресного пространства соответственно.</p>
    <p>Код функций <code>switch_to()</code> и <code>switch_mm()</code> выполнен отдельно для каждой аппаратной платформы, которую поддерживает операционная система Linux. Когда операционная система Linux портируется на новую аппаратную платформу, то для новой аппаратной платформы просто необходимо реализовать эти функции.</p>
    <p>Файлы, которые относятся к определенной аппаратной платформе, находятся в каталоге <code>arch/&lt;аппаратная платформа&gt;/</code> и <code>include/asm-&lt;аппаратная платформа&gt;/</code>, где <code>&lt;аппаратная платформа&gt;</code> — это короткое имя, которое представляет аппаратную платформу, поддерживаемую ядром Linux. Например, аппаратной платформе Intel x86 присвоено имя <code>i386</code>. Для этого типа машин файлы находятся в каталогах <code>arch/i386</code> и <code>include/asm-i386</code>. Ядра серии 2.6 поддерживают следующие аппаратные платформы: <code>alpha</code>, <code>arm</code>, <code>cris</code>, <code>h8300</code>, <code>i38</code>, <code>ia64</code>, <code>m68k</code>, <code>m68knommu</code>, <code>mips</code>, <code>mips64</code>, <code>parisc</code>, <code>ppc</code>, <code>ppc64</code>, <code>s390</code>, <code>sh</code>, <code>spare</code>, <code>sparc64</code>, <code>um</code>, <code>v850</code> и <code>x86-64</code>. Более полное описание приведено в табл. 19.1.</p>
   </section>
   <section>
    <title>
     <p>История переносимости Linux</p>
    </title>
    <p>Когда Линус Торвальдс впервые выпустил операционную систему Linux в ничего не подозревающий мир, эта ОС работала только на аппаратной платформе Intel i386. Хотя данная операционная система и была достаточно хорошо обобщена и хорошо написана, переносимость для нее не была основным требованием. Однажды Линус даже говорил, что операционная система Linux не будет работать ни на какой аппаратной платформе, кроме i386! Тем не менее в 1993 году началась работа по портированию ОС Linux на машины Digital Alpha. Аппаратная платформа Digital Alpha была повой высокопроизводительной RISC-платформой с поддержкой 64-разрядной адресации памяти. Она очень сильно отличалась от аппаратной платформы i386, о которой говорил Линус. Тем не менее, первоначальный перенос на аппаратную платформу Alpha занял около года, и аппаратная платформа Alpha стала первой официально поддерживаемой аппаратной платформой после x86. Это портирование было, наверное, самым сложным, потому что — первым. Вместо простого переписывания ядра для поддержки новой аппаратной платформы, части ядра были переписаны с целью введения переносимости<a l:href="#n91" type="note">[91]</a>. Хотя это и привело к выполнению большого количества работы, в результате получился более ясный для понимания код, и в будущем перенос стало выполнять более просто.</p>
    <p>Первые выпуски ОС Linux поддерживали только платформу i386, а серия ядер 1.2 уже поддерживала Digital Alpha, Intel x86, MIPS и SPARC, хотя такая поддержка была отчасти экспериментальной.</p>
    <p>С выпуском ядра версии 2.0 была добавлена официальная поддержка платформ Motorola 68k и PowerPC. В дополнение к этому поддержка всех аппаратных платформ, которые ранее поддерживались ядрами серии 1.2, стала официальной и стабильной.</p>
    <p>В серию ядер 2.2 была введена поддержка еще большего количества аппаратных платформ: добавлены ARM, IBM S/390 и UltraSPARC. Через несколько лет в серии ядер 2.4 количество поддерживаемых аппаратных платформ было почти удвоено, и их количество стало равным 15. Была добавлена поддержка платформ CRIS, IA-64, 64-разрядная MIPS, HP PA-RISC, 64-разрядная IBM S/390 и Hitachi SH.</p>
    <p>В серии 2.6 количество поддерживаемых аппаратных платформ было доведено до 20 за счет добавления платформ Motorola 68k бел устройства MMU, H8/300, IBM POWER, v850, x86-64 и версии ядра, которое работает на виртуальной машине под ОС Linux - Usermode Linux. Поддержка 64-разрядной s390 была объединена с 32- разрядной платформой s390, чтобы избежать дублирования.</p>
    <p>Необходимо заметить, что каждая из этих аппаратных платформ поддерживает различные типы машин и микросхем. Некоторые из поддерживаемых аппаратных платформ, такие как ARM и PowerPC, поддерживают очень большое количество типов микросхем и машин. Поэтому, хотя ОС Linux и работает на 20 аппаратных платформах, она работает на гораздо большем количестве типов компьютеров!</p>
   </section>
   <section>
    <title>
     <p>Размер машинного слова и типы данных</p>
    </title>
    <section>
     <p>Машинное слово (word) — это количество данных, которые процессор может обработать за одну операцию. Здесь можно применить аналогию документа, состоящего из <emphasis>символов</emphasis> (<emphasis>character</emphasis>, 8 бит) и страниц (много слов). Слово— это некоторое количество битов, как правило 16, 32 или 64. Когда говорят о "n-битовой" машине, то чаще всего имеют в виду размер машинного <emphasis>слова</emphasis>. Например, когда говорят, что процессор Intel Pentium — это 32-разрядный процессор, то обычно имеют в виду размер машинного слова, равный 32 бит, или 4 байт.</p>
     <p>Размер процессорных регистров общего назначения равен размеру машинного слова этого процессора. Обычно разрядность остальных компонентов этой же аппаратной платформы в точности равна размеру машинного слова. Кроме того, по крайней мере для аппаратных платформ, которые поддерживаются ОС Linux, размер адресного пространства соответствует размеру машинного слова<a l:href="#n92" type="note">[92]</a>. Следовательно, размер указателя равен размеру машинного слова. В дополнение к этому, размер типа <code>long</code> языка С также равен размеру машинного слова. Например, для аппаратной платформы Alpha размер машинного слова равен 64 бит. Следовательно, регистры, указатели и тип <code>long</code> имеют размер 64 бит. Тип <code>int</code> для этой платформы имеет размер 32 бит. Машины платформы Alpha могут обработать 64 бит — одно слово с помощью одной операции.</p>
     <cite>
      <subtitle>Слова, двойные слова и путаница</subtitle>
      <p>Для некоторых операционных систем и процессоров стандартную порцию данных не называют машинным <emphasis>словом</emphasis>. Вместо этого, <emphasis>словом</emphasis> называется некоторая фиксированная порция данных, название которой выбрано случайным образом или имеет исторические корни. Например, в некоторых системах данные могут разбиваться на байты (byte — 8 бит), слова (word — 16 бит), двойные слова (double word — 32 бит) и четверные слова (quad word — 64 бит), несмотря на то что на самом деле система является 32-разрядной. В этой книге и вообще в контексте операционной системы Linux под машинным словом понимают стандартную порцию данных процессора, как обсуждалось ранее.</p>
     </cite>
     <p>Для каждой аппаратной платформы, поддерживаемой операционной системой Linux, в файле <code>&lt;asm/types.h&gt;</code> определяется константа <code>BITTS_PER_LONG</code>, которая равна размеру типа long языка С и совпадает с размером машинного слова системы. Полный список всех поддерживаемых аппаратных платформ и их размеры машинного слова приведены в табл. 19.1.</p>
     <empty-line/>
     <p><strong>Таблица 19.1</strong>. Поддерживаемые аппаратные платформы</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Аппаратная платформа</th>
       <th align="left" valign="top">Описание</th>
       <th align="left" valign="top">Размер машинного слова</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top">alpha</td>
       <td align="left" valign="top">Digital Alpha</td>
       <td align="left" valign="top">64 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">arm</td>
       <td align="left" valign="top">ARM и StrongARM</td>
       <td align="left" valign="top">32 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">cris</td>
       <td align="left" valign="top">CRIS</td>
       <td align="left" valign="top">32 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">h8300</td>
       <td align="left" valign="top">H8/300</td>
       <td align="left" valign="top">32 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">I386</td>
       <td align="left" valign="top">Intel x86</td>
       <td align="left" valign="top">32 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">ia64</td>
       <td align="left" valign="top">IA-64</td>
       <td align="left" valign="top">64 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">m68k</td>
       <td align="left" valign="top">Motorola 68k</td>
       <td align="left" valign="top">32 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">m86knommu</td>
       <td align="left" valign="top">m68k без устройства MMU</td>
       <td align="left" valign="top">32 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">mips</td>
       <td align="left" valign="top">MIPS</td>
       <td align="left" valign="top">32 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">mips64</td>
       <td align="left" valign="top">64-разрядная MIPS</td>
       <td align="left" valign="top">64 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">parisc</td>
       <td align="left" valign="top">HP PA-RISC</td>
       <td align="left" valign="top">32 бит, или 64 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">ppc</td>
       <td align="left" valign="top">PowerPC</td>
       <td align="left" valign="top">32 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">ppc64</td>
       <td align="left" valign="top">POWER</td>
       <td align="left" valign="top">64 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">s390</td>
       <td align="left" valign="top">IBM S/390</td>
       <td align="left" valign="top">32 бит, или 64 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">sh</td>
       <td align="left" valign="top">Hitachi SH</td>
       <td align="left" valign="top">32 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">sparс</td>
       <td align="left" valign="top">SPARC</td>
       <td align="left" valign="top">32 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">sparc64</td>
       <td align="left" valign="top">UltraSPARC</td>
       <td align="left" valign="top">64 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">um</td>
       <td align="left" valign="top">Usermode Linux</td>
       <td align="left" valign="top">32 бит, или 64 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">v850</td>
       <td align="left" valign="top">v850</td>
       <td align="left" valign="top">32 бит</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">x86_64</td>
       <td align="left" valign="top">X86-64</td>
       <td align="left" valign="top">64 бит</td>
      </tr>
     </table>
     <p>Стандарт языка С явно указывает, что размер памяти, которую занимают переменные стандартных типов данных, зависит от аппаратной реализации<a l:href="#n93" type="note">[93]</a>, при этом также определяется минимально возможный размер типа. Неопределенность размеров стандартных типов языка С для различных аппаратных платформ имеет свои положительные и отрицательные стороны. К плюсам можно отнести то, что для стандартных типов языка С можно пользоваться преимуществами, связанными с размером машинного слова, а также отсутствие необходимости явного указания размера. Для ОС Linux размер типа <code>long</code> гарантированно равен размеру машинного слова. Это не совсем соответствует стандарту ANSI С, однако является стандартной практикой в ОС Linux. Как недостаток можно отметить, что при разработке кода нельзя рассчитывать на то, что данные определенного типа занимают в памяти определенный размер. Более того, нельзя гарантировать, что переменные типа <code>int</code> занимают столько же памяти, сколько и переменные типа <code>long</code><a l:href="#n94" type="note">[94]</a>.</p>
     <p>Ситуация еще более запутывается тем, что одни и те же типы данных в пространстве пользователя и в пространстве ядра не обязательно должны соответствовать друг другу. Аппаратная платформа sparc64 предоставляет 32-разрядное пространство пользователя, а поэтому указатели, типы <code>int</code> и <code>long</code> имеют размер 32 бит. Однако в пространстве ядра для аппаратной платформы размер типа int равен 32 бит, а размер указателей и типа <code>long</code> равен 64 бит. Тем не менее такая ситуация не является обычной.</p>
     <p>Всегда необходимо помнить о следующем.</p>
     <p>• Как и требует стандарт языка С, размер типа <code>char</code> всегда равен 8 бит (1 байт),</p>
     <p>• Нет никакой гарантии, что размер типа <code>int</code> для всех поддерживаемых аппаратных платформ будет равен 32 бит, хотя сейчас для всех платформ он равен именно этому числу.</p>
     <p>• То же касается и типа <code>short</code>, который для всех поддерживаемых аппаратных платформ сейчас равен 16 бит.</p>
     <p>• Никогда нельзя надеяться, что тип <code>long</code> или указатель имеет некоторый заданный размер. Этот размер для поддерживаемых аппаратных платформ может быть равен 32, или 64 бит.</p>
     <p>• Так как размер типа <code>long</code> разный для различных аппаратных платформ, никогда нельзя предполагать, что <code>sizeof(int) == sizeof(long)</code>.</p>
     <p>• Точно так же нельзя предполагать, что размер типа <code>int</code> и размер указателя совпадают.</p>
    </section>
    <section>
     <title>
      <p>Скрытые типы данных</p>
     </title>
     <p>Скрытые (opaque) типы данных — это те типы, для которых не раскрывается их внутренняя структура, или формат. Они похожи на <emphasis>черный ящик</emphasis>, насколько это можно реализовать в языке программирования С. В этом языке программирования нет какой-либо особенной поддержки для этих типов. Вместо этого, разработчики определяют новый тип данных через оператор <code>typedef</code>, называют его скрытым и надеются на то, что никто не будет преобразовывать этот тип в стандартный тип данных языка С. Любые использования данных этих типов возможны только через специальные интерфейсы, которые также создаются разработчиком. Примером может быть тип данных <code>pid_t</code>, в котором хранится информация об идентификаторе процесса. Размер этого типа данных не раскрывается, хотя каждый может смошенничать, использовать размер по максимуму и работать с этим типом, как с типом int. Если нигде явно не используется размер скрытого типа данных, то размер этого типа может быть изменен, и это не вызовет никаких проблем. На самом деле так уже однажды случилось: в старых Unix-подобных операционных системах тип <code>pid_t</code> был определен как <code>short</code>.</p>
     <p>Еще один пример скрытого типа данных — это тип <code>atomic_t</code>. Как уже обсуждалось в главе 9, "Средства синхронизации в ядре", этот тип содержит данные целочисленного типа, с которыми можно выполнять атомарные операции. Хотя этот тип и соответствует типу int, использование скрытого типа данных позволяет гарантировать, что данные этого типа будут использоваться только в специальных функциях, которые выполняют атомарные операции. Скрытые типы позволяют скрыть размер типа данных, который не всегда равен полным 32 разрядам, как в случае платформы SPARC.</p>
     <p>Другие примеры скрытых типов данных в ядре — это <code>dev_t</code>, <code>gid_t</code> и <code>uid_t</code>. При работе со скрытыми типами данных необходимо помнить о следующем.</p>
     <p>• Нельзя предполагать, что данные скрытого типа имеют некоторый определенный размер в памяти.</p>
     <p>• Нельзя преобразовывать скрытый тип обратно в стандартный тип данных.</p>
     <p>Разрабатывать код необходимо с учетом того, что размер и внутреннее представление скрытого типа данных могут изменяться.</p>
    </section>
    <section>
     <title>
      <p>Специальные типы данных</p>
     </title>
     <p>Некоторые данные в ядре, кроме того, что представляются с помощью скрытых типов, требуют еще и специальных типов данных. Два примера — счетчик импульсов системного таймера <code>jiffies</code> и параметр <code>flags</code>, используемый для обработки прерываний. Для хранения этих данных всегда должен использоваться тип <code>unsigned long</code>.</p>
     <p>При хранении и использовании специфических данных всегда необходимо обращать особенное внимание на тот тип данных, который представляет эти данные, и использовать именно его. Часто встречающейся ошибкой является использование другого типа, например типа <code>unsigned int</code>. Хотя для 32-разрядных аппаратных платформ это не приведет к проблемам, на 64-разрядных системах возникнут проблемы.</p>
    </section>
    <section>
     <title>
      <p>Типы с явным указанием размера</p>
     </title>
     <p>Часто при программировании необходимы типы данных заданного размера. Обычно это необходимо для удовлетворения некоторых внешних требований, связанных с аппаратным обеспечением, сетью или бинарной совместимостью. Например, звуковой адаптер может иметь 32-разрядный регистр, пакет сетевого протокола — 16-разрядное поле данных, а исполняемый файл — 8 битовый идентификатор cookie. В этих случаях тип, который представляет данные, должен иметь <emphasis>точно</emphasis> заданный размер.</p>
     <p>В ядре типы данных явно заданного размера определены в файле <code>&lt;asm/types.h&gt;</code>, который включается из файла <code>&lt;linux/types.h&gt;</code>. В табл. 19.2 приведен полный список таких типов данных.</p>
     <empty-line/>
     <p><strong>Таблица 19.2</strong>. Типы данных явно заданного размера</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Тип</th>
       <th align="left" valign="top">Описание</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>s8</code></td>
       <td align="left" valign="top">байт со знаком</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>u8</code></td>
       <td align="left" valign="top">байт без знака</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>s16</code></td>
       <td align="left" valign="top">16-разрядное целое число со знаком</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>u16</code></td>
       <td align="left" valign="top">16-разрядное целое число без знака</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>s32</code></td>
       <td align="left" valign="top">32-разрядное целое число со знаком</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>u32</code></td>
       <td align="left" valign="top">32-разрядное целое число без знака</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>s64</code></td>
       <td align="left" valign="top">64-разрядное целое число со знаком</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>u64</code></td>
       <td align="left" valign="top">64-разрядное целое число без знака</td>
      </tr>
     </table>
     <p>Варианты со знаком используются редко.</p>
     <p>Эти типы данных, с явно заданным размером, просто определены с помощью оператора <code>typedef</code> через стандартные типы данных языка С. Для 64-разрядной машины они могут быть определены следующим образом.</p>
     <p><code>typedef signed char s8;</code></p>
     <p><code>typedef unsigned char u8;</code></p>
     <p><code>typedef signed short s16;</code></p>
     <p><code>typedef unsigned short u16;</code></p>
     <p><code>typedef signed int s32;</code></p>
     <p><code>typedef unsigned int u32;</code></p>
     <p><code>typedef signed long s64;</code></p>
     <p><code>typedef unsigned long u64;</code></p>
     <p>Для 32-разрядной машины их можно определить, как показано ниже.</p>
     <p><code>typedef signed char s8;</code></p>
     <p><code>typedef unsigned char u8;</code></p>
     <p><code>typedef signed short s16;</code></p>
     <p><code>typedef unsigned short u16;</code></p>
     <p><code>typedef signed int s32;</code></p>
     <p><code>typedef unsigned int u32;</code></p>
     <p><code>typedef signed long long s64;</code></p>
     <p><code>typedef unsigned long long u64;</code></p>
    </section>
    <section>
     <title>
      <p>Знак типа данных <code>char</code></p>
     </title>
     <p>В стандарте языка С сказано, что тип данных <code>char</code> может быть со знаком или без знака. Ответственность за определение того, какой вариант типа данных <code>char</code> использовать по умолчанию, лежит на компиляторе, препроцессоре или на обоих.</p>
     <p>Для большинства аппаратных платформ тип <code>char</code> является знаковым, а диапазон значений данных этого типа от -128 до 127. Для небольшого количества аппаратных платформ, таких как ARM, тип <code>char</code> по умолчанию без знака, а возможные значения данных этого типа лежат в диапазоне от 0 до 255.</p>
     <p>Например, для систем, на которых тип <code>char</code> без знака, выполнение следующего кода приведет к записи в переменную <code>i</code> числа 255 вместо -1.</p>
     <p><code>char i = -1;</code></p>
     <p>На других машинах, где тип <code>char</code> является знаковым, этот код выполнится правильно и в переменную i запишется значение -1. Если действительно нужно, чтобы в любом случае было записано значение -1, то предыдущий код должен выглядеть следующим образом.</p>
     <p><code>signed char i = -1;</code></p>
     <p>Если в вашем коде используется тип данных <code>char</code>, то следует помнить, что этот тип может на самом деле быть как <code>signed char</code>, так и <code>unsigned char</code>. Если необходим строго определенный вариант, то это нужно явно декларировать.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Выравнивание данных</p>
    </title>
    <section>
     <p>Выравнивание (alignment) соответствует размещению порции данных в памяти. Говорят, что переменная имеет <emphasis>естественное выравнивание</emphasis> (<emphasis>naturally aligned</emphasis>), если она находится в памяти по адресу, значение которого кратно размеру этой переменной. Например, переменная 32-разрядного типа данных имеет естественное выравнивание, если она находится в памяти по адресу, кратному 4 байт (т.е. два младших бита адреса равны нулю). Таким образом, структура данные размером 2<sup>n</sup> байт должна храниться в памяти по адресу, младшие n битов которого равны нулю.</p>
     <p>Для некоторых аппаратных платформ существуют строгие требования относительно выравнивания данных. На некоторых системах, обычно RISC, загрузка неправильно выровненных данных приводит к генерации системного прерывания (trap), ошибки, которую можно обработать. На других системах с неестественно выравниваемыми данными можно работать, но это приводит к уменьшению производительности. При написании переносимого кода необходимо предотвращать проблемы, связанные с выравниванием, а данные всех типов должны иметь естественное выравнивание.</p>
    </section>
    <section>
     <title>
      <p>Как избежать проблем с выравниванием</p>
     </title>
     <p>Компилятор обычно предотвращает проблемы, связанные с выравниванием, путем естественного выравнивания всех типов данных. На самом деле, разработчики ядра обычно не должны заниматься проблемами, связанными с выравниванием, об этом должны заботиться разработчики компилятора gcc. Однако такие проблемы все же могут возникать, когда разработчику приходится выполнять операции с указателями и осуществлять доступ к данным, не учитывая того, как компилятор выполняет операции доступа к данным.</p>
     <p>Доступ к адресу памяти, для которого выполнено выравнивание, через преобразованный указатель на тип данных большего размера может привести к проблемам выравнивания (для разных аппаратных платформ это может проявляться по-разному). Следующий код может привести к указанной проблеме.</p>
     <p><code>char dog[10];</code></p>
     <p><code>char *p = &amp;dog[1];</code></p>
     <p><code>unsigned long l = *(unsigned long*)p;</code></p>
     <p>В этом примере указатель на данные типа <code>unsigned char</code> используется, как указатель на тип <code>unsigned long</code>, что может привести к тому, что 32-разрядное значение типа <code>unsigned long</code> будет считываться из памяти по адресу, не кратному четырем.</p>
     <p>Если вы думаете: "<emphasis>Зачем мне это может быть нужно?</emphasis>", то вы, скорее всего, правы. Тем не менее, если мы такое сделали только что, то такое можно сделать и кто-нибудь еще, поэтому необходимо быть внимательными. Примеры, которые встречаются в реальной жизни, не обязательно будут так же очевидны.</p>
    </section>
    <section>
     <title>
      <p>Выравнивание нестандартных типов данных</p>
     </title>
     <p>Как уже указывалось, при выравнивании адрес стандартного типа данных должен быть кратным размеру этого типа. Нестандартные (сложные) типы данных подчиняются следующим правилам выравнивания.</p>
     <p>• Выравнивание массива выполняется так же, как и выравнивание типа данных первого элемента (все остальные элементы будут корректно выровнены автоматически).</p>
     <p>• Выравнивание объединения (union) соответствует выравниванию самого большого, по размеру, типа данных из тех, которые включены в объединение.</p>
     <p>• Выравнивание структуры соответствует выравниванию самого большого, по размеру, типа данных среди типов всех полей структуры.</p>
     <p>В структурах также могут использоваться различные способы заполнения (padding).</p>
    </section>
    <section>
     <title>
      <p>Заполнение структур</p>
     </title>
     <p>Структуры заполняются таким образом, чтобы каждый ее элемент имел естественное выравнивание. Например, рассмотрим следующую структуру данных на 32- разрядной машине.</p>
     <p><code>struct animal_struct {</code></p>
     <p><code> char           dog; /* 1 байт */</code></p>
     <p><code> unsigned long  cat; /* 4 байт */</code></p>
     <p><code> unsigned short pig; /* 2 байт */</code></p>
     <p><code> char           fox; /* 1 байт */</code></p>
     <p><code>};</code></p>
     <p>Эта структура данных в памяти выглядит не так, что связано с необходимостью естественного выравнивания. В памяти компилятор создает структуру данных, которая похожа на следующую.</p>
     <p><code>struct animal_struct {</code></p>
     <p><code> char dog;           /* 1 байт */</code></p>
     <p><code> u8 __pad0[3];       /* 3 байт */</code></p>
     <p><code> unsigned long cat;  /* 4 байт */</code></p>
     <p><code> unsigned short pig; /* 2 байт */</code></p>
     <p><code> char fox;           /* 1 байт */</code></p>
     <p><code> u8 __pad1;          /* 1 байт */</code></p>
     <p><code>};</code></p>
     <p>Переменные заполнения вводятся для того, чтобы обеспечить естественное выравнивание всех элементов структуры. Первая переменная заполнения вводит дополнительные затраты памяти для того, чтобы разместить поле <code>cat</code> на границе 4-байтового адреса. Вторая переменная используется для выравнивания размера самой структуры. Дополнительный байт гарантирует, что размер структуры будет кратен четырем байтам и что каждый элемент массива таких структур будет иметь естественное выравнивание.</p>
     <p>Следует обратить внимание, что выражение <code>sizeof (foo_struct)</code> равно значению 12 для <emphasis>любого</emphasis> экземпляра этой структуры на большинстве 32-разрядных аппаратных платформ. Компилятор языка С автоматически добавляет элементы заполнения, чтобы гарантировать необходимое выравнивание.</p>
     <p>Часто имеется возможность переставить поля структуры так, чтобы избежать необходимости заполнения. Это позволяет получить правильно выровненные данные без введения дополнительных элементов заполнения и, соответственно, структуру меньшего размера.</p>
     <p><code>struct animal struct {</code></p>
     <p><code> unsigned long  cat; /* 4 байта */</code></p>
     <p><code> unsigned short pig; /* 2 байта */</code></p>
     <p><code> char           dog; /* 1 байт */</code></p>
     <p><code> char           fox; /* 1 байт */</code></p>
     <p><code>};</code></p>
     <p>Эта структура данных имеет размер 8 байт. Однако не всегда существует возможность перестановки элементов структуры местами и изменения определения структуры. Например, если структура поставляется как часть стандарта, или уже используется в существующем коде, то порядок следования полей менять нельзя. Иногда, по некоторым причинам, может потребоваться специальный порядок следования полей структуры, например специальное выравнивание переменных для оптимизации попадания в кэш. Заметим, что, согласно стандарту ANSI С, компилятор никогда не должен менять порядок следования полей в структурах<a l:href="#n95" type="note">[95]</a> данных — этим правом обладает только программист.</p>
     <p id="___temp_view_cursor_for_clear_format__3">Разработчики ядра должны учитывать особенности заполнения при обмене структурами данных: передача структур по сети или непосредственное сохранение на диск, потому что необходимое заполнение может быть разным для различных аппаратных платформ. Это одна из причин, по которой в языке программирования С нет оператора сравнения структур. Память, которая используется для заполнения структур данных, может содержать случайную информацию, что делает невозможным побайтовое сравнение структур. Разработчики языка, программирования С правильно сделали, что оставили решение задачи сравнения структур на усмотрение программиста, который может создавать свои функции сравнения в каждом конкретном случае, чтобы использовать особенности построения конкретных структур.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Порядок следования байтов</p>
    </title>
    <section>
     <p><emphasis>Порядок следования байтов</emphasis> (<emphasis>byte ordering</emphasis>) — это порядок, согласно которому байты расположены в машинном слове. Для разных процессоров может использоваться один из двух типов нумерации байтов в машинном слове: наименее значимый (самый младший) байт является либо самым первым (самым левым, left-most), либо самым последним (самым правым, right-most) в слове. Порядок байтов называется <emphasis>обратным</emphasis> (<emphasis>big-endian</emphasis>), если наиболее значимый (самый старший) байт хранится первым, а за ним идут байты в порядке убывания значимости. Порядок байтов называется <emphasis>прямым</emphasis> (<emphasis>little-endian</emphasis>), если наименее значимый (самый младший) байт хранится первым, а за ним следуют байты в порядке возрастания значимости.</p>
     <p>Даже не пытайтесь основываться на каких-либо предположениях о порядке следования байтов при написании кода ядра (конечно, если код не предназначен для какой-либо конкретной аппаратной платформы). Операционная система Linux поддерживает аппаратные платформы с обоими порядками байтов, включая и те машины, на которых используемый порядок байтов можно сконфигурировать на этапе загрузки системы, а общий код должен быть совместим с любым порядком байтов.</p>
     <p>На рис. 19.1 показан пример обратного порядка следования байтов, а на рис. 19.2 — прямого порядка следования байтов.</p>
     <image l:href="#img_25.jpeg"/>
     <p><strong>Рис. 19.1</strong>. Обратный (big-endian) порядок следования байтов</p>
     <image l:href="#img_26.jpeg"/>
     <p><strong>Рис. 19.2</strong>. Прямей (little-endian) порядок следования байтов</p>
     <p>Аппаратная платформа i386 использует прямой (little-endian) порядок байтов. Большинство других аппаратных платформ обычно использует обратный (big-endian) порядок.</p>
     <p>Рассмотрим, что эти типы кодирования обозначают на практике и как выглядит двоичное представление числа 1027, которое хранится в виде четырехбайтового целочисленного типа данных.</p>
     <p><code>00000000 00000000 00000100 00000011</code></p>
     <p>Внутренние представления этого числа в памяти при использовании прямого и обратного порядка байтов отличаются, как это показано в табл. 19.3.</p>
     <empty-line/>
     <p><strong>Таблица 19.3</strong>. Расположение данных в памяти для разных порядков следования байтов</p>
     <table>
      <tr align="left">
       <th align="left" valign="top">Адрес</th>
       <th align="left" valign="top">Обратный порядок</th>
       <th align="left" valign="top">Прямой порядок</th>
      </tr>
      <tr align="left">
       <td align="left" valign="top">0</td>
       <td align="left" valign="top">00000000</td>
       <td align="left" valign="top">00000011</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">1</td>
       <td align="left" valign="top">00000000</td>
       <td align="left" valign="top">00000100</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">2</td>
       <td align="left" valign="top">00000100</td>
       <td align="left" valign="top">00000000</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top">3</td>
       <td align="left" valign="top">00000011</td>
       <td align="left" valign="top">00000000</td>
      </tr>
     </table>
     <p>Обратите внимание на то, что для аппаратной платформы с обратным порядком байтов самый старший байт записывается в самый минимальный адрес памяти.</p>
     <p>И наконец, еще один пример — фрагмент кода, который позволяет определить порядок байтов для той аппаратной платформы, на которой он выполняется.</p>
     <p><code>int x = 1;</code></p>
     <p><code>if (*(char*)&amp;x == 1)</code></p>
     <p><code> /* прямой порядок */</code></p>
     <p><code>else</code></p>
     <p><code> /* обратный порядок */</code></p>
     <p>Этот пример работает как в ядре, так и в пространстве пользователя.</p>
    </section>
    <section>
     <title>
      <p>История терминов big-endian и little-endian</p>
     </title>
     <p>Термины big-endian и little-endian заимствованы из сатирического романа Джонатана Свифта "Путешествие Гулливера", который был издан в 1726 году. В этом романс наиболее важной политической проблемой народа лилипутов была проблема, с какого конца следует разбивать яйцо: с тупого (big) или острого (little). Тех, кто предпочитал тупой конец называли "тупоконечниками" (big-endian), тех же, кто предпочитал острый конец, называли "остроконечниками" (little-endian).</p>
     <p>Аналогия между дебатами лилипутов и спорами о том, какой порядок байтов лучше, говорит о том, что это вопрос больше политический, чем технический.</p>
    </section>
    <section>
     <title>
      <p>Порядок байтов в ядре</p>
     </title>
     <p>Для каждой аппаратной платформы, которая поддерживается ядром Linux, в файле <code>&lt;asm/byteorder.h&gt;</code> определена одна из двух констант <code>__BIG_ENDIAN</code> или <code>__LITTLE_ENDIAN</code>, в соответствии с используемым порядком байтов.</p>
     <p>В этот заголовочный файл также включаются макросы из каталога <code>include/linux/byteorder/</code>, которые помогают конвертировать один порядок байтов в другой. Ниже показаны наиболее часто используемые макросы.</p>
     <p><code>u32 __cpu_to_be32(u32); /* преобразовать порядок байтов текущего</code></p>
     <p><code>                           процессора в порядок big-endian */</code></p>
     <p><code>u32 __cpu_to_le32(u32); /* преобразовать порядок байтов текущего</code></p>
     <p><code>                           процессора в порядок little-endian */</code></p>
     <p><code>u32 __be32_to_cpu(u32); /* преобразовать порядок байтов big-endian в</code></p>
     <p><code>                           порядок байтов текущего процессора */</code></p>
     <p><code>u32 __lе32_to_cpu(u32); /* преобразовать порядок байтов little-endian</code></p>
     <p><code>                           в порядок байтов текущего процессора */</code></p>
     <p>Эти макросы выполняют преобразование одного порядка байтов в другой. В случае когда порядки байтов, между которыми выполняется преобразование, одинаковы (например, если выполняется преобразование в обратный порядку байтов и процессор тоже использует такой же порядок), то эти макросы не делают ничего. В противном случае возвращается преобразованное значение.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Таймер</p>
    </title>
    <p>Никогда нельзя привязываться к какой-либо конкретной частоте генерации прерывания системного таймера и, соответственно, к тому, сколько раз в секунду изменяется переменная <code>jiffies</code>. Всегда необходимо использовать константу <code>HZ</code>, чтобы корректно определять интервалы времени. Это очень важно, потому что значение частоты системного таймера может отличаться не только для разных аппаратных платформ, но и для одной аппаратной платформы при использовании разных версий ядра.</p>
    <p>Например, константа <code>HZ</code> для аппаратной платформы x86 сейчас равна 1000. Это значит, что прерывание таймера возникает 1000 раз в секунду, или каждую миллисекунду. Однако до серии ядер 2.6 для аппаратной платформы x86 значение константы <code>HZ</code> было равно 100. Для разных аппаратных платформ эти значения отличаются: для аппаратной платформы alpha константа <code>HZ</code> равна 1024, а для платформы ARM — 100.</p>
    <p>Никогда нельзя сравнивать значение переменной <code>jiffies</code> с числом, таким как 1000, и думать, что это всегда будет означать одно и то же. Для получения интервалов времени необходимо всегда умножать или делить на константу <code>HZ</code>, как в следующем примере.</p>
    <p><code>HZ /* одна секунда */</code></p>
    <p><code>(2*HZ) /* две секунды */</code></p>
    <p><code>(HZ/2) /* полсекунды */</code></p>
    <p><code>(HZ/100) /* 10 мс */</code></p>
    <p><code>(2*HZ/100) /* 20 мс */</code></p>
    <p>Константа <code>HZ</code> определена в файле <code>&lt;asm/param.h&gt;</code>. Об этом подробно рассказано в главе 10, "Таймеры и управление временем".</p>
   </section>
   <section>
    <title>
     <p>Размер страницы памяти</p>
    </title>
    <p>При работе со страницами памяти никогда нельзя привязываться к конкретному размеру страницы. Программисты, которые разрабатывают для аппаратной платформы x86, часто делают ошибку, считая, что размер страницы всегда равен 4 Кбайта. Хотя это справедливо для платформы x86, для других аппаратных платформ размер станицы может быть другим. Некоторые аппаратные платформы поддерживают несколько размеров страниц! В табл. 19.1 приведен список размеров страниц памяти для всех поддерживаемых аппаратных платформ.</p>
    <empty-line/>
    <p><strong>Таблица 19.4</strong>. Размеры страниц памяти для разных аппаратных платформ</p>
    <table>
     <tr align="left">
      <th align="left" valign="top">Аппаратная платформа</th>
      <th align="left" valign="top">Значение PAGE_SHIFT</th>
      <th align="left" valign="top">Значение PAGE_SIZE</th>
     </tr>
     <tr align="left">
      <td align="left" valign="top">alpha</td>
      <td align="left" valign="top">13</td>
      <td align="left" valign="top">8 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">arm</td>
      <td align="left" valign="top">12, 14, 15</td>
      <td align="left" valign="top">4 Кбайт, 16 Кбайт, 32 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">cris</td>
      <td align="left" valign="top">13</td>
      <td align="left" valign="top">8 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">h8300</td>
      <td align="left" valign="top">12</td>
      <td align="left" valign="top">4 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">i386</td>
      <td align="left" valign="top">12</td>
      <td align="left" valign="top">4 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">ia64</td>
      <td align="left" valign="top">12, 13, 14, 16</td>
      <td align="left" valign="top">4 Кбайт, 8 Кбайт, 32 Кбайт, 64 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">m68k</td>
      <td align="left" valign="top">12, 13</td>
      <td align="left" valign="top">4 Кбайт, 8 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">m86knommu</td>
      <td align="left" valign="top">12</td>
      <td align="left" valign="top">4 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">mips</td>
      <td align="left" valign="top">12</td>
      <td align="left" valign="top">4 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">mips64</td>
      <td align="left" valign="top">12</td>
      <td align="left" valign="top">4 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">parisc</td>
      <td align="left" valign="top">12</td>
      <td align="left" valign="top">4 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">ppc</td>
      <td align="left" valign="top">12</td>
      <td align="left" valign="top">4 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">ppc64</td>
      <td align="left" valign="top">12</td>
      <td align="left" valign="top">4 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">s390</td>
      <td align="left" valign="top">12</td>
      <td align="left" valign="top">4 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">sh</td>
      <td align="left" valign="top">12</td>
      <td align="left" valign="top">4 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">spare</td>
      <td align="left" valign="top">12,13</td>
      <td align="left" valign="top">4 Кбайт, 8 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">sparc64</td>
      <td align="left" valign="top">13</td>
      <td align="left" valign="top">8 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">v850</td>
      <td align="left" valign="top">12</td>
      <td align="left" valign="top">4 Кбайт</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">x86_64</td>
      <td align="left" valign="top">12</td>
      <td align="left" valign="top">4 Кбайт</td>
     </tr>
    </table>
    <p>При работе со страницами памяти необходимо использовать константу <code>PAGE_SIZE</code>, которая содержит размер страницы памяти в байтах.</p>
    <p>Значение макроса <code>PAGE_SHIFT</code> — это количество битов, на которое необходимо сдвинуть влево значение адреса, чтобы получить номер соответствующей страницы памяти. Например, для аппаратной платформы x86, для которой размер страницы равен 4 Кбайт, макрос <code>PAGE_SIZE</code> равен 4096, а макрос <code>PAGE_SHIFT</code> — 12. Эти значения содержатся в заголовочном файле <code>&lt;asm/page.h&gt;</code>.</p>
   </section>
   <section>
    <title>
     <p>Порядок выполнения операций процессором</p>
    </title>
    <p>Вспомните из материала главы 9, "Средства синхронизации в ядре", что для различных аппаратных платформ процессоры в разной степени изменяют порядок выполнения программных инструкций. Для некоторых процессоров порядок выполнения операций строго соблюдается, запись данных в память и считывание данных из памяти выполняются в строго указанном в программе порядке. Другие процессоры имеют ослабленные требования к порядку выполнения операций считывания и записи данных и могут изменять порядок выполнения этих операций с целью оптимизации.</p>
    <p>Если код зависит от порядка выполнения операций чтения-записи данных, то необходимо гарантировать, что даже процессор с самыми слабыми ограничениями на порядок выполнения чтения-записи будет выполнять эти операции в правильном порядке. Это делается с помощью соответствующих барьеров, таких как <code>rmb()</code> и <code>wmb()</code>. Более подробная информация приведена в главе 9, "Средства синхронизации в ядре".</p>
   </section>
   <section>
    <title>
     <p>Многопроцессорность, преемптивность и верхняя память</p>
    </title>
    <p>Может показаться неправильным включать поддержку симметричной многопроцессорности, возможность вытеснения процессов в режиме ядра и работу с верхней памятью в вопросы переносимости. В конце концов, это не особенности аппаратной платформы, которые влияют на операционную систему, а функции ядра Linux, которые по многом не зависят от аппаратной платформы. Тем не менее для этих функций существуют важные конфигурационные параметры, которые необходимо учитывать при разработке кода. Программировать всегда необходимо под SMP, с поддержкой преемптивности и с использованием верхней памяти, чтобы код был безопасным всегда, при любых конфигурациях. Необходимо всегда соблюдать следующие правила.</p>
    <p>• Всегда необходимо учитывать, что код может выполняться на SMP-системе и использовать соответствующие блокировки.</p>
    <p>• Всегда необходимо учитывать, что код может выполняться при включенной преемптивности ядра, поэтому необходимо всегда использовать необходимые блокировки и операции для управления преемптивностью.</p>
    <p>• Всегда необходимо учитывать, что код может выполняться на системе с поддержкой верхней памяти (непостоянно отображаемая память) и при необходимости использовать функцию <code>kmap()</code>.</p>
   </section>
   <section>
    <title>
     <p>Пару слов о переносимости</p>
    </title>
    <p>Если говорить коротко, то написание переносимого, ясного и красивого кода подразумевает следующие два момента.</p>
    <p>• Код необходимо разрабатывать с учетом самого общего сценария: следует предполагать, что все, что может случиться, обязательно случится, и принять на этот счет все возможные меры.</p>
    <p>• Всегда необходимо все подводить под наибольший общий знаменатель: нельзя полагаться на то, что будут доступны все возможности ядра, следует опираться только на минимум возможностей, которые доступны всем аппаратным платформам.</p>
    <p>Написание переносимого кода требует строгого учета многих факторов: размер машинного слова, размеры типов данных, выравнивание в памяти, порядок байтов, размер страницы, изменение порядка операций процессора и т.д. В большинстве случаев при программировании ядра следует гарантировать, что типы данных используются правильно. Тем не менее время от времени все равно всплывают проблемы, связанные с особенностью той или другой аппаратной платформы. Важно понимать проблемы, связанные с переносимостью, и всегда писать четкий и переносимый код ядра.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 20</p>
    <p>Заплаты, разработка и сообщество</p>
   </title>
   <section>
    <p>Одно из самых больших преимуществ операционной системы Linux — это связанное с ней большое сообщество пользователей и разработчиков. Сообщество предоставляет множество глаз для проверки кода и множество пользователей для тестирования и отправки сообщений об ошибках. Наконец, сообщество решает, какой код включать в основное ядро. Поэтому важно понимать, как это все происходит.</p>
   </section>
   <section>
    <title>
     <p>Сообщество</p>
    </title>
    <p>Если говорить о том, где физически существует сообщество разработчиков ядра Linux, то можно сослаться на <emphasis>список рассылки разработчиков ядра Linux</emphasis> (<emphasis>Linux Kernel Mail List</emphasis>, или, сокращенно, <emphasis>lkml</emphasis>). Список разработчиков ядра Linux — это то место, где происходит большинство дискуссий, дебатов и флеймов вокруг ядра Linux. Здесь обсуждаются новые возможности, и большая часть кода отправляется в этот список рассылки перед тем, как этот код для чего-нибудь начинает использоваться. В списке рассылки насчитывается до 300 сообщений в день — количество не для слабонервных. Подписаться на этот список (или но крайней мере читать его обзор) рекомендуется всем, кто серьезно занимается разработкой ядра. Даже только наблюдая за работой специалистов, можно узнать достаточно много.</p>
    <p>Подписаться на данный список рассылки можно, отправив сообщение</p>
    <p><code>subscribe linux-kernel &lt;your@email.address&gt;</code></p>
    <p>в виде обычного текста на адрес <code>majordomo@vger.kernel.org</code>. Больше информации доступно по Интернет-адресу <code>http://vger.kernel.org/</code>, а список часто задаваемых вопросов (FAQ) — по адресу <code>http://www.tux.org/lkml/</code>.</p>
    <p>Много других WWW-сайтов и списков рассылки посвящены как ядру, так и вообще операционной системе Linux. Отличный ресурс для начинающих хакеров — http://www.kernelnewbies.org/, сайт, который сможет удовлетворить желания всех, кто, стачивая зубы, грызет основы разработки ядра. Два других отличных источника информации — это сайт http://www.lwn.net/, Linux Weekly News, на котором есть большая колонка новостей ядра, и сайт http://www.kerneltraffic.org, Kernel Traffic, который содержит сводку сообщений из списка рассылки разработчиков ядра Linux с. комментариями.</p>
   </section>
   <section>
    <title>
     <p>Стиль написания исходного кода</p>
    </title>
    <section>
     <p>Как и для любого большого программного проекта, для ядра Linux определен стиль написания исходного кода, который определяет форматирование и размещение кода. Это сделано не потому, что стиль написания, который принят для Linux, лучше других (хотя очень может быть), и не потому, что все программисты пишут неразборчиво (хотя тоже бывает), а потому, что <emphasis>одинаковость</emphasis> стиля является важным моментом для обеспечения <emphasis>производительности разработки</emphasis>. Часто говорят, что стиль написания исходного кода не важен, потому что он не влияет на скомпилированный объектный код. Однако для большого программного проекта, в котором задействовано большое количество разработчиков, такого как ядро, важна слаженность стиля. Слаженность включает в себя одинаковость восприятия, что ведет к упрощению чтения, к избежанию путаницы и вселяет надежду на то, что и в будущем стиль останется одинаковым. К тому же, это приводит к увеличению количества разработчиков, которые смогут нормально читать ваш код, и увеличивает количество кода, который вы сможете нормально читать. Для проектов с открытым исходным кодом чем больше будет глаз, тем лучше.</p>
     <p>Пока стиль еще не выбран и широко не используется, не так важно, <emphasis>какой именно</emphasis> стиль выбрать. К счастью, еще очень давно Линус представил на рассмотрение стиль, который необходимо использовать, и при написании большей части кода сейчас стараются придерживаться именно этого стиля. Подробное описание стиля приведено в файле <code>Documentation/CodingStyle</code> со свойственным Линусу юмором.</p>
    </section>
    <section>
     <title>
      <p>Отступы</p>
     </title>
     <p>Для выравнивания текста и введения отступов используются символы табуляции. Размер одного символа табуляции при отображении соответствует восьми позициям. Это не означает, что для структурирования можно использовать восемь или четыре символа "пробел" либо что-нибудь еще. Это означает, что каждый уровень отступа равен одному символу табуляции от предыдущего и что при отображении длина символа табуляции равна восьми символам. По непонятным причинам, это правило почти всегда нарушается, несмотря на то что оно очень сильно влияет на читабельность. Восьмисимвольная табуляция позволяет очень легко визуально различать отдельные блоки кода даже после нескольких часов работы.</p>
     <p>Если табуляция в восемь символов кажется очень большой, то не нужно делать так много вложений кода. Почему ваши функции имеют пять уровней вложенности? Необходимо исправлять код, а не отступы.</p>
    </section>
    <section>
     <title>
      <p>Фигурные скобки</p>
     </title>
     <p>Как располагать фигурные скобки, это личное дело каждого, и практически нет никаких принципиальных причин, по которым одно соглашение было бы лучше другого, но какое-нибудь соглашение все-таки должно быть. Принятое соглашение при разработке ядра — это размещать открывающую скобку в первой строке, сразу за соответствующим оператором. Закрывающая скобка помещается в первой позиции с новой строки, как в следующем примере.</p>
     <p><code>if (fox) {</code></p>
     <p><code>        dog();</code></p>
     <p><code>        cat();</code></p>
     <p><code>}</code></p>
     <p>В случае, когда за закрывающей скобкой продолжается то же самое выражение, то продолжение выражения записывается в той же строке, что и закрывающая скобка, как показано ниже</p>
     <p><code>if (fox) {</code></p>
     <p><code>        ant();</code></p>
     <p><code>        pig();</code></p>
     <p><code>} else {</code></p>
     <p><code>        dog();</code></p>
     <p><code>        cat();</code></p>
     <p><code>}</code></p>
     <p>или следующим образом.</p>
     <p><code>do {</code></p>
     <p><code>        dog();</code></p>
     <p><code>        cat();</code></p>
     <p><code>} while (fox);</code></p>
     <p>Для функций это правило не действует, потому что внутри одной функции тело другой функции описывать нельзя.</p>
     <p><code>unsigned long func (void)</code></p>
     <p><code>{</code></p>
     <p><code>       /* ... */</code></p>
     <p><code>}</code></p>
     <p>И наконец, для выражений, в которых фигурные скобки не обязательны, эти скобки можно опустить.</p>
     <p><code>if (foo)</code></p>
     <p><code>bar();</code></p>
     <p>Логика всего этого базируется на K&amp;R<a l:href="#n96" type="note">[96]</a>.</p>
    </section>
    <section>
     <title>
      <p>Длинные строки</p>
     </title>
     <p>При написании кода ядра необходимо стараться, насколько это возможно, чтобы длина строки была не больше 80 символов. Это позволяет строкам, при отображении на терминале размером 80&#215;24 символа, вмещаться в одну строку терминала.</p>
     <p>Не существует стандартного правила, что делать, если длина строки кода обязательно должна быть больше 80 символов. Некоторые разработчики просто пишут длинные строки, возлагая ответственность за удобочитаемое отображение строк на программу текстового редактора. Другие разработчики разбивают такие строки на части и вручную вставляют символы конца строки в тех местах, которые кажутся им наиболее подходящими для этого, и отделяют продолжения разбитой строки от ее начала двумя символами табуляции.</p>
     <p>Некоторые разработчики помещают параметры функции друг под другом, если параметры не помещаются в одной строке, как в следующем примере.</p>
     <p><code>static void get_pirate_parrot(const char *name,</code></p>
     <p><code>                           unsigned long disposition,</code></p>
     <p><code>                           unsigned long feather_quality);</code></p>
     <p>Другие разработчики разбивают длинную строку на части, но не располагают параметры функций друг под другом, а просто используют два символа табуляции для отделения продолжений длинной строки от ее начала, как показано ниже.</p>
     <p><code>int find_pirate_flag_by_color(const char *color,</code></p>
     <p><code>                const char *name, int len);</code></p>
     <p>Поскольку на этот счет нет определенного правила, выбор остается за разработчиками, то есть за вами.</p>
    </section>
    <section>
     <title>
      <p>Имена</p>
     </title>
     <p>В именах нельзя использовать символы разных регистров. Назвать переменную именем <code>idx</code>, или даже <code>i</code> — это очень хорошо, но при условии, что будет понятно назначение этой переменной. Слишком хитрые имена, такие как <code>theLoopIndex</code>, недопустимы. Так называемая "венгерская запись" (Hungarian notation), когда тип переменной кодируется в ее имени, В данном случае — признак плохого тона. Это С, а не Java и Unix, а не Windows.</p>
     <p>Тем не менее, глобальные переменные и функции должны иметь наглядные имена. Если глобальной функции присвоить имя <code>atty()</code>, то это может привести к путанице. Более подходящим будет имя <code>get_active_tty()</code>. Это все-таки Linux, а не BSD.</p>
    </section>
    <section>
     <title>
      <p>Функции</p>
     </title>
     <p>Существует мнемоническое правило: функции не должны по объему кода превышать двух экранов текста и иметь больше <emphasis>десяти</emphasis> локальных переменных. Каждая функция должна выполнять одно действие, но делать это хорошо. Не вредно разбить функцию на последовательность более мелких функций. Если возникает беспокойство по поводу накладных расходов за счет вызова функций, то можно использовать подстановку тела — <code>inline</code>.</p>
    </section>
    <section>
     <title>
      <p>Комментарии</p>
     </title>
     <p>Очень полезно использовать комментарии кода, но делать это нужно правильно. Обычно необходимо описывать, <emphasis>что</emphasis> делает код и <emphasis>для чего</emphasis> это делается. То, <emphasis>как</emphasis> реализован алгоритм, описывать не нужно, это должно быть ясно из кода. Если так сделать не получается, то, возможно, стоит пересмотреть то, что вы написали. Кроме того, комментарии не должны включать информацию о том, кто написал функцию, когда это было сделано, время модификации и др. Такую информацию логично размещать в самом начале файла исходного кода.</p>
     <p>В ядре используются комментарии в стиле С, хотя компилятор gcc поддерживает также и комментарии в стиле C++. Обычно комментарии кода ядра должны быть похожи на следующие (только на английском языке, конечно).</p>
     <p><code>/*</code></p>
     <p><code>* get_ship_speed() - возвращает текущее значение скорости</code></p>
     <p><code>* пиратского корабля</code></p>
     <p><code>* Необходима для вычисления координат корабля.</code></p>
     <p><code>* Может переходить в состояние ожидания,</code></p>
     <p><code>* нельзя вызывать при удерживаемой блокировке.</code></p>
     <p><code>*/</code></p>
     <p>Комментарии внутри функций встречаются редко, и их нужно использовать только в специальных ситуациях, таких как документирование дефектов, или для важных замечаний. Важные замечания часто начинаются со строки <code>"XXX: "</code>, а информация о дефектах — со строки <code>"FIXME: "</code>, как в следующем примере.</p>
     <p><code>/*</code></p>
     <p><code>* FIXME: Считается, что dog == cat.</code></p>
     <p><code>* В будущем это может быть не так</code></p>
     <p><code>*/</code></p>
     <p>У ядра есть возможность автоматической генерации документации. Она основана на GNOME-doc, но немного модифицирована и называется Kernel-doc. Для создания документации в формате HTML необходимо выполнить следующую команду.</p>
     <p><code>make htmldocs</code></p>
     <p>Для генерации документации в формате postscript команда должна быть следующей.</p>
     <p><code>make psdocs</code></p>
     <p>Документировать код можно путем введения комментариев в специальном формате.</p>
     <p><code>/**</code></p>
     <p><code>* find_treasure - нахождение сокровищ, помеченных на карте крестом</code></p>
     <p><code>* @map - карта сокровищ</code></p>
     <p><code>* @time - момент времени, когда были зарыты сокровища</code></p>
     <p><code>*</code> </p>
     <p><code>* Должна вызываться при удерживаемой блокировке pirate_ship_lock.</code></p>
     <p><code>*/</code></p>
     <p><code>void find_treasure(int dog, int cat)</code></p>
     <p><code>{</code></p>
     <p><code>       /* ... */</code></p>
     <p><code>}</code></p>
     <p>Для более подробной информации см. файл <code>Documentation/kernel-doc-nano-HOWTO.txt</code>.</p>
    </section>
    <section>
     <title>
      <p>Использование директивы <code>typedef</code></p>
     </title>
     <p>Разработчики ядра не любят определять новые типы с помощью оператора <code>typedef</code>, и причины этого довольно трудно объяснить. Разумное объяснение может быть следующим.</p>
     <p>• Определение нового типа через оператор <code>typedef</code> скрывает истинный вид структур данных.</p>
     <p>• Поскольку новый тип получается скрытым, то код более подвержен таким нехорошим вещам, как передача структуры данных в функцию по значению, через стек.</p>
     <p>• Использование оператора <code>typedef</code> — признак лени.</p>
     <p>Чтобы избежать насмешек, лучше не использовать оператор <code>typedef</code>.</p>
     <p>Конечно, существуют ситуации, в которых полезно использовать оператор <code>typedef</code>: сокрытие специфичных для аппаратной платформы деталей реализации или обеспечение совместимости при изменении типа. Нужно хорошо подумать, действительно ли оператор <code>typedef</code> необходим или он используется только для того, чтобы уменьшить количество символов при наборе кода.</p>
    </section>
    <section>
     <title>
      <p>Использование того, что уже есть</p>
     </title>
     <p><emphasis>Не нужно изобретать паровоз</emphasis>. Ядро предоставляет функции работы со строками, подпрограммы для сжатия и декомпрессии данных и интерфейс работы со связанными списками — их необходимо использовать.</p>
     <p>Не нужно инкапсулировать стандартные интерфейсы в другие реализации обобщенных интерфейсов. Часто приходится сталкиваться с кодом, который переносится с других операционных систем в систему Linux, при этом на основе существующих интерфейсов реализуется некоторая громоздкая функция, которая служит для связи нового кода с существующим. Такое не нравится никому, поэтому необходимо непосредственно использовать предоставляемые интерфейсы.</p>
    </section>
    <section>
     <title>
      <p>Никаких директив <code>ifdef</code> в исходном коде</p>
     </title>
     <p>Использование директив препроцессора <code>ifdef</code> в исходном коде категорически не рекомендуется. Никогда не следует делать чего-нибудь вроде следующего.</p>
     <p><code> ...</code></p>
     <p><code>#ifdef CONFIG_FOO</code></p>
     <p><code> foo();</code></p>
     <p><code>#endif</code></p>
     <p><code> ...</code></p>
     <p>Вместо этого, если макрос <code>CONFIG_FOO</code> не определен, необходимо определять функцию <code>foo()</code>, как ту, которая ничего не делает.</p>
     <p><code>#ifdef CONFIG_FOO</code></p>
     <p><code>static int foo(void)</code></p>
     <p><code>{</code></p>
     <p><code> /* ... */</code></p>
     <p><code>}</code></p>
     <p><code>#else</code></p>
     <p><code>static inline int foo(void) { }</code></p>
     <p><code>#endif</code></p>
     <p>После этого можно вызывать функцию <code>foo()</code> без всяких условий. Пусть компилятор поработает за вас.</p>
    </section>
    <section>
     <title>
      <p>Инициализация структур</p>
     </title>
     <p>Структуры необходимо инициализировать, используя метки полей. Это позволяет предотвратить некорректную инициализацию при изменении структур. Это также позволяет выполнять инициализацию не всех полей. К сожалению, в стандарте C99 принят довольно "страшненький" формат меток полей, а в компиляторе gcc ранее использовавшийся формат меток полей в стиле GNU признан устаревшим. Следовательно, для кода ядра необходимо использовать новый формат, согласно стандарту C99, каким бы ужасным он ни был.</p>
     <p><code>struct foo my_foo = {</code></p>
     <p><code> .a = INITIAL_A,</code></p>
     <p><code> .b = INITIAL_B,</code></p>
     <p><code>};</code></p>
     <p>где <code>а</code> и <code>b</code> — это поля структуры <code>struct foo</code>, а параметры <code>INITIAL_A</code> и <code>INITIAL_B</code> — соответственно, их начальные значения. Если поле не указано при инициализации, то оно устанавливается в свое начальное значение, согласно стандарту ANSI С (указателям присваивается значение <code>NULL</code>, целочисленным полям — нулевое значение, а полям с плавающей точкой— значение 0.0). Например, если структура <code>struct foo</code> также имеет поле <code>int с</code>, то это поле в предыдущем примере будет инициализировано в значение 0.</p>
     <p>Да, это ужасно. Но у нас нет другого выбора.</p>
    </section>
    <section>
     <title>
      <p>Исправление ранее написанного кода</p>
     </title>
     <p>Если в ваши руки попал код, который даже близко не соответствует стилю написания кода ядра Linux, то все равно не стоит терять надежды. Немного упорства, и утилита <code>indent</code> поможет сделать все как надо. Программа <code>indent</code> — отличная утилита GNU, которая включена во многие поставки ОС Linux и предназначена для форматирования исходного кода в соответствии с заданными правилами. Установки по умолчанию соответствуют стилю форматирования GNU, который выглядит не очень красиво. Для того чтобы утилита выполняла форматирование в соответствии со стилем написания кода ядра Linux, необходимо использовать следующие параметры.</p>
     <p><code>indent -kr -i8 -ts8 -sob -180 -ss -bs -psl &lt;файл&gt;</code></p>
     <p>Можно также использовать сценарий <code>scripts/Lindent</code>, который вызывает утилиту <code>indent</code> с необходимыми параметрами.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Организация команды разработчиков</p>
    </title>
    <p>Разработчики — это хакеры, которые занимаются развитием ядра Linux. Некоторые делают это за деньги, для некоторых это хобби, но практически все делают это с удовольствием. Разработчики ядра, которые внесли существенный вклад, перечислены в файле <code>CREDITS</code>, который находится в корневом каталоге дерева исходных кодов ядра.</p>
    <p>Для различных частей ядра выбираются <emphasis>ответственные разработчики</emphasis> (<emphasis>maintainers</emphasis>), которые официально выполняют их поддержку. Ответственные разработчики — это один человек или группа людей, которые полностью отвечают за свою часть ядра. Каждая подсистема ядра, например сетевая подсистема, также имеет связанного с ней ответственного. Разработчики, которые отвечают за определенный драйвер или подсистему, обычно перечислены в файле <code>MAINTAINERS</code>. Этот файл тоже находится в корневом каталоге дерева исходных кодов.</p>
    <p>Среди ответственных разработчиков существует специальный человек, который отвечает за все ядро в целом (kernel maintainer). Исторически, Линус отвечает за разрабатываемую серию ядер (где наиболее интересно) и за первые выпуски стабильной версии ядра. Вскоре после того как ядро становится стабильным, Линус передает бразды правления одному из ведущих разработчиков. Они продолжают поддержку стабильной серии ядра, а Линус начинает работу над новой разрабатываемой серией. Таким образом, серии ядер 2.0, 2.4 и 2.6 все еще активно поддерживаются.</p>
    <p>Несмотря на слухи, здесь нет <emphasis>никаких</emphasis> других, в том числе тайных, организаций.</p>
   </section>
   <section>
    <title>
     <p>Отправка сообщений об ошибках</p>
    </title>
    <p>Если вы обнаружили ошибку, то наилучшим решением будет исправить ее, сгенерировать соответствующую заплату, оттестировать и отправить, как это будет рассказано в следующих разделах. Конечно, можно и просто сообщить об ошибке, чтобы кто-нибудь исправил ее для вас.</p>
    <p>Наиболее важная часть сообщения об ошибке — это полное описание проблемы. Необходимо описать симптомы, системные сообщения и декодированное сообщение oops (если такое есть). Еще более важно, чтобы вы смогли пошагово описать, как устойчиво воспроизвести проблему, и кратко описать особенности вашего аппаратного обеспечения.</p>
    <p>Следующий этап — определение того, кому отправить сообщение об ошибке. В файле <code>MAINTAINERS</code> приведен список людей, которые отвечают за каждый драйвер и подсистему. Эти люди должны получать все сообщения об ошибках, которые возникают в том коде, поддержку которого они выполняют. Если вы не смогли найти необходимого разработчика, то отправьте вопрос в список рассылки разработчиков ядра Linux по адресу <code>linux-kernel@vger.kernel.org</code>. Даже если вы нашли нужное ответственное лицо, то никогда не помешает отправить копию сообщения в список рассылки разработчиков.</p>
    <p>Больше информации об этом приведено в файлах <code>REPORTING-BUGS</code> и <code>Documentation/oops-tracing.txt</code>.</p>
   </section>
   <section>
    <title>
     <p>Генерация заплат</p>
    </title>
    <p>Все изменения исходного кода ядра Linux распространяются в виде заплат (patch). Заплаты представляют собой результат вывода утилиты GNU <code>diff(1)</code> в формате, который может подаваться на вход программы <code>patch(1)</code>. Наиболее просто сгенерировать заплату можно в случае, когда имеется два дерева исходных кодов ядра: одно — стандартное, а другое — с вашими изменениями. Обычная схема имен состоит в том, что каталог, в котором находится стандартное ядро, называется <code>linux</code>-x.y.z (каталог, в который разворачивается архив дерева исходного кода в формате tar), a имя модифицированного ядра — <code>linux</code>. Для генерации заплаты на основе двух каталогов <emphasis>с</emphasis> исходным кодом необходимо выполнить следующую команду из каталога, в котором находятся два рассмотренных дерева исходного кода.</p>
    <p><code>diff -urN linux-x.y.z/linux/ &gt; my-patch</code></p>
    <p>Обычно это делается где-нибудь в домашнем каталоге, а не в каталоге <code>/usr/src/linux</code>, поэтому нет необходимости иметь права пользователя <code>root</code>. Флаг <code>-u</code> указывает, что необходимо использовать унифицированный формат вывода команды <code>diff</code>. Без этого флага внешний вид заплаты получается неудобочитаемым. Флаг <code>-r</code> указывает на необходимость рекурсивного анализа каталогов, а флаг <code>-N</code> указывает, что новые файлы, которые появились в измененном каталоге, должны быть включены в результат вывода команды <code>diff</code>. Если же необходимо получить только изменения одного файла, то можно выполнить следующую команду.</p>
    <p><code>diff -u linux-x.y.z/some/file_linux/some/file &gt; my-patch</code></p>
    <p>Обратите внимание на то, что вычислять изменения необходимо всегда, находясь в одном текущем каталоге, а именно в том, где находятся оба дерева исходного кода. При этом получается заплата, которую легко могут использовать все, даже в случаях, когда имена каталогов исходного кода отличаются от тех, которые использовались при генерации заплаты. Для того чтобы применить заплату, которая сгенерирована таким образом, необходимо выполнить следующую команду из корневого каталога дерева исходного кода.</p>
    <p><code>patch -p1 &lt; ../my-patch</code></p>
    <p>В этом примере имя файла, который содержит заплату, — <code>my-patch</code>, а находится он в родительском каталоге по отношению к каталогу, в котором хранится дерево исходного кода ядра. Флаг <code>-p1</code> означает, что необходимо игнорировать (strip) имя первого каталога в путях всех файлов, в которые будут вноситься изменения. Это позволяет применить заплату независимо от того, какие имена каталогов кода ядра были на той машине, где создавалась заплата.</p>
    <p>Полезная утилита <code>diffstat</code> позволяет сгенерировать гистограмму изменений, к которым приведет применение заплаты (удаление и добавление строк). Для того чтобы получить эту информацию для какой-либо заплаты, необходимо выполнить следующую команду.</p>
    <p><code>diffstat -p1 my-patch</code></p>
    <p>Обычно полезно включить результат выполнения этой команды при отправлении заплаты в список рассылки lkml. Так как программа <code>patch(1)</code> игнорирует все строки до того момента, пока не будет обнаружен формат <code>diff</code>, то вначале заплаты можно включить короткое описание.</p>
   </section>
   <section>
    <title>
     <p>Представление заплат</p>
    </title>
    <p>Заплата должна быть сгенерирована так, как описано в предыдущем разделе. Если заплата касается определенного драйвера или подсистемы, то заплату нужно отправить соответствующему ответственному разработчику, одному из тех, которые перечислены в файле <code>MAINTAINERS</code>. Другой вариант — это отправить сообщение в список рассылки разработчиков ядра по адресу <code>linux-kernel@vger.kernel.org</code>.</p>
    <p>Обычно тема (subject) письма, в котором содержится заплата, должна быть похожа на следующую <code>"[PATCH] короткое описание."</code>. В теле письма должны быть описаны основные технические детали изменений, которые вносятся заплатой, а также обоснования необходимости этих изменений. Описание должно быть максимально конкретным. Также необходимо указать, на какую версию ядра рассчитана заплата.</p>
    <p>Большинство разработчиков ядра будут просматривать заплату прямо в теле письма и при необходимости записывать все письмо в файл. Следовательно, лучше всего будет вставить заплату прямо в тело письма, в самом конце сообщения. Будьте внимательны, потому что некоторые злобные почтовые клиенты вводят в сообщения дополнительное форматирование. Это испортит заплату и будет надоедать разработчикам. Если ваш почтовый клиент делает такие вещи, то необходимо поискать возможность включения текста без изменений ("Insert Inline") или что-нибудь аналогичное. Нормально работает также присоединение (attachment) заплаты в виде обычного текста, без перекодировки.</p>
    <p>Если заплата большая или содержит <emphasis>несколько</emphasis> принципиальных изменений, то лучше разбить ее на логические части. Например, если вы предложили новый API и изменили несколько драйверов с целью его использования, то эти изменения можно разбить на две заплаты (новый API и изменения драйверов) и выслать их двумя письмами. Если в каком-либо случае необходимо вначале применить предыдущую заплату, то это необходимо специально указать.</p>
    <p>После отправки наберитесь терпения и подождите ответа. Не нужно обижаться на негативный ответ — в конце концов это тоже ответ! Обдумайте проблему и вышлите обновленную версию заплаты. Если ответа нет, то попытайтесь разобраться, что было сделано не так, и решить проблему. Спросите у ответственного разработчика и в списке рассылки по поводу комментариев. Если повезет, то ваши изменения будут включены в новую версию ядра!</p>
   </section>
   <section>
    <title>
     <p>Заключение</p>
    </title>
    <p>Наиболее важными качествами любого хакера являются желание и умение работать — нужно искать себе проблемы и решать их. В этой книге приведено описание основных частей ядра, рассказано об интерфейсах, структурах данных, алгоритмах и принципах работы. Книга предоставляет вид ядра изнутри и делает это в практической форме. Она предназначена для того, чтобы удовлетворить ваше любопытство и стать отправной точкой в разработке ядра.</p>
    <p>Тем не менее, как уже было сказано, единственный способ начать разрабатывать ядро — это начать <emphasis>читать</emphasis> и <emphasis>писать</emphasis> исходный код. Операционная система Linux предоставляет возможность работать в сообществе, которое не только позволяет это делать, но и активно побуждает к указанным действиям. Если есть желание действовать — вперед!</p>
   </section>
  </section>
  <section>
   <title>
    <p>Приложение А</p>
    <p>Связанные списки</p>
   </title>
   <section>
    <p>Связанный список — это структура хранения информации (контейнер), которая может содержать переменное количество элементов данных, часто называемых <emphasis>узлами</emphasis>, и позволяет манипулировать этими данными. В отличие от статического массива, элементы связанного списка можно создавать динамически. Это дает возможность создавать переменное количество элементов списка, причем указанное количество может быть неизвестно на этапе компиляции. Так как элементы связанных списков создаются в разные моменты времени, они не обязательно будут находиться в смежных областях оперативной памяти. Поэтому элементы списка должны быть связаны друг с другом таким образом, чтобы каждый элемент содержал указатель на <emphasis>следующий</emphasis> за ним элемент (<code>next</code>). Вставка или удаление элементов списка выполняется простым изменением указателей на следующий элемент. Структура связанного списка показана на рис. А.1.</p>
    <image l:href="#img_27.jpeg"/>
    <p><strong>Рис. A.1</strong>. Односвязный список</p>
    <p>В. некоторых связанных списках содержится указатель не только на следующий, но и на <emphasis>предыдущий</emphasis> элемент (<code>prev</code>). Эти списки называются <emphasis>двухсвязными</emphasis> (<emphasis>doubly linked</emphasis>), потому что они связаны как вперед, так и назад. Связанные списки, аналогичные тем, что показаны на рис. А.1, называются <emphasis>односвязными</emphasis> (<emphasis>singly linked</emphasis>). Двухсвязный список показан на рис. А.2.</p>
    <image l:href="#img_28.jpeg"/>
    <p><strong>Рис. А.2</strong>. Двухсвязный список</p>
   </section>
   <section>
    <title>
     <p>Кольцевые связанные списки</p>
    </title>
    <section>
     <p>Последний элемент связанного списка не имеет следующего за ним элемента, и значение указателя <code>next</code> последнего элемента обычно устанавливается равным специальному значению, обычно <code>NULL</code>, чтобы показать, что этот элемент списка является последним. в определенных случаях последний элемент списка не указывает на специальное значение, а указывает на первый элемент этого же списка. Такой список называется <emphasis>кольцевым связанным списком</emphasis> (<emphasis>circular linked list</emphasis>), поскольку связи образуют топологию кольца. Кольцевые связанные списки могут быть как односвязными, так и двухсвязными. В двухсвязных кольцевых списках указатель prev первого элемента указывает на последний элемент списка. На рис. А.3 и А.4 показаны соответственно односвязные и двухсвязные кольцевые списки.</p>
     <image l:href="#img_29.jpeg"/>
     <p><strong>Рис. A.3</strong>. Односвязный кольцевой список</p>
     <image l:href="#img_30.jpeg"/>
     <p><strong>Рис. А.4</strong>. Двухсвязный кольцевой список</p>
     <p>Стандартной реализацией связанных списков в ядре Linux является <emphasis>двухсвязный кольцевой список</emphasis>. Такие связанные списки обеспечивают наибольшую гибкость работы.</p>
    </section>
    <section>
     <title>
      <p>Перемещение по связанному списку</p>
     </title>
     <p>Перемещение по связанному списку выполняется последовательно (линейно). После того как просмотрен текущий элемент, выполнятся разыменование его указателя <code>next</code>, что позволяет обратиться к следующему за ним элементу и т.д. Это самый простой и наиболее подходящий метод перемещения но связанному списку. Если важна возможность произвольного доступа к любому элементу контейнера, то связанные списки не используются. Связанные списки используются, когда важна возможность динамического добавления и удаления элементов, а также возможность последовательного прохождения по всем элементам списка.</p>
     <p>Часто первый элемент списка представлен с помощью специального указателя, который называется <emphasis>головным элементом</emphasis> или <emphasis>головой</emphasis> (head), что дает возможность быстро и легко обращаться к первому элементу. В некольцевом связанном списке последний элемент отличается тем, что его указатель равен значению <code>NULL</code>. В кольцевом связанном списке последний элемент отличается тем, что указывает на головной элемент. Таким образом прохождение списка можно выполнить линейно, начиная с первого элемента и заканчивая последним. В двухсвязном списке прохождение можно также выполнить и в противоположном направлении, начиная с последнего и заканчивая первым элементом. Конечно, если задан определенный элемент списка, то можно перейти по списку вперед и назад на заданное количество элементов. При этом нет необходимости проходить весь список.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Реализация связанных списков в ядре Linux</p>
    </title>
    <section>
     <p>В ядре Linux для прохождения по связанным спискам используется унифицированный подход. При прохождении связанного списка, если не важен порядок прохода, эту операцию не обязательно начинать с головного элемента, на самом деле вообще не важно, с какого элемента списка начинать прохождение! Важно только, чтобы при таком прохождении были пройдены все узлы. В большинстве случаев нет необходимости вводить концепции первого и последнего элементов. Если в кольцевом связанном списке содержится коллекция несортированных данных, то <emphasis>любой</emphasis> элемент можно назвать головным. Для прохождения всего связанного списка необходимо взять любой элемент и следовать за указателями, пока снова не вернемся к тому элементу, с которого начали обход списка. Это избавляет от необходимости вводить специальный головной элемент. Кроме того, упрощаются процедуры работы со связанными списками. Каждая подпрограмма должна просто принимать указатель на один элемент — <emphasis>любой</emphasis> элемент списка. Разработчики ядра даже немножко гордятся такой остроумной реализацией.</p>
     <p>Связанные списки в ядре, так же как и в любой сложной программе, встречаются часто. Например, в ядре связанный список используется для хранения списка задач (структура <code>task_struct</code> каждого процесса является элементом связанного списка).</p>
    </section>
    <section>
     <title>
      <p>Структура элемента списка</p>
     </title>
     <p>Раньше в ядре было несколько реализаций связанных списков. Тем не менее в таких случаях необходима единая реализация с целью убрать разный код, который выполняет одинаковые действия. Во время разработки серии ядер 2.1 была предложена единая реализация связанных списков в ядре. Сегодня во всех подсистемах ядра используется официальная реализация. Для новых разработок необходимо использовать только существующий интерфейс и не нужно изобретать велосипед.</p>
     <p>Код работы со связанными списками определен в файле <code>&lt;linux/list.h&gt;</code>, a основная структура данных имеет очень простой вид.</p>
     <p><code>struct list_head {</code></p>
     <p><code> struct list_head *next, *prev;</code></p>
     <p><code>};</code></p>
     <p>Обратите внимание на характерное имя структуры <code>list_head</code>. Такое название выбрано, чтобы подчеркнуть, что списку не нужно головного элемента. Наоборот, обход списка можно начинать с любого элемента, и каждый элемент может быть головным. В связи с этим все элементы списка называются головными (list head). Указатель <code>next</code> указывает на следующий элемент списка, а указатель <code>prev</code> — на предыдущий элемент списка. Если текущий элемент списка является последним, то его указатель next указывает на первый узел. Если же текущим элементом является первый, то его указатель <code>prev</code> указывает на последний узел списка. Благодаря элегантной реализации связанных списков без концепции головного элемента, можно вообще не вводить понятия <emphasis>первого</emphasis> и <emphasis>последнего</emphasis> элементов.</p>
     <p>Структура <code>list_head</code> сама по себе бесполезна. Ее необходимо включать в другие структуры данных.</p>
     <p><code>struct my_struct {</code></p>
     <p><code> struct list_head list;</code></p>
     <p><code> unsigned long dog;</code></p>
     <p><code> void *cat;</code></p>
     <p><code>};</code></p>
     <p>Перед использованием элементы связанных списков должны быть инициализированы. Так как элементы связанных списков обычно создаются динамически (иначе, вероятно, не нужно использовать связанный список), то эти элементы, как правило, инициализируются во время выполнения.</p>
     <p><code>struct my_struct *p;</code></p>
     <p><code>/* выделить память для структуры my_struct ... */</code></p>
     <p><code>p-&gt;dog = 0;</code></p>
     <p><code>p-&gt;cat = NULL;</code></p>
     <p><code>INIT_LIST_HEAD(&amp;p-&gt;list);</code></p>
     <p>Если структура данных создается статически во время компиляции и на нее есть непосредственная ссылка, то инициализацию можно выполнить следующим образом.</p>
     <p><code>struct my_struct mine = {</code></p>
     <p><code> .list = LIST_HEAD_INIT(mine.list),</code></p>
     <p><code> .dog = 0,</code></p>
     <p><code> .cat = NULL</code></p>
     <p><code>};</code></p>
     <p>Для того чтобы создать и инициализировать связанный список, можно использовать следующее объявление.</p>
     <p><code>static LIST_HEAD(fox);</code></p>
     <p>Эта команда позволяет статически создать связанный список с именем <code>fox</code>.</p>
     <p>Нет необходимости явно выполнять какие-либо операции со служебными полями элементов связанного списка. Вместо этого необходимо просто включить структуру узла связанного списка в вашу структуру данных, чтобы можно было легко манипулировать данными и выполнять прохождение по связанному списку.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Работа со связанными списками</p>
    </title>
    <p>Для работы со связанными списками ядро предоставляет семейство функций. Все они принимают указатели на одну или более структур <code>list_head</code>. Все функции выполнены как функции с подстановкой тела (inline) на языке С, и их все можно найти в файле <code>&lt;linux/list.h&gt;</code>.</p>
    <p>Интересно, что время выполнения всех этих функций масштабируется как <emphasis>O(1)</emphasis><a l:href="#n97" type="note">[97]</a>. Это значит, что они выполняются в течение <emphasis>постоянного интервала</emphasis> времени независимо от количества элементов списка, для которого они вызываются. Например, время добавления элемента в связанный список из 3 и 3000 элементов будет одинаковым. Это, возможно, и не вызывает удивления, но тем не менее, приятно.</p>
    <p>Для добавления элемента в связанный список можно использовать следующую функцию.</p>
    <p><code>list_add(struct list_head *new, struct list head *head);</code></p>
    <p>Эта функция добавляет узел <code>new</code> в заданный связанный список сразу после элемента <code>head</code>. Поскольку связанный список является кольцевым и для него не существует понятий <emphasis>первого</emphasis> и <emphasis>последнего</emphasis> узлов, в качестве параметра <code>head</code> можно использовать указатель на любой элемент списка. Если в качестве рассмотренного параметра всегда передавать указатель на последний элемент, то эту функцию можно использовать для организации стека.</p>
    <p>Для добавления элемента в конец связанного списка служит следующая функция.</p>
    <p><code>list_add_tail(struct list_head *new,</code></p>
    <p><code> struct list_head *head);</code></p>
    <p>Эта функция добавляет новый элемент new в связанный список сразу перед элементом, на который указывает параметр <code>head</code>. Поскольку связанный список является кольцевым, то, как и в случае функции <code>list_add()</code>, в качестве параметра <code>head</code> можно передавать указатель на любой элемент списка. Эту функцию можно использовать для реализации очереди, если передавать указатель на первый элемент.</p>
    <p>Для удаления узла списка служит следующая функция.</p>
    <p><code>list_del(struct list_head *entry);</code></p>
    <p>Эта функция позволяет удалить из списка элемент, на который указывает параметр <code>entry</code>. Обратите внимание, что эта функция не освобождает память, выделенную под структуру данных, содержащую узел списка, на который указывает параметр <code>entry</code>. Данная функция просто удаляет узел из списка. После вызова этой функции обычно необходимо удалить структуру данных, в которой находится узел <code>list_head</code>.</p>
    <p>Для удаления узла из списка и повторной инициализации этого узла служит следующая функция.</p>
    <p><code>list_del_init(struct list head *entry);</code></p>
    <p>Эта. функция аналогична функции <code>list_del()</code>, за исключением того, что она дополнительно инициализирует указанную структуру <code>list_head</code> из тех соображений, что эта структура данных больше не нужна в качестве элемента текущего списка и ее повторно можно использовать.</p>
    <p>Для перемещения узла из одного списка в другой предназначена следующая функция.</p>
    <p><code>list_move(struct list_head *list, struct list_head *head);</code></p>
    <p>Эта функция удаляет элемент <code>list</code> из одного связанного списка и добавляет его в другой связанный список после элемента <code>head</code>.</p>
    <p>Для перемещения элемента из одного связанного списка в конец другого служит следующая функция.</p>
    <p><code>list_move_tail(struct list_head *list,</code></p>
    <p><code> struct list_head *head);</code></p>
    <p>Эта функция выполняет то же самое, что и функция <code>list_move()</code>, но вставляет элемент перед элементом <code>head</code>.</p>
    <p>Для проверки того, пуст ли список, служит функция.</p>
    <p><code>list_empty(struct list_head *head);</code></p>
    <p>Эта функция возвращает ненулевое значение, если связанный список пуст, и нулевое значение в противном случае.</p>
    <p>Для объединения двух не перекрывающихся связанных списков служит следующая функция.</p>
    <p><code>list_splice(struct list_head *list,</code></p>
    <p><code> struct list_head *head);</code></p>
    <p>Эта функция вставляет список, на который указывает параметр <code>list</code>, в другой список после параметра head.</p>
    <p>Для объединения двух не перекрывающихся списков и повторной инициализации старого головного элемента служит следующая функция.</p>
    <p><code>list splice_init(struct list head *list, struct list head *head);</code></p>
    <p>Эта функция аналогична функции <code>list_splice()</code>, за исключением того, что параметр <code>list</code>, представляющий список, из которого удаляются элементы, повторно инициализируется.</p>
    <cite>
     <subtitle>Как избежать двух лишних разыменований</subtitle>
     <p>Если вам уже доступны указатели <code>next</code> и <code>prev</code>, то можно сэкономить пару процессорных тактов (в частности, время выполнения операций разыменования указателей) путем вызова внутренних функций работы со связанными списками. Все ранее рассмотренные функции в сущности не делают ничего, кроме получения указателей <code>next</code> и <code>prev</code> и вызовов внутренних функций. Внутренние функции имеют те же имена, что и их оболочки, но перед именем используется два символа подчеркивания. Вместо того чтобы вызвать функцию <code>list_del(list)</code>, можно вызвать функцию <code>__list_del(prev, next)</code>. Это имеет смысл, только когда указанные указатели уже известны. В противном случае просто получится некрасивый код. Для подробной информации об этих интерфейсах можно обратиться к файлу <code>&lt;linux/list.h&gt;</code>.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>Перемещение по связанным спискам</p>
    </title>
    <p>Теперь мы уже знаем, как объявлять, инициализировать и работать со связанными списками в ядре. Это все хорошо, но не имеет никакого смысла, если нет возможности работать С данными, которые хранятся в списках! Связанный список — это просто контейнер, в котором хранятся важные данные. Необходимо иметь способ перемещения по списку и доступа к данным. К счастью, ядро предоставляет набор полезных интерфейсов для перемещения по связанным спискам и обращения к структурам данных, которые хранятся в этих списках.</p>
    <p>Обратите внимание, что, в отличие от подпрограмм управления списками, операции перебора элементов списка из <code>n</code> узлов масштабируются как <emphasis>O(n)</emphasis>.</p>
    <p>Наиболее простой способ выполнять итерации по элементам связанного списка — это использовать макрос <code>list_for_each()</code>. Этот макрос принимает два параметра — указатели на структуры <code>list_head</code>. Первый параметр указывает на текущий элемент списка, а второй — на любой элемент списка, для которого необходимо обойти все узлы. На каждой итерации цикла первый параметр макроса указывает на текущий элемент списка, пока не будут пройдены все элементы, как в следующем примере.</p>
    <p><code>struct list_head *p;</code></p>
    <p><code>list_for_each(p, list) {</code></p>
    <p><code> /* p указывает на каждый элемент списка list */</code></p>
    <p><code>}</code></p>
    <p>Это пока все еще бесполезно! Указатель на структуру узла списка — это не то, что нам нужно. Нам нужен указатель на структуру данных, в которой содержится структура узла. В показанном ранее примере структуры данных <code>my_struct</code> необходимо получить указатель на каждый экземпляр структуры <code>my_struct</code>, а не на их поля <code>list</code>. Макрос <code>list_entry()</code> возвращает структуру данных, которая содержит соответствующий элемент <code>list_head</code>. Этот макрос принимает три параметра: указатель на текущий узел, тип структуры данных, в которую включен узел списка, и имя поля структуры данных, в которой хранится этот узел.</p>
    <p><code>struct list_head *p;</code></p>
    <p><code>struct my_struct *my;</code></p>
    <empty-line/>
    <p><code>list_for_each(p, mine-&gt;list) {</code></p>
    <p><code> my = list_entry(p, struct my_struct, list);</code></p>
    <p><code> /*</code></p>
    <p><code> * указатель my указывает на все структуры данных,</code></p>
    <p><code> * в которые включено поле list</code></p>
    <p><code> */</code></p>
    <p><code>}</code></p>
    <p>Макрос <code>list_for_each()</code> раскрывается в обычный цикл <code>for</code>. Предыдущий пример раскрывается следующим образом.</p>
    <p><code>for (p = mine-&gt;list-&gt;next; p != mine-&gt;list; p = p-&gt;next)</code></p>
    <p>Кроме этого, макрос <code>list_for_each()</code> также выполняет предварительную загрузку (prefetch) данных в память, если процессор поддерживает такую возможность, чтобы все данные следующих элементов списка гарантированно находились в памяти. Когда нет необходимости выполнять предварительную загрузку, можно использовать макрос <code>__list_for_each()</code>, который работает в точности, как цикл <code>for</code>. Если нет гарантии, что список содержит очень мало элементов или пустой, то всегда необходимо использовать версию с предварительной загрузкой. Никогда нельзя программировать цикл вручную, необходимо всегда использовать макрос.</p>
    <p>Если необходимо выполнить прохождение по спискам в обратном порядке, то следует использовать макрос <code>list_for_each_prev()</code>, который использует для прохождения указатель <code>prev</code>, а не указатель <code>next</code>.</p>
    <p>Обратите внимание, что при прохождении связанного списка ничто не мешает удалять элементы из этого списка. Обычно, чтобы предотвратить конкурентный доступ, следует использовать блокировки. Макрос <code>list_for_each_safe()</code> использует временные переменные, чтобы сделать прохождение списка безопасным при одновременном удалении элементов.</p>
    <p><code>struct list_head *p, *n;</code></p>
    <p><code>struct my_struct *my;</code></p>
    <empty-line/>
    <p><code>list_for_each_safe(p, n, &amp;mine-&gt;list) {</code></p>
    <p><code> my = list_entry(p, struct my_struct, list);</code></p>
    <p><code> /*</code></p>
    <p><code> * указатель my указывает на каждый экземпляр</code></p>
    <p><code> * структуры my_struct в списке</code></p>
    <p><code> */</code></p>
    <p><code>}</code></p>
    <p>Обратите внимание, что этот макрос защищен <emphasis>только</emphasis> от операций удаления узлов списка. Для защиты отдельных элементов списка от конкурентного доступа необходимо использовать блокировки.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Приложение Б</p>
    <p>Генератор случайных чисел ядра</p>
   </title>
   <section>
    <p>В ядре Linux реализован генератор случайных чисел, который теоретически может генерировать <emphasis>истинно случайные числа</emphasis>. Генератор случайных чисел собирает в <emphasis>пул энтропии</emphasis> шумы внешней среды, которые поступают из драйверов устройств. Этот пул доступен как в ядре, так и для пользовательских процессов в качестве источника данных, которые не только случайны внутри системы, но и недетерминированы для внешних источников атак. Такие случайные числа используются различными внешними приложениями, особенно для целей криптографии.</p>
    <p>Истинно случайные числа отличаются от псевдослучайных чисел, которые генерируются библиотечными функциями языка С. Псевдослучайные числа создаются с помощью детерминированных функций. Хотя такие функции и могут генерировать последовательности чисел, которые обладают некоторыми свойствами истинно случайных чисел, тем не менее такие числа только статистически случайны. Псевдослучайные числа являются детерминированными, потому что если известно хотя бы одно число последовательности, то можно определить и все остальные. Если известно так называемое <emphasis>порождающее число</emphasis> последовательности (<emphasis>seed</emphasis>), то обычно по нему определяется и вся последовательность. Для приложений, которые требуют истинно случайных чисел, как, например, криптография, псевдослучайные числа обычно не подходят.</p>
    <p>В отличие от псевдослучайных чисел, истинно случайные числа не зависят от той функции, которая используется для их генерации. Более того, если известен некоторый член последовательности истинно случайных чисел, то внешний наблюдатель не сможет определить, какие числа будет выдавать генератор в будущем, т.е. такой генератор — недетерминированный.</p>
    <p>Физический термин <emphasis>энтропия</emphasis> — это мера беспорядка и случайности в любой системе. Энтропия измеряется в единицах энергии на единицу температуры (Джоуль на градус Кельвина). Когда Клод Шеннон (Claude Shennon)<a l:href="#n98" type="note">[98]</a>, создатель информационной теории, искал термин для представления случайности информации, великий математик Джон фон Нейман (John von Neumann)<a l:href="#n99" type="note">[99]</a> предложил ему использовать термин энтропия, потому что никто толком не понимает, что за этим понятием кроется. Шеннон согласился, и сегодня это звучит как <emphasis>энтропия Шеннона</emphasis>. Некоторые ученые считают, что такое двойное название только вносит путаницу, и когда речь идет об информации, то используют термин <emphasis>неопределенность</emphasis>. Разработчики ядра, наоборот, считают, что "энтропия" — это "круто", и поддерживают использование данного термина.</p>
    <p>При рассмотрении генераторов случайных чисел понятие энтропии Шеннона является очень важным. Эта характеристика измеряется в битах на символ. Высокое значение энтропии означает, что в последовательности символов мало полезной (точнее, предсказуемой) информации и много случайного "мусора". Ядро поддерживает пул энтропии, который пополняется данными, возникающими в результате недетерминированных событий, связанных с аппаратными устройствами. В идеале, этот пул содержит полностью случайные данные. Для того чтобы иметь представление о значении энтропии пула, ядро постоянно вычисляет меру неопределенности данных в пуле. По мере того как ядро добавляет данные в пул, оно оценивает меру случайности добавляемых данных. И наоборот, по мере того как данные извлекаются из пула, ядро уменьшает значение оценки энтропии. Соответствующая количественная характеристика называется <emphasis>оценкой энтропии</emphasis>. Если значение оценки энтропии становится равным нулю, то ядро может отказаться выполнять запрос по считыванию данных из пула.</p>
    <p>Генератор случайных чисел ядра был предложен в версии 1.3.30 и находится в файле <code>drivers/char/random.c</code>.</p>
   </section>
   <section>
    <title>
     <p>Принцип работы и реализация</p>
    </title>
    <section>
     <p>Компьютеры — это предсказуемые устройства. Действительно, трудно найти случайное поведение в системе, поведение которой можно практически полностью программировать. Однако окружающая среда, где находится машина, полна различных шумов, которые недетерминированы и которые можно измерить. Источники таких шумов включают моменты времени, в которые возникают события, связанные с аппаратными устройствами, а также события, связанные с взаимодействием пользователей и компьютера. Например, интервалы времени между нажатиями клавиш, перемещения мыши, интервалы времени между некоторыми типами прерываний и время выполнения запроса блочного ввода-вывода являются недетерминированными, и, кроме того, их не может измерить внешний злоумышленник. Случайная информация, которая получается из этих событий, записывается в пул энтропии. Пул растет и заполняется случайными и непредсказуемыми шумовыми данными. По мере добавления данных в пул вычисляется оценка энтропии, и итоговое значение запоминается. Это позволяет всегда иметь информацию о значении энтропии в пуле. На рис. Б. 1 показана диаграмма прохождения потока энтропии в пул и из пула.</p>
     <image l:href="#img_31.jpeg"/>
     <p><strong>Рис. Б.1</strong>. Прохождение энтропии через пул энтропии ядра</p>
     <p>Для доступа к пулу энтропии, как из пространства ядра, так и из пространства пользователя, ядро предоставляет набор интерфейсов. Когда выполняется обращение к этим интерфейсам, ядро вначале вычисляет хеш-значение <emphasis>SHA</emphasis> данных из пула. Алгоритм SHA (Secure Hash Algorithm, алгоритм вычисления безопасного хеш- значения) — это алгоритм вычисления дайджеста сообщения (профиля сообщения, message digest), который был разработан Агентством национальной безопасности (National Security Agency, NSA) и утвержден в качестве федерального стандарта США Национальным институтом стандартов и технологий (NIST) (федеральный стандарт по обработке информации, FIPS 186). Вычисление дайджеста сообщения выполняется с помощью специального алгоритма, который принимает сообщение переменного размера (большое или маленькое) и выдает на выходе хеш-значение фиксированного размера (обычно размером 128 или 160 байт). Это фиксированное значение и представляет собой дайджест. Входное сообщение не может быть реконструировано по его хеш-значению. Более того, простое изменение входного сообщения (например, изменение одного символа) приведет к радикальному изменению хеш-значения. Алгоритмы вычисления дайджестов сообщений могут использоваться по-разному, включая проверку подлинности данных и дактилоскопию. Другие алгоритмы вычисления дайджестов — это MD4 и MD5. Пользователю возвращается хеш-значение SHA пула, к содержимому пула энтропии непосредственно обращаться нельзя. Считается, что по хеш-значению невозможно получить никакую информацию о состоянии пула. Поэтому если известно несколько значений из пула, то это не дает никакой информации о прошлых и будущих значениях. Ядро может использовать оценку энтропии и отказаться выполнить запрос на считывание данных из пула, если значение энтропии равно нулю. По мере того как из пула считываются данные, оценка энтропии уменьшается. Это реакция на то, что о пуле становится известно больше информации.</p>
     <p>Когда значение оценки энтропии достигает нуля, то ядро все равно может возвращать случайные числа. Однако в этом случае теоретически появляется возможность того, что злоумышленник сможет предугадать результат вывода. Для этого требуются все результаты вывода из пула энтропии, а также чтобы злоумышленник смог выполнить криптографический анализ алгоритма SHA. Так как алгоритм SHA считается безопасным, то это невозможно. Для высоконадежной криптографии оценка энтропии позволяет гарантировать устойчивость случайных чисел. Для большинства пользователей такая дополнительная гарантия не нужна.</p>
     <cite>
      <subtitle>Почему это реализовано в ядре?</subtitle>
      <p>Критерием того, что какую-либо возможность необходимо реализовать в ядре, является сложность реализации этой возможности в пространстве пользователя. Недопустимо вводить что-либо в ядро только потому, что мы это можем сделать. Может показаться, что генератору случайных чисел и пулу энтропии не место в ядре. Однако существует, по крайней мере, три причины, по которым они должны быть в ядре. Во первых, генератору необходим доступ к системным событиям, таким как прерывания и ввод данных пользователями. Для обеспечения доступа к информации об этих событиях из пространства пользователя необходимо экспортировать специальные интерфейсы, чтобы информировать пространство пользователя о том, что эти события произошли. Даже если эти данные будут экспортироваться, то доступ к ним будет не простым и не быстрым. Во-вторых, генератор случайных чисел должен быть безопасным. Хотя такая система и может выполняться с правами пользователя root, тем не менее ядро является значительно более безопасным местом для пула энтропии. И наконец, самому ядру также необходимы случайные числа. Получать информацию о случайных числах, которая необходима ядру, из пространства пользователя — это не практично. В связи с этим генератор случайных чисел работает в ядре.</p>
     </cite>
    </section>
    <section>
     <title>
      <p>Проблема с загрузкой системы</p>
     </title>
     <p>Когда ядро загружается, оно выполняет последовательность действий, которые гарантированно можно предугадать. Следовательно, злоумышленник может предсказать состояние пула энтропии на этапе загрузки. Еще хуже то, что каждая загрузка очень похожа на все остальные и пул инициализируется при каждой загрузке в очень близкие значения. Это уменьшает точность оценки энтропии, потому что нет никакого способа обеспечить, чтобы энтропия, которая добавляется на этапе загрузки, была менее предсказуема, чем энтропия, которая добавляется при такой же загрузке в другое время.</p>
     <p>Для решения проблемы большинство Linux-систем сохраняет на диске содержимое части пула энтропии между перегрузками системы. При старте системы сохраненные данные считываются и записываются в пул энтропии. Загрузка предыдущего содержимого пула в текущий пул позволяет обойтись без увеличения оценки энтропии.</p>
     <p>Таким образом, злоумышленник не может предугадать состояние пула энтропии, не зная одновременно <emphasis>предыдущего</emphasis> и текущего состояний системы.</p>
    </section>
   </section>
   <section>
    <title>
     <p>Интерфейсы для ввода энтропии</p>
    </title>
    <p>Ядро экспортирует следующее семейство интерфейсов, которые могут использоваться драйверами и системами для ввода данных в пул энтропии.</p>
    <p><code>void add_interrupt_randomness(int irq);</code></p>
    <p><code>void add_keyboard_randomness(unsigned char scancode);</code></p>
    <p><code>void add_mouse_randomness(__u32 mouse_data);</code></p>
    <p>Функция <code>add_interrupt_randomness()</code> вызывается системой обработки прерываний, когда приходит прерывание, обработчик которого зарегистрирован с флагом <code>SA_SAMPLE_RANDOM</code>. Параметр <code>irq</code> — это номер прерывания. Генератор случайных чисел использует интервалы времени между прерываниями, как источник шума. Следует помнить, что не все устройства для этого подходят. Если устройства генерируют прерывания детерминированным образом (например, прерывания таймера) или на них может воздействовать внешний злоумышленник (например, сетевые устройства), то такие устройства нельзя использовать для ввода информации в пул. Подходящее устройство — жесткий диск, который генерирует прерывания с непредсказуемой частотой.</p>
    <p>Функция <code>add_keyboard_randomness()</code> использует скан-коды и интервалы времени между нажатиями клавиш для ввода энтропии в пул. Интересно, что эта функция достаточно интеллектуальна и игнорирует повторение символов при постоянном нажатии клавиши, потому что повторяющиеся скан-коды и интервалы времени вносят мало энтропии.</p>
    <p>Функция <code>add_mouse_randomness()</code> использует позицию указателя мыши и интервалы времени между прерываниями для заполнения пула. Параметр <code>mouse_data</code> — это позиция указателя, которая возвращается аппаратным обеспечением.</p>
    <p>Эти три функции добавляют передаваемые данные в пул энтропии, вычисляют оценку энтропии добавляемых данных и увеличивают оценку энтропии пула на вычисленное значение.</p>
    <p>Все эти экспортируемые интерфейсы используют внутреннюю функцию <code>add_timer_randomness()</code> для ввода данных в пул. Эта функция вычисляет интервалы времени между успешными событиями одного типа и добавляет эти значения в пул. Например, интервалы времени между успешными прерываниями жесткого диска достаточно случайны, особенно если измерять достаточно точно. Самые младшие биты — это обычно электрический шум. После того как эта функция вводит данные в пул, она вычисляет количественную характеристику того, насколько эти данные случайны. Это делается путем вычисления отклонения первого, второго и третьего порядка от предыдущего момента времени и изменения этих отклонений первого, второго и третьего порядка. Наибольшее из этих отклонений, округленное до 12 бит, используется в качестве оценки энтропии.</p>
   </section>
   <section>
    <title>
     <p>Интерфейсы для вывода энтропии</p>
    </title>
    <p>Для получения случайных чисел внутри ядра экспортируется один интерфейс.</p>
    <p><code>void get_random_bytes(void *buf, int nbytes);</code></p>
    <p>Эта функция сохраняет <code>nbytes</code> случайных байтов в буфере памяти, на который указывает параметр <code>buf</code>. Функция возвращает данные, даже если оценка энтропии равна нулю. Для ядра это не так критично, как для пользовательских криптографических программ. Случайные данные мало используются в ядре, в основном они нужны сетевой подсистеме для генерации стартового номера последовательности сегментов при соединении по протоколу TCP.</p>
    <p>Код ядра может выполнить следующий код для получения случайных данных размером в одно машинное слово.</p>
    <p><code>unsigned long rand;</code></p>
    <empty-line/>
    <p><code>get_random_bytes(&amp;rand, sizeof(rand));</code></p>
    <p>Для программ, которые выполняются в пространстве пользователя, предоставляется два символьных устройства: <code>/dev/random</code> и <code>/dev/urandom</code>. Первое устройство, <code>/dev/random</code>, используется, когда необходимы гарантированно случайные данные для криптографических приложений с высоким уровнем безопасности. Это устройство выдает только то количество битов данных, которое соответствует оценке энтропии в ядре. Когда оценка энтропии становится равной нулю, операция чтения устройства <code>/dev/random</code> блокируется и не возвращает данные, пока значение энтропии не станет существенно положительным. Устройство <code>/dev/urandom</code> не имеет последней возможности, а в остальном работает аналогично. Оба устройства возвращают данные из одного и того же пула.</p>
    <p>Чтение из обоих файлов выполняется очень просто. Ниже показана функция пользовательской программы, которая служит для считывания одного машинного слова случайных данных.</p>
    <p><code>unsigned long get_random(void) {</code></p>
    <p><code> unsigned long seed = 0;</code></p>
    <p><code> int fd;</code></p>
    <empty-line/>
    <p><code> fd = open("/dev/urandom", O_RDONLY);</code></p>
    <p><code> if (fd == -1) {</code></p>
    <p><code>  perror("open");</code></p>
    <p><code>  return 0;</code></p>
    <p><code> }</code></p>
    <p><code> if (read(fd, &amp;seed, sizeof(seed)) &lt; 0) {</code></p>
    <p><code>  perror("read");</code></p>
    <p><code>  seed = 0;</code></p>
    <p><code> }</code></p>
    <p><code> if (close(fd))</code></p>
    <p><code>  perror("close");</code></p>
    <empty-line/>
    <p><code> return seed;</code></p>
    <p><code>}</code></p>
    <p>Можно также считать <code>$bytes</code> байтов в файл <code>$file</code>, используя программу <code>dd</code>.</p>
    <p><code>dd if=/dev/urandom of=$file count=1 bs=$bytes</code></p>
   </section>
  </section>
  <section>
   <title>
    <p>Приложение В</p>
    <p>Сложность алгоритмов</p>
   </title>
   <section>
    <p>В компьютерных и связанных с ними дисциплинах полезно выражать сложность, или <emphasis>масштабируемость</emphasis>, алгоритмов с помощью количественных значащих характеристик (в отличие от менее наглядных характеристик, таких как быстрый или медленный). Существуют различные методы представления масштабируемости. Один из наиболее часто используемых подходов — это исследование асимптотического <emphasis>поведения алгоритмов</emphasis>. Асимптотическое поведение — это поведение алгоритма при достаточно больших значениях входных параметров или, другими словами, при <emphasis>стремлении</emphasis> входных параметров <emphasis>к бесконечности</emphasis>. Асимптотическое поведение показывает, как масштабируется алгоритм, когда его входные параметры принимают все большие и большие значения. Исследование масштабируемости алгоритмов, т.е. изучение свойств алгоритма при больших значениях входных параметров, позволяет смоделировать поведение алгоритма по отношению к тестовым задачам и лучше понять особенности этого поведения.</p>
   </section>
   <section>
    <title>
     <p>Алгоритмы</p>
    </title>
    <p>Алгоритм — это последовательность действий, возможно, с одним входом или более и, в конечном счете, с одним результатом или выходом. Например, подсчет количества людей в комнате представляет собой алгоритм, для которого люди, находящиеся в комнате, являются входными данными, а количество людей в комнате — выходными данными. Операции замещения страниц в ядре Linux или планирование выполнения процессов — это тоже примеры алгоритмов. Математически алгоритм аналогичен функции (или, по крайней мере, может быть смоделирован с помощью функции). Например, если мы обозначим алгоритм подсчета людей в комнате буквой <code>f</code>, а количество людей, которых необходимо посчитать, буквой <code>x</code>, то функцию подсчета количества людей можно записать следующим образом.</p>
    <p><code>y=f(x)</code></p>
    <p>В этом выражении буквой <code>y</code> обозначено время подсчета количества людей в комнате.</p>
   </section>
   <section>
    <title>
     <p>Множество О</p>
    </title>
    <p>Полезным обозначением асимптотического поведения функции является верхняя граница — функция, значения которой всегда больше значений изучаемой функции. Говорят, что верхняя граница некоторой функции растет быстрее, чем рассматриваемая функция. Специальное обозначение "большого-O" используется для описания этого роста. Это записывается как <emphasis>f</emphasis>(<emphasis>x</emphasis>) &#8712; <emphasis>О</emphasis>(<emphasis>g</emphasis>(<emphasis>x</emphasis>)) и читается так: <emphasis>f</emphasis> принадлежит множеству "O-большого" от <emphasis>g</emphasis>. Формальное математическое определение имеет следующий вид.</p>
    <cite>
     <p>Если <emphasis>f</emphasis>(<emphasis>x</emphasis>) принадлежит множеству большого <emphasis>O</emphasis>(<emphasis>g</emphasis>(<emphasis>x</emphasis>)) , то &#8707;<emphasis>c</emphasis> и <emphasis>x</emphasis>', такие что <emphasis>f</emphasis>(<emphasis>x</emphasis>)&#8804;c&#8729;<emphasis>g</emphasis>(<emphasis>x</emphasis>), &#8704;<emphasis>x</emphasis>&gt;<emphasis>x</emphasis>'</p>
    </cite>
    <p>Это означает, что время вычисления функции <emphasis>f</emphasis>(<emphasis>x</emphasis>) всегда меньше времени вычисления функции <emphasis>g</emphasis>(<emphasis>x</emphasis>), умноженного на некоторую константу, и это справедливо всегда, для всех значений <emphasis>x</emphasis>, больших некоторого начального значения <emphasis>х</emphasis>'.</p>
    <p>Другими словами, мы ищем функцию, которая ведет себя не лучше, чем наш алгоритм в наихудшей ситуации. Можно посмотреть на результаты того, как ведет себя функция при очень больших значениях входных параметров, и понять, как ведет себя алгоритм.</p>
   </section>
   <section>
    <title>
     <p>Множество большого-тета</p>
    </title>
    <p>Когда говорят об обозначении большого-О, то чаще всего имеют в виду то, что Дональд Кнут (Donald Knuth) описывал с помощью обозначения "большого-тета". Обозначение "большого-О" соответствует верхней границе. Например, число 7 — это верхняя граница числа 6, кроме того, числа 9, 12 и 65 — это тоже верхние границы числа 6. Когда рассматривают рост функции, то обычно наиболее интересна <emphasis>наименьшая верхняя граница</emphasis> или функция, которая моделирует как верхнюю, так и нижнюю границу<a l:href="#n100" type="note">[100]</a>. Профессор Кнут описывает это с помощью обозначения большого-тета следующим образом.</p>
    <cite>
     <p>Если <emphasis>f</emphasis>(<emphasis>x</emphasis>) принадлежит множеству большого-тета от <emphasis>g</emphasis>(<emphasis>x</emphasis>), то <emphasis>g</emphasis>(<emphasis>x</emphasis>) является одновременно и верхней и нижней границей <emphasis>f</emphasis>(<emphasis>x</emphasis>)</p>
    </cite>
    <p>Можно также сказать, что функция <emphasis>f</emphasis>(<emphasis>x</emphasis>) порядка функции <emphasis>g</emphasis>(<emphasis>x</emphasis>). Порядок, или множество "большого-тета" алгоритма, — один из наиболее важных математических инструментов изучения алгоритмов.</p>
    <p>Следовательно, когда говорят об обозначении большого-О, то чаще всего имеют в виду наименьший возможный вариант "большого-О" — "большое-тета". Об этом не нужно особо волноваться, если, конечно, нет желания доставить удовольствие профессору Кнуту.</p>
   </section>
   <section>
    <title>
     <p>Объединяем все вместе</p>
    </title>
    <p>Вернемся снова к подсчету количества людей в комнате. Допустим, что можно считать по одному человеку за секунду. Следовательно, если в комнате находится 7 человек, то подсчет займет 7 секунд. Очевидно, что если будет <emphasis>n</emphasis> человек, то подсчет всех займет <emphasis>n</emphasis> секунд. Поэтому можно сказать, что этот алгоритм масштабируется, как <emphasis>O</emphasis>(<emphasis>n</emphasis>). Что если задача будет состоять в том, чтобы станцевать перед всеми, кто находится в комнате? Поскольку, независимо от того, сколько человек будет в комнате, это займет одно и то же время, значит, этот алгоритм масштабируется, как <emphasis>O</emphasis>(1). В табл. В.1 показаны другие часто встречающиеся характеристики сложности.</p>
    <empty-line/>
    <p><strong>Таблица В.1</strong>. Значения масштабируемости алгоритмов во времени</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><emphasis>O</emphasis>(<emphasis>g</emphasis>(<emphasis>x</emphasis>))</td>
      <td align="left" valign="top">Название</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">1</td>
      <td align="left" valign="top">Постоянная (отличная масштабируемость)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">log(<emphasis>n</emphasis>)</td>
      <td align="left" valign="top">Логарифмическая</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><emphasis>n</emphasis></td>
      <td align="left" valign="top">Линейная</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><emphasis>n</emphasis>&#178;</td>
      <td align="left" valign="top">Квадратичная</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><emphasis>n</emphasis>&#179;</td>
      <td align="left" valign="top">Кубическая</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">2&#8319;</td>
      <td align="left" valign="top">Показательная, или экспоненциальная (плохо)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><emphasis>n</emphasis>!</td>
      <td align="left" valign="top">Факториал (очень плохо)</td>
     </tr>
    </table>
    <p>Как масштабируется алгоритм представления всех людей в комнате друг другу? Какая функция может промоделировать этот алгоритм? Для представления одного человека необходимо 30 секунд, сколько времени займет представление 10 человек друг другу? Что будет в случае 100 человек?</p>
   </section>
   <section>
    <title>
     <p>Опасность, связанная со сложностью алгоритмов</p>
    </title>
    <p>Очевидно, что будет разумным избегать алгоритмов, которые масштабируются, как <emphasis>О</emphasis>(<emphasis>n</emphasis>!) или <emphasis>O</emphasis>(2&#8319;). Более того, замена алгоритма, который масштабируется, как <emphasis>O</emphasis>(<emphasis>n</emphasis>), алгоритмом, который масштабируется, как <emphasis>O</emphasis>(1), — это обычно серьезное улучшение. Тем не менее это не всегда так, и нельзя принимать решение вслепую, базируясь только на описании "большого-О". Вспомните, что в определении множества <emphasis>О</emphasis>(<emphasis>g</emphasis>(<emphasis>x</emphasis>)) фигурирует константа, на которую умножается значение функции <emphasis>g</emphasis>(<emphasis>x</emphasis>). Поэтому есть возможность, что алгоритм, который масштабируется, как <emphasis>O</emphasis>(<emphasis>1</emphasis>), будет выполняться в течение 3 часов. Следовательно, он будет выполняться всегда в течение 3 часов, независимо от количества входных данных, но это может оказаться дольше, по сравнению с алгоритмом, который масштабируется, как <emphasis>O</emphasis>(<emphasis>n</emphasis>), при небольшом количестве входных данных. При сравнении алгоритмов необходимо всегда принимать во внимание количество входных данных. Не стоит слепо оптимизировать для некоторого случайно выбранного варианта.</p>
   </section>
  </section>
  <section>
   <title>
    <p>Приложение Г</p>
    <p>Библиография и список литературы</p>
   </title>
   <section>
    <p>Список литературы отсортирован и содержит некоторые из самых интересных и полезных книг, которые близки по теме, или дополняют материал данной книги.</p>
    <p>Полезность этих книг проверена временем. Некоторые из них представляют собой "священные писания" но соответствующим темам, в то время как другие просто кажутся автору интересными, глубокими или занимательными. Автор надеется, что читателю они тоже окажутся полезными.</p>
    <p>Наилучшая ссылка на "дополнительное чтение", которая лучше всего дополняет материал данной книги — это исходный код ядра. Для работы с ОС Linux у нас есть неограниченный доступ к полному исходному коду ядра современной операционной системы. Не принимайте это как должное! Разберитесь с ним! Читайте код! Пишите код!</p>
   </section>
   <section>
    <title>
     <p>Книги по основам построения операционных систем</p>
    </title>
    <p>В этих книгах рассмотрены принципы работы операционных систем в объеме учебных курсов. В них описываются основные понятия, алгоритмы и проблемы, связанные с построением высокофункциональных операционных систем, а также решения указанных проблем. Все эти книги могут быть рекомендованы, но если нужно выделить одну, то это, конечно, книга H. Deitel.</p>
    <p>• Deitel H., Deitel P. and Choffnes D. <emphasis>Operating Systems</emphasis>. Prentice Hall, 2003. Прекрасная книга по теории операционных систем с отличными примерами из теории и практики. Автор помогал в техническом редактировании этой книги, что, может быть, и является причиной его предвзятого отношения, но все же хочется верить, что от этого книга стала значительно лучше.</p>
    <p>• Tanenbaum Andrew. <emphasis>Operating Systems: Design and Implementation</emphasis>.. Prentice Hall, 1997. Хорошие начальные сведения об основах построения, принципах работы и реализации Unix-подобной операционной системы Minix.</p>
    <p>• Tanenbaum Andrew. <emphasis>Modern Operating Systems</emphasis>. Prentice Hall, 2001. Детальный обзор стандартных проблем разработки операционных систем, а также обсуждение многих концепций, которые используются в современных операционных системах, таких как Unix и Windows.</p>
    <p>• Silberschatz A., Galvin P. and Gagne G. <emphasis>Operating System Concepts</emphasis>. John Wiley and Sons, 2001. Также известна, как "книга про динозавров", в связи с тем что на обложке нарисованы динозавры, которые не имеют никакого отношения к теме. Хорошее введение в основы построения операционных систем. Книга часто перерабатывается, но все издания должны быть хорошими.</p>
   </section>
   <section>
    <title>
     <p>Книги о ядрах Unix</p>
    </title>
    <p>В этих книгах описываются принципы работы и особенности реализации ядер Unix. В первых пяти рассмотрены конкретные варианты Unix, в двух последних — общие моменты всех вариантов Unix.</p>
    <p>• Bach Maurice. <emphasis>The Design of the Unix Operating System</emphasis>. Prentice Hall, 1986. Обсуждение особенностей построения операционной системы Unix System V, Release 2.</p>
    <p>• McKusick M., Bostic K., Karels M. and Quarterman J. <emphasis>The Design and Implementation of the 4.4BSD Operating System</emphasis>. Addison-Wesley, 1996. Описание особенностей построения и реализации операционной системы 4.4BSD от разработчиков этой системы.</p>
    <p>• McKusick M. and Neville-Neil G. <emphasis>The Design and Implementation of the FreeBSD Operating System</emphasis>. Addison-Wesley, 2004. Основы построения операционной системы FreeBSD 5.</p>
    <p>• Mauro J. and McDougall R. <emphasis>Solaris Internals: Core Kernel Architecture</emphasis>. Prentice Hall, 2000. Интересное обсуждение основных подсистем и алгоритмов работы ядра ОС Solaris.</p>
    <p>• Cooper С. and Moore С. <emphasis>HP-UX 11i Internals</emphasis>. Prentice Hall, 2004. Обзор внутреннего устройства операционной системы HP-UX аппаратной платформы PA-RISC.</p>
    <p>• Vahalia, Uresh. <emphasis>Unix Internals: The New Frontiers</emphasis>. Prentice Hall, 1995. Отличная книга о возможностях современных Unix-подобных операционных систем, включая управление потоками и вытеснением кода в режиме ядра.</p>
    <p>• Schimmel Curt. <emphasis>UNIX Systems for Modern Architectures: Symmetric Multiprocessing and Caching for Kernel Programmers</emphasis>. Addison-Wesley, 1994. Прекрасная книга о проблемах поддержки современных аппаратных платформ современными Unix-подобными операционными системами.</p>
   </section>
   <section>
    <title>
     <p>Книги о ядрах Linux</p>
    </title>
    <p>В этих книгах, как и в текущей, рассказывается о ядрах Linux.</p>
    <p>• Rubini A. and Corbet J. <emphasis>Linux Device Drivers</emphasis>. O'Reilly and Associates, 2001. Прекрасная книга о том, как писать драйверы устройств для ядер Linux серии 2.4.</p>
    <p>• Bovet D. and Cesati M. <emphasis>Understanding the Linux Kernel</emphasis>. O'Reilly and Associates, 2002. Обсуждение основных алгоритмов работы ядер Linux серии 2.4. Основное внимание уделено основополагающим принципам функционирования ядра.</p>
    <p>• Mosberger D. and Eranian S. <emphasis>IA-64 Linux Kernel: Design and Implementation</emphasis>. Prentice Hall, 2002. Отличная книга, посвященная аппаратной платформе Intel Itanium и ядру Linux серии 2.4 для этой аппаратной платформы.</p>
   </section>
   <section>
    <title>
     <p>Книги о ядрах других операционных систем</p>
    </title>
    <p>Понимать врагов, точнее не врагов, а конкурентов, — никогда не повредит. В этих книгах обсуждаются основы работы и особенности реализации операционных систем, отличных от операционной системы Linux. Смотрите, что у них хорошо, а что — плохо.</p>
    <p>• Kogan M. and Deitel H. <emphasis>The Design of OS/2</emphasis>. Addison-Wesley, 1996. Интересный обзор операционной системы OS/2 2.0.</p>
    <p>• Solomon D. and Russinovich M. <emphasis>Inside Windows 2000</emphasis>. Microsoft Press, 2000. Интересный взгляд на операционную систему, которая чрезвычайно отличается от Unix.</p>
    <p>• Richter Jeff. <emphasis>Advanced Windows</emphasis>. Microsoft Press, 1997. Описание низкоуровневого и системного программирования под ОС Windows.</p>
   </section>
   <section>
    <title>
     <p>Книги по API Unix</p>
    </title>
    <p>Детальное описание системы Unix и API этой операционной системы важно не только для того, чтобы писать мощные прикладные программы, но и для понимания того, что требуется от ядра.</p>
    <p>• Stevens W. Richard. <emphasis>Advanced Programming in the UNIX Environment</emphasis>. Addison-Wesley, 1992. Отличное, если не самое полное, обсуждение интерфейса системных вызовов Unix.</p>
    <p>• Stevens W. Richard. <emphasis>UNIX Network Programming, Volume 1</emphasis>. Prentice Hall, 1998. Классический учебник по API сокетов операционной системы Unix.</p>
    <p>• Johnson M. and Troan E. <emphasis>Linux Application Development</emphasis>. Addison-Wesley, 1998. Общий обзор операционной системы Linux и интерфейсов, которые специфичны для этой операционной системы.</p>
   </section>
   <section>
    <title>
     <p>Другие работы</p>
    </title>
    <p>Книги, которые не посвящены операционным системам, но имеют к ним прямое отношение.</p>
    <p>• Knuth Donald. <emphasis>The Art of Computer Programming, Volume 1</emphasis>. Addison-Wesley, 1997. Бесценный курс по фундаментальным алгоритмам и теории вычислительных систем, который включает лучшие и не самые лучшие алгоритмы управления памятью. (Имеется русский перевод: Кнут Дональд Эрвин. Искусство программирования. Том 1. Основные алгоритмы, 3-е издание. — M: "Вильямс", 2000.)</p>
    <p>• Kernighan В. and Ritchie D. <emphasis>The С Programming Language</emphasis>. Prentice Hall, 1988. Наилучшая книга по языку программирования С. (Имеется русский перевод: Брайан Керниган, Деннис Ритчи. Язык программирования С — M: "Вильямс", 2005 г.)</p>
    <p>• Hofstadter Douglas. <emphasis>Godel, Escher, Bach: An Eternal Golden Braid</emphasis>. Basic Books, 1999. Глубокий взгляд на человечество через исследование различных предметов, включая компьютерные науки.</p>
   </section>
   <section>
    <title>
     <p>Web-сайты</p>
    </title>
    <p>Эти WWW-сайты предоставляют последние новости и другую информацию, связанную с операционной системой Linux и ее ядром.</p>
    <p>• <emphasis>Kernel Traffic</emphasis>. Отличный обзор сообщений в списке рассылки разработчиков ядра Linux (lkml) за последнюю неделю (очень рекомендуется). <code>http://www.kerneltraffic.org/</code></p>
    <p>• <emphasis>Linux Weekly News</emphasis>. Хороший сайт новостей о том, что произошло касательно ядра Linux за последнюю неделю, с прекрасными комментариями (очень рекомендуется). <code>http://www.lwn.net/</code></p>
    <p>• <emphasis>Kernel Newbies</emphasis>. Сайт "Kernel Newbies" — это проект сообщества разработчиков с целью предоставления информации и помощи начинающим хакерам, которые стремятся заниматься разработкой ядра. <code>http://www.kernelnewbies.org/</code></p>
    <p>• <emphasis>Kernel.org</emphasis>. Официальный архив исходных кодов ядра. Здесь также находятся домашние страницы многих разработчиков основных подсистем ядра с соответствующими заплатами. <code>http://www.kernel.org/</code></p>
    <p>• <emphasis>KernelTrap</emphasis>. Сайт, посвященный всему, что связано с ядром операционной системы, с большим уклоном в сторону ядра Linux. На сайте много информации о новостях и обзорах из области разработки ядра Linux. Здесь также в большом количестве размещаются интервью с ведущими разработчиками ядра. <code>http://www.kerneltrap.org</code></p>
    <p>• <emphasis>OS News</emphasis>. Новости об операционных системах, а также статьи, интервью и обзоры из этой же области. <code>http://www.osnews.com/</code></p>
    <p>• <emphasis>Сайт, посвященный этой книге</emphasis>. Новости, сообщения об ошибках и другая информация, которая касается этой замечательной книги. <code>http://tech9.net/rml/kernel_book/</code></p>
   </section>
  </section>
 </body>
 <body name="notes">
  <title>
   <p>Примечания</p>
  </title>
  <section id="n1">
   <title>
    <p>1</p>
   </title>
   <p>Это решение было принято на саммите разработчиков ядра Linux (Linux Kernel Development Summit), который состоялся летом 2004 года в г. Оттава, Канада.</p>
  </section>
  <section id="n2">
   <title>
    <p>2</p>
   </title>
   <p>Как насчет версии System IV? Ходят слухи, что это внутренняя экспериментальная версия.</p>
  </section>
  <section id="n3">
   <title>
    <p>3</p>
   </title>
   <p>Да, конечно, не все, но многое представлено в виде файла. В современных операционных системах, таких как Plan9 (наследник Unix), практически все представляется в виде файлом.</p>
  </section>
  <section id="n4">
   <title>
    <p>4</p>
   </title>
   <p>Для тех, кому интересно, дискуссии по поводу отличия свободного кода от открытого доступна в Интернет по адресам <code>http://www.fsf.org</code> и <code>http://www.opensource.org</code>.</p>
  </section>
  <section id="n5">
   <title>
    <p>5</p>
   </title>
   <p>Вероятно, вам нужно прочесть лицензию GNU GPL, если вы еще не читали ее. В файле COPYING, в исходном коде ядра, есть копия этой лицензии. В Интернет лицензия доступна по адресу <code>http://www.fsf.org</code>.</p>
  </section>
  <section id="n6">
   <title>
    <p>6</p>
   </title>
   <p>Иными словами, заранее неизвестно, в какой момент времени это событие произойдет и в каком состоянии будет система в этот момент времени. — <emphasis>Прим. перев</emphasis>.</p>
  </section>
  <section id="n7">
   <title>
    <p>7</p>
   </title>
   <p>Стандарт ISO C99 — это последняя основная версия редакции стандарта ISO С. Редакция C99 содержит многочисленные улучшения предыдущей основной редакции этого стандарта. Стандарт ISO C99 вводит поименную инициализацию полей структур и тип <code><emphasis>complex</emphasis></code>.</p>
  </section>
  <section id="n8">
   <title>
    <p>8</p>
   </title>
   <p>Другая абстракция — это файл.</p>
  </section>
  <section id="n9">
   <title>
    <p>9</p>
   </title>
   <p>В ядре реализован системный вызов <code>wait4()</code>. В операционной системе Linux через библиотеку функций языка С доступны функции <code>wait()</code>, <code>waitpid()</code>, <code>wait3()</code> и <code>wait4()</code>. Все эти функции возвращают информацию о состоянии завершившегося процесса, хотя в несколько разной семантике.</p>
  </section>
  <section id="n10">
   <title>
    <p>10</p>
   </title>
   <p>Иногда в литературе по построению операционных систем этот список называется <emphasis>task array</emphasis> (массив задач). Поскольку в ядре Linux используется связанный список, а не статический массив, его называют <emphasis>task list</emphasis>.</p>
  </section>
  <section id="n11">
   <title>
    <p>11</p>
   </title>
   <p>Причиной создания структуры <code><emphasis>thread_info</emphasis></code> было не только наличие аппаратных платформ, обедненных регистрами процессора, но и то, что положение этой структуры позволяет достаточно просто рассчитывать смешения адресов для значений ее полей при использовании языка ассемблера.</p>
  </section>
  <section id="n12">
   <title>
    <p>12</p>
   </title>
   <p>Скрытый тип (opaque type) — это тип данных, физическое представление которого неизвестно или не существенно.</p>
  </section>
  <section id="n13">
   <title>
    <p>13</p>
   </title>
   <p>Именно из-за этого появляются наводящие ужас "неубиваемые" процессы, для которых команда <code>ps(1)</code> показывает значение состояния, равное <code>D</code>. Так как процесс не отвечает на сигналы, ему нельзя послать сигнал <code>SIGKILL</code>. Более того, завершать такой процесс было бы неразумно, так как этот процесс, скорее всего, выполняет какую-либо важную операцию и может удерживать семафор.</p>
  </section>
  <section id="n14">
   <title>
    <p>14</p>
   </title>
   <p>Отличным от контекста процесса является контекст прерывания, описанный в главе 6, "Прерывания и обработка прерываний". В контексте прерывания система работает не от имени процесса, а выполняет обработчик прерывания. С обработчиком прерывании не связан ни один процесс, поэтому и контекст процесса отсутствует.</p>
  </section>
  <section id="n15">
   <title>
    <p>15</p>
   </title>
   <p>Под <code>exec()</code> будем понимать любую функцию из семейства <code>exec*()</code>. В ядре реализован системный вызов <code>execve()</code>, на основе которого реализованы библиотечные функции <code>execlp()</code>, <code>execle()</code>, <code>execv()</code> и <code>execvp()</code>.</p>
  </section>
  <section id="n16">
   <title>
    <p>16</p>
   </title>
   <p>В действительности сейчас это работает не так, как хотелось бы, однако усилия прилагаются к тому, чтобы порожденный процесс запускался на выполнение первым.</p>
  </section>
  <section id="n17">
   <title>
    <p>17</p>
   </title>
   <p>В действительности уже сейчас есть заплаты для добавления такой функции в ОС Linux. Хотя, скорее всего, возможность совместного использования таблиц страниц в ядрах серии 2.6 реализована не будет, такая возможность может появиться в будущих версиях.</p>
  </section>
  <section id="n18">
   <title>
    <p>18</p>
   </title>
   <p>Как пример можно привести тесты по измерению времени создания процессов (и даже потоков) и операционной системе Linux по сравнению с другими операционными системами. Результаты очень хорошие.</p>
  </section>
  <section id="n19">
   <title>
    <p>19</p>
   </title>
   <p>Обозначение O(1) — это пример обозначения "большого O". Практически, эта запись означает, что планировщик может выполнить все свои действии за постоянное время, независимо от объема входных данных. Полное объяснение того, что такое обозначение "большого O", приведено в приложении В, "Сложность алгоритмов".</p>
  </section>
  <section id="n20">
   <title>
    <p>20</p>
   </title>
   <p>Вместо термина timeslice (квант времени) иногда также используется quantum (квант) или processor slice. В ОС Linux применяется термин timeslice.</p>
  </section>
  <section id="n21">
   <title>
    <p>21</p>
   </title>
   <p>Может возникнуть вопрос: почему используется файл <code>kernel/sched.c</code>, а не заголовочный файл <code>include/linux/sched.h</code>? Потому что желательно абстрагироваться от реализации кода планировщика и обеспечить доступность для остального кода ядра только лишь некоторых интерфейсов.</p>
  </section>
  <section id="n22">
   <title>
    <p>22</p>
   </title>
   <p>Для аппаратной платформы x86 используется инструкция <code>bsfl</code>, а для платформы PPC — инструкция <code>cntlzw</code>.</p>
  </section>
  <section id="n23">
   <title>
    <p>23</p>
   </title>
   <p>Для аппаратной платформы x86 существует около 250 системных вызовов (для каждой аппаратной платформы разрешается определять свои уникальные системные вызовы). Хотя не для всех операционных систем опубликованы действительные системные вызовы, но по оценкам для некоторых операционных систем таких вызовов более тысячи.</p>
  </section>
  <section id="n24">
   <title>
    <p>24</p>
   </title>
   <p>IEEE, eye-triple-E (Институт инженеров по электротехнике и радиоэлектронике, Institute of Electrical and Electronics Engineers) является бесприбыльной профессиональной ассоциацией, действующей во многих технических областях и отвечающей за многие важные стандарты, такие как стандарт POSIX. Больше информации доступно по адресу: <code>http://www.ieee.org</code>.</p>
  </section>
  <section id="n25">
   <title>
    <p>25</p>
   </title>
   <p>Следует обратить внимание на слово "могут". Хотя почти все вызовы создают различные побочные эффекты (т.е. приводят к каким-либо изменениям в состоянии системы), тем не менее небольшое количество вызовов, как, например, вызов <code>getpid()</code>, просто возвращают некоторые данные ядра.</p>
  </section>
  <section id="n26">
   <title>
    <p>26</p>
   </title>
   <p>Тип <code>long</code> используется для совместимости с 64-разрядными платформами.</p>
  </section>
  <section id="n27">
   <title>
    <p>27</p>
   </title>
   <p>Может быть, интересно, почему вызов <code>getpid()</code> возвращает поле <code>tgid</code>, которое является идентификатором группы потоков (thread group ID)? Это делается потому, что дли обычных процессов значение параметра <code>TGID</code> равно значению параметра <code>PID</code>. При наличии нескольких потоков значение параметра <code>TGID</code> одинаково дли всех потоков одной группы. Такая реализация дает возможность различным потокам вызывать функцию <code>getpid()</code> и получать одинаковое значение параметра <code>PID</code>.</p>
  </section>
  <section id="n28">
   <title>
    <p>28</p>
   </title>
   <p>Большая часть дальнейшего описания процесса обработки системных вызовов базируется на версии для аппаратной платформы x86. Но не стоит волноваться, для других аппаратных платформ это выполняется аналогичным образом.</p>
  </section>
  <section id="n29">
   <title>
    <p>29</p>
   </title>
   <p>Обработчики прерываний не могут переходить в приостановленное состояние и, следовательно, более ограничены в своих действиях по сравнению с системными вызовами, которые работают в контексте процесса.</p>
  </section>
  <section id="n30">
   <title>
    <p>30</p>
   </title>
   <p>Регистрации новых постоянных системных вызовов в ядре требует компиляции системного вызова в образ ядра. Тем не менее есть принципиальная возможность с помощью динамически загружаемого модуля ядра перехватить существующие системные вызовы и даже, ценой некоторых усилий, динамически зарегистрировать новые. — <emphasis>Примеч. перев.</emphasis></p>
  </section>
  <section id="n31">
   <title>
    <p>31</p>
   </title>
   <p>Какой-нибудь процесс выполняется всегда. Если не выполняется никакой процесс, то выполняется холостая задача (idle task).</p>
  </section>
  <section id="n32">
   <title>
    <p>32</p>
   </title>
   <p>После прочтения главы 10, "Таймеры и управление временем", можно ли сказать, сколько времени (в единицах HZ) машина работала без перегрузки исходя из числа прерываний таймера?</p>
  </section>
  <section id="n33">
   <title>
    <p>33</p>
   </title>
   <p>Многие старые устройства, в частности устройства ISA, не предоставляют возможности определить, являются ли они источником прерывания. Из-за этого линии прерывания для ISA-устройств часто не могут быть совместно используемыми. Поскольку спецификация шины PCI требует обязательной поддержки совместно используемых прерываний, современные устройства PCI поддерживают совместное использование прерываний. В современных компьютерах практически все линии прерываний могут быть совместно используемыми.</p>
  </section>
  <section id="n34">
   <title>
    <p>34</p>
   </title>
   <p>Термин softirq часто переводится, как "программное прерывание", однако, чтобы не вносить путаницу с синхронными программными прерываниями (исключительными ситуациями) в этом контексте используется термин "отложенное прерывание". (<emphasis>Прим. ред.</emphasis>)</p>
  </section>
  <section id="n35">
   <title>
    <p>35</p>
   </title>
   <p>В связи с глобальным синхронизмом выполнения обработчиков BH друг с другом, не так просто было их конвертировать для использования механизмов отложенных прерываний и тасклетов. Однако в ядрах серии 2.5 это наконец-то получилось сделать.</p>
  </section>
  <section id="n36">
   <title>
    <p>36</p>
   </title>
   <p>Они не имеют ничего общего с понятием task (задача). Их следует понимать как простые в использовании отложенные прерывания (softirq).</p>
  </section>
  <section id="n37">
   <title>
    <p>37</p>
   </title>
   <p>Большинство драйверов использует для обработки своих нижних половин механизм тасклетов. Тасклеты построены на механизме softirq, как это будет показано ниже.</p>
  </section>
  <section id="n38">
   <title>
    <p>38</p>
   </title>
   <p>На самом деле эта операция выполняется при всех запрещенных на локальном процессоре прерываниях, что не показано в упрощенной версии. Если бы прерывания не были запрещены, то в период времени между сохранением и очисткой маски могло бы быть сгенерировано новое отложенное прерывание (которое бы ожидало на выполнение), что привело бы к неверной очистке бита соответствующего отложенного прерывания.</p>
  </section>
  <section id="n39">
   <title>
    <p>39</p>
   </title>
   <p>Это еще один пример плохой терминологии. Почему отложенные прерывания (softirq) генерируются (rise), а тасклеты (tasklet) планируются (schedule)? Кто знает? Оба термина означают, что обработчики нижних половин помечаются как ожидающие на выполнение и в скором времени будут выполнены.</p>
  </section>
  <section id="n40">
   <title>
    <p>40</p>
   </title>
   <p>Отсутствие строгой последовательности выполнения хорошо сказывается на производительности, но приводит к усложнению программирования. Конвертация обработчиков BH в обработчики тасклетов, например, требует тщательного осмысления того, безопасно ли выполнять этот же код в то же самое время другим тасклетом? Однако после того как конвертация выполнена, это окупается повышением производительности.</p>
  </section>
  <section id="n41">
   <title>
    <p>41</p>
   </title>
   <p>Названия для механизмов обработки нижних половин, очевидно, выбираются из соображений конспирации, чтобы сбивать с толку молодых и неопытных разработчиков ядра.</p>
  </section>
  <section id="n42">
   <title>
    <p>42</p>
   </title>
   <p>На самом деле этот счетчик используется как системой обработки прерываний, так и системой обработки нижних половин. Наличие одного счетчика для задания позволяет в операционной системе Linux реализовать атомарность заданий. Как показала практика, такой подход очень полезен для нахождения ошибок, например, связанных с тем, что задание переходит в состояние ожидания в то время, когда выполняет атомарные операции (sleeping-while-atomic bug).</p>
  </section>
  <section id="n43">
   <title>
    <p>43</p>
   </title>
   <p>Термин поток выполнения подразумевает любой выполняющийся код. Это включает, например, задание в ядре, обработчик прерывания или поток пространства ядра. В этой главе поток выполнения сокращенно называется просто поток. Следует помнить, что этот термин подразумевает любой выполняющийся код.</p>
  </section>
  <section id="n44">
   <title>
    <p>44</p>
   </title>
   <p>Иными словами, "вращаются" (spin) в замкнутом цикле, ожидая на освобождение блокировки.</p>
  </section>
  <section id="n45">
   <title>
    <p>45</p>
   </title>
   <p>Дальше будет показано, что, за некоторыми исключениями, код, который безопасен при SMP-обработке, также безопасен и при вытеснениях.</p>
  </section>
  <section id="n46">
   <title>
    <p>46</p>
   </title>
   <p>Б некоторых ядрах такой тип тупиковой ситуации предотвращается с помощью рекурсивных блокировок, которые позволяют одному потоку выполнения захватывать блокировку несколько раз. В операционной системе Linux, к счастью, таких блокировок нет. И это считается хорошим тоном. Хотя рекурсивные блокировки позволяют избежать проблемы самоблокировок, они приводят к небрежному использованию блокировок.</p>
  </section>
  <section id="n47">
   <title>
    <p>47</p>
   </title>
   <p>Сейчас это требование становится еще более важным, так как ядро является преемптивным. Время, в течение которого удерживаются блокировки, эквивалентно времени задержки (латентности) системного планировщика.</p>
  </section>
  <section id="n48">
   <title>
    <p>48</p>
   </title>
   <p>Использование этих функций может привести к тому, что код становится "грязным". Нет необходимости часто проверять значение спин-блокировок — код или всегда должен захватывать блокировку, или вызываться только, если блокировка захвачена. Однако существуют некоторые ситуации, когда такие функции логично использовать, поэтому эти интерфейсы и предоставляются.</p>
  </section>
  <section id="n49">
   <title>
    <p>49</p>
   </title>
   <p>Как будет показано дальше, несколько процессов могут при необходимости одновременно удерживать один семафор.</p>
  </section>
  <section id="n50">
   <title>
    <p>50</p>
   </title>
   <p>Доктор Дейкстра (1930–2002 г.) один из самых талантливых ученых за всю (конечно, не очень долгую) историю существования вычислительной техники как области науки. Его многочисленные труды включают работы но проектированию операционных систем и по теории алгоритмов, сюда же входит концепция семафоров. Он родился в городе Роттердам, Нидерланды, и преподавал в университете штата Техас в течение 15 лет. Тем не менее, он был бы не очень доволен большим количеством директив <code>GOTO</code> в ядре Linux.</p>
  </section>
  <section id="n51">
   <title>
    <p>51</p>
   </title>
   <p>Хотя, может быть, она и не такая страшная, какой ее иногда пытаются представить, вес же некоторые люди считают ее "воплощением дьявола" в ядре.</p>
  </section>
  <section id="n52">
   <title>
    <p>52</p>
   </title>
   <p>Процессоры Intel x86 никогда не переопределяют порядок операций записи, т.е. выполняют запись всегда в указанном порядке. Тем не менее другие процессоры могут нести себя и по-другому,</p>
  </section>
  <section id="n53">
   <title>
    <p>53</p>
   </title>
   <p>Если быть точными, то функции, которые управляются временем, также управляются и событиями. В этом случае событие соответствует ходу времени. В этой главе будут рассмотрены, в основном, события, управляемые временем,так как они встречаются очень часто и являются важными для ядра.</p>
  </section>
  <section id="n54">
   <title>
    <p>54</p>
   </title>
   <p>Эмулятор платформы IA-64 имеет частоту 32 Гц. Настоящая машина платформы IA-64 имеет частоту 1024 Гц.</p>
  </section>
  <section id="n55">
   <title>
    <p>55</p>
   </title>
   <p>Здесь имеется в виду не точность измерения, а точность в вычислительном плане. Точность измерения (в общенаучном смысле) — это статистическая мера повторяемости результата. В вычислительном (компьютерном) смысле точность — это количество значащих цифр, которые используются для представления того или другого значения.</p>
  </section>
  <section id="n56">
   <title>
    <p>56</p>
   </title>
   <p>В связи с ограничениями аппаратной платформы и протокола NTP, значение переменной HZ не может быть произвольным. Для платформы x86 значения 100, 500 и 1000 работают хорошо.</p>
  </section>
  <section id="n57">
   <title>
    <p>57</p>
   </title>
   <p>Необходима специальная функция, так как на 32-разрядных аппаратных платформах нельзя атомарно обращаться к двум машинным словам 64-разрядного значения, Специальная функция, перед тем как считать значение, блокирует счетчик импульсов системного таймера с помощью блокировки <code>xtime_lock</code>.</p>
  </section>
  <section id="n58">
   <title>
    <p>58</p>
   </title>
   <p>ля некоторых аппаратных платформ функция <code>sys_time()</code> не реализована, а вместо этого она эмулируется библиотекой функций языка С на основании вызова <code>gettimeofday()</code>.</p>
  </section>
  <section id="n59">
   <title>
    <p>59</p>
   </title>
   <p>Другая причина состоит в том, что в ядрах старых версий (до 2.3) существовали статические таймеры. Такие таймеры создавались во время компиляции, а не во время выполнения. Они имели ограниченные возможности и из-за их отсутствия сейчас никто не огорчается.</p>
  </section>
  <section id="n60">
   <title>
    <p>60</p>
   </title>
   <p>На самом деле, ни один подход не гарантирует, что время задержки будет точно равно указанному значению. Некоторые подходы обеспечивают задержки, очень близкие к точному значению, тем не менее все подходы гарантируют, что время ожидания будет, по крайней мере, не меньше, чем нужно. В некоторых случаях период ожидания получается существенно больше указанного.</p>
  </section>
  <section id="n61">
   <title>
    <p>61</p>
   </title>
   <p>Некоторые некачественные устройства PCI также могут выполнять прямой доступ к памяти только к 24-битовом адресном пространстве. Но эти устройства работают не правильно.</p>
  </section>
  <section id="n62">
   <title>
    <p>62</p>
   </title>
   <p>Это не имеет ничего общего с верхней памятью в операционной системе DOS.</p>
  </section>
  <section id="n63">
   <title>
    <p>63</p>
   </title>
   <p>Данная функция может выделить памяти больше, чем указано, и нет никакой возможности узнать, на сколько больше! Поскольку в своей основе система выделения памяти в ядре базируется на страницах, некоторые запросы на выделение памяти могут округляться, чтобы хорошо вписываться е области доступной памяти. Ядро никогда не выделит меньше памяти, чем необходимо. Если ядро не в состоянии найти хотя бы указанное количество байтов, то операция завершится неудачно и функции возвратит значение <code>NULL</code>.</p>
  </section>
  <section id="n64">
   <title>
    <p>64</p>
   </title>
   <p>Буфер TLB (translation lookside buffer или буфер быстрого преобразования адреса) — это аппаратный буфер памяти, который используется в большинстве аппаратных платформ для кэширования отображений виртуальных адресов памяти в физические адреса. Этот буфер позволяет существенно повысить производительность системы, так как большинство операций доступа к памяти выполняются с использованием виртуальной адресации.</p>
  </section>
  <section id="n65">
   <title>
    <p>65</p>
   </title>
   <p>И позже документированы в работе Bonwirk J. "The Slab Allocator: An Object-Caching Kernel Memory Allocator," USENIX, 1994.</p>
  </section>
  <section id="n66">
   <title>
    <p>66</p>
   </title>
   <p>РАЕ — Physical Address Extension (расширение физической адресации). Эта функция процессоров x86 позволяет физически адресовать до 36 разрядов (64 Гбайт) памяти, несмотря на то что размер виртуального адресного пространства соответствует только 32 бит.</p>
  </section>
  <section id="n67">
   <title>
    <p>67</p>
   </title>
   <p>Сейчас в операционной системе Linux эта иерархическая структура является уникальной для каждого процесса, т.е. каждый процесс имеет свое пространство имен. По умолчанию каждый процесс наследует пространство имен своего родительского процесса, поэтому кажется, что существует одно глобальное пространство имен.</p>
  </section>
  <section id="n68">
   <title>
    <p>68</p>
   </title>
   <p>В отличие от указания буквы, которая соответствует определенному диску, например С:. В последнем случае пространство имен разбивается на части, которые соответствуют различным устройствам или разделам устройств. Поскольку такое разделение выполняется случайным образом, в качестве представления для пользователя его можно считать не самым идеальным вариантом.</p>
  </section>
  <section id="n69">
   <title>
    <p>69</p>
   </title>
   <p>Часто многие этого не замечают и даже отрицают, но тем не менее в ядре много примеров объектно-ориентированного программирования. Хотя разработчики ядра и сторонятся языка C++ и других явно объектно-ориентированных языков программировании (ООП), иногда очень полезно мыслить в терминах объектов. Подсистема VFS — это хороший пример того, как просто и эффективно объектно-ориентированное программирование реализуется на языке С, в котором нет объектно-ориентированных конструкций.</p>
  </section>
  <section id="n70">
   <title>
    <p>70</p>
   </title>
   <p>Файловые системы, которые не имеют индексов, обычно хранят необходимую информацию как часть файла. Некоторые современные файловые системы также применяют базы данных для хранения метаданных файла. В любом случае объект индекса создается тем способом, который подходит для файловой системы.</p>
  </section>
  <section id="n71">
   <title>
    <p>71</p>
   </title>
   <p>Расширенные атрибуты — это новая функциональность, которая появилась в ядре 2.6 для того, чтобы создавать параметры файлов в виде пар имя/значение по аналогии с базой данных. Эти параметры поддерживаются не многими файловыми системами, и к тому же они еще используются не достаточно широко.</p>
  </section>
  <section id="n72">
   <title>
    <p>72</p>
   </title>
   <p>Это название несколько сбивает с толку. В таких объектах нет ничего негативного или отрицательного. Более удачным было бы, наверное, название invalid dentry или несуществующий элемент каталога.</p>
  </section>
  <section id="n73">
   <title>
    <p>73</p>
   </title>
   <p>А также при захваченной блокировке <code>dentry-&gt;d_lock</code>. — <emphasis>Примеч. перев</emphasis>.</p>
  </section>
  <section id="n74">
   <title>
    <p>74</p>
   </title>
   <p>Для создания потоков обычно указываются флаги <code>CLONE_FILES</code> и <code>CLONE_FS</code>, поэтому они совместно используют структуры <code>files_struct</code> и <code>fs_struct</code>. С другой стороны, для обычных процессов эти флаги не указываются, поэтому для каждого процесса существует своя информация о файловой системе и своя таблица открытых файлов.</p>
  </section>
  <section id="n75">
   <title>
    <p>75</p>
   </title>
   <p>Это ограничение является искусственным и в будущем оно может быть отменено. Тем не менее требование, чтобы размер блока был меньше или равен размеру страницы памяти, позволяет значительно упростить ядро.</p>
  </section>
  <section id="n76">
   <title>
    <p>76</p>
   </title>
   <p>Это необходимо подчеркнуть особо. Системы, не имеющие таких функций или в которых эти функции плохо реализованы, будут иметь очень плохую производительность даже при небольшом количестве операций блочного ввода-вывода.</p>
  </section>
  <section id="n77">
   <title>
    <p>77</p>
   </title>
   <p>Однако все же не желательно задерживать операции записи на неопределенное время. Запросы записи также должны немедленно отправляться на диск, но это не так критично, как в случае запросов чтения.</p>
  </section>
  <section id="n78">
   <title>
    <p>78</p>
   </title>
   <p>Для deadline-планировщика операция вставки в начало запроса выполняется опционально. Обычно невыполнение вставки в начало запроса не приводит к проблемам, так как в большинстве случаев количество запросов, которые могут быть добавлены в начало, очень незначительно.</p>
  </section>
  <section id="n79">
   <title>
    <p>79</p>
   </title>
   <p>Термин "BSS" сложился исторически и является достаточно старым. Он означает <emphasis>block started by symbol</emphasis> (<emphasis>блок, начинающийся с символа</emphasis>). Неинициализированные переменные в выполняемом файле не хранятся, поскольку с ними не связано никакого значения. Тем не менее стандарт языка С требует, чтобы неинициализированным переменным присваивалось определенное значение по умолчанию (обычно все заполняется нулями). Поэтому ядро загружает переменные (без их значений) из выполняемого файла в память и отображает в эту память нулевую страницу, тем самым переменным присваивается нулевое значение без необходимости зря тратить место в объектном файле на ненужную инициализацию.</p>
  </section>
  <section id="n80">
   <title>
    <p>80</p>
   </title>
   <p>В более новых версиях библиотеки glibc функция <code>malloc()</code> реализована через системный вызов <code>mmap()</code>, а не через вызов <code>brk()</code>.</p>
  </section>
  <section id="n81">
   <title>
    <p>81</p>
   </title>
   <p>Между дескриптором процесса, дескриптором памяти и соответствующими функциями существует тесная связь. Поэтому структура <code>struct mm_struct</code> и определена в заголовочном файле <code>sched.h</code>.</p>
  </section>
  <section id="n82">
   <title>
    <p>82</p>
   </title>
   <p>Утилита <code>pmap(1)</code> печатает форматированный список областей памяти процесса. Результат ее вывода несколько более удобочитаем, чем информация, получаемая из файловой системы <code>/proc</code>, но это одна и та же информация. Данная утилита включена в новые версии пакета <code>procps</code>.</p>
  </section>
  <section id="n83">
   <title>
    <p>83</p>
   </title>
   <p>Начиная с ядра версии 2.6.11 таблицы страниц в ОС Linux для 64-разрядных аппаратных платформ стали 4-уровневыми, что позволяет в полном объеме использовать все виртуальное адресное пространство. Для 32-разрядных аппаратных платформ осталось 3 уровня, как и раньше. — <emphasis>Примеч. ред</emphasis>.</p>
  </section>
  <section id="n84">
   <title>
    <p>84</p>
   </title>
   <p>Как было показано в главе 12," Виртуальная файловая система", операции страничного ввода-вывода непосредственно выполняются не системными вызовами <code>read()</code> и <code>write()</code>, а специфичными для файловых систем методами <code>file-&gt;f_op-&gt;read()</code> и <code>file-&gt;f_op-&gt;write()</code>.</p>
  </section>
  <section id="n85">
   <title>
    <p>85</p>
   </title>
   <p>Например, размер страницы физической памяти для аппаратной платформы x86 равен 4 Кбайт, в то время как размер дискового блока для большинства устройств и файловых систем равен 512 байт. Следовательно, в одной странице памяти может храниться 8 блоков. Блоки не обязательно должны быть смежными, так как один файл может быть физически "разбросанным" по диску.</p>
  </section>
  <section id="n86">
   <title>
    <p>86</p>
   </title>
   <p>Реализация ядра основана на базисном дереве поиска по приоритетам, предложенном в работе Edward M. McCreight, опубликованной в журнале SIAM Journal of Computing, May 1985, vol. 14. №2, P. 257–276.</p>
  </section>
  <section id="n87">
   <title>
    <p>87</p>
   </title>
   <p>Слово "gang" не является жаргонным. Этот термин часто используется в компьютерных науках, чтобы указать группу чего-либо, что может выполняться параллельно.</p>
  </section>
  <section id="n88">
   <title>
    <p>88</p>
   </title>
   <p>Да, название функции не совсем верное. Должно было бы быть <code>wakeup_pdflush()</code>. В следующем разделе рассказано, откуда произошло это название.</p>
  </section>
  <section id="n89">
   <title>
    <p>89</p>
   </title>
   <p>Если вас заинтересовала информация о файловой системе sysfs, то, вероятно, вам будет интересно также ознакомиться с HAL, hardware abstraction layer (уровень абстракции аппаратного обеспечения), информация о котором доступна по адресу <code>http://hal.freedesktop.org/</code>. Подсистема HAL позволяет создать в оперативной памяти базу данных на основании информации файловой системы sysfs, объединяя вместе понятия классов, устройств и драйверов. На основании этих данных уровень HAL предоставляет API, которое позволяет разрабатывать более интеллектуальные программы.</p>
  </section>
  <section id="n90">
   <title>
    <p>90</p>
   </title>
   <p>Более подробную информацию о демоне D-BUS можно найти на сайте <code>http://dbus.freedesktop.org/</code>.</p>
  </section>
  <section id="n91">
   <title>
    <p>91</p>
   </title>
   <p>Это нормальная ситуация при разработке ядра. Если что-либо должно быть сделано, то это должно быть сделано хорошо! Разработчики ядра неохотно переписывают большие участки кода даже во имя совершенства.</p>
  </section>
  <section id="n92">
   <title>
    <p>92</p>
   </title>
   <p>Размер адресуемой памяти может быть меньше максимального значения машинного слова. Например, для 64-разрядных аппаратных платформ размер указателя ранен 64 бит, однако только 48 бит можно использовать для адресации. В дополнение к этому, общее количество физической памяти может быть больше максимального значения машинного слова, как, например, это имеет место при наличии расширения Intel PAE..</p>
  </section>
  <section id="n93">
   <title>
    <p>93</p>
   </title>
   <p>За исключением размера типа <code>char</code>, который всегда равен 8 бит.</p>
  </section>
  <section id="n94">
   <title>
    <p>94</p>
   </title>
   <p>На самом деле, для 64-разрядных аппаратных платформ, которые поддерживаются ОС Linux, размеры типов <code>int</code> и <code>long</code> не совпадают. Размер типа int равен 32 бит, а размер типа <code>long</code> — 64 бит. Для хорошо знакомых 32-разрядных аппаратных платформ оба типа данных имеют размер 32 бит.</p>
  </section>
  <section id="n95">
   <title>
    <p>95</p>
   </title>
   <p>Если бы компилятор имел возможность изменять порядок следования нолей структуры данных, то любой существующий код, который уже использует эту структуру, мог бы испортить данные. В языке программирования С функции вычисляют положение полей просто путем введения смещений от начального адреса структуры в памяти.</p>
  </section>
  <section id="n96">
   <title>
    <p>96</p>
   </title>
   <p>Брайан У. Керниган, Деннис M. Ритчи, <emphasis>Язык программирования С, 2-е изд</emphasis>. Пер. с англ. — M.: Издат. дом "Вильямс", 2005.</p>
  </section>
  <section id="n97">
   <title>
    <p>97</p>
   </title>
   <p>Вопросы сложности алгоритмов рассматриваются в приложении В.</p>
  </section>
  <section id="n98">
   <title>
    <p>98</p>
   </title>
   <p>Клод Шеннон (30 апреля 1916–24 февраля 2001) работал инженером в компании Bell Labs. В его наиболее известной работе Математическая теории связи, опубликованной в 1948 году, впервые были разработаны основы информационной теории и было введено понятие энтропии Шеннона. Шеннон также любил кататься на одноколесном велосипеде.</p>
  </section>
  <section id="n99">
   <title>
    <p>99</p>
   </title>
   <p>Джон фон Нейман (28 декабря 1903–8 февраля 1957) работал в Институте специальных исследований в Принстоне (Institute for Advanced Study; Princeton). Он внес большой вклад в математические, экономические и компьютерные науки. Среди наиболее значительных его разработок — теория игр, фон-неймановские алгебры и фон-неймановская проблема узких мест.</p>
  </section>
  <section id="n100">
   <title>
    <p>100</p>
   </title>
   <p>Если интересно, то нижняя граница описывается с помощью обозначения большого-омега. Определение аналогично определению множества большого-О, за исключением того, что значения функции <emphasis>g</emphasis>(<emphasis>x</emphasis>) должны быть меньше значений функции <emphasis>f</emphasis>(<emphasis>x</emphasis>) или равны им.</p>
  </section>
 </body>
 <binary id="img_0.jpeg" content-type="image/jpeg">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
Za2WtlrZa2W4y1stbLcZbjLcZa1uMtxluMtxluMtxluMtxlust1lw12kt4We5RxIJP0c5clw
Ni5vCz3OBgJLMonZ8/0N30sUjm4RLhPc4We5xCLWZsQEBsX6CZsK/lKUELZrhPdYWe5xi9CO
J2cJNXz5y6UwObszM0GHCe5wsh/sYh7OBxakEji/zkkqCLGD1LhPc4Tv/sEGaccsPtXxMGNo
tTfNy6nIImDwQe4uFdzhY7liyTtmnjRdt4DbKL5qJv8AN4K/uyHpfgxO9zCx3OJ/UXHwSen5
N/HD7r+rBy0p5XfDgnd4WO4TYOyyTgn+iZS/f5qL3i9SOVP9Xw4J3eFjuE3h05rQ+c3r+aj9
2WRhNzIvDwTu8LHcMm+zYfbwE2p3HL5kiYUUz5+LgveYWPfZN6WwdMnwb7v9tPyz/RHMnfyO
C95hO3+wv/hak7itwFugt+NcwCewGnmGW+mNi+TOUQRykazWazWpalmyzWeHBe7wtzG1vdNb
prWSzfyoz1fIzSuz+XwLvMLneebB9/kLHu+XwLvcLneebB8jY93y+Bd7hc7zzYPkbHu+XwLv
cLneeTms1nhUrnKHJyrk5VycqelKuSlXJTLkplyUq5KVcnK65SVlysq5WVcpKnqyrlZVysq5
WVcpKuVlXKSrlZVysq5WVctKuVlXKSrlJlysq5SZcpMuVlXKzLlZVysq5WVcpMuTmXKTrlJl
yU65KZclMuUnXKTLlJlycy5SVXIijl8vgXe4XO98Ga1LPxZOy4Q3+r5LeF/LfypPt5bt9eNd
z5fAu9wud7ms1n4MkwoYiJNVTtACKQXWpBYlAebnXNzrnp2XULK6lZXU7K6pZXVbK6tZTcXs
Mur2F1ewusTrrM66xMuszLrMq60a6ya6zIusyrrMi6ya6zIutSJ+MyrrMq6zOusTrrFhdXsL
q9hdXsLq9hdXsp+Lzu3WJ11iddYnXWJ11iddYnXWLC6vYXVrC6rZXV7Kfi1lWLElgl9FpWXk
cC73C73uOSyQwmSaqnKCJPZNOZF4M8M8M1ms1n5+fl5+PPytKywzWbLJZeHgPe4XO9yWSGAy
Q1VrhjRWSTmT+HPxtGTpq7o4BGP4BtOYxREtgFy4LlY3UtVxT+PJZLJfXDSssOBd7hZhM7bV
RZa4okVonTmT+XlmmgJ00AsmBmxN8w+BYslHYyTGzpnwliA0cZA/mZC64KGm3hbsuNgjI/Iz
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
vW2o1bPcf0Vvz9iNR3qfdf0Vvz9hzUpd9/RW/P2DNSU/Af0Vvz7t6xBYgr6t6wrD6pzU/Cf0
V0oB3rEsRWI+HP1GyPEf0feul6vGPrr+j7iul6vGd66/+39xXS9XjH11/R9xXS9XjEtVyuqu
VyuVywq5XK5YVhWFYVhWBYFgWBYVgWBYCsBWBYVgWBYFgWBYFgKwLCsCwrCsCwq5XLAsKwrC
sKg7h4j+j7ium6vFzR6vHH6X5HiBDLxH9H3FdL1eDJq2nQX7lssaKoNeQOS812q812q81y80
rzF5ix/wsQ0V40X06L6dF9OiuYsLFhasLFgavLC8sLy2rA1eW1YGry2ry2rA1YWrCxXN0X06
L6dF9Oi+nRfTovp0VzVhYrmaK5uiuYrmaL6dF9Oi+nRfTor26K8aLENEHUhj4j/7f3FdN1d6
QW25bIiVKSm72C/+39xXTdVdyi5ylNSkpnxJBTKPoMwpVXLetman47/7f3FdLwtLacpCJUpK
bvDkuCnNXVn0PaUjXzUx4z+iukaBcVM+JNwCmYqUO7f6LJbSlVArZ8V/RXS9XjTl6bJQdXNT
u8N/RXS9XiTl3JX+mXKUVNpFU1FmiwlYSsJWE95/RXTdXhTUu5AemSKmsXg2m91/R966bq78
ltKQ7srvUOKnLwOXcf0feum6u7JbSkO7NcvU5Kav7sD3H9H3rpuruRKl35+ryKmpHuQNT+j7
103UpKal4E/W5zV9VoVP6PvXSkn6lLwLI/Wj0K9TClcn9H3rpuqq9Xq9Xq9XrEf1orPob+j7
103V6QfQ39H3FdN1ey3/ANv7ium6vZb/AO39xXTdfop9Gf8A2/uK6br9lv6PuK3PcXxJjer3
6q9+qvpNVipNVe/VYqTVYqTVYqTVYqTVX0mqxUmqxUmqxUmqxUmqxUmqxUmqxUmqxUmqxUmq
xUmqxUmqxUmqxUmqxUmqxUmqxUmqxUmqxUmqxUmqxUmqxUmqxUmqxUmqxUmqxUmqxUmqhafq
sT1ipNVipNVipNVipdVipdVipNVipNVipdVipdVjpdVipdVipdVipdVipdVipdVipdVipdQs
VLqsVLqsdLqsVLqsdLqsdLqFjpdVjpdQsdLqFjpdVjpdQsdLqsdLqFjpdQsdLqFjpdVjpdQs
dLqFjpdQi+jc8kiG1/pKtQjMCCDH0RYXXTiiYiV6lRQbxcfsrMRFQiIq8Jz4RghRvoiyIiJq
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
888888X9+++++++888888888888888888888888888888X9+++++++4nNhTNAF8999999999
999999999999f9+++++++/8Avvvvvvvvvvvvvvvvvvvvvvvvvvvvlvfvvvvvvvvvvvvvvvvv
vvvvvvvvvvvvvvvvvvvvVfvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvdfvvvvvvvvvvv
vvvvvvvvvvvvvvvvvvvvvvvvvvVfvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvXfvvv8A
7777777777777777777777777777777771X775Wn77777770j7/77777/wC++++/E+A+++++
+91++/8ASWOzt51WQgoUWJm5aSc7WKg+P1OHLqNm/vVfvv8AxBGYF9r4oUrO6nk4tK89gFof
7/N7+jipj71X776D5BLbECgDuWBVEXZmGFejSUaBAAiAHLTH71X775fCCReb0wlQjb7/AO++
/wDvvvvvvvvvvvvv/wD71X775w1rPvGJKOqaz77777777777777777777771X7777777/wC+
+++++++++++++++++++++++++++9V+//AP8A/wD/AP8A/wD/AP8A7/77777/AO//AP8A/wD/
AP8A/wD+ovjOIl9q4tPzLLPPDzLLLT/xxAjD0DLLBFdhl/ftNPLfI4LlG0999t9d91t9Nt99
9pNDjDDDDDCDD3KGSiUr2DRqiKxJxZ5xxhxRhhRx19tPD/3rLPrLDHLGE68W8/k9P/tJJFNN
NFNNFtBBFxzz7/8A6/8A/wDHfrIuDAi6Iklz6xxl19x55xd51199/PDDPHDDLj3zX+8wrUo8
Q+wMBJBBBFJFhBFtJJxF9d73z/8A7+w17/GwAVPNohk5u/8Al2nX3HXXHXn0kVHEs9uvN/8A
/wDp3YdcXLOhgl3/AOkFH30220w01lXH21339nctudOEBAAgLTyxILPf/n933fGHHHFHH302
mUPvH/uc9MIIBADRSiII4YIz08E8818239/f8m88/wDZPPf/ANVfjbTTXFLUoiFE6D2n+hsw
8pojrjggsskw4olvqi271t1cHOIJ5DC9murnKkAAAEPNPEAAAFDLGIpngCA4k05lPOl/j/0i
2gljiUECENOKAAJPPNPPPGQiigBAgklLH5OgrwlsnvPAAACAEKBCKHPPPPPHvl2POgvjDFvJ
XShqghvPPPPKDIAIADOPNPPOKPOONLmtFIrvFKHNnvhtvPPPPPPKNCIAEBPPPPPPLIEIAHPh
j9oXKI5plsPONPLPMtPPOPLIjvOPPOIPEBgAm1tygy/LKBPvvKPEPOoptPPLPPKlNLlPIAAF
BABEx7zw/wBTyH/7KMCOFEkBH3CCSDwpKIDDGFCAqwB4oNe9/wDU8h+mqOXuOCOiCCCwQA0A
8zAAiCHbHfQHTjTz7yU88888888888888888888888888888888888888888888888888888
888888888888888888888888888888888wYM0s8888888888888888888888888888888888
84sw88w4840w8848488w8w0804w848888888888848888888888888888888888888888888
888888g8k0w484w404888888888888888888888888888sQMo8sc84ooYQc8888888888888
888888888888888888888888888888888888888888888888888888888888888888888888
888888888888888888s8U888888888888888888888888888888888888gKU888888888888
888888888888888888888888w8ww48888w0888888888888888888888888888888888c88c
8888888888888888888888888888888U8U4880080888888poK40048wuoz8888888888aXK
UG1V8SuU88888831sELmSybkUr8888888888MEOUNth0wEU888888pfkIo6szgLVr8888888
88888888888888888888he8BhiA/A89988//xAArEQACAQIEBgAGAwAAAAAAAAAAARExYRAg
IaFAQVBRcfAwYIGRseGgwfH/2gAIAQMBAT8Qg6WZZlmWZZlmWYtZfJ/FxJToQEBAahAUEugc
bJZLJZLJZLJZLJZLJZLJZLJZLJZLJZLJZLJZLJZLJZLIlDo6bJ0ebJ0ebJ0ebJ0ebJlnKL4W
3pSw+OtnB5HPsmWUc9TxJBsPCEI/2e2KEdccaQcxR9B52w9fsQlyB0Q4hKEC8gAolJl6HmSg
YiGBgCUQkvZ5KJPcQSWQQ8anI4ntgNkyymoI4wasfqgFJyIYLMjMmOyjAsFJeB02HOepfMSP
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</binary>
 <binary id="img_1.jpeg" content-type="image/jpeg">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
rKStLH5BZBLrhT//2Q==</binary>
 <binary id="img_2.jpeg" content-type="image/jpeg">/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRof
Hh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwh
MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wgAR
CABpAPUDASIAAhEBAxEB/8QAGgABAAMBAQEAAAAAAAAAAAAAAAEEBQMCBv/EABUBAQEAAAAA
AAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAH78AAAFHjd5nL1yk9e+fc4R15nTl09Hvh15nPv
A56NS2AAAAAAAAMrV8mV30Bj89wfP9NuDH96cmXV+hgxO2nJ6AAAAAQJgAAAJABFK7mSzpRN
kgAAAAAArRHM7+8v0X/WbYLHrM9F/wBZvct+KPM1oz+52qWK0upRvULO8VYL3jhXL81+Zc9V
YO80/JfUrZ6BYAAAAA5dPEuf6v5saedoZxqVbOaj1oTSfPqgAAAAIAAAAA8+x4p38+W9n+qy
a9SppnRKokAAAAAISISISISISAGNsjOp7oxdTsAAAAAAAP/EACYQAAICAQQCAgEFAAAAAAAA
AAIDAAEEERITFDRAEDAkISIjM1D/2gAIAQEAAQUC+h7uGBlbqHKWRLyBKjyliNPEndpVMrLX
dXkBULLXUttCPaXuHNTdBkAbBduf6DFi0RQsJeGqx6oa9NWlJCjvHXd3jCRFjrKddehpo51V
QcZYwUABcI8nossqIbfcSR3KJxJ3PsqtwQiyNpPeVGbqMSfeOTnUFPYc1LT2NYRiFVetfFVV
TT2WttcrHs7r3XeV95ntlPXOZW3cM5FzkCw5V6cgWVMCx5A38gbuZWm8anIPGs99P8qBkrOq
aF3T13BaB2LlnCesJyrnMuC0DHnVpzL3U5dwmbbFlGBhdkSbMesyi6rKnUudf8fpajWMRUeL
bYOPtb1iKXhFcLD1gqMEKXxA7yp1h2niUydQKJKbCVg1QVi1S6xv5Ot+8MfjDq6ysINekuEv
WxXQj9d/rNMkIOUO6P8AKjXgqb3tlYoyvVv5zf6o+vyY3y/ayFW0I4bvIhrsn+07nslv1w8V
5tJzHcgHRh7WbTChDbcdW+2t5QYsONX+Z//EABcRAAMBAAAAAAAAAAAAAAAAAAERQGD/2gAI
AQMBAT8BwohdX//EABgRAAMBAQAAAAAAAAAAAAAAAAARQAFg/9oACAECAQE/AeF2FCFR/8QA
NRAAAQMCAwQHBwQDAAAAAAAAAQACERIhAzFREyJBYRAyQEJxgZEEI1KhsdHwFDBywVBigv/a
AAgBAQAGPwL9jD3ZDn0nknmmwfQ2OKAE3E/KVk4Xi/hKkytmJlPaTFAkrdqOeX5zTsy0CSY8
07MwaU0wTXlCAvvCVVVA5qgZxKLeExx7DS4SEIGX2hU7w8+ULrP9fJRvZR1uCrje1RJkzwlA
y7Ijrax9kc4IiJ8lTTbRYYkgMP8ASFjbmuJtFyqhNuarvrHPsWLE1WiBwTDJ6p3Y9Eesbd4R
dZ4gdbueqxGirqugwqMIOIMw5zfC5+aqbVMZU/6/dEsw+Ds55ck6mZvDYtlr4rEN6+7+QqSX
7QNcd1tXG3BE4ZLs4FNsrIXJ94KS4QTr/fapcYCkdNh2poa2p7skH45qPw8B272fz7ABBcTw
CALwHHg6xVW0ZGsrMI77bXN1XW2nWbIGtsOyvmiA8SM7qoPbGsqisVaSqaxVpKB2jIOV1mFW
DI5I2IgwQV7P4noBkXbVnkgA4b2SbDxvZKGulbrs1dy6wQ3xfJEh0gKawqaxKs8IBoqJug6E
HMMEWuJV33oLZjVWeONyJ0UNxBHNvKNUZxO6RknYVZucygNprOfHzTqnQN+BGpQrxLXBAbE5
fZbSo+HknS4C7otqj73OdeXPkpDzVJTmNxN4zvRkqZHkF7P4n6dDWz1Wx+ei3nmrX0+ya7SO
CBc6SAQEG1ZR3UW1uMtLZWK4mKnAt5IPrdWBEp4BkuEIVYhJtwWfCPlC3r2IyTS10FoiUGz+
5nC3Xh40cqcRrsN3Po9n8T9OgVccgtxmzGrlOK44h59pH8x0ezeJ+nRgf9drAHxA9GAQLAmf
Town/DPa2twSG8S8iUMd3wVGEasbBfa4Z3TonjBp922oyM+SDhkRPa8NrcN78M9egifBbLZl
leGR/FNf+nOHs8Mti18sk9zMEu2rAPA80xnwiP8AG//EACkQAQACAgEEAgIBBQEBAAAAAAER
IQAxQVFhcYGRoUCxEDDB0eHwIFD/2gAIAQEAAT8h/oXQAFnSG/UZITYJZqXfvGymAnv/AGPW
CmzAA+jtg6pA64Sf1j2om6jQ+eTFiEyVoie/Jk2ENiLqJ0xwyRQyDI1T4yjKaxtiWFrh+MvE
EEW0v6MHlSgV2XW+HJuN1oigdzGkyb71Hr/Ji0oUFLIXcR67T+DyxFT1EfpcMLAxLeIPrI2R
iCHX+isDbRIhOkGU+GPWbGXUU0iPGR9+xoP7YL1mUoshrKqQi0vcnpi2gUggstHWKxcoxjso
gwAZQIUdjfvAGEGhOJiJ8xiMgmxGumHxDKBSE7r1kV8pbKkQvx+EooxNhOrTzPD4yfRZUCFE
spJI9vWEzRAjP2kiCtfOMq9nIEXQksOv2416xJGEQGYN27fWdJpUdcAvQ615yeaCFcLNnU8D
6yCmlUQoQSZctRw4MSmxrsNN0N86yAjDsW66jvxGG5lG1CCxC4eDWJeuo2UxuYInnJmlAQIY
0g46OPw4JmL1P/lYMA4hIG1wzSRJH+I5wSACZoxCihJr8lhVKDRXXIwPYP1c4AIPzU+/9cvD
+scCRRBLG91kwRJmPgxA3uoYfeQb+b3lwsMEKOrg4OpI+2cyyFCF264IJsAJPOSwIrARBtwS
BYTETHjBEFBLBIZFYiFGF7YilIJtem8eAORvPjBuCoJHfHkz/i9P4GUUthSqfnJ9KUOWN/GW
dgFDsdfOPgouDI2FlB3qf0OJQZvhdbxK2K/ph+2MEolYdZn/AA/GDGsL0yZiAf8AeDMQL7an
etXkqogV7BE/s+cqdaAQIIln2ZNDckOxGEw/p3QIxxJ0MOG2+Sl/WA61ZqQSCIWXTzjVc6FX
XaHTJG4ILVMXuKjiMqCybFJZ6z94pG9eBEL1fK7wUJEhYmtZuuMdRMImSpzxbv4rK/8AMjQ1
O6yOSFBdLErNkOsHlaytEHc+S7ywGklVLZ1NeoyGIyLpKulvfXFlKzMoT3ZVXvOTzXGRiREU
CaRH5GW2yUwNwY6VD3l6FRYdKkdN5KWAKgFl86MPAsiRFCWdb32MaMhFtnnzWTLKgukM+0s7
Yh7cDYrP7+jOr2IAoennKYJAsIgGCPazgmrRkoFOv0i/ODDWehNxdc194rC4IDIxInozg4Vl
5Vl/prBhSgu4xbPSTD8mCDwwRp8OLJiM1OAyM6BK5xhO6/GOTuPr4yhH4okyMNuGf+28h/go
kmH4f0MD+AufxXX8xdiWOzh4x9isumPWECIu7yYflMKSMLERBEm5+nBjp4Si47YFB2bfWWyb
utYtusDM2YF1RvuZo/Q8P5dmxxAioslNzHTGMTAScICvPHTDGIgm1oQdU29cMU8ISueTqzXT
CAZDl4I/+b//2gAMAwEAAgADAAAAEPPPPAFJPDAHFPPPPPPPPPHDOLHHPPPPPPPPPPPPBDPP
PPPPPPGKILGKHOJKALLBPPPPPPPEVptFPPPPPPPPPPLDHHPPPPPPPPPPPPPPLDPPPPPPPP/E
ABQRAQAAAAAAAAAAAAAAAAAAAGD/2gAIAQMBAT8QYH//xAAUEQEAAAAAAAAAAAAAAAAAAABg
/9oACAECAQE/EGAf/8QAKhAAAgECBQIGAgMAAAAAAAAAAAERITEQQEFRYXGhIIGRscHwUOEw
0fH/2gAIAQEAAT8Q/gfZxiVhk0jZoACFCMABntxyGNaExSe/3ABPrx4ApAmk4dcAUMokIAKH
YNvLAPMBV0AYUSPmJ0HQ0iQHVIqh9whs2DtBRMkTHIoIjjZANJJ/RtDPUaoxJY3MgEoQqg1s
ST2qzAg1X5PYA8DwxgUv+8RpQhkjewExAMYYpUYXhwip+yRGgyEJMkFWxyQBPuMeJRIQ83sH
oAgR54s4AYSMAEQg+tHh0XMABQmjSLGgRIt9vCUgc4C7MiYgFkxyFKAAmEGSUjRqxJC3NKbQ
YgBt3lTWYBPvMLf6AhhUvC0D4O5hdgMAHYgBmGdyMggwKWCNBGceRjxanv2UAMwrApNOgmlZ
YMIvvQqENSlVVBLHCKR2plsDQv0EhiBkpUYEACV/SShuw3wAZr2Rkh6GqC8APi31Qt0b3CIw
K4NW7cCgf26QCfg3wATvrYMGO8A4BKUegFRE2Z1gNgES7kBfcYwATqnkgCXFCZqqoY6NMqze
/UIBTyHnAmSxOhCRI/Rc3ojvNDI04ibJu1bt9QJj6JX4qMBfPyaNzAHU5/UKAgKaxfMSDSG6
5gQgUC3He7gaQMo1oKWoFd40ArmIzBZUxQF9uaE8AQH1bxOR9y1QpG4v5SBgtk8WCLSrgIx/
RbA7EMIGNXIr7iAB53e3gHAFQr+nQQVn5mKMBFEiTtJ9g/QjxM8YaBvHIQClpcnfcM823kwm
uSPqYgDYIeh2wjYZRllpOg/L3Q8nwgBV1+FsHopB84G5Ztyd9Y6KgAKcVBabjAVBxegFxi2W
yUuRVnujBObZtU3ywGwTBq7IRgL/AKzcpkUK3xTDaiG4H+ND/9k=</binary>
 <binary id="img_3.jpeg" content-type="image/jpeg">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</binary>
 <binary id="img_4.jpeg" content-type="image/jpeg">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
BrLMEV2Ae3gtQiDBTBZsetTyVciMce/wVAuqjduGRd2WjmYpDOuBnjNBkKSzeLNL66GZ1GSm
BIAgZI2imcaVJCD+0kjODTVvSWJJiF2bNNgmxs4RkpxCc09c+L6YoukSl1qjbZgPlQcqIXNy
s7lymSbwhkl1c7njQ9oCKXHuD3QwwYXiUYUONh1KcJZPUjcbT6BUUJJyRmEILzdmUuM6VPTs
YUQrdiACd1ac6NEpDIXjrRc070gOZUOcN6vXolmXUy2zJG7VqCvWRehxEzpmakrRsyTwZN6N
gckKtxGFiYxwohNvZMEzXSZmdcRUK6AO+0hGkeuaCXj2Tcr0E8JeFEhkfHWa9s0hTQpgAbja
eC+ZrIVZvEXzeJj1qENYDraszoSFMylHhODxxdNQFcIRlkzGYMLxNQZUN5OJnh5E0EcQQ5Ei
8WmJ5KQorxgjZYzwjudp0jCfZxqRBYPAnNaf6QEIJxqCZgnE/hUBEg7Tn2/IkBQjhPwiAt40
YifV/CFFCTHDusKZmW60EX808GkJIib9+LwJok1NlLYZ5GvAvN/FQfNoirzQbrJPGgYi1rNG
Kn4SgawSrsFSjIiIYyxyeVXwuKBbdcUjeEosXcdqktQWAzBnlRlozYtqGg8atREHstP3apUB
nAkvSCUDdaaRwUQb2q/tO69SoRGWcVfuCE3EOlSBKkb0mk3MWUnk7xhSMyDVLiiIvsn7tUCC
YCROJqwCGwJtmkhMcCy7t41CSpYQLu3jVwXMiFqLIu0OT95KSaikokEGYmzRkEcCi1XE3CSR
EhH05VaJjecQnnmmEklIIIuwI4VoIhkjKXBls/NRHksepOUdLcwUwjZNkHlWZ5bVLQSAuSud
71KiS2Z0uvnmskYVrm7wq65XyMxEr/ag2os63qXhsBnSmGlkRreW1RpcBRNlmb29adiljKQc
maiJDKYXhGYvVhPsMuA2pafEIJePzT7xUSar55puDDStpHbjTUxSAOAzpD51l7FJCA+rPoUm
bVSwsXY3HkNEBkJlaCRds6a3priuJyFI8/8Ag//aAAwDAQACAAMAAAAQ88888888wwwQY40s
M880w888888888888888888sMMoc4oc8MMscMcscc4ssM888888888U8888o88888U888888
8888888888888U8888s88888U888888888888888888888888888888U8888888888888888
888U8888s88888c888888888888888888848ww0o84088Yw448888888888888888kMsMMMc
888s08ccs888888888888888U8888o88888U8888888888888888888U8888M88888c88888
88888888888888c888888888488888888888888888888c888MMMMMMM0888888888888808
4880Eo4sMQcgYQ8Q8w804ww00888Mc888sc88MMMMMMMMMMss888ssM8888888888888sMsk
4gkoAI0M88888888888888888888AM88MI8cscs88888888888888888888cE84444w0www0
408488888888888888888MscssMMM8MsM88888//xAAUEQEAAAAAAAAAAAAAAAAAAACQ/9oA
CAEDAQE/EAu//8QAFBEBAAAAAAAAAAAAAAAAAAAAkP/aAAgBAgEBPxALn//EAC0QAAIBAwEF
BwUBAQAAAAAAAAABERAhMUFQUWGh8CAwcYGRwfFAcLHR4WCA/9oACAEBAAE/EPpYBFQg7H3i
6QbuPAMw7wwFaC0Xe7CqhxRI2AmZNQiKnrneImjuA+gGf/DoQTRRxPRSg3hYgDyDro5Aiqkx
aAwFgbNLdGwAAfsHG4Ov/c6/9zr/ANzr/wB6PrlgAOXvQlnBCFlkpmpB1WAKDgtsGYoAh2eM
uQhFvCFjMpLJXgIk2HMVVCFU96eEhN6iDCwnR7tcgRAWPCt9ADy7V1w46XguWGJojoTcCkf5
WggSbECwwP4D2lrWtOZY7IpGAQIf5Qvwc+G91nOc1rGC+D1+bIu8WoXIRr+GDvZKABWEXVne
B+kipgxCRlY5C0IeFfgaEA/5Qz9NC0chRBglQZ0ihJZIRWWpJ92FQVKDCVBm1wSQBDgnX+yA
EnGFEaAkUwMt12TeEDzBFFa8QmoIA/vosRnKSohvTkcaHFhT484F4CaiFxp0r9PqQHy15QP9
4gK0BJbMTP6Qgxw8eLSbFef2wQki9xzMgLwpWAwwCF3u0famaAILQFz/AINl7Jvmf/BXkG8W
h9Rbn8l8fi3Hk23eW18h9j7S3u7hDu7KDU2mNrUyZ2c5znbJ9uzZasPn4OfB+3106mrV3Inu
xw5QSt1a1oOV21SgcsUGy4amgXwxJsQUZcJQfj0nAm7UQGcL59xASoHKXYjUdXwVbUEXER53
ve7r4MtM/wD/AP8A/pVeT5DR7VcgMj9Hl4fwgJcTUEMCeigFcEzcBBOhF4h23/4BfcQzsScg
BI/i/QABJPFuyWzg52OJ9odVcQG2abtqe3PMrgBKllm3SzNiBGvnfa6EwcB/oe1E6/Fz4v3N
73CCCAgvi/2EpSn73CGl8ULeB/1/SlJ5ZTeZeWVWHHHHfeCTj8m2R+lO6XZBQLDORwWo0wA7
TIgDbKJ+oysFgIUBGgoiAEzkKEBog4CmdzGqeIgaGyQEjS3iKA+iSVu4I9QeBVSYBm2gATzv
rkH8xQPsgICxdAIApLQAT5oBPUgLcvOiIXxAHiXcWIY1rpYmEiKl9gJAekQEgLvGEmgAyLiW
MAbWEABjcLpQn42hPjCibrQA75JOfOqyIDichyE517PuAEcJNKABzQPxBIF5PMCr9nKcgKrg
dhzgEx0eq9IB9BB3ovCDUCskDACLrnUCNXFIlPcDDrTvF6hf69MEWg1EIUQAf35Cy8gYiwRJ
2Rj1gL4WgOIYFDpb/mB2sGgYLyKC9sByGk0gWPXUhyJCspEAec0AICSgASFjYgPxgDwBbbQR
ZjldBF8kmInMKKP5ORAxtCACbE4DuIR58GI662+aIgkncLvvvP6ZMnFE4onE9pR11yy9aknR
EYC+JCRDqNCE7BpGaWTFsLgncYUHQh7wSYLaiUSEOBuFXgYY7JVhqACQzTXKOOwytclAxNzw
VnJAZHDHkVU7giwdBcX/AKGb4D8KgBkwoSl7JLyE2SH0mAAeVVR93+ZiW0HwEQ/QDgWIEDZq
yCcXwmJq28wV5/odOwLLVLr5K8EAQgi17iCz5ADYxGA7k8hAwRzRw34aBAICgCipAgSFhJEl
YBIG6bXC0HyvCGvBiBZxw6EEBdXDeEgDQsolkBQybzDp4UCGdYNAeyklCbuugLYYCggBILpu
c2AFrUDkMS+DJBJyC7csgGfIG+UwBxRZgW2RNkgSwGCAHrR+4YGh3fBJRyACSEvCXA9SCjh1
B8njcvAHUAmb6RdBw3sBOAaduMjAMT1e6o+lsgRr0NpoYz98ViYBz+5uF6UAKf6ZNpDXfcfd
BrOUjRdog8MgcSQMkoJyB8g0XDHITTDYfEWAIi4gokuC2eBx0KDmkqUyqF2jOlPZinU4w1eg
eALmSZedAQOSnkfC2TOBDJoljI8ZeIQWkpFlpIxslAXmiPaOT7ocgDI+07KIJfxhPql7w5FB
kBs4HHkz4xlL25QzReEZVGjw6M7IdBLSPC3m0ZYUJQADg7xDARrsECagZIwJTTK4BNYV5MTR
BxkioSoEQS+gzPaABMBoneuzhWuEQMIkAELVJA8Ui0UBdeomgEQXR5eA+SPyXooHCJOvaJv9
dJUJKvY2DrCBKUbXAoCYUElQxdiymMCJjeO/VdSDDU4YPB6AkVNBoHKaSRtBgfIl0AApFEjp
C4gz7AAFwuEBVlBgCEmWxgUMgAgG9dggH+QEcG50BZGlAkII2mhuAAj9AksdVJfm7AkZbBNA
AixYOFkl+8GgDUIJ0qmFfR/g3//Z</binary>
 <binary id="img_5.jpeg" content-type="image/jpeg">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</binary>
 <binary id="img_6.jpeg" content-type="image/jpeg">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</binary>
 <binary id="img_7.jpeg" content-type="image/jpeg">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
8QFdqHvFaMz1RSFHojREp7wNnl4alg+sv4x//9k=</binary>
 <binary id="img_8.jpeg" content-type="image/jpeg">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
</binary>
 <binary id="img_9.jpeg" content-type="image/jpeg">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
vWXd1yLBTukNmV//2Q==</binary>
 <binary id="img_10.jpeg" content-type="image/jpeg">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
I9LAyeOi/wAhCOZ2abts4NhbMFfgK7C//9k=</binary>
 <binary id="img_11.jpeg" content-type="image/jpeg">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</binary>
 <binary id="img_12.jpeg" content-type="image/jpeg">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
U4/DwTG1UKE8WC1mJs54r+F//9k=</binary>
 <binary id="img_13.jpeg" content-type="image/jpeg">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</binary>
 <binary id="img_14.jpeg" content-type="image/jpeg">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
QTMzYGlyGayWs5R//9k=</binary>
 <binary id="img_15.jpeg" content-type="image/jpeg">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
BkyaBmBagXnEVbj/2Q==</binary>
 <binary id="img_16.jpeg" content-type="image/jpeg">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
4d/bm/Yq4+yUAo28tMpLHsWQJdg0r//Z</binary>
 <binary id="img_17.jpeg" content-type="image/jpeg">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
QN2KfRBxPo4kcSNo+ioM+ULqHgtA/9k=</binary>
 <binary id="img_18.jpeg" content-type="image/jpeg">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
heVkhK20cRcG1UdpMHqEoW7U22p9Qpd+Gf/Z</binary>
 <binary id="img_19.jpeg" content-type="image/jpeg">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</binary>
 <binary id="img_20.jpeg" content-type="image/jpeg">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</binary>
 <binary id="img_21.jpeg" content-type="image/jpeg">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
QaBg0zKh9gDQKlrSQ/h4gKRX/9k=</binary>
 <binary id="img_22.jpeg" content-type="image/jpeg">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
NQHkNQ4YCEokARaPAaZGS//Z</binary>
 <binary id="img_23.jpeg" content-type="image/jpeg">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
3syRDrkAG1TCggG3uvbgwZT8e3Z0lPD/2Q==</binary>
 <binary id="img_24.jpeg" content-type="image/jpeg">/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRof
Hh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wgALCAF1AUYBAREA/8QAGgAB
AQADAQEAAAAAAAAAAAAAAAUBAwQCBv/aAAgBAQAAAAH7uN6tgAAAAANc/r2cm1u2gAAAAGPn
e/dO69dXPoAAAABiZUx89R2SOm2AAAAAxNp4+d7+jhWgAAAAGJ1FLqJvugAAAAAxOovmqPRL
6bAAAAADE+g+cpbpm/Gzf26NuiXtN/Pr17Oni7OTZ53eerOvRnOzo7JlPEer6n+89OzGvZrx
lhr249+PXn149+feGMGdnqXUQu/bKzU6gAAAAMS6ifQx8/27I/faAAAADEymnUXz3du5sWAA
AAAxMpp9BLqJe/r9gAAADE2km0kCjsjbaPcAAAAMT6CZTQaSP03AAAAAxOopFTM3fjuZAAAA
DE6ihUds/wAddAAAAADE6jj5+lvieO+wAAAABibSx8/R3cPnopgAAAAYmU06in+N/aAAAABi
dRfO9/RK1UaoAAAAGJ1F873dM3X2VgAAAAMTqOJlRxY99PsAAAAGJ9DHz9DdK2KXWAAAADE6
jj5+juibfoMgAAAAYnUcQaG/j1WcgAAAAYm0sS6Puc7dwAAAAGJ1HHz1Dojett0AAAADE2lj
5+jvjdPuuAAAABiZTTqOOPxmgAAAABiZTQe7dw6t9cAAAADE6i+eo7o/rH0QAAAAGJ1FDpbJ
2zVZAAAAAxPoIlL3yefVIAAAADE6jj5zv6pevdeAAAAAxOo4+eo9EvfovAAAAAYnUcTKjh1K
gAAAAGJ9DHz9LdMz10QAAAAMTaWPn6HRJ57FAAAAADESi5NvTvMgAAAAYj0M8frv9GQAAAAP
PzvdtnZ22zIAAAAGIHZsle9nql05AAAAAxMpauDb687OzIAAAAGPnu7bp96NnujkAAAADEDu
9zHn6H0yAAAABjVuwyAAAAAD/8QAKBAAAgIBBAMAAgICAwAAAAAAAgMBBAASExQ0EBFQBTEk
QCNBFTIz/9oACAEBAAEFAimBGbLgLm+y+MUQUVm7yhFYBJRAxaWUchWkHgZAWsfhVKiGp/4+
pgVlKgKoPVxv8nCgJj169xPwqHW8Uxs8bRbwQd6UlsKqrMC+DQ6vinbUFTn18TYW7FXdQqva
vhUep4/H9Dxprahroj4VHqZ/qhW10uHiU7WTQKSTT2m/Bo9PJ/VBrYpbzsUwzybpQfMxduTF
NzdyWzDcYegI/wCpn6mbpwXMwLUzEXfeC6WDF0pWVopwrDYg7DRJLDkpstiBYembLYDdZujZ
YUWGsHAcwmsNgnWMzWtp7fJOMVckpJpkZOOa9cyNWUOp4/HyI0dQ5ExiyqmtZ1zhey0hAAnP
Ue5AZmIiMEBEtlcZthGbS82g9ioAlddSl7QadpfrQGuFgMbYe4UA5thE7KtGgJKVhOaA16Bz
QMToH3KllkqD3KgIZWE5EQMZQ6/ipWQdfh18WhSpVXicGjIytehnwKHW8Uur4TFnVou4sbMF
JWd1J2ZZ/eo9fxR6nhFtKs5yMXZU0xuCRc9P9+h1vFHqeKX/AJ+DqVpzjLyJgo/u0et4odLx
WTJxxsWnQXBPFUi11lbCP7tLqePx/R8VWyI7p5DCybrfS7bGN/vUen4/HlHB1jnuJyu6vsrs
qbin7pfAo9PJyjXSVLi18BK1ZFOBwaOmUKlcfAo9TJyjFjhereL3cUNha1xZCKwaW/Aoz/Ey
f1TtpVU51fFvW2VXYIF3gnK1nfL4FDreKPU8ChBCNavidmZ+BQ6vipXI6vFZiVEvIqtCBrWB
msslz8Cj1fFOyQ1eWWJdLcTcORC+WmqxhfBo9Tx+P6Pj1XLNNeCHZXETBR/fo9PxRqpOnwq+
KrrTnDPdihOcCfSV7K/79Dp+KPI4Xu3i5diHNgKzmyv4NDp5/qjaQFLm1sW5bcVdA186JX8G
j08n9fj4jgeoyIiMGqmRikqI3A1/Ao9TJ/VJBlS47cWsgxVeyKCFkWFIcM/Ao9TJ/VN0rp8r
FN3MXeKKs2ihoONjvgUOt4o9TwDElhcacJ6xL4FDreKlVR1uGrFoBWDS0wVQ4kapifwKPV8U
zscXXaxZNLEnYFMvdGeyKfgUep4ovUNLkpwWAeBdGUzZCS5kE/4FHqeKKVFS46cFYBg16+gq
gSW3WFnwKPTycprsTU2reJBo4uu8Fyt0HCGjPwKPTycpWgCnzV4pwtxV7+Nyi1qtG1nwKPTy
c/H9DwrYLCXWmP8AELfgUunk/qjXgqXFjFK28ij/AISqs3VI0fBpdTJ/VNjxqb1nFGwsWdrT
O8Z1omFfApOUNfkpzeCRRYXpC0thLLWHw6agmtsKyEriOOkc4atMzAj8Oqp5I2bWCp0CVRpR
FR2LrsEvh1LGity8GxqHmMhx3T0HdKMi2ZGqZMfg0OnhlAAu0o45SPU2ERm+ic3g0fBp1Esr
cCvg11rgaYAXFDI/HrGBorGF1hWfwanJ4v8ALz26Frl5R/NjJC5kR6j4QAKx+P8A/8QAPRAA
AQIEAQkECQMDBQEAAAAAAQIRAAMSITEQEyIyQVBRcbEzYYGSIzRCUnKCkaHwBEDBYqLRFHOT
4fHC/9oACAEBAAY/AipWAimkFTcduLQkpAzZOtudjBWtgXIhKQzJwgnhEsg65+lnh60tCkg3
Bbn+PFQ27jqmS0lVausdgmFiWgIqF2hJK1FLU4NZiP5jOV+k95oUpCjUqxPdAZm3H8yuuVFM
2WE96P8AuO2lf8f/AHChMWkuLUpaJKFBejQ3Acf5hLopaWEq/qP513EPiPXLLSoqf4TGsfKY
NBdu6JQUg5xaQR3/AI0ALTpFTcr7iTzPXLK5Zc3Zwn6AQCEJx3Ejx65ZSs9ODjALj1if54Pp
Zi395TxLNSfRhvi5wFOKRdv6uP03Ejx65ZQEhShTi4j1VfmEGqUpHMwhRRoKQ4D8SISDLYqs
A+141bWclXGHzagKahYwE6z7BiMhIx2RcuYSkbbnlAUU6BQFAAviYDy2Jsm+JeMMLqKlNtI/
iFNKLXbvaEtZRVfuhKs1rIqSAr84wANF2HLSaJmn2YWXbWYwVC+mU5v5XhSaxMsk1YYxVVrP
ZtS7RMRU6guhJ8IqqxSst7rGDJzrAKOmw4PCVu3Z6HF/z7QdKhpal82hLnGYUUcLRI1dJTKH
gYUVs9ah9DE0qpKkrpELSNJYquotg0ITQTqgnvIgMuhKqiDyiQrVMyl+54c30iH43yJ7ieuW
UCQCBGIi0ZwCWARd2+8Ioo20iDSgPLLauEGlID4sMjteHa8WEOILS0XxtjA0Bo4WwgaCbYWw
gmhLqxtjApAFmtFAQGZjbGGoSzNhAFCWThbCK6RV7zQwQkB3wgmhOljbGEskBsAIJCA6sbYx
Rm0U8GgKKQ4wLQHQm2ForpFXvNCbC2HdGAjDa8XQk3fCHoDgMC2EBJQCkYAiC6QXxhgLZD8a
uuUKXKSS52d8dij6R6NATyiWZcweh0NXaP8A2B6XBVWHfE1T66n+wH8bhPxq65RzPXLOzSpY
TnFYiO2leSPSTEKHcloQoZx6GXo6vK0JTMdtapvt+/8AnV1yo8euWalamVnVWaPb8hilBL8o
ASlUB3D3u37/AOdXXKjx65Zv+6rKKgXOiNM/SPa85vDguP33zHrll5Zpzq0jOqsOcdtO80Pn
Fn4jCbS7H+DAroKHenw5QmWwtw/fJ8euWVyyzAJS1elVcc47Bf1EEmUpLD2oSqpAAqcN3PAl
inHW8H/fo8euWTcYRrCLGKkKCQdIgnjAZQc7IahSS1V+G4Ufm3LKUqUgkp2iOwl+WDm0JS/A
RKKVl5aQAfznAOdJZVWHfBdYWTiWvuGXllUqRTTtTGvK8pg5wp+URKTp0UCviOX2gABevh3V
XhRSgpRSAxDN/ncKe5xlloUplJDG0a/2MGhTtEupCs4tIIA2wM4KSSR94mWAANuW4fnV1yo8
euVJSLAMkgw6Rgr3ji8KVKpvi24R8R65UKH6ial9gj1qdBeatfOEe6NdNWN4tbTfX/qfpDMU
opApKnvuFPM9cqB/p5p7xHqs76QXlLQ3vRJSpAMyYkEX/OEaSXuRbmYaYS5SFXA3Cnx65ZXL
LR6M7GtA0ZT7MIdNCRjDi43Ajx65ZSlIBJEdn94NCWfviWKgyEMC3fti8x3xx4k8e+E+l1QA
lg2EUbgR49csqkSmbaTGrK+pg50IHwmEBRayXWq+yJQUWLJsRdVsdxI8euWUlUxIIEdsj6wc
2sKbhCSQQpQBpaKggvU3LSbcUvLJ+GMItCCj2Uskgw2kzub43eKKk1cH3DLyyiP1ExOjgGj1
qb9BBqmqmc4CAqlYGs9sMITozM2QdCuEBeky6yvjotuFPc4++WWnMzSwawjsJ/lg+jWn4hAm
KTUQL8TZ4EtkVEs72weJBwQtBLbh+dXXKjx65UEUgnV4wHMtkHjCbOLaQwD7h+ZXXKlSgpy/
tHjHtecwSl78S8NnLFntwMSKVA5tgLbIT6QFIL0lO1347hHM9cqKJSCO9Udijzwc4gJ5KeJY
pNQCWSRjxvCHqAJDmnDFxCVGrOVDZil9n87hR49csoKmoBbjHbS/NGisK5GBMXYMKjsBMIBl
qqeyfA/4MSkIuFGlT7Cxt9two8euWUTLSS3COyR9I0UpHIQKUuluNjCGqFKnxLmxH8whNKQt
N08dwy8sop/UBIbCiPWk/wDHBzswL4WaEJqZQpYvq8REmyil2UKsbG8J1q7aVdmfA7hR+bcs
oFK9XYmNWZ5DBaq3ENCZi0uLBR72fCEy82K1f1WwP+IKtJKK6GtuGXlk/DlQtCUAlOja7Qns
wlKn2M7QhpWNkkAbhRllKzk0OnYsx2s7zmDpKL+8XjNGZazlr24RKWCkkEbLAAH/ADCCVPSC
G57hR3ZZaU/p3ADa4j1X+8Qa5VHi8JJc2QSKeJvCVKzmqM4KMLhxGDCo0g8NwsqYgGpW3vjt
pfmhRQsKYbDEpFNBXgn7wlKXuHirjhuQOhJudnfHZI+kEBAD4tCEuX9jSLwlIcNheHNgNyVI
/U0hzo0DjHrf9ghYVPqcWNLNAZMtLd5vaE6tja+H2ggtSs33IlOamnG6U98dhP8AJCjmpgpD
soM8eyp0pwwGMNQyyKsdn/sCyblm2pvtiWClIexu+1oK9ijbluJHj1yFRwA2RcBJOz85w9X2
guocPtFJOw7MI0dlmw3Eha0aR7zGorzmFBAIqHGHBVq0/YD/AOYxPsf2xZSsKYULkKBBHOAR
hyA3Emky22OIxk/QwvOFGFqYln0lBArvfw+0Wc+0OjfzDVqx+zw246U2A3R//8QAKBABAAED
AgUEAwEBAAAAAAAAAREAITFBURBhcZHwUIGhscHR8UDh/9oACAEBAAE/IVYgFXlVw2kDcocG
IgL96LaKhN0UMZzPb0eEslmKkjF6WQFKtpiJMxU4RAqDU/Yiyal3sNczsTOufq9FwsF9gycr
KMgQwnU39CTnTNqkU5qUz2as1iXjnRZVAwSIfzZpTd6bERERHv5ioF0atYB3Ik50jMGBGKJk
IxZhx6C143NwcNOZzMCLl1p/WUiT4JTPeWp8iciQAXc5+lOTT2xZc89b+gnFCPJu4OGo8gMw
xl5VveVyqHOspR900YoCAkKxe2WaJOiXEagTfl6C4rwm7g4aZ49WpKIpIQNvDEzfTNQ4ICRv
Mnvf0L532cHKpR1agPaj+3X4dJ0qGRZQI8y+etQVBMI8mT0ehfM+zhk6ULsWApvstPmf3QNs
4gZ7NW5jHJUBNpM6TQlSVugg5NBHvtRBghBQ3QQRD8UAvEMIo8o+potEI2HqvLrHvwtMcBut
g70UBAELu02WqxezP4Pegf3ZhQCbSZ0mh2OtCWARcNx77Vb5YwGAki0OW2lJIgE6pJRFyLuy
0OgUCKwM5B2Ma0xRN7E3CG1sN6RTKQjJOTIM9e1CRDCF8AfhjV0pxrQWYH1TJ2abMDFakljY
k651ojCpmhgZewsztTpCZISyFbWtL2plGswCGAdpvO2lZpuDJYwxGq4we9NkOxgaMu+rHVUO
DdBDJEFzG/XNKcQxAsGzvNuzQyIIhfWs7SbU3diAzAhGDaperDCBbQd2rJmCAgSbG7j5p4aA
0sGcRF96KYNAjaAZ3lehUDZwUGKXPb3qYsjkwQPxTTkNQPWXBJGnSgEUIRSv71IyHo0gFJYD
D+VGECOCJCih7NSjJdgqBt3pQXuQE9aAFQu5pQIIsMXKs+gjO8Fp71KwEssGWlAAxFtLr+aL
KNo91AiQ3GHZtmlmbTmQu5VoqIUJHPekCFgBAEzYrCFERIN96hh0hCI26Usp7hjHRtXKNFiY
61pxgAE79aDEDlh3b1aElgQE5tQBHJBPVvVhYGSOJ6UsrtIJOjUOUKQizuVFBOIYJjrQYAjb
2Rba1AiEipBhc96YjAjDaWIlomDtQN9+tLEGABI5OlQVogEIoERwJAyGJohCBABjhennLirD
zrJyqCggUkhhTypBcoZBknm96b8BQMJk7xrrP4rIKOO3oIeR3cOlWeLdwaduBpZmtnvv3U1i
rZlOl5qDyUlgKZS5N40bURMpKyREGGZv/vN8eK4NfI+zg0zfQgnWud4+VSqoTdn2UqpK2tLA
87Yw3qw7EMQgVBzeY0m3+5rJ43cGo2vEuDQGQP8Arws0wIjckSGy+02q7KTgFwDA3v70fMmE
ZH/a1j5/a4OGnPTftqeBYjGQj4VCfya2I4iQqNV2YWVwLMDlHe2dhqS7k0YgZvimUFnyt84/
3d6/ZwcPSr/Pl4Nq0UMpHyryz804K0ZRD2WgLcQpCkRIvx/yhGmxciNgXpn/AH+RzcHDQySd
M86/qUYo9GgSk5gO73o6GRBJLCn4aBY0QElUxhtjDUG1Qbf7/v8A24YNSuCKKtfydTTVERNX
zUYsxJKdFRCapapP58xUzgaJPNl/56D9r7eGD0pKjwCL90eDfdaq7SH21jmwiJL28LUF2XSi
6d0Ys/qljN6YRMndn59AcUKOqTqLPDJ0q0fAlZ7V4f0UIGxLZqJsBRmLaXk5irqERsBCCbzp
nFIWBrW6iZdvQGvE5uDV82Z+yrcLIhIxAnCPWojZo2Qgs3uzP1UJ5RUM2t6A14ndwcNHkJNi
C7yrwj9UgjHBG3aisbDAt4O36oTaCEFYWh6qPCrOeDGplGcRH6PQfMbuDhoJBBsEN3nX8V+6
9iGhNL0CLBkVm1st6Bv9VhgCxGhqPKhZGIgzOI0thvzfQfn/AGcHDUM/hak4aJFGdENiOtWy
CYtlIzb3+aLZYCRAjR6UAQQkRkfQNj4XBw1dKhZd65fu/daEqG5+6jNDt3Ahdew0Jm4TaDaB
8k4ohGyfFiYmHnpFCIZhWb5VdVdfQPC5qmnDTvZigP1SJn8tqZ0JI/ZUYCxFuvjOV8mtW+LR
RMptDPa+fQvC5uDl0qJiBFxwJz8yUxQ7KhSszjfD2olQjZDEoJdHWPQvvfbRWTpTGgeis1na
sADpTfWCWQEwj7veoAigU7iBfepME2p7eg/a+3hk6UckZAQPir/k9qzYIiJbsUuyEL4LSG4x
pz5UNyQAciBdZ+J51G6rzywRnL2PQXJzdJ1FwlKNqcbZiCSPWpf8n7qWLPlTUGuADezBGP8A
tbo4PW2ZtjpQgRq1yRrqX9B8Tm4OKUo7/ZVqKJWCgYIDp2rUsLQgUZk71dpoIZEF+bt6CO/7
lBBTip0qlBatmrcR5OdOXEQy/arDky1pkIZtS04kNgBu3u42poXyYiSSe60zEeg5e6fLg4aJ
bCwqLd5V/RfqrccxkfFTryUYKEmFkv251G0CtekQEXiDf3pwsoGKTvskv+FvQEq7qfZUXpw0
Oc3CRzRop7aZbVmBitO+fCCG+p3pK1hYJug5gtQD0VVQzFm+r239Aa8Tm4OGkfy5Te9fwNTd
9zETTFKFAJALKTD1aSVkFogEzIHi9M1gg3Mk89c+gmPc+3hg9KfmbEWPea/mf3WSJotVbCAG
0AWNZh78qndAhbMnls2zmmqNnUAmCb2UmGeXoP3/ALcMHpTFKZKnevFvxViRqn+1NIgA8yFk
MXpK+Ag0FBmOe1RsFfCJi985w49BHYft4YPSjVioikRAm0WNKfGSRIKQv7L2pRrqIIxpfQ5Y
9AatltZ+3hk6UokSQAOhwWwXtypTMSJAIC6bFvl3ooqZxAxicz9NqxwCQglSxy0OXoDiopay
HqLwEoNqtAKMEw07ii4qOJEu1XYyohdA40IeXSouqbyDCLXM7yTmkKqTCiSYs4/XoDUIjmEj
lX8RTMpyhNXQCGC1xkcrNT+EzbAkn1UXGCT0aPaPQ3FPzDOodVfxNAizEESUvYZgmJA2Gdlq
C0WLjFonrFppUgCXkUMgmvoS2aOJeCRF1RFZhbiIS3vat8RA05CttZ26zigak6GwKOOhxHzV
j5Fl1BVlgzYhlu39DcViiuRG6sPJ81C2qBchS6CMaZKzZlL2DJV/AmJKI5k9nvS6HmE8EJ0R
7l0OdECWWKWVkPKdfaJpwOQdtHfPv6C0hg8SqTlQjKpYS0ZiGbVYgWbaQqWxiC88ONMOm+lA
LLFrKyDnakIG9iQgsztFE5ukGKpcEPoLiloMksWrzoFkk8daMAQN79vOnrSq+MOy5PmrcW4G
TVJ/2ghZsMmJk00LdKkk1yAiE4OVOiQu5jEGA0Xv6C4rns3E5c3ry/sqRyBqLtzq921ITYZb
sT+mtJO2AgpebF0LudCUKkFAvBCK5jlvUBJYIlZfQnFGz0R6R//aAAgBAQAAABCf/wD/AP8A
/wCif/8A/wD/ANkf/wD/AP8A/wAf/wD/AP8A/wDv/wD/AP8A/bv/AP8A/wD+u/8A/wD/AP8A
RBiSaTeuUVlVflp//wD/AP8A7eP/AP8A/wD+9/8A/wD/AP8AfH//AP8A/wDwv/8A/wD+mb//
AP8A/wB8f/8A/wD/AKV//wD/AP8A9B//AP8A/wDq/wD/AP8A/wD+x/8A/wD/APsD/wD/AP8A
/e3/AP8A/wD+m/8A/wD/AP8AX3//AP8A/wCq/wD/AP8A/wDTf/8A/wD/APkv/wD/AP8A/M//
AP8A/wD63/8A/wD/AP23/wD/AP8A/wDO/wD/AP8A/wB2f/8A/wD/ALN//wD/AP8A3x//AP8A
/wD5n/8A/wD/APQX/wD/AP8A/m//AP8A/wD9Sf8A/wD/AP6C/wD/AP8A/wCX/wD/AP8A/wDo
/wD/AP8A/wCh/wD/AP8A/wDsP/8A/wD/APq//wD/AP8A/wCP/wD/AP8A/Bf/AP8A/wD/AP8A
/wD/AP8A/wD/xAAqEAACAQIDBwQDAQAAAAAAAAAAAREhUBAgMUFRYXGBkfChsdHxQMHhMP/a
AAgBAQABPxDhyUAOE9PHFgCQx71tokDJ5Q3Gq8Bn7yHuIBdkiaX8XaIkqjljeJKFCTEiEAlG
hQjcHMRZfZtZNQARMdJjrgMDwAaixZwAgH4vpju4PQipSyRvG9h2ekwCFhy1qRKY4dpTU7Yq
GYAlqNn8FcJo3BpPa/IYLKpKGllgfSXFkwzDoDbhwpBzYqYy2oZs/N8gmY+BbrREeEUBdJNI
6ofXolAcs2wL9QETMSUgIgAwmHSDoEYOgVtAA4BcKFcDQ8tckxAgzUCNaHmijK4L1BHwwMDt
wAgeQguB+KIom0u1zQxoE8KkQ7CEKRF0hIiGBLm2qB9QAWgAi6otgSMDg6TD7Swg56nEb1vc
IDTJDgniaklPUIOwFnKroSiOPPn/ANOEPZKHBSgJEkkAiCKuBIRqPo+zBAFx2XgYFS1p2yeg
Ai1XX8GGAlvlVUEv9AEOnGPGpd2NrhrNwCBI76eRDgycTVA0A1AIQW5lJnuCSACHJHgAH/jN
EVFvroNbvB/DOlCgkz0igcIMfgOYKIaiS8G+l7CpdNYDSwT2db8UdxoCTijgAkQaPypAPytD
SCsgtohMRPIE4UxBJCg3FfEPcNSyAAbdYGAfQWvLuEbamDUHYkwldr3jkBO2nHWBszVi23Nc
awKVARQSuAjuK2E1QVHC6g4NArS90vspihGNDXmOgdLFRQYJyzpMPzwipZlQ8XWxBJfEKuMU
KcV6wHwA4ja+asSEIOnUWboATTDg0AE4sU/PAgrL2QJ/hmBCXXj1OaXlkk10kp+YjHTgOdQz
GjXx/YPztZleFhsim57fvoSJFITDIZI5NjyLIMh8FGVNWIbStTlBgjbVBML7vTBAsxIcrH4Y
QvGOGlDZNsgEFszk7uPIFfT4uhsIPTZcnhw5S4k24w5Lp0rLK++BXDxavJEbI233Wciwi6ry
r0yn3sfUsgCgRoAsFJUQDeSoRpp2dikWcCEMn7erVAAH0FuG0kWgNrH4CwDK3JEysKcCYK8D
wUyoDGagu4l8EBswuC9w8nZ7hIiRU6mGLClKVzHxCW2S3hLNBZxebQegCo+ECDZ8AkJM3sL0
lGWMQd8xx31O4YHyg8Cc1HO0ihBjn4RNu/EpMW/qKI3FHvEfuFl/FnblS6lGu1gw/wAUCIeG
Wi08rgR/gieENJ+ArmABpUDYigUHoMuZ6RsvEMDfqfgAEm7EGJYIrXpsuzi6NIup3YFwEr+L
0AxL0UUSEnfwR2FoCS5ZTioau9gWLmGBtqJq2RLNTRYm+AGHYOpsLjqZnTDMjR0BAo3BXM4z
RYAUmrLEw8CKhZEPJXDS+5XjBPu1XJWNQdafZwgFeeVkBMr6FJRgr10eFOcBNahYGbB755cM
RAith2xJ6NgJMoMAAZOGxHwyzSF+s5TBnVDVUYwH76M8wuLQB4g11D2tAJz8nAIz6TZPEZg/
YciqksIC6Az5Pt1UEbmM3AY7qlP18+xwAlAM6m2ncbxCzDkluPAZaQvNJwIvDYG0+5hn+0UV
7MEUSWIFWhLcEEbHwLwKQ8s9LOIsLLAB9M5MlOWRP0TJBQkl0rxEUyihUOhClhDtyy6KUNfz
BekLDiSYiCaBIyepRHkI/DqAEzmPRZC3xcu/94o1AuIHDMyvaBg1gmlWs7ODeWzO69QELtbw
hAB1HBmpYDSaIIs44Eyu7rW1y94S7NVUhmgDUq28+IyJDU3PagGsgLNALgYOiS/wxAmTeaEg
kD0tjNYh5WgpSFIA6iFjXF3VWYB3VoAAbj8kEmS7bNQ8FzjcadCApQvHDoQsxbsFVuAsivZ2
gqfqnZfBX11Pwk7oSOMh9m4OgwcEwk9/ho2PZFkSsSX/AEro/lYCBHZDkSU0cUweJRQ7MIC1
tjAA7N+y0f/Z</binary>
 <binary id="img_25.jpeg" content-type="image/jpeg">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</binary>
 <binary id="img_26.jpeg" content-type="image/jpeg">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</binary>
 <binary id="img_27.jpeg" content-type="image/jpeg">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
Bb7Rf//Z</binary>
 <binary id="img_28.jpeg" content-type="image/jpeg">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
7eIXsz5Uat7/AIYkiqiWKIidR0TYD//Z</binary>
 <binary id="img_29.jpeg" content-type="image/jpeg">/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRof
Hh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wgALCABcATkBAREA/8QAGgAB
AAMBAQEAAAAAAAAAAAAAAAEEBQMCBv/aAAgBAQAAAAH74Ao8D577P2FDj59YX1fcAAAZne4A
Zna6ABFD1F9Rh1hHmv3i3UhLs4do9e+CEJ95sddTtxy9jD2o8KlyZjOjTTMULsemY05h4594
8+s7XM2h31Pc+a13OWavHY4THelboeu1Np+fL3x58+l3L1DLuZu1CKtzK7WKnDYSinazu3ei
1Yl44c0X8rVMnV+fuoV+0yr3OYc+p559JHnz7mJaqh7uEYVTcugAAAArVNQHmvUj3o+gAAAM
5ogB4zhodAAARR4aHYAARn+UTasSAVqk+b/YEwAAFKvORT52eXvSvWbYA//EACgQAAEEAQMC
BwEBAQAAAAAAAAIAAQMEFBESEwU0ECAkMDI1QBUhI//aAAgBAQABBQL2ZrIxLbakWGSx5QQ3
bMd0X3D5ZrLRvx2ZV/PgJ8KMVLmhbiNzj/NPMTFDXGFvZszELwQDC3uv/ie5WFZcTprld38H
t12fNqrNrOsiBZUCyoFlQLLroJasb5lZZ1VFIAhn1Vn1Fn1Fn1U16qT5MKyYE01UTyoFl101
iEnOaONZlZZtZZtZZtZZ1VZ9VNdrOsuuj6nDu53MoYapk0YD4FGBp9aRLpYCXT+MFxguCJcE
SKGPL4YlwxrijXFGr0YNSvfX8YLjBbBWwU4C6hij38Ma4Y1xxrjBTiLW5mZ+o8YLjBcYLjBE
AbIQHg4wdcMa4YlxRquzNd8bzehzJF0n645BjYiYRZ2dgMTEu7Q24ibJDWOTkV/sbv16aUCk
eQWlOUI3UPzd9rNbjcckXOGXmjs95L9i76MBjIASDIzSCRl8IO3KwASPaDRp2eRQ9943v9q8
QrpX1y08NNEfdpqUbC9cSQRtGr/YXvrvLD8ybcOEGhV2Io42jGz3cv2HkL41+2OsJyDXEWCs
IeEPf+L/APa6q0NytBreWt1brq3Xk7Xnk3X1uvrW8tbylC7NFZhKWnreWt5euXrlreQ5Yvuu
LdcW66t15cdmSxZimKfW8tby9cvXL1zoAuxx+uZbry3XlreVaKUZvCxLxDXh4YvJfhskNOWa
KCOzBIX7pphhCGInPzaKSCKVsQom5LUTZ0LOzs/6pbOhw19C9t2Z09GHdw2RfmsC+dX1ExL8
klqIC2zzqOIIh/Bojo1zd6psvWAOROKzoENiI0z6+0U8QJ70S5rBrGORRxBEP59EVeA01Gvp
iMIycsSK9ZFB1Oy6/oWEfUbLJ7ll1AByu1SJ0NaGP2//xAA8EAABAgMEBQkGBQUBAAAAAAAB
AAIDEZESITIzBCIxUXIQEyBBUnGBkqEjMEBhscFCQ2JzgiQ0RKLRsv/aAAgBAQAGPwL3Nm97
+y3ajac2E39N5X93pFR/xTbpTzx3pxdCLoZ22WmoQI6VgC3EOxoXtIvNjsw/+qcRts/qJR5t
0SHwuQfDa94bdMiU0HFpadx+HEGDmn/ULe47XHafdCDCviP9BvW9x2uPX7/PZVTBMuEqzzrZ
7jdyyMZlVnsqs9lVnMqs5nmWcyqzmVWcyqc7nmzcZklyz2VWeyqtl0m71nsqs9lVnsqs9lVI
R2T71nQ/Ms5nmRfzsKZ67SzmeZZ7PMpNitJ70Lbw2e9Z7KrOZVZ7KrPZVZ7KrPYro7KrOZVW
YWv4yC19LhMHZZtqjJ/PEdp81cxo8OTWY094QLTOATeD+HkhEtCwCiwNosplFlMoofs24HdX
cstlFltosttFltoopDWi7cvL9VgbRYG0WEUWwK9oUbUbj3fILLbRZbaLA2iwNotFkAL3fRaP
O/VcsDaLAKLA2iwtojqtooeqMIV7BRZbaLLbRZbaLSZCWHoRuFdShKbjK+SLjcAp9StNMwVC
4XfbktX7Ab+uexM1XTfsuT7pWXWVG4V5fryGGDrATITYZOsbwmhxxGQ5I/7n2CJ3Kd/VIb01
snTcJi5W5SWid5+i0fhcplBzbwUbJnIyTmA3t2oqFwhWDOd3rsTyA4honcFDaGnXmZ8mk/x+
nQdDG2JqBYVC6FyhdzvtyStPwtbTYhN75t2FOl+I2lG4Uf4/XpR/3PsERvWN3VI7pJrrTpt2
KyNi0XiP0WjcL/t0SoXAEXlzr7P+pmiA51k9U9iZIu1Jy7t3JpX8fp0B2YP/AK5GwhzRA+aw
wfVYYPqsuF5llwapr7EHV+ay4NSsECpWCBUrDAqU6GRAAPzKMJsp3LDAqVh0epWyB6r/AB/V
XtgnxKeebhaxnjWVC8yy4XmKwQvMsEHzFQnxRDDWdkqFFg2JsniWGBUrDAqVsgeq/I9V+R6p
rBzFwl1rZANVgg1KwQalYINSosWLZm+WHlkBN7rmD5qztO1x3no29HiuB7M0DEhxIs9rgZkK
y2ILXZ6/j5nwG9c/FxnYOyPca7AV/Tx3t+TtYLWgiL82FARCYbtzxJXfFc3CHORd25c5FNuL
v6h3e8vE1aa0sO9hktXSLQ7L2r2mjXb2OmpOcWH9TZLVIPd8JZnN/ZC1/ZM7I2lWWNkPgrRh
Ce8XL2WkxG996udDi94so87oxA3sdNXuLOISWrFYfH3etEaPFXW3cLCV7OBIb3uR5+MXg/hb
qhWWNAHxOtBYfBXQ7PCZLVixh/NaukxfGSui+gWIUWIUWIUWcaBa2kRarWtP4nlasJo8Pd//
xAApEAEAAgEBBgYDAQEAAAAAAAABABEhMRBBUWFx8SCRobHB0TCB8EDh/9oACAEBAAE/Idp4
kyjNDuNeNoHzVxFc5eRQPws0IHyCPsisY1jJB0kll+IMrmH1eBNZc9Q6r4l07jKfVlXKOLXk
4iKS4Gm8nPrExozrD4b/ABVtqtPDUUjdunEfiXUXOzk67aleKg0WL03yjilz9cvBUrYFbQms
rYgKoBvZYCqbgr6QFZcR9UrY91LPnNdisJGkphlWsQ6ixjtGdnTsmV/VmSvSw7vSAvTpE8vk
52PEZhLVpXhdazrifRgv0Z21AFgxptnZ07ChJkwAFZytC1XKfpxDXwTGAdwi9WIQ+vKLJvWh
ft+IZB/AfM+orPqrq/rSV50gGw2ieQwo2gZeWiPDlLOHrEsFHdznYUXKWOiU/RnZMAAY2Y8Y
7FnZ87KnZURyF7BBaKvPtQr+JOwIH9aW/VB6ScEjBXCphLsuAnw52vO3JlfNAqBAA00vhOwJ
2FOwJ2JOWZ3I+7pRwjqD1E7Dmbe9E7KgsQ3QrdK23Zapo9Mz+RPRvvDlIgdXSOyAtXdBARRd
wEDQJvn9vjF0OLioIKVKokp+xlwFIpaWjSZ3/E39sZb6nrkef90Q0JecNQ0HSUQDQrUNfeVC
3S9Wbr2dGpdC2pRlLyCmxZX6vygc1u+lm/XhvlSGVQHkp8bBlX9+kBkoC2KHSseJHioj6msv
k1U4XpPSszT+qJUfeWsFk9RUHdHGyy0U6VEAghjVFZ/dmx+X4LjxH7Gva5y89O+8qIREsd0A
CjSAFAA4bDnI5rnKHxAbLLqjGtrNZ6Gwsbu9N+nTEvNqVDuXWHzE/p5IaEANJRdxLm6tnViq
USzWUULQqJaFCY4LKJKiyrNb1N95l7VsoO629nRkHMAMBUoNMSgtDWZdJmf9GCChOiVRYN3F
h7iWaAW0OTOC9XrC6NMGz03gsIXXd7lYr9HvK5sC0nVVzuMYMryGO/8A6nesqcIQL5uvqFmd
jUEws3UD1DSkGwDKK4wj8Q3FOwwv/ti3u80ANjcAPiE9drljAcOU7z+p/XfE7u+p/CfEHIlc
itlb5YIAo01rh0gsjIQL/wCiL3zzRiR1QgqCF8BLS+gsbCndbIsDU4A10K37awSucXxxgFa3
qDV8DK+4ZNL6S9HXKQapHJU6ouwP05mGY/PUqBX4K2Gmdlk8rQargTcVq3HC68XwudgDQ1iI
6+Z85YiPF97PrKg4xSfJ+4KqN49WkFtCcn/SwU5UuOZd0fPQrgOA3TQ27/AmythtEOCXG5x3
Y9BiFuHCPuQLFd4o8moNkRVN7iB31Rcs8FfgfFZAdzaDb/yAy92q31ndKnOF/gqJSkvrEoDz
lXpGsLwofWUKy5j9LmYAbh6cMK/1j+4gVgcjA0I9HaeLdCFpuIm5+aR7RUF9ux6Fs6Ylexl8
5yYUeFaP8aEyQ3zoxJ80exjuVuN94nyepfEo7P39USz5eZj2MUK8rNwHT6Ibu9CexMte5j5i
9tcafj//2gAIAQEAAAAQ/wCr+H//AP8AP/8A/wDfCzCXcawaJRGsvn2o3LveSSNCiN7mMnso
O+v/AP8A/wD39T//AP8A5/8Ay/8A/wDP/wDwL/y//wD/AJtx/wD/xAAlEAACAQIGAgMBAQAA
AAAAAAAAAREQICEwMUFRYUDwULHBgdH/2gAIAQEAAT8QySYGAtHUIwx0JqPkaFgkfshmZDlP
/QEpsRDEkApcLj0sJ8kFIl8QVe6BPD5ss8klVgmzLYMHiqgQXi2ARuYftFMyLHBZrSHpAeia
WZhBnUN7PqSAJ8gxzPXoec76eGjEsNWSWaoop2GGC2n2Mmgi3vf0yreB1AJrZMxbS07X9YVE
GLqi4+rN1Jdy+EKCLRBzwKddSoEzDRRWZKlA2LpuEzac6EsAAUZScheUEdHddfqjLG5LAUSf
9VhpesaiBOIts1FoI5wLYgm6SC6AXo9iAJOsXYANOUs4IlhVkLQdeU9AEoj4zQzVWAF0w4cC
phQ+wC/oVaWHSL4H6MT1mIuswBFSjw8wiRKVZJqYMSdnYBiiokeuMRHRFXVQiRkAqOPgKhSC
Q4MR0IWv/gKhgR0yEAJMhOy6QZr4sEAkq3EUrNB+Hc/kHRBWWFeAbtpRQRA3gaqzC2MSt+oW
KZscAA70jaB6DS4GCe2ArBCLO5UMcvFgRE+jOMEMW7w9u+/dU0wi1X9shDI4MJKVdJnEU4EK
hsjiSxwbMImMmo1UeREK8v3LdRIfKE6usGOpr7igsMQg9yKCE3Xd8/QABSbByH5O8cs24lTj
BC/SC2mMRzJneyHnYfCdPtA4mEdxshktf9JFoqXFq0Iam8ZwF2kxLcGt5RlOB4cZKoY8TKMS
YBNuDIrVNxFFIgVGO0scrYk+WwtJJdYODFWVDOJuFVwUou4l4cGD1IroRyPT4BEukfqUsOcd
qdZYbU4lSfIK3pwfSJmGq4+rBrHzCypwrm+hKMr/2Q==</binary>
 <binary id="img_30.jpeg" content-type="image/jpeg">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
8Rqyh1O8v0Iv6CbYn//Z</binary>
 <binary id="img_31.jpeg" content-type="image/jpeg">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</binary>
</FictionBook>
