Чтение онлайн

ЖАНРЫ

Разработка ядра Linux (Второе издание)
Шрифт:

 struct address_spacs *assoc_mapping; /* соответствующие

буферы */

};

Поле

i_mmap
— это дерево поиска по приоритетам для всех совместно используемых и частных отображений. Дерево поиска по приоритетам— это хитрая смесь базисных и частично упорядоченных бинарных деревьев [86] .

Всего в адресном пространстве nrpages страниц памяти.

86

Реализация

ядра основана на базисном дереве поиска по приоритетам, предложенном в работе Edward M. McCreight, опубликованной в журнале SIAM Journal of Computing, May 1985, vol. 14. №2, P. 257–276.

Объект

address_space
связан с некоторым другим объектом ядра, обычно с файловым индексом. Если это так, то поле
host
указывает на соответствующий файловый индекс. Если значение поля
host
равно
NULL
, то соответствующий объект не является файловым индексом; например, объект
address_space
может быть связан с процессом подкачки страниц (swapper).

Поле

a_ops
указывает на таблицу операций с адресным пространством так же, как и в случае объектов подсистемы VFS. Таблица операций представлена с помощью структуры
struct address_space_operations
, которая определена в файле
<linux/fs.h>
следующим образом.

struct address_space_operations {

 int (*writepage)(struct page*, struct writeback_control*);

 int (*readpage)(struct file*, struct page*);

 int (*sync_page)(struct page*);

 int (*writepages)(struct address_space*,

struct writeback_control*);

 int (*set_page_dirty)(struct page*);

 int (*readpages)(struct file*, struct address_space*,

struct list_head*, unsigned);

 int (*prepare_write)(struct file*, struct page*,

unsigned, unsigned);

 int (*commit_write)(struct file*, struct page*,

unsigned, unsigned);

 sector_t (*bmap)(struct address_space*, sector_t);

 int (*invalidatepage)(struct page*, unsigned long);

 int (*releasepage)(struct page*, int);

 int (*direct_IO)(int, struct kiocb*, const struct iovec*,

loff_t, unsigned long);

};

Методы

read_page
и
write_page
являются наиболее важными. Рассмотрим шаги, которые выполняются при страничной операции чтения.

Методу чтения в качестве параметров передается пара значений: объект

address_space
и смещение. Эти значения используются следующим образом для поиска необходимых данных в страничном кэше.

page = find_get_page(mapping, index);

где параметр

mapping
— это заданное адресное пространство, a
index
— заданная позиция в файле.

Если в кэше нет необходимой страницы памяти, то новая страница памяти

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

struct page *cached_page;

int error;

cached_page = page_cache_alloc_cold(mapping);

if (!cached_page)

 /* ошибка выделения памяти */

error =

 add_to_page_cache_lru(cached_page, mapping, index, GFP_KERNEL);

if (error)

 /* ошибка добавления страницы памяти в страничный кэш */

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

error = mapping->a_ops->readpage(file, page);

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

SetPageDirty(page);

Ядро выполняет запись этой страницы памяти позже с помощью вызова метода

writepage
. Операции записи для файлов, открытых обычным образом (без отображения в память), выполняются более сложным путем. В основном, общая операция записи, которая реализована в файле
mm/filemap.с
, включает следующие шаги.

page =

 __grab_cache_page(mapping, index, &cached_page, &lru_pvec);

status =

 a_ops->prepare_write(file, page, offset, offset+bytes);

page_fault =

 filemap_copy_from_user(page, offset, buf, bytes);

status =

 a_ops->commit_write(file, page, offset, offset+bytes);

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

prepare_write
, чтобы подготовить запрос на запись. После этого данные копируются из пространства пользователя в буфер памяти в пространстве ядра. И наконец данные записываются на диск с помощью функции
commit_write
.

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

Базисное дерево

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

Поделиться с друзьями: