RussianLDP Рейтинг@Mail.ru
WebMoney: 
WMZ Z294115950220 
WMR R409981405661 
WME E134003968233 
Visa 
4274 3200 2453 6495 

Назад Оглавление


4. Linux Page Cache

В этой главе мы описываем Linux 2.4 pagecache. Pagecache представляет собой кэш физических страниц. В UNIX концепция pagecache стала популярной со введения SVR4 UNIX, где это заменило buffercache для операций ввода-вывода данных.

В то время, как pagecache в SVR4 используется только для кэширования данных файловой системы и таким образом применяет struct vnode и смещение в файле как параметры хэширования, Linux pagecache разработан, чтобы быть более универсальным, и следовательно использует struct address_space (объяснен ниже) как первый параметр. Поскольку Linux pagecache сильно привязан к записи адресных пространств, Вы будете нуждаться по крайней мере в базисном понимании adress_spaces, чтобы понять путь, которым работает pagecache. Address_space представляет собой некоторое программное обеспечение MMU, который отображает все страницы одного объекта (например, inode) к другому параллелизму (обычно физические дисковые блоки). Struct address_space определена в include/linux/fs.h так:

  struct address_space
  {
    struct list_head                  pages;
    unsigned long                     nrpages;
    struct address_space_operations * a_ops;
    void *                            host;
    struct vm_area_struct *           i_mmap;
    struct vm_area_struct *           i_mmap_shared;
    spinlock_t                        i_shared_lock;
  };

Чтобы понимать путь, которым работает address_spaces, мы должны рассмотреть только несколько из этих полей: pages представляет собой двунаправленный список всех страниц, которые принадлежат этому address_space, nrpages задает число страниц в pages, a_ops определяет методы этого address_space, а host задает указатель на объект, которому этот address_space принадлежит. Использование pages и nrpages очевидно, так что мы будем рассматривать более подробно только структуру address_space_operations, определенную в том же самом файле заголовка:

  struct address_space_operations
  {
    int (*writepage)(struct page *);
    int (*readpage)(struct file *, struct page *);
    int (*sync_page)(struct page *);
    int (*prepare_write)(struct file *, struct page *, unsigned, unsigned);
    int (*commit_write)(struct file *, struct page *, unsigned, unsigned);
    int (*bmap)(struct address_space *, long);
  };

Для базисного представления в принцип address_spaces (и pagecache) мы должны смотреть на ->writepage и ->readpage, но на деле мы должны смотреть на ->prepare_write и ->commit_write.

Только рассматривая имена Вы можете, вероятно, предполагать то, что эти методы делают, но чтобы объяснить их, будем смотреть на самого большого пользователя pagecache в Linux: ввод-вывод данных файловой системы. В отличие от большинства других UNIX-систем, Linux имеет универсальные операции для файлов (подмножество операций SYSVish vnode) для ввода-вывода данных сквозь pagecache. Это означает, что данные непосредственно не будут взаимодействовать с файловой системой при read/write/mmap, но будут работать с pagecache всякий раз, когда возможно. Pagecache должен получить данные из фактической файловой системы низкого уровня в случае, если пользователь хочет читать из страницы, которая еще не в памяти, или писать данные на диск в случае, если память нужна для чего-то другого.

В пути чтения универсальные методы сначала пробуют находить страницу, которая соответствует требуемому inode/index tuple.

hash = page_hash(inode->i_mapping, index);

Затем мы пробуем узнать, что страница фактически существует.

hash = page_hash(inode->i_mapping, index);
page = __find_page_nolock(inode->i_mapping, index, *hash);

Когда она не существует, мы распределяем новую свободную страницу и добавляем ее к хэшу page-cache.

page = page_cache_alloc();
__add_to_page_cache(page, mapping, index, hash);

После того, как страница хэширована, мы используем операцию ->readpage для address_space, чтобы фактически заполнить страницу данными (файл открыт экземпляром inode).

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

В заключение мы можем скопировать данные в пространство пользователя.

Для записи в файловую систему есть два пути: один для перезаписываемых отображений (mmap) и один для семейства системных вызовов write(2). Случай mmap очень прост, так что он будет рассмотрен сначала. Когда пользователь изменяет отображения, подсистема VM отмечает грязную страницу.

SetPageDirty(page);

Поток bdflush ядра пробует освобождать страницы как фоновое действие или потому, что нужна память, пробует вызвать ->writepage на страницах, которые явно отмечены как грязные. Метод ->writepage теперь должен записать соответствующие страницы на диск и освободить страницу.

Второй путь записи много сложнее. Для каждой записываемой страницы мы в основном делаем следующее (для изучения полного кода изучите mm/filemap.c:generic_file_write()):

page = __grab_cache_page(mapping, index, &cached_page);
mapping->a_ops->prepare_write(file, page, offset, offset+bytes);
copy_from_user(kaddr+offset, buf, bytes);
mapping->a_ops->commit_write(file, page, offset, offset+bytes);

Сначала мы пробуем находить хэшированную страницу или распределить новую, затем мы вызываем метод ->prepare_write из address_space, копируем буфер пользователя в ядерную память и в заключение вызываем метод ->commit_write. Как Вы вероятно видели, ->prepare_write и ->commit_write существенно отличаются от ->readpage и ->writepage, поскольку они вызваны не только когда физический ввод-вывод фактически требуется, а каждый раз, когда пользователь изменяет файл. Имеется два способа обрабатывать это, первый использует Linux buffercache, чтобы задержать физический ввод-вывод, заполняя указатель page->buffers с buffer_heads, который будет использоваться в try_to_free_buffers (fs/buffers.c), чтобы запросить ввод-вывод, как только понадобится память, и используется очень широко распространенный в текущем ядре. Другой путь только устанавливает страницу как грязную и полагается на ->writepage, чтобы сделать всю работу. Из-за недостатка точечного рисунка корректности в struct page это не работает с файловыми системами, которые имеют меньший granuality, чем PAGE_SIZE.


Назад Оглавление

Поиск

 

Найди своих коллег!