Рейтинговые книги
Читем онлайн Разработка ядра Linux - Роберт Лав

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 84 85 86 87 88 89 90 91 92 ... 132

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

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

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

Структура bio

Основным контейнером для операций ввода-вывода в ядре является структура bio, которая определена в файле <linux/bio.h>. Эта структура представляет активные операции блочного ввода-вывода в виде списка сегментов (segment). Сегмент — это участок буфера, который является непрерывным в физической памяти, т.е. отдельные буферы не обязательно должны быть непрерывными в физической памяти. Благодаря тому, что буфер может представляться в виде нескольких участков, структура bio даст возможность выполнять операции блочного ввода-вывода, даже если данные одного буфера хранятся в разных местах памяти. Ниже показана структура bio с комментариями, описывающими назначение каждого поля.

struct bio {

 sector_t            bi_sector; /* соответствующий сектор на диске */

 struct bio          *bi_next;         /* список запросов */

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

 unsigned long       bi_flags;         /* состояние и флаги команды */

 unsigned long       bi_rw;            /* чтение или запись? */

 unsigned short      bi_vcnt;          /* количество структур bio vec

                                          в массиве bi_io_vec */

 unsigned short      bi_idx;    /* текущий индекс в массиве bi_io_vec */

 unsigned short      bi_phys_segments; /* количество сегментов

                                          после объединения */

 unsigned short      bi_hw_segments;   /* количество сегментов после

                                          перестройки отображения */

 unsigned int        bi_size;          /* объем данных для ввода-вывода */

 unsigned int        bi_hw_front_size; /* размер первого

                                          объединяемого сегмента */

 unsigned int        bi_hw_front_size; /* размер последнего объединяемого

                                          сегмента */

 unsigned int        bi_max_vecs;      /* максимально возможное количество

                                          структур bio_vecs */

 struct bio_vec      *bi_io_vec;       /* массив структур bio_vec */

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

 atomic_t            bi_cnb;           /* счетчик использования */

 void                *bi_private;      /* поле для информации создателя */

 bio_destructor_t    *bi_destructor;   /* деструктор */

};

Главное назначение структуры bio — это представление активной (выполняющейся) операции блочного ввода-вывода. В связи с этим большинство полей этой структуры являются служебными. Наиболее важные поля — это bi_io_vecs, bi_vcnt и bi_idx.

Поле bi_io_vecs указывает на начало массива структур bio_vec. Эти структуры используются в качестве списка отдельных сегментов в соответствующей операции блочного ввода-вывода. Каждый экземпляр структуры bio_vec представляет собой вектор следующего вида: <страница памяти, смещение, размер>, который описывает определенный сегмент, соответственно страницу памяти, где этот сегмент хранится, положение блока — смещение внутри страницы — и размер блока. Массив рассмотренных векторов описывает весь буфер полностью. Структура bio_vec определена в файле <linux/bio.h> следующим образом.

struct bio_vec {

 /* указатель на страницу физической памяти, где находится этот буфер */

 struct page *bv_page;

 /* размер буфера в байтах */

 unsigned int bv_len;

 /* смещение в байтах внутри страницы памяти, где находится буфер */

 unsigned int bv_offset;

};

Для каждой операции блочного ввода-вывода создается массив из bi_vcnt элементов типа bio_vec, начало которого содержится в поле bi_io_vecs. В процессе выполнения операции блочного ввода-вывода поле bi_idx используется для указания на текущий элемент массива.

В общем, каждый запрос на выполнение блочного ввода-вывода представляется с помощью структуры bio. Каждый такой запрос состоит из одного или более блоков, которые хранятся в массиве структур bio_vec. Каждая из этих структур представляет собой вектор, который описывает положение в физической памяти каждого сегмента запроса. На первый сегмент для операции ввода-вывода указывает поле bi_io_vec. Каждый следующий сегмент следует сразу за предыдущим. Всего в массиве bi_vcnt сегментов. В процессе того, как уровень блочного ввода-вывода обрабатывает сегменты запроса, обновляется значение поля bi_idx, чтобы его значение соответствовало номеру текущего сегмента. На рис. 13.2 показана связь между структурами bio, bio_vec и page.

Рис. 13.2. Связь между структурами struct bio, struct bio_vec и struct page

Поле bi_idx указывает на текущую структуру bio_vec в массиве, что позволяет уровню блочного ввода-вывода поддерживать частично выполненные операции блочного ввода-вывода. Однако более важное использование состоит в том, что драйверы таких устройств, как RAID (Redundant Array of Inexpensive/Independent Disks, массив недорогих/независимых дисковых устройств с избыточностью — специальный способ использования жестких дисков, при котором один логический том может быть распределен но нескольким физическим дискам для увеличения надежности или производительности), могут одну структуру bio, которая изначально была адресована одному устройству, разбивать на несколько частей, которые предназначаются различным дискам RAID массива. Все, что необходимо сделать драйверу RAID, это создать необходимое количество копий структуры bio, которая предназначалась одному устройству, и изменить для каждой копии значение поля bi_idx, чтобы оно указывало на ту часть массива, откуда каждый диск должен начать свою операцию ввода-вывода.

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

void bio_get(struct bio *bio);

void bio_put(struct bio *bio);

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

1 ... 84 85 86 87 88 89 90 91 92 ... 132
На этой странице вы можете бесплатно читать книгу Разработка ядра Linux - Роберт Лав бесплатно.
Похожие на Разработка ядра Linux - Роберт Лав книги

Оставить комментарий