Книга: Разработка ядра Linux

Объект address_space

Объект address_space

Физическая страница памяти может содержать данные из нескольких несмежных физических дисковых блоков[85].

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

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

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

struct address_space {
 struct inode                    *host;     /* файловый индекс, которому
                                               принадлежит объект */
 struct radix_tree_root          page_tree; /* базисное дерево
                                               всех страниц */
 spinlock_t                      tree_lock; /* блокировка для защиты
                                               поля page_tree */
 unsigned int                    i_mmap_wrltable; /* количество областей
                                             памяти с флагом VM_SHARED */
 struct prio_tree_root           i_mmap;    /* список всех отображений */
 struct list_head                i_mmap_nonlinear; /* список областей
                                         памяти с флагом VM_NONLINEAR */
 spinlock_t                      i_mmap_lock; /* блокировка поля i_mmap */
 atomic_t                        truncate_count; /* счетчик запросов
                                                    truncate */
 unsigned long                   nrpages; /* общее количество страниц */
 pgoff_t                         writeback_index; /* смещения начала
                                                     обратной записи */
 struct address_space_operations *a_ops;          /* таблица операций */
 unsigned long                   flags;           /* маска 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 — это дерево поиска по приоритетам для всех совместно используемых и частных отображений. Дерево поиска по приоритетам— это хитрая смесь базисных и частично упорядоченных бинарных деревьев[86].

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

Объект 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().

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

Оглавление книги

Оглавление статьи/книги

Генерация: 1.975. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз