Второе издание
Скачать 3.09 Mb.
|
Состояние элементов каталога Действительный объект элемента каталога, может быть в одном из трех состоя- ний: используемый fused), неиспользуемый (unused) и негативный (negative). Используемый объект соответствует существующему файловому индексу (т.е. поле d_inode указывает на связанный объект типа mode) и используется один или более раз (т.е. значение поля d_count — положительное число). Используемый элемент ка- талога используется подсистемой VFS, а также указывает на существующие данные, поэтому не может быть удален. Неиспользуемый объект типа dentry соответствует существующему объекту inode (поле d_inode указывает на объект файлового индекса), но подсистема VFS в дан- ный момент не использует этот элемент каталога (поле d_count содержит нулевое значение). Так как элемент каталога указывает на существующий объект, то он сохра- няется на случай, если вдруг окажется нужным. Если объект не ликвидировать преж- девременно, то его и не нужно будет создавать заново, если вдруг он понадобится в будущем, и поиск по имени пути пройдет быстрее. Когда же появляется необхо- димость освободить память, то такой объект элемента каталога может быть удален, потому что он никем не используется. Негативный объект dentry 6 не связан с существующим файловым индексом (поле d_inode равно значению NULL), потому что или файловый индекс был удален, или соответствующий элемент пути никогда не существовал. Такие объекты элементов каталогов сохраняются, чтобы в будущем поиск по имени пути проходил быстрее. Хотя такие объекты dentry и полезны, но они при необходимости могут уничтожать- ся, поскольку никто их на самом деле не использует. Объект dentry может быть освобожден, оставаясь в слябовом кэше объектов, как обсуждалось в предыдущей главе. В таком случае на этот объект нет ссылок ни в коде VFS, ни в коде файловых систем. Кэш объектов dentry После того как подсистема VFS преодолела все трудности, связанные с переводом всех элементов пути в объекты элементов каталогов, и был достигнут конец пути, то было бы достаточно расточительным выбрасывать на ветер всю проделанную рабо- ту. Ядро кэширует объекты в кэше элементов каталога, который называют dcache. Кэш объектов dentry состоит из трех частей. • Список "используемых" объектов dentry, которые связаны с определенным файловым индексом (поле i _ d e n t r y объекта inode). Поскольку указанный файловый индекс может иметь несколько ссылок, то ему может соответсвовать несколько объектов dentry, а следовательно используется связанный список. • Двухсвязный список неиспользуемых и негативных объектов dentry "с наи- более поздним использованием" (last recently used, LRU). Вставки элементов в этот список отсортированы по времени, поэтому элементы, которые нахо- дятся в начале списка, — самые новые. Когда ядро должно удалить элементы каталогов для освобождения памяти, то эти элементы берутся из конца списка. 6 Это название несколько сбивает с толку. В таких объектах нет ничего негативного или отрица- тельного. Более удачным было бы, наверное, название invalid denlry или несуществующий элемент каталога. 280 Глава 12 потому что там находятся элементы, которые использовались наиболее давно и для которых меньше шансов, что они понадобятся в ближайшем будущем. • Хеш-таблица и хеш-функция, которые позволяют быстро преобразовать задан- ный путь в объект dentry. Указанная хеш-таблица представлена с помощью массива d e n t r y _ h a s h t a b l e . Каждый элемент массива — это указатель на список тех объектов dentry, которые соответствуют одному ключу. Размер этого массива зависит от объема физической памяти в системе. Значение ключа определяется функцией d_hash (), что позволяет для каждой файловой системы реализовать свою хеш-функцию. Поиск в хеш-таблице выполняется с помощью функции d_lookup (). Если в кэше dcache найден соответствующий объект, то это значение возвращается. В случае ошибки возвращается значение NULL. В качестве примера рассмотрим редактирование файла исходного кода в вашем домашнем каталоге, / h o m e / d r a c u l a / s r c / f о о . с . Каждый раз, когда производится доступ к этому файлу (например, при первом открытии, при последующей записи, при компиляции и так далее), подсистема VFS должна пройти через псе элементы каталогов в соответствии с путем к файлу: /, home, dracula, s r e и, наконец, foo.с. Для того чтобы каждый раз при доступе к этому (и любому другому) имени пути из- бежать выполнения данной операции, которая требует довольно больших затрат времени, подсистема VFS вначале может попытаться найти это имя пути в dentry- кэше. Если поиск проходит успешно, то необходимый конечный элемент каталога нужного пути получается без особых усилий. Если же данного элемента каталога нет в dentry-кэше, то подсистема VFS должна самостоятельно отследить путь. После за- вершения поиска найденные объекты dentry помещаются в кэш dcache, чтобы уско- рить поиск в будущем. Кэш dcache также является интерфейсом к кэшу файлопых индексов icache. Объекты inode связаны с объектами dentry, поскольку объект dentry поддерживает положительное значение счетчика использования для связанного с ним индекса. Это в свою очередь позволяет объектам dentry удерживать связанные с ними объ- екты mode в памяти. Иными словами, если закэширован элемент каталога, то соот- ветственно оказывается закэшированным и соответствующий ему файловый индекс. Следовательно, если поиск в кэше для некоторого имени пути прошел успешно, то соответствующие файловые индексы уже закэшированы в памяти. Операции с элементами каталогов Структура d e n t r y _ o p e r a t i o n s содержит методы, которые подсистема VFS мо- жет вызывать для элементов каталогов определенной файловой системы. Эта струк- тура определена в файле struct dentry_operations { int (*d_revalidate) (struct dentry *, int); int (*d_hash) (struct dentry *, struct qstr * ) ; int (*d_corapare) (struct dentry *, struct qstr *, struct qstr * ) ; int (*d_delete) (struct dentry * ) ; void (*d_release) (struct dentry * ) ; void (*d_iput) (struct dentry *, struct inode * ) ; }; Виртуальная файловая система 281 Методы служат для следующих целей • i n t d _ r e v a l i d a t e ( s t r u c t dentry *dentry, i n t flags) Эта функция определяет, является ли указанный объект элемента каталога дей- ствительным. Подсистема VFS вызывает эту функцию, когда она пытается ис- пользовать объект dentry из кэша dcache. Для большинства файловых систем этот метод установлен в значение NULL, потому что объекты denry, которые находятся в кэше, всегда действительны. • i n t d_hash(struct dentry *dentry, s t r u c t q s t r *name) Эта функция создает значение хеш-ключа на основании указанного объекта dentry. Подсистема VFS вызывает эту функцию всякий раз, когда добавляет объ- ект элемента каталога в хеш-таблицу. • i n t d_compare(struct dentry *dentry, s t r u c t q s t r *narael, s t r u c t q s t r *name2) Эта функция вызывается подсистемой VFS для сравнения двух имен файлов namel и name2. Большинство файловых систем используют умолчание VFS, которое соответствует простому сравнению двух строк. Для некоторых файло- вых систем, таких как FAT, не достаточно простого сравнения строк. Файловая система FAT не чувствительна к регистру символов в именах файлов, поэтому появляется необходимость в реализации функции, которая при сравнении не учитывает регистр символов. Эта функция вызывается при захваченной блоки- ровке dcache_lock 7 • i n t d_delete ( s t r u c t dentry *dentry) Эта функция вызывается подсистемой VFS, когда количество ссылок d_count указанного объекта dentry становится равным пулю. Функция вызывается при захваченной блокировке dcache_lock. • void d _ r e l e a s e ( s t r u c t dentry *dentry) Эта функция вызывается подсистемой VFS, когда она собирается освободить указанный объект dentry. По умолчанию данная функция не выполняет ника- ких действий. • void d _ i p u t ( s t r u c t dentry *dentry, s t r u c t inode *inode) Эта функция вызывается подсистемой VFS, когда элемент каталога теряет связь со своим файловым индексом (например, когда этот элемент каталога удаляет- ся с диска). По умолчанию подсистема VFS просто вызывает функцию i p u t (), чтобы освободить соответствующий объект inode. Если файловая система пе- реопределяет эту функцию, то она также должна вызывать функцию i p u t () в дополнение к специфичной для файловой системы работе. 7 А также при захваченной блокировке dentry->d_lock. — Примеч. перев. 2 8 2 Глава 12 Объект file Последним из основных объектов подсистемы VFS рассмотрим объект файла. Объект File используется для представления файлов, которые открыты процессом. Когда мы думаем о подсистеме VFS с точки зрения пространства пользователя, то объект файла — это то, что первое приходит в голову. Процессы непосредственно работают с файлами, а не с суперблоками, индексами или элементами каталогов. Не удивительно, что информация, которая содержится в объекте file, наиболее привыч- на (такие данные, как режим доступа или текущее смещение), а файловые операции очень похожи на знакомые системные вызовы, такие как read () и w r i t e (). Объект файла — это представление открытого файла, которое хранится в опе- ративной памяти. Объект (а не сам файл) создается в ответ на системный вызов open () и уничтожается в результате системного вызова c l o s e () . Все вызовы, свя- занные с файлом, на самом деле являются методами, которые определены в таблице операций с файлом. Так как несколько процессов могут одновременно открыть и ис- пользовать один и тот же файл, то для одного файла может существовать несколько объектов file. Файловый объект просто представляет открытый файл с точки зрения процесса. Этот объект содержит указатель на соответствующий элемент каталога (ко- торый, в свою очередь, указывает на файловый индекс), представляющий открытый файл. Соответствующие объекты inode и dentry, конечно, являются уникальными. Файловый объект представляется с помощью структуры s t r u c t f i l e , которая определена в файле struct file { struct list_head f_list; /* список объектов file*/ struct dentry *f_dentry; /* связанный объект dentry */ struct vfsmount *f_vfsmnt; /* связанная смонтированная файловая система */ struct file_operations *f_op; /* таблица файловых операций */ atomic_t f_count; /* счетчик ссылок на этот объект */ unsigned int f_flags; /* флаги, указанные при вызове функции open */ mode_t f_mode; /* режим доступа к файлу */ loff_t f_pos; /* смещение в файле (file pointer, offset) */ struct fown_struct f_owner; /* информация о владельце для обработки сигналов */ unsigned int f_uid; /* идентификатор пользователя владельца, UID */ unsigned int f_gid; /* идентификатор группы владельца, GID */ int f_error; /* код ошибки */ struct file_ra_state f_ra; /* состояние предварительного считывания */ unsigned long f_version; /* номер версии */ void *f_security; /* модуль безопасности */ void *private_data; /* привязка для драйвера терминала */ struct list_head f_ep_links; /* список ссылок eventpoll (опрос событий) */ spinlock_t f_ep_lock; /* блокировка eventpoll */ struct address_space *f_mapping; /* отображение в страничном кэше */ }; Виртуальная файловая система 233 По аналогии с объектом элемента каталога объект файла на самом деле не соот- ветствует никакой структуре, которая хранится на жестком диске. Поэтому в этой структуре нет никакого флага, который бы указывал, что объект изменен (dirty) и требует обратной записи на диск. Объект file указывает на связанный с ним объект dentry с помощью указателя f_dentry. Объект dentry в свою очередь содержит ука- затель на связанный с ним индекс файла, который содержит информацию о том, изменен ли файл. Файловые операции Как и для других объектоп подсистемы VFS, таблица файловых операций являет- ся важной структурой. Операции, связанные со структурой s t r u c t file, — это зна- комые системные вызовы, составляющие основу системных вызовов ОС Unix. Методы работы с файловым объектом хранятся в структуре file_operations и определены в файле struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, char *, size_t, loff_t); ssize_t (*write) (struct file *, const char *, size_t, loff_t * ) ; ssize_t (*aio_write) (struct kiocb *, const char *, size_t, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct * ) ; int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct * ) ; int (*open) (struct inode *, struct file * ) ; int (*flush) (struct file * ) ; int (*release) (struct inode *, struct file * ) ; int (*fsync) (struct file *, struct dentry *, int); int (*aio_fsync) (struct kiocb *, int); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock * ) ; ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t * ) ; ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t * ) ; ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void * ) ; ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area) (struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags) (int flags); int (*dir_notify) (struct file *filp, unsigned long arg); int (*flock) (struct file *filp, int cmd, struct file_lock *fl); }; 284 Глава 12 Файловые системы могут реализовать уникальную функцию для каждой из этих операций или использовать общий существующий метод. Общие методы нормально работают для обычных Unix-подобных файловых систем. Разработчики файловых систем не обязаны реализовать все эти функции, хотя основные методы должны быть реализованы. Если какой-либо метод не представляет интереса, то его можно установить в значение NULL. Рассмотрим каждую операцию подробнее. • loff_t llseek(struct file *file, loff_t offset, int origin) Эта функция устанавливает значения указателя текущей позиции в файле (file pointer) в заданное значение параметра offset. Функция вызывается из си- стемного вызова lseek(). • ssize_t read(struct f i l e * f i l e , char *buf, size_t count, loff_t *offset) Эта функция считывает count байт данных из указанного файла, начиная с позиции, заданной параметром offset, в буфер памяти, на который указы- вает параметр buf. После этого значение указателя текущей позиции в файле должно быть обновлено. Данная функция вызывается из системного вызова read(). • ssize_t aio_read(struct kiocb *iocb, char *buf, size_t count, loff_t offset) Эта функция запускает асинхронную операцию считывания count байт данных из файла, который описывается параметром iocb, в буфер памяти, описанный параметром buf. Эта функция вызывается из системного вызова aio_read (). • ssize_t w r i t e ( s t r u c t f i l e * f i l e , const char *buf, size_t count, loff_t *offset) Эта функция записывает count байт данных в указанный файл, начиная с по- зиции offset. Данная функция вызывается из системного вызова write (). • ssize_t aio_write(struct kiocb *iocb, const char *buf, size_t count, loff_t offset) Эта функция запускает асинхронную операцию записи count байт данных в файл, описываемый параметром iocb, из буфера памяти, на который указывает параметр buf. Данная функция вызывается из системного вызова aio_write. • i n t r e a d d i r ( s t r u c t f i l e * f i l e , void *dirent, f i l l d i r _ t f i l l d i r ) Эта функция возвращает следующий элемент из списка содержимого каталога. Данная функция вызывается из системного вызова readdir (). • unsigned int p o l l ( s t r u c t f i l e * f i l e , struct poll_table_struct *poll_table) Эта функция переводит вызывающий процесс в состояние ожидания для ожи- дания действий, которые производятся с указанным файлом. Она вызывается из системного вызова p o l l ( ) . Виртуальная файловая система 285 • int ioctl(struct inode *inode, struct file *file, unsigned int cmd, signed long arg) Эта функция используется для того, чтобы отправлять устройствам пары зна- чений команда/аргумент. Функция используется, когда открытый файл— это специальный файл устройства. Данная функция вызывается из системного вы- зова i o c t l ( ) . • i n t mmap(struct f i l e * f i l e , s t r u c t vra_area_struct *vma) Эта функция отображает указанный файл на область памяти в указанном адрес- ном пространстве и вызывается из системного вызова mmap(). • i n t open(struct inode *inode, s t r u c t f i l e * f i l e ) Эта функция создает новый файловый объект и связывает его с указанным файловым индексом. Она вызывается из системного вызова open (). • int flush(struct f i l e *file) Эта функция вызывается подсистемой VFS, когда уменьшается счетчик ссылок на открытый файл. Назначение данной функции зависит от файловой системы. • int r e l e a s e ( s t r u c t inode *inode, s t r u c t f i l e *file) Эта функция вызывается подсистемой VFS, когда исчезает последняя ссылка на файл, например, когда последний процесс, который использовал соответству- ющий файловый дескриптор, вызывает функцию c l o s e () или завершается. Назначение этой функции также зависит от файловой системы. • i n t f s y n c ( s t r u c t f i l e * f i l e , s t r u c t dentry *dentry, i n t datasync) Эта функция вызывается из системного вызова fsync() для записи на диск всех закэшированных данных файла. • i n t aio_fsync(struct kiocb *iocb, i n t datasync) Эта функция вызывается из системного вызова a i o f sync () для записи на диск всех закэшированных данных файла, связанного с параметром iocb. • i n t fasyn (fint fd, s t r u c t f i l e * f i l e , i n t on) Эта функция разрешает или запрещает отправку сигнала для уведомлении о со- бытиях при асинхронном вводе-выводе. • i n t l o c k ( s t r u c t f i l e * f i l e , i n t cmd, s t r u c t file_lock *lock) Эта функция управляет файловыми блокировками для данного файла. • s s i z e _ t r e a d v ( s t r u c t f i l e * f i l e , const s t r u c t iovec *vector, unsigned long count, loff_t *offset) Эта функция вызывается из системного вызова readv () для считывания дан- ных из указанного файла в count буферов, которые описываются параметром vector. После этого указатель текущей позиции файла должен быть соответ- ственным образом увеличен. |