Второе издание
![]()
|
2 8 6 Глава 12 • ssize_t writev(struct file *file, const struct iovec *vector, unsigned long count, loff_t *offset) Эта функция вызывается из системного вызова writev () для записи в указан- ный файл буферов, описанных параметром vector; количество буферов равно count. После этого должно быть соответственным образом увеличено значе- ние текущей позиции в файле. • s s i z e _ t s e n d f i l e ( s t r u c t f i l e * f i l e , loff_t *offset, size_t size, read_actor_t actor, void *target) Эта функция вызывается из системного вызова sendfile () для копирования данных из одного файла в другой. Она выполняет операцию копирования ис- ключительно в режиме ядра и позволяет избежать дополнительного копирова- ния данных в пространство пользователя. • ssize_t sendpage(struct f i l e * f i l e , s t r u c t page *page, i n t offset, size_t s i z e , loff_t *pos, i n t more) Эта функция используется для отправки данных из одного файла в другой. • unsigned long get_unmapped_area(struct f i l e * f i l e , unsigned long addr, unsigned long len, unsigned long offset, unsigned long flags) Эта функция получает неиспользуемое пространство адресов для отображения данного файла. • i n t check_flags(int flags) Эта функция используется для проверки корректности флагов, которые пере- даются в системный вызов f c n t l (), при использовании команды SETFL. Как и в случае многих операций подсистемы VFS, для файловой системы нет необхо- димости реализовать функцию check_flags (). Сейчас это сделано только для файловой системы NFS. Эта функция позволяет файловой системе ограничить некорректные значения флагов команды SETFL в обобщенном системном вы- зове f c n t l ( ) . Для файловой системы NFS не разрешается использовать ком- бинацию флагов O_APPEND и O_DIRECT. • i n t flock(struct f i l e * f i l p , i n t cmd, s t r u c t file_lock *fl) Эта функция используется для реализации системного вызова flock(), кото- рый служит для выполнения рекомендованных блокировок. Виртуальная файловая система 287 Структуры данных, связанные с файловыми системами В дополнение к фундаментальным объектам подсистемы VFS, ядро использует и другие стандартные структуры данных для управления данными, связанными с фай- ловыми системами. Первый объект используется для описания конкретного типа файловой системы, как, например, ext.3 или XFS. Вторая структура данных использу- ется для описания каждого экземпляра смонтированной файловой системы. Поскольку операционная система Linux поддерживает множество файловых си- стем, то ядро должно иметь специальную структуру для описания возможностей и поведения каждой файловой системы. struct file_system_type { const char *name; /* название файловой системы */ struct subsystem subsys; /* объект подсистемы sysfs */ int fs_flags; /* флаги типа файловой системы */ /* следующая функция используется для считывания суперблока с диска */ struct super_block * (*get_sb) (struct file_system_type *, int, char*, void * ) ; /* эта функция используется для прекращения доступа к суперблоку */ void (*kill_sb) (struct super_block * ) ; struct module *owner; /* соответствующий модуль (если есть) */ struct file_system_type *next; /* следующая файловая система в списке */ struct list_head fs_supera; /* список объектов типа суперблок */ }; Функция get_sb () служит для считывания суперблока с диска и заполнения объ- екта суперблока соответствующими данными при монтировании файловой системы. Остальные параметры описывают свойства файловой системы. Для каждого типа файловой системы существует только одна структура file_sys- tem_type, независимо от того, сколько таких файловых систем смонтировано и смонтирован ли хотя бы один экземпляр соответствующей файловой системы. Значительно интереснее становится, когда файловая система монтируется, при этом создается структура vfsmount. Эта структура используется для представления конкретного экземпляра файловой системы, или, другими словами, точки монтиро- вания. Структура vfsmount определена в файле struct vfsmount { struct list_head mnt_hash; /* список хеш-таблицы */ struct vfsmount *mnt_parent; /* родительская файловая система */ struct dentry *mnt_mountpoint; /* объект элемента каталога точки монтирования */ struct dentry *mnt_root; /* объект элемента каталога корня данной файловой системы */ struct super_block *mnt_sb; /* суперблок данной файловой системы */ struct list_head mnt_mounts; /* список файловых систем, смонтированных к данной */ struct list_head mnt_child; /* потомки, связанные с родителем */ 288 Глава 12 atornic_t mnt count; /* счетчик использования */ int mnt_flags; /* флаги монтирования */ char *mnt_devname; /* имя смонтированного устройства */ struct list_hcad mnt_list; /* список дескрипторов */ struct listhead mnt_fslinkk; /* истекший список, специфичный для файловой системы */ struct namespace *mnt_namespace; /* связанное пространство имен */ }; Самая сложная задача — это поддержание списка всех точек монтирования и вза- имоотношений между данной файловой системой и другими точками монтирования. Эта информация хранится в различных связанных списках структуры vf smount. Структура vfsmount также содержит иоле mnt_flags. В табл. 12.1 приведен спи- сок стандартных флагов монтирования. Таблица 1 2 . 1 . Список стандартных флагов монтирования Эти флаги полезны, в основном, для сменных носителей, которым администра- тор не доверяет. Структуры данных, связанные с процессом Каждый процесс в системе имеет свои открытые файлы, корневую файловую си- стем); текущий рабочий каталог, точки монтирования и т.д. Следующие три структу- ры данных связывают вместе подсистему VFS и процессы, которые выполняются в системе. Это структуры f i l e s _ s t r u c t , fs_struct и namespace. Структура f i l e s _ s t r u c t определена в файле struct files_struct { atomic_t count; /* счетчик ссылок на данную структуру */ spinlock_t file_lock; /* блокировка для защиты данной структуры */ int max_fds; /* максимальное количество файловых объектов */ int max_fdset; /* максимальное количество файловых дескрипторов */ int next_fd; /* номер следующего файлового дескриптора */ struct file **fd; /* массив всех файловых объектов */ fd_set *close on exec;/* файловые дескрипторы, которые должны закрываться при вызове ехес() */ fd_set *open_fds; /* указатель на дескрипторы открытых файлов */ fd_set close_on_exec init; /* первоначальные файлы для закрытия при вызове exec () */ fd_set open_fds_init; /* первоначальный набор файловых дескрипторов */ struct file *fd_array[NR_OPEN_DEFAULT]; /* массив файловых объектов */ }; Виртуальная файловая система 289 Флаг Описание M N T _ N O S U I D Запрещает использование флагов setuid и s e t g i d для бинарных файлов на файловой системе MNT_NODEV Запрещает доступ к файлам устройств на файловой системе MNT_NOEXEC Запрещает выполнение программ на файловой системе Массив fd указывает на список открытых файловых объектов. По умолчанию это массив fd_array. Так как по умолчанию значение константы NR_OPEN_DEFAULT равно 32, то это соответствует 32 файловым объектам. Если процесс открывает боль- ше 32 файловых объектов, то ядро выделяет новый массив и присваивает полю fd указатель на него. При таком подходе доступ к небольшому количеству файловых объектов осуществляется быстро, потому что они хранятся в статическом массиве. В случае, когда процесс открывает аномально большое количество файлов, ядро мо- жет создать новый массив. Если большинство процессов в системе открывает боль- ше 32 файлов, то для получения оптимальной производительности администратор может увеличить значение константы NR_OPEN_DE_FAULT с помощью директивы препроцессора. Следующая структура данных, связанная с процессом, — это струк- тура f s _ s t r u c t , которая содержит информацию, связанную с процессом, и на ко- торую указывает поле fs дескриптора процесса. Эта структура определена в файле < l i n u x / f s _ s t r u c t . h > и имеет следующий вид с поясняющими комментариями. struct fs struct { atomic_t count; /* счетчик ссылок на структуру */ rwlock_l lock; /* блокировка для защиты структуры */ int umask; /* права доступа к файлу, используемые по умолчанию */ struct dentry *root; /* объект dentry корневого каталога */ struct dentry *pwd; /* объект dentry текущего рабочего каталога */ struct dentry *allroot; /* объект dentry альтернативного корня */ struct vfsmounL *rootmnt; /* объект монтирования корневого каталога */ struct vfsmount *pwdmnt; /* объект монтирования текущего рабочего каталога */ struct vfsmount *altrootrnnt; /* объект монтирования альтернативного корня */ }; Эта структура содержит текущий рабочий каталог и корневой каталог данного процесса. Третья, и последняя, структура— это структура namespace, которая определена в файле были введены в ядрах Linux серии 2.4. Это позволило создать для каждого процес- са уникальное представление о смонтированных файловых системах. Иными слова- ми, процесс может иметь не только уникальный корневой каталог, но и полностью уникальную иерархию смонтированных файловых систем, если это необходимо. Как обычно, ниже приведена соответствующая структура данных с комментариями. struct namespace { atomic_t count; /* счетчик ссылок на структуру */ struct vfsmount *root; /* объект монтирования корневого каталога */ struct list_head list; /* список точек монтирования */ struct rw_semaphore sem; /* семафор для защиты пространства имен */ }; Поле l i s t представляет собой двухсвязный список смонтированных файловых систем, которые составляют пространство имен. Каждый дескриптор процесса имеет связанные с ним рассмотренные структу- ры данных. Для большинства процессов их дескриптор процесса указывает на уни- кальную структуру f i l e s _ s t r u c t и структуру f s _ s t r u c t . Однако для процессов, 290 Глава 12 созданных с флагами CLONE_FILES и CLONE_FS, эти структуры являются совмест- но используемыми 8 . Отсюда следует, что несколько дескрипторов процессов могут указывать на одну и ту же структуру f i l e s _ s t r u c t , или структуру f s _ s t r u c t . Поле count каждой структуры содержит счетчик использования, что предотвращает уни- чтожение структуры данных, когда ее использует хотя бы один процесс. Структура n a m e s p a c e используется несколько по-другому. По умолчанию вес процессы совместно используют одно пространство имен (и соответственно одну иерархию файловых систем). Только когда для системного вызова c l o n e () указан флаг CLONE_NEWNS, для процесса создается уникальная копия пространства имен. Поскольку для большинства процессов этот флаг не указывается, процессы обычно наследуют пространство имен родительского процесса. Следовательно, для большин- ства систем существует только одно пространство имен. Файловые системы в операционной системе Linux Операционная система Linux поддерживает большой набор файловых систем, от "родных" ext2 и ext3 до сетевых файловых систем, таких как NFS или Coda. Сейчас в официальном ядре ОС Linux поддерживается более 50 файловых систем. Уровень VFS обеспечивает все эти разнообразные файловые системы общей базой для их реа- лизации и общим интерфейсом для работы со стандартными системными вызовами. Следовательно, уровень виртуальной файловой системы позволяет четким образом реализовать поддержку новых файловых систем в операционной системе Linux, a также дает возможность работать с этими файловыми системами с помощью стан- дартных системных вызовов Unix. В этой главе было описано назначение подсистемы VFS и рассмотрены соответ- ствующие структуры данных, включая такие важные объекты, как inode, dentry и superblock. В главе 12, "Виртуальная файловая система", будет рассказано о том, как данные физически поступают на файловые системы. 8 Для создания потоков обычно укапываются флаги CLONE_FILES и CLONB_FS, поэтому они совмест- но используют структуры £iles_struct и fs_struct. С другой стороны, для обычных процессов эти флаги не указываются, поэтому для каждого процесса существует своя информация о файло- вой системе и своя таблица открытых файлов. Виртуальная файловая система 291 13 Уровень блочного ввода-вывода У стройства блочного ввода-вывода (блочные устройства, устройства ввода-вы- вода блоками, block devices) — это аппаратные устройства, которые позволяют случайным образом (т.е. не обязательно последовательно) осуществлять доступ к фрагментам данных фиксированного размера, называемых блоками. Наиболее часто встречающееся устройство блочного впода-вывода — это жесткий диск, но существу- ют и другие блочные устройства, например устройства работы с гибкими дисками, оптическими компакт-дисками (CD-ROM) и флеш-памятью. Следует обратить внима- ние, что файловые системы монтируются с таких устройств. Именно таким образом обычно и осуществляется доступ к устройствам блочного ввода-вывода. Другой фундаментальный тип устройства — это устройство посимвольного вво- да-вывода (символьное устройство, character device, char device). Это— устройство, к которому можно обращаться, только как к последовательному потоку данных, т.е. байт за байтом. Пример символьных устройств — это последовательный порт и кла- виатура. Если же устройстпо позволяет обращаться к данным случайным образом (не последовательно), то это блочное устройство. Существенное различие между этими типами устройств проявляется, в основном, в возможности случайного доступа к данным, т.е. в возможности производить поиск (seek) по устройству, перемещаясь из одной позиции в другую. Как пример, рассмо- трим клавиатуру. Драйвер устройства клавиатуры на выходе выдает поток данных. Когда печатают слово "fox", то драйвер клавиатуры возвращает поток данных, в ко- тором три символа идут строго в указанном порядке. Считывание символов в другом порядке или считывание какого-нибудь другого символа, кроме следующего символа в потоке, имеет немного смысла. Поэтому драйвер клавиатуры — это устройство по- символьного ввода-вывода, он позволяет на выходе получить поток символов, кото- рые пользователь вводит на клавиатуре. Операция чтения данных с устройства воз- вращает сначала символ "f", затем символ "о" и в конце символ "х". Когда нажатий клавиш нет, то поток— пустой. Жесткий диск же работает по-другому Драйвер жест- кого диска может потребовать чтения содержимого определенного блока, а затем прочитать содержимое другого блока, и эти блоки не обязательно должны следовать друг за другом. Поэтому доступ к данным жесткого диска может выполняться случай- ным образом, а не как к потоку данных, и поэтому жесткий диск— блочное устрой- ство. Управление блочными устройствами в ядре требует большего внимания, подго- товки и работы, чем управление устройствами посимвольного ввода-вывода. Все это потому, что символьные устройства имеют всего одну позицию — текущую, в то вре- мя как блочные устройства должны иметь возможность перемещаться туда и обрат- но между любыми позициями на физическом носителе информации. Оказывается, что нет необходимости создавать в ядре целую подсистему для обслуживания сим- вольных устройств, а для блочных устройств это необходимо. Такая подсистема не- обходима отчасти из-за сложности блочных устройств. Однако основная причина та- кой мощной поддержки в том, что блочные устройства достаточно чувствительны к производительности. Выжать максимум производительности из жесткого диска зна- чительно важнее, чем получить некоторый прирост скорости при работе с клавиату- рой. Более того, как будет видно дальше, сложность блочных устройств обеспечивает большой простор для таких оптимизаций. Предмет данной главы — как ядро управ- ляет работой блочных устройств и запросами к этим устройствам. Рассматриваемая часть ядра называется уровнем, блочного ввода-вывода (block I/O layer). Интересно, что усовершенствование подсистемы блочного ввода-вывода было одной из целей раз- рабатываемой серии ядра 2.5. В этой главе рассматриваются все новые особенности уровня блочного ввода-вывода, которые появились в ядрах серии 2.6. Анатомия блочного устройства Наименьший адресуемый элемент блочного устройства называется сектором. Размеры секторов — это числа, которые являются целыми степенями двойки, одна- ко наиболее часто встречающийся размер — 512 байт. Размер сектора— это физи- ческая характеристика устройства, а сектор — фундаментальный элемент блочного устройства. Устройства не могут адресовать или другим образом работать с элемен- тами данных, размер которых меньше, чем один сектор, тем не менее многие блоч- ные устройства могут передавать несколько секторов за один раз. Хотя большинство блочных устройств и имеет размер сектора, равный 512 байт, все же существуют и другие стандартные размеры сектора (например, большинство компакт-дисков CD-ROM имеют размер сектора, равный 2 Кбайт). У программного обеспечения несколько другие цели, и поэтому там существует другая минимально адресуемая единица, которая называется блок. Блок— это аб- стракция файловой системы, т.е. все обращения к файловым системам могут вы- полняться только с данными, кратными размеру блока. Хотя физические устройства сами по себе адресуются на уровне секторов, ядро выполняет все дисковые опера- ции в терминах блоков. Так как наименьший возможный адресуемый элемент —- это сектор, то размер блока не может быть меньше размера одного сектора и должен быть кратен размеру сектора. Более того, для ядра (так же как и для аппаратного обеспечения в случае секторов) необходимо, чтобы размер блока был целой степе- нью двойки. Ядро также требует, чтобы блок имел размер, не больший, чем размер страницы памяти (см. главу 11, "Управление памятью" и главу 12, "Виртуальная фай- ловая система") 1 1 Это ограничение является искусственным и в будущем оно может быть отменено. Тем не менее требование, чтобы размер блока был меньше или равен размеру страницы памяти, позволяет зна- чительно упростить ядро. 294 Глава 13 Поэтому размер блока равен размеру сектора, умноженному на число, которое яв- ляется целой степенью двойки. Наиболее часто встречающиеся размеры блока— это 512 байт, один килобайт и четыре килобайта. Часто сбивает с толку то, что некоторые люди называют секторы и блоки по- разному. Секторы, наименьшие адресуемые элементы устройства, иногда называют "аппаратными секторами" (hardware sector) или "блоками аппаратного устройства" (device block). В то время как блоки, наименьшие адресуемые единицы файловых систем, иногда называются "блоками файловой системы" (filesyst.em block) или "бло- ками ввода-вывода" (I/O block). В этой главе будут использованы термины "сектор" (sector) и "блок" (block), однако следует помнить и о других возможных названиях. На рис. 13.1 показана диаграмма соответствия между секторами и блоками. Существует еще одна часто встречающаяся терминология. По крайней мере в отношении.к жестким дискам, используются термины кластеры, (clusters), цилиндры (cylinder, дорожка) и головки (head). Такие обозначения являются специфическими только для некоторых типов блочных устройств, они, в основном, невидимы для пользовательских программ и в этой главе рассматриваться не будут. Причина, по которой секторы являются важными для ядра, состоит в том, что все операции вво- да-вывода должны выполняться в единицах секторов. Поэтому более высокоуров- невые концепции ядра, которые основаны на блоках, являются надстройками над секторами. Жесткий диск Блок Отображение секторов на блоки i Рис. 13.1. Связь между секторами и бликами |