Главная » Ядро Linux » Страничный кэш

0

Как следует из названия, страничный кэш (page cache) — это кэш страниц; памяти. Соответствующие страницы памяти  получаются в результате  чтения  и записи  обычных файлов  на файловых системах,  специальных файлов  блочных  устройств  и файлов,  отображаемых в память.  Таким  образом, в страничном кэше  содержатся страницы  памяти, полностью заполненные данными из файлов, к которым  только  что производился доступ.  Перед  выполнением операции страничного ввода-вывода, как, например, read() 1 , ядро  проверяет, есть ли те данные, которые  нужно  считать,  в страничном кэше.  Если  данные  находятся  в кэше,  то ядро может  быстро  возвратить требуемую  страницу  памяти.

Объект address_space

Физическая страница памяти может  содержать данные из нескольких несмежных

физических дисковых блоков2.

1 Какбыло показано в главе 12 ," Виртуальная файлова я система",  операци и страничного ввода-вывода непосредственн о выполняютс я  не системным и вызовами read( )  и write(),  а специфичным и для   файловых   систем   методами           file->f_op->read( )          и   file~>f_op->wriie().

2 Например ,разме р страниц ы физическо й памяти  для  аппаратно й платформ ы х86 равен  4 Кбайт, в  то  врем я  как  разме р  дисковог о  блок а для  большинств а  устройст в  и  файловы х систе м  раве н

512 байт.  Следовательно, в одной  страниц е памят и может храниться 8 блоков.  Блок и не обязательно должны  быть смежными, так как один  фай л может  быть физическ и "разбросанным" по диску.

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

Более  того, страничный кэш  ядра  Linux  является хранилищем данных  достаточно  общего  характера  в отношении того, какие  страницы памяти в нем  могут кэшироваться. Первоначально страничный кэш  был  предложен в операционной системе  System  V (SVR 4)  для  кэширования только  данных  из  файловых систем. Следовательно, для управления страничным кэшем операционной системы SVR 4 использовался эквивалент файлового объекта, который назывался  struc t  vnode. Кэш  операционной системы Linux разрабатывался с целью кэширования любых объектов, основанных на страницах памяти, что включает множество типов  файлов и отображений в память.

Для получения необходимой общности в страничном кэше  операционной системы  Linux  используется структура  address_space (адресное пространство),  которая позволяет идентифицировать страницы памяти, находящиеся в кэше.  Эта структура определена в файле  <linux/fs.h >  следующим образом.

struct address_space {

struct inode          *host; struct radix_tree_root page_tree; spinlock_ t            tree_lock; unsigned int         i_mmap_wrltable;

struct prio_tree_root i_mmap;

struct list_head      i_mmap_nonlinear; spinlock_t           i_mmap_lock; atomic_t             truncate_counl; unsigned long        nrpages;

pgoff_t              writeback_index;

struct address_space_operations *a_ops;

unsigned long        flags;

/* файловый индекс, которому принадлежит объект*/

/* базисное дерево всех страниц */

/* блокировка для защиты поля page_tree */

/* количество областей памяти

с флагом VM_SHARED */

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

/* список областей памяти

с флагом VM_NONLINEAR */

/* Блокировка поля i_mmap */

/* счетчик запросов truncate */

/* общее количество страниц */

/* смещения начала обратной записи*/

/* таблица операций */

/* маска gfp_mask и флаги ошибок */

struct backing_dev_info *backing_dev_info; /* информация упреждающего чтения */

spinlock_t          private_lock; struct list_head   private_list; struct address_spacs   *assoc_mapping;

};

/* блокировка для частных отображений */

/* список частных отображений */

/* соответствующие буферы */

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

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

3 Реализаци я ядра  основана  на  базисном  дерев е  поиска  по  приоритетам ,  предложенно м  в работ е

Edward  M.  McCreight,  опубликованно й  в  журнале  SIAM Journa l  of Computing ,  May  1985,  vol.  14.

№2 , P. 257-276.

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

Поле a_ops указывает на таблицу операгций с  адресным пространством так же, как и в случае объектов подсистемы VFS. Таблица операций представлена с помощью структуры struc t  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 *, structwriteback_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_pag e   и   write_pag e   являются   наиболее   важными.   Рассмотрим шаги,   которые  выполняются  при  страничной  операции   чтения.

Методу  чтения   в  качестве  параметров   передается   пара  значений:  объект  address _ spac e  и  смещение.  Эти  значения  используются   следующим   образом   для  поиска   необходимых  данных   в  страничном  кэше.

page = find_get_page(mapping, index);

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

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

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);

Ядро  выполняет запись этой  страницы памяти позже  с  помощью вызова  метода writepag e () .  Операции записи для  файлов,  открытых обычным образом  (без  отображения в память), выполняются более  сложным путем.  В основном, общая  операция  записи,  которая реализована в  файле   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_writ e () , чтобы  подготовить запрос на  запись. После   этого  данные  копируются из  пространства пользователя в буфер  памяти в пространстве ядра. И наконец данные записываются на диск  с помощью функции comnit_writ e () .

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

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

По теме:

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