Внутреннее устройство ядра Linux 2.4

       

Управление Суперблоком и точкой монтирования


В Linux, информация о смонтированных файловых системах хранится в двух различных структурах - super_block и vfsmount. Сделано это для того, чтобы имелась возможность смонтировать одну и ту же файловую систему к нескольким точкам монтирования одновременно, это означает, что одна и та же структура super_block может соответствовать нескольким структурам vfsmount.

В первую очередь рассмотрим структуру struct super_block, объявленную в include/linux/fs.h:

struct super_block { struct list_head s_list; /* Хранится первым */ kdev_t s_dev; unsigned long s_blocksize; unsigned char s_blocksize_bits; unsigned char s_lock; unsigned char s_dirt; struct file_system_type *s_type; struct super_operations *s_op; struct dquot_operations *dq_op; unsigned long s_flags; unsigned long s_magic; struct dentry *s_root; wait_queue_head_t s_wait;

struct list_head s_dirty; /* "грязные" inodes */ struct list_head s_files;

struct block_device *s_bdev; struct list_head s_mounts; /* vfsmount(s) of this one */ struct quota_mount_options s_dquot; /* параметры для Diskquota */

union { struct minix_sb_info minix_sb; struct ext2_sb_info ext2_sb; ..... Информация sb-private, необходимая для всех файловых систем ... void *generic_sbp; } u; /* * Следующее поле предназначено *только* для VFS. * Ни одна файловая система не должна изменять его, * даже если она обращается к этому полю. * Вас предупредили. */ struct semaphore s_vfs_rename_sem; /* Kludge */

/* Следующее поле используется демоном knfsd для преобразования(inode number based) * file handle в dentry. Поскольку путь в дереве dcache строится снизу вверх * то в течение некоторого времени путь является неполным, никак не связанным * с главным деревом. Этот семафор гарантирует существование единственного * такого свободного пути в файловой системе. * Заметьте, что такие "несвязанные" файлы допустимы * но не каталоги. */ struct semaphore s_nfsd_free_path_sem; };

Более подробно о полях структуры super_block:

  • s_list: двусвязный список всех активных суперблоков; Заметьте, что я не говорю "всех смонтированных файловых систем", потому что в Linux всем смонтированным экземплярам файловой системы соответствует единственный суперблок.

  • s_dev: предназначено для файловых систем, требующих наличие блочного устройства, т.е. для файловых систем, зарегистрированных с флагом FS_REQUIRES_DEV, это поле представляет собой копию i_dev блочного устройства. Для других файловых систем (называемых анонимными) представляет собой целое число MKDEV(UNNAMED_MAJOR, i), где i принадлежит диапазону от 0 до 255 включительно и является порядковым номером первого неустановленного бита в массиве unnamed_dev_in_use. Смотрите fs/super.c:get_unnamed_dev()/put_unnamed_dev(). Неоднократно предлагалось отказаться от использования поля s_dev анонимными файловыми системами.


  • s_blocksize, s_blocksize_bits: Размер блока и количество бит, необходимое для хранения размера блока (log2(blocksize)).


  • s_lock: индикатор блокировки суперблока функциями lock_super()/unlock_super().




  • s_dirt: устанавливается при внесении изменений в суперблок и сбрасывается при записи его обратно на диск.


  • s_type: указатель на структуру struct file_system_type, соответствующую файловой системе. Метод файловой системы read_super() не должен устанавливать это поле, так как это поле устанавливается VFS в функции fs/super.c:read_super(), в случае успешного вызова метода read_super() конкретной файловой, и сбрасывется в NULL в противном случае.


  • s_op: указатель на структуру (список) super_operations, которая содержит специфичные для заданной файловой системы методы, такие как чтение/запись inode и пр. Корректное заполнение этой структуры - задача метода файловой системы read_super().


  • dq_op: операции по дисковому квотированию.


  • s_flags: флаги суперблока.


  • s_magic: "магическое" число файловой системы. Используется файловой системой minix для различения разных вариантов ее.


  • s_root: dentry корня файловой системы. Метод read_super() считывает корневой inode с диска и передает его в d_alloc_root(), который выделяет память под dentry и заполняет ее. Некоторые файловые системы используют иное обозначение корня, нежели "/", поэтому используется более общая функция d_alloc() для образования полного имени, например pipefs использует "pipe:" для обозначения своего корня.




  • s_wait: очередь ожидания, в которой нахдятся процессы, ожидающие снятия блокировки с суперблока.


  • s_dirty: список всех "грязных" (измененных) inodes. Напомню, что если inode изменился (т.е. inode->i_state & I_DIRTY), то этот список связуется через inode->i_list.


  • s_files: список всех открытых файлов в данном суперблоке. Полезен при принятии решения о перемонтировании файловой системы в режиме "только для чтения", см. fs/file_table.c:fs_may_remount_ro(), которая просматривает список sb->s_files и отвергает возможность перемонтирования если имеется хотя бы один файл, открытый "на запись" (file->f_mode & FMODE_WRITE) или ожидающий удаления (inode->i_nlink == 0).


  • s_bdev: для случая FS_REQUIRES_DEV указывает на структуру block_device, описывающую блочное устройство, с которого смонтирована файловая система.


  • s_mounts: список всех структур vfsmount для каждого смонтированного экземпляра данного суперблока.


  • s_dquot: используется при квотировании диска.


  • Методы управления суперблоком перечисляются в структуре super_operations, объявленной в include/linux/fs.h:

    struct super_operations { void (*read_inode) (struct inode *); void (*write_inode) (struct inode *, int); void (*put_inode) (struct inode *); void (*delete_inode) (struct inode *); void (*put_super) (struct super_block *); void (*write_super) (struct super_block *); int (*statfs) (struct super_block *, struct statfs *); int (*remount_fs) (struct super_block *, int *, char *); void (*clear_inode) (struct inode *); void (*umount_begin) (struct super_block *); };

  • read_inode: операция чтения inode из файловой системы. Вызывается только в fs/inode.c:get_new_inode() из iget4()

    (и следовательно из iget()). Если файловая система предполагает вызов iget() то метод read_inode() должен быть реализован, в противном случае get_new_inode() будет приводить к "впадению в панику" (panic). Во время чтения inode заблокирован (inode->i_state = I_LOCK). Когда функция возвращает управление, все процессы из очереди inode->i_wait пробуждаются. В задачу метода read_inode() входит обнаружение дискового блока, который содержит заданный inode и с помощью функйии буферного кэша bread() прочитать его и инициализировать различные поля в структуре inode, например inode->i_op и inode->i_fop, чтобы уровень VFS "знал" какие операции над inode и соответствующим ему файлом, считаются допустимыми. Имеются файловые системы, в которых метод read_inode() не реализован - это ramfs и pipefs. Так ramfs имеет свою собственную функцию генерации inode (ramfs_get_inode()).




  • write_inode: операция записи inode на диск. так же как и read_inode() отыскивает нужный дисковый блок и вызывает функцию буферного кэша mark_buffer_dirty(bh). Этот метод вызывается для "грязных" inode (которые были помечены вызовом mark_inode_dirty()) при возникновении необходимости синхронизации как отдельно взятого inode, так и файловой системы в целом.


  • put_inode: вызывается всякий раз при уменьшении счетчика ссылок.


  • delete_inode: вызывается всякий раз, когда inode->i_count и inode->i_nlink

    достигают нулевого значения. Файловая система удаляет дисковую копию inode и вызывает clear_inode() для VFS inode, чтобы "прекратить его существование окончательно".


  • put_super: вызывается на последней стадии работы системного вызова umount(2), чтобы уведомить файловую систему о том, что любая приватная информация, удерживаемая ею, должна быть освобождена. Обычно это brelse() блока, содержащего суперблок, и kfree() для освобождения всех ранее размещенных блоков, inodes и т.п.


  • write_super: вызывается в случае необходимости записать суперблок на диск. Должен отыскать блок, содержащий суперблок, (обычно хранится в области sb-private) и вызвать mark_buffer_dirty(bh). А так же должен сбросить флаг sb->s_dirt flag.


  • statfs: реализация системного вызова fstatfs(2)/statfs(2). Заметьте, что указатель на struct statfs, передаваемый в качестве аргумента, является указателем пространства ядра а не пользовательского пространства, поэтому не следует выполнять каких либо операций ввода-вывода в пользовательском пространстве. В случае отсутствия этого метода вызов statfs(2) будет возвращвть код ошибки ENOSYS.


  • remount_fs: вызывается всякий раз при перемонтировании файловой системы.


  • clear_inode: вызывается из функции clear_inode() уровня VFS. Файловая система должна освободить приватную информацию в структуре inode (присоединенную через поле generic_ip).


  • umount_begin: вызывается в случае принудительно размонтирования для уведомления файловой системы заранее, чтобы убедиться, что она не занята. В настоящее время используется только NFS. Этот метод не имеет никакого отношения к идее поддержки принудительного размонтирования на уровне VFS.




  • Теперь рассмотрим последовательность действий, выполняемых при монтировании дисковой (FS_REQUIRES_DEV) файловой системы. Реализация системного вызова mount(2)

    находится в fs/super.c:sys_mount(), которая по сути является лишь оберткой, которая передает опции монтирования, тип файловой системы и название устройства в функцию do_mount().

  • В случае необходимости, загружается модуль драйвера файловой системы и увеличивается счетчик ссылок на этот модуль. Примечательно, что в процессе монтирования счетчик ссылок на модуль файловой системы увеличивается дважды - один раз в do_mount(), вызываемой из get_fs_type(), и один раз в get_sb_dev(), вызываемой из get_filesystem(), если read_super()

    выполнилась успешно. Первое увеличение предотвращает выгрузку модуля пока выполняется метод read_super() и второе увеличение указывает на то, что модуль используется смонтированным экземпляром. Вполне понятно, что перед завершением do_mount() уменьшает счетчик ссылок на единицу, таким образом суммарное приращение счетчика составляет единицу после каждого монтирования.


  • Для нашего случая выражение fs_type->fs_flags & FS_REQUIRES_DEV истинно, поэтому далее инициализируется суперблок, вызовом get_sb_bdev(), который получает ссылку на блочное устройство и вызывом метода read_super() заполняет поля суперблока. Если все прошло гладко, то структура super_block считается инициализированной и мы получаем дополнительно ссылку на модуль файловой системы и ссылку на основное блочное устройство.


  • В памяти размещается новая структура vfsmount и "прицепляется" к списку sb->s_mounts и к глобальному списку vfsmntlist. С помощью поля mnt_instances структуры vfsmount можно найти все смонтированные экземпляры файловой системы для одного и того же суперблока. С помощью списка mnt_list можно отыскать все смонтированные экземпляры файловых систем для всех суперблоков в системе. Поле mnt_sb указывает на данный суперблок, а mnt_root получает новую ссылку на sb->s_root dentry.



  • Содержание раздела