WebMoney: WMZ Z294115950220 WMR R409981405661 WME E134003968233 |
Visa 4274 3200 2453 6495 |
В этой главе мы описываем 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
определена в Чтобы понимать путь, которым работает address_spaces, мы должны
рассмотреть только несколько из этих полей: Для базисного представления в принцип address_spaces (и pagecache) мы
должны смотреть на -> Только рассматривая имена Вы можете, вероятно, предполагать то, что эти
методы делают, но чтобы объяснить их, будем смотреть на самого большого
пользователя pagecache в Linux: ввод-вывод данных файловой системы. В отличие
от большинства других UNIX-систем, Linux имеет универсальные операции для
файлов (подмножество операций SYSVish vnode) для ввода-вывода данных сквозь
pagecache. Это означает, что данные непосредственно не будут
взаимодействовать с файловой системой при read/write/mmap, но будут работать
с pagecache всякий раз, когда возможно. Pagecache должен получить данные из
фактической файловой системы низкого уровня в случае, если пользователь хочет
читать из страницы, которая еще не в памяти, или писать данные на диск в
случае, если память нужна для чего-то другого.
В пути чтения универсальные методы сначала пробуют находить страницу,
которая соответствует требуемому inode/index tuple.
Затем мы пробуем узнать, что страница фактически существует.
Когда она не существует, мы распределяем новую свободную страницу и
добавляем ее к хэшу page-cache.
После того, как страница хэширована, мы используем операцию
-> В заключение мы можем скопировать данные в пространство пользователя.
Для записи в файловую систему есть два пути: один для перезаписываемых
отображений (mmap) и один для семейства системных вызовов write(2). Случай
mmap очень прост, так что он будет рассмотрен сначала. Когда пользователь
изменяет отображения, подсистема VM отмечает грязную страницу.
Поток bdflush ядра пробует освобождать страницы как фоновое действие или
потому, что нужна память, пробует вызвать -> Второй путь записи много сложнее. Для каждой записываемой страницы мы в
основном делаем следующее (для изучения полного кода изучите
Сначала мы пробуем находить хэшированную страницу или распределить новую,
затем мы вызываем метод ->
4. Linux Page Cache
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;
};
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);
};
writepage
и ->readpage
, но на
деле мы должны смотреть на ->prepare_write
и
->commit_write
.
hash = page_hash(inode->i_mapping, index);
hash = page_hash(inode->i_mapping, index);
page = __find_page_nolock(inode->i_mapping, index, *hash);
page = page_cache_alloc();
__add_to_page_cache(page, mapping, index, hash);
readpage
для address_space, чтобы фактически
заполнить страницу данными (файл открыт экземпляром inode).
error = mapping->a_ops->readpage(file, page);
SetPageDirty(page);
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
.
Назад
Оглавление
Найди своих коллег! |