Билет 10 - honeycarbs/bmstu-os-6sem GitHub Wiki
Создание собственной файловой системы. Структура, описывающая файловую систему и пример ее заполнения. Регистрация и дерегистрация файловых систем. Монтирование файловой системы. Структура struct super_operations. Структура inode_operations. Функции simple и generic. Точка монтирования. Функции монтирования. Функция printk(). Пример создания файловой системы, ее регистрация и монтирование (лаб. раб.).
ФС реализовывается в виде загружаемого модуля ядра. ФС становится доступной, если она подмонтирована. Функции суперблока выполняются при вызове mount, но описана также kill_sb. В ядре имеется список таких структур (поле next). В системе может существовать только один тип ФС, например, ext2. В это же время одна и та же ФС может быть подмонтирована много раз, причем к разным директориям, которые будут для нее корневыми.
Linux поддерживает большое количество файловых систем. Это возможно благодаря тому, что в VFS определена структура struct file_system_type, описывающая ФС:
struct file_system_type {
const char *name;
int fs_flags;
int (*init_fs_context)(struct fs_context *);
const struct fs_parameter_spec *parameters;
struct dentry *(*mount) (struct file_system_type *, int, const char *, void *);
void (*kill_sb) (struct super_block *); /*прекращение доступа к суперблоку*/
struct module *owner; /*счетчик ссылок на файловую систему*/
...
};Для каждого типа файловой системы существует только одна структура file_system_type, независимо от количества смонтированных ФС одного типа.
WARNING: это на свой страх и риск. Мы проводили ЭЭЭЭксперементы исследование с НЮ на лабе. Модуль загрузился нормально в ядро, ВФС была зарегистрирована, но маунт совершал суицид
Миниальный набор действий для регистрации ФС:
#define FS_MAGIC 0x13090D15
static struct file_system_type my_vfs_type = {
.owner = THIS_MODULE,
.name = "myvfs",
};
static int __init my_vfs_init(void)
{
printk(KERN_INFO "+ module is loaded.");
int result_register = register_filesystem(&my_vfs_type);
return result_register;
}
static void __exit my_vfs_exit(void)
{
unregister_filesystem(&my_vfs_type);
printk(KERN_INFO "+ module is unloaded.");
}Файловую систему невозможно будет подмонтировать, но она будет зарегистрирована.
Монтирование файловой системы это – набор действий, в результате выполнения которых файловая система становится доступной. Когда файловая система монтируется, создается структура struct vfsmount, которая представляет конкретный экземпляр файловой системы, или, иными словами, точку монтирования. Точкой монтирования является обычная директория дерева каталогов.
После подключения файловой системы точка монтирования становится корневым каталогом смонтированной файловой системы.
Функция mount() служит для монтирования ФС, определенной конкретной структурой file_system_type, и заполнения суперблока соответствующими данными.
Когда ФС монтируется, создается структура struct vfsmount, которая представляет конкретный экземпляр ФС или, другими словами, точку монтирования.
struct vfsmount {
struct dentry *mnt_root; /* корень подмонтированного дерева */
struct super_block *mnt_sb; /* указатель на суперблок */
int mnt_flags; /*флаги монтирования*/
};Обычно используют одну из функций монтирования:
-
mount_bdev— для монтирования ФС, находящейся на блочном устройстве, -
mount_nodev— для монтирования ФС, не связанной ни с каким устройством, -
mount_single— для монтирования ФС, точки монтирования которой разделяют один единственный экземпляр ФС. Передаваемая функции mount() функция fill_super() заполняет поля структуры struct super_block.
mount - команда монтирования файловой системы.
Если в системе присутствует некоторый образ диска "image", а также создан каталог, который будет являться точкой монтирования файловой системы "dir", то примонтировать файловую систему можно, используя команду:
mount -o loop -t myfs ./image ./dirПараметр -o указывает список параметров, разделенных запятыми. Одним из прогрессивных типов монтирования, является монтирование через петлевое (loop, по сути, это «псевдоустройство» (то есть устройство, которое физически не существует), которое позволяет обрабатывать файл как блочное устройство) устройство. Если петлевое устройство явно не указано в строке (а как раз параметр `-o loop' это задает), тогда mount попытается найти неиспользуемое в настоящий момент петлевое устройство и применить его.
Аргумент следующий за -t указывает тип файловой системы.
./image - это устройство. ./dir - это каталог.
umount - команда для размонтирования файловой системы:
sudo umount ./dirСтруктура struct super_operations описывает операции, определенные на суперблоке.
<linux/fs.h>
struct super_operations
{
// создание и инициализация нового объекта inode.
struct inode *(*alloc_inode)(struct super_block *sb);
// Удаление объекта
void (*destroy_inode)(struct inode *);
// dirty - изменённый inode. Вызывается, чтобы обновить информацию в журналируемых файловых системах (например EXT3)
void (*dirty_inode)(struct inode *, int flags);
// Запись indoe на диск (второй параметр сейчас указывает на то, как должна выполняться операция, например, синхронно)
int (* write_inode)(struct inode *, struct writeback_control * wbc);
// Вызывается при исчезновении последней ссылки на inode. Обычная ФС Unix никогда не определяет эту фукцию, в этом случае подсистема ВФС удаляет этот inode
int (* drop_inode)(struct inode *);
// Вызывается при размонтировании ВФС для освобождения указанного суперблока
void (* put_super)(struct super_block *);
// Синхронизирует метаданные ФС с данным на диске. Параметр указывает на то, синхронно или асинхронно будет выполняться данное действие.
int(* sync_fs)(struct super_block *sb, int wait);
...
// Получение статистики для ФС (записывается в структуру statfs)
int (* statfs)(struct dentry, struct kstatfs *);
// Вызывается, когда ФС монтируется с другими параметрами монтирования
int (*remount_fs)(struct super_block, int *, char *);
// Прерывание монтирования
int (*umount_begin)(struct super_block *);
}Структура struct inode_operations описывает операции, определенные inode.
struct inode_operations {
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
int (*permission) (struct inode *, int);
struct posix_acl * (*get_acl)(struct inode *, int);
int (*readlink) (struct dentry *, char __user *,int);
int (*create) (struct inode *,struct dentry *, umode_t, bool);
int (*link) (struct dentry *,struct inode *,struct dentry *);
int (*unlink) (struct inode *,struct dentry *);
int (*symlink) (struct inode *,struct dentry *,const char *);
int (*mkdir) (struct inode *,struct dentry *,umode_t);
int (*rmdir) (struct inode *,struct dentry *);
int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
int (*rename) (struct inode *, struct dentry *, struct inode *, struct dentry *, unsigned int);
int (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (const struct path *, struct kstat *, u32, unsigned int);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len);
int (*update_time)(struct inode *, struct timespec64 *, int);
int (*atomic_open)(struct inode *, struct dentry *, struct file *, unsigned open_flag, umode_t create_mode);
int (*tmpfile) (struct inode *, struct dentry *, umode_t);
int (*set_acl)(struct inode *, struct posix_acl *, int);
}Для поиска inode требуется, чтобы VFS вызывала метод lookup() родительского каталога inode. Этот метод устанавливается конкретной реализацией файловой системы, в которой «живет» inode. Как только VFS находит требуемый dentry (и, следовательно, inode), можно открывать файл системным вызовом open(2) или получать информацию о файле функцией stat(2), которая просматривает данные inode и передает часть их в пространство пользователя.
ibfs содержит в себе файловые и inode-операции.
const struct file_operations simple_dir_operations = {
.open = dcache_dir_open,
.release = dcache_dir_close,
.llseek = dcache_dir_lseek,
.read = generic_read_dir,
.iterate = dcache_readdir,
.fsync = noop_fsync,
};
EXPORT_SYMBOL(simple_dir_operations);
const struct inode_operations simple_dir_inode_operations = {
.lookup = simple_lookup,
};
EXPORT_SYMBOL(simple_dir_inode_operations);
static const struct super_operations simple_super_operations = {
.statfs = simple_statfs,
};
…
int simple_fill_super(struct super_block *s, unsigned long magic,
struct tree_descr *files)
{
...
}
EXPORT_SYMBOL(simple_fill_super);Функция printk определена в ядре Linux и доступна модулям. Функция ведёт себя аналогично библиотечной функции printf. Став частью ядра модуль не может вызывать обычные библиотечные функции, поэтому ядро предоставляет модулю функцию printk.
Функция printk позволяет отправлять сообщения в системный журнал. Сама функция не производит запись в системный журнал, а записывает сообщение в специальный буфер ядра. Из буфера ядра записанные сообщения могут быть прочитаны демоном протоколирования. Функция printk выводит информацию в /var/log/message. Существует несколько способов вывести это информацию в консоль, в частности это может быть команда (dmesg).