Главная » Ядро Linux » Буферы и заголовки буферов

0

Когда  блок  хранится в памяти (скажем, после  считывания или  в ожидании записи), то  он  хранится в  структуре  данных, называемой  буфером   (buffer).    Каждый буфер связан строго  с  одним   блоком.  Буфер   играет  роль  объекта,  который  представляет блок  в оперативной памяти. Вспомним,  что  блок  состоит  из  одного  или  больше  секторов, но  по  размеру  не  может  быть  больше  одной  страницы памяти.  Поэтому одна страница  памяти  может  содержать  один   или  больше   блоков.   Поскольку для  ядра требуется  некоторая управляющая информация,  связанная  с  данными  (например, какому   блочному устройству   и  какому   блоку  соответствует буфер),  то  каждый   буфер связан со  своим  дескриптором.  Этот  дескриптор называется заголовком,  буфера  (buffer head)  и  представляется  с  помощью  структуры   struc t   buffer_head .

Структура buffer_hea d  содержит информацию,  которая  необходима ядру для управления буферами и определена в файле <linux/buffer_head.h> .

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

.  struct buffer_head {

unsigned long      b_state;         /* флаги состояния буфера */ atomic_t           b_count;         /* счетчик использования буфера */ struct buffer_head *b_this_page;   /* список буферов в текущей

странице памяти */

struct page sector_t

u32

*b_page; b_blocknr;  b_size;

/* соответствующая страница памяти */

/* логический номер блока */

/* размер блока (в байтах)  */

char

*b_data;

/*указатель на буфер в странице памяти*/

struct block_device *b_bdev;         /* соответствующее блочное устройство*/

bh_end_io_t

*b_end_io;

/* метод завершения  ввода-вывода */

void

*b_private;

/* данные метода завершения  */

struct list_head   b_assoc_buffers; /* список связанных отображений */

};

Поле b_stat e содержит состояние определенного буфера. Это значение может содержать один или несколько флагов, которые перечислены в табл. 13.1. Возможные значения флагов описаны в виде перечисления bh_state_bits , которое описано в файле <Linux/buffer_head.h>.

Таблица 13.1 . Значения флагов поля bh_stat e

Флаг состояния                   Назначение

BH_Uptodat e                       Буфер содержит действительные данные

BH_Dirt y                             Буфер изменен (содержимое  буфера новее соответствующих данных на диске, и поэтому буфер должен быть записан на диск)

BH_Lock                               Для буфера выполняется операция чтения-записи  дисковых данных, и буфер заблокирован, чтобы предотвратить конкурентный доступ

BH_Req                                 Буфер включен в запрос

BH_Mapped                          Буфер является действительным и отображается  на дисковый блок BH_NEW                               Буфер только что выделен и к нему еще не было доступа BH_Async_  Read         Для         буфера выполняется асинхронная операция чтения

вн_Азуnс  Writ e        Для        буфера выполняется асинхронная операция записи вн_Dеlа у                             С буфером еще не связан дисковый блок

BH_Boundary                      Буфер является последним в последовательности смежных блоков —

следующий за ним блок не является смежным с этой серией

Перечисление bh_state_bit s  также содержит в качестве последнего элемента флаг BH_PrivateStart . Этот флаг не является разрешенным значением флага, а соответствует первому биту, который можно использовать по усмотрению разработчиков кода. Все биты,  номер  которых больше или равен значению  BH_PrivateStart ; не используются подсистемой блочного ввода-вывода и безопасно могут использоваться драйверами, которым необходимо хранить информацию в поле b_state .

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

Поле  b_coun t — это  счетчик использования буфера.  Значение этого  поля  увеличивается и уменьшается двумя  функциями,  которые определены в  файле   <linux / buffer_head.h >   следующим  образом.

static inline void get_bh (struct buffer_head *bh)

{

atomic_inc{&bh->b_count);

}

static inline void put_bh (struct buffer_head *bh)

{

atomic_dec (&bh->b_count);

}

Перед  тем  как  манипулировать  заголовком  буфера,  необходимо увеличить значение  счетчика использования  с  помощью функции  get_bh( ) , что  гарантирует, что во  время   работы   буфер  не  будет  освобожден.  Когда  работа  с  заголовком буфера  закончена,  необходимо уменьшить значение счетчика, ссылок с  помощью функции put_b h  () .

Физический блок  на  жестком диске,  которому соответствует буфер, — это  блок с  логическим  номером  b_blocknr ,  которы й  находится  на  блочном устройстве b_bdev.

Физическая страница памяти, в  которой хранятся данные буфера, соответствует значению поля  b_page .  Поле  b_dat a — это  указатель прямо на  данные блока  (которые  хранятся где-то  в  странице памяти  b_page) , размер   блока  хранится в  поле b_size .  Следовательно, блок  хранится в памяти,  начиная с адреса  b_dat a  и  заканчивая  адресом    (b_dat a   +  b_size) .

Назначение заголовка буфера—  это  описание отображения  между  блоком на диске  и  буфером в  физической памяти (т.е.  последовательностью байтов, которые хранятся   в указанной странице памяти).  Выполнение роли  дескриптора отображения буфер-блок —единственное назначение этой  структуры данных  ядра.

В ядрах  до  серии   2.6  заголовок буфера  был  значительно более  важной структурой данных. По  существу, это  была  единица ввода-вывода данных  в ядре.  Он  не  только выполнял роль дескриптора для  отображения буфер-блок-страница физической памяти, но  и  выступал контейнером для  всех  операций блочного ввода-вывода.  Это  приводило  к двум  проблемам. Первая проблема заключалась в том, что  заголовок буфера был  большой и громоздкой структурой данных  (сегодня он  несколько уменьшился в  размерах), а кроме  того, выполнение операций блочного ввода-вывода в терминах заголовков буферов было  непростой и довольно непонятной  задачей. Вместо  этого, ядру лучше  работать  со страницами памяти, что  одновременно и проще и позволяет получить большую производительность.  Использовать  большой  заголовок  буфера, описывающий  отдельный буфер  (который может  быть  размером со  страницу памяти), — неэффективно. В связи  с этим  в ядрах  серии  2.6 было  сделано много  работы, чтобы  дать  возможность ядру  выполнять операции непосредственно со  страницами памяти и пространствами адресов, вместо  операций с буферами. Некоторые из  этих операций обсуждаются в главе  15, "Страничный кэш  и обратная запись страниц", где также   рассматривается   структура  address_spac e  и  демоны  pdflush .

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

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

По теме:

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