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

       

Управление файловой структурой


Структура file объявлена в include/linux/fs.h:

struct fown_struct { int pid; /* pid или -pgrp процесса, которому должен передаваться SIGIO */ uid_t uid, euid; /* uid/euid процесса-владельца */ int signum; /* posix.1b rt signal to be delivered on IO */ };

struct file { struct list_head f_list; struct dentry *f_dentry; struct vfsmount *f_vfsmnt; struct file_operations *f_op; atomic_t f_count; unsigned int f_flags; mode_t f_mode; loff_t f_pos; unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin; struct fown_struct f_owner; unsigned int f_uid, f_gid; int f_error;

unsigned long f_version;

/* требуется для драйвера tty, а возможно и для других */ void *private_data; };

Остановимся подробнее на полях struct file:

  • f_list: поле связи с одним (и только одним) из списков:

    а) sb->s_files - список всех открытых файлов в данной файловой системе, если соответствующий inode не является анонимным, то dentry_open() (вызываемая из filp_open()) вставляет файл в этот список;

    б) fs/file_table.c:free_list - список неиспользуемых структур;

    в) fs/file_table.c:anon_list - в этот список включаются структуры, создаваемые в get_empty_filp().

    Доступ к этим спискам производится под блокировкой files_lock.

  • f_dentry: dentry файла. Создается в процессе поиска nameidata в open_namei() (или точнее в path_walk()), но в действительности поле file->f_dentry заполняется в dentry_open().
  • f_vfsmnt: указатель на структуру vfsmount файловой системы, содержащей файл. Заполняется функцией dentry_open() и является частью nameidata, поиск которой производится в open_namei()



    (или точнее в path_init()).

  • f_op: указатель на список file_operations, который содержит адреса методов для работы с файлом. Копируется из inode->i_fop

    методом s_op->read_inode(), вызываемым в процессе поиска nameidata. Более подробно на списке file_operations мы остановимся ниже в этом разделе.

  • f_count: счетчик ссылок, изменяется в get_file/put_filp/fput.
  • f_flags: флаги O_XXX системного вызова open(2), копируются функцией dentry_open() (с небольшими изменениями в filp_open()), при чем флаги O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC


    сбрасываются, поскольку они не могут модифицироваться по параметру F_SETFL (или F_GETFL) в системном вызове fcntl(2).


  • f_mode: комбинация флагов состояния, устанавливается в dentry_open(). Флаги режимов доступа для чтения и записи выведены в отдельные биты, чтобы облегчить контроль состояния: (f_mode & FMODE_WRITE) и (f_mode & FMODE_READ).


  • f_pos: текущая позиция чтения/записи в файле. Для архитектуры i386 имеет тип long long, т.е. 64 бита.


  • f_reada, f_ramax, f_raend, f_ralen, f_rawin: поддержка опережающего чтения (readahead) слишком сложна, чтобы обсуждаться простыми смертными ;)


  • f_owner: владелец файла, который будет получать I/O уведомления посредством механизма SIGIO

    (см. fs/fcntl.c:kill_fasync()).


  • f_uid, f_gid - user id и group id процесса, открывшего файл, заполняются во время создания структуры в get_empty_filp(). Если файл является сокетом, то эти поля могут быть использованы в ipv4 netfilter.


  • f_error: используется клиентом NFS для возврата ошибки записи. Поле устанавливается в fs/nfs/file.c и проверяется в mm/filemap.c:generic_file_write().


  • f_version - механизм контроля версий, служит для синхронизации с кэшем. Увеличивается на единицу (используя глобальный event) всякий раз, когда изменяется f_pos.


  • private_data: скрытая информация о файле, может использоваться файловой системой (например coda хранит здесь удостоверения) или драйверами устройств. Драйверы устройств (при наличии devfs) могут использовать это поле для различения нескольких экземпляров вместо классического анализа младшего номера версии в file->f_dentry->d_inode->i_rdev.


  • Перейдем к рассмотрению списка методов управления файлом file_operations. Позволю себе напомнить, что он копируется из inode->i_fop методом s_op->read_inode(). Структура (список методов) объявлена в include/linux/fs.h:

    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 (*write) (struct file *, 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 datasync); 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 *); };



  • owner: указатель на модуль рассматриваемой подсистемы. Это поле устанавливается только драйверами устройств, файловая система может игнорировать его, поскольку счетчик ссылок модуля файловой системы изменяется во время монтирования/демонтирования, в то время как для драйверов это должно делаться во время открытия/закрытия устройства.


  • llseek: реализация системного вызова lseek(2). Обычно опускается и используется fs/read_write.c:default_llseek(). (TODO: Принудительно устанавливать это поле в NULL, тем самым сэкономится лишний if() в llseek())


  • read: реализация системного вызова read(2). Файловые системы могут использовать mm/filemap.c:generic_file_read() для обычных файлов и fs/read_write.c:generic_read_dir() (которая просто возвращает -EISDIR) для каталогов.


  • write: реализация системного вызова write(2). Файловые системы могут использовать mm/filemap.c:generic_file_write() для обычных файлов и игнорировать его для каталогов.


  • readdir: используется файловой системой. Реализует системные вызовы readdir(2) и getdents(2) для каталогов и игнорируется для обычных файлов.


  • poll: реализация системных вызовов poll(2) и select(2)


  • ioctl: реализация специфичного для драйвера или для файловой системы метода ioctl ( управление вводом/выводом). Обратите внимание: общие методы ioctl типа FIBMAP, FIGETBSZ, FIONREAD

    реализуются на более высоком уровне, поэтому они никогда не пользуются методом f_op->ioctl().


  • mmap: реализация системного вызова mmap(2). Файловая система может использовать generic_file_mmap для обычных файлов и игнорировать это поле для каталогов.


  • open: вызывается во время выполнения open(2) функцией dentry_open(). Редко используется файловыми системами, например coda пытается кэшировать файл во время открытия.


  • flush: вызывается при каждом вызове close(2) для заданного файла, не обязательно в последнем (см. метод release() ниже). Единственная файловая система, которая вызывает этот метод - это NFS клиент, которая "выталкивает" все измененные страницы. Примечательно, что этот метод может завершаться с кодом ошибки, который передается обратно в пространство пользователя, откуда делался системный вызов close(2).




  • release: метод вызывается в последнем вызове close(2) для заданного файла, т.е. когда file-> f_count станет равным нулю. Хотя и возвращает целое (int) значение, но VFS игнорирует его (см. code>fs/file_table.c:__fput()).


  • fsync: преобразуется в системные вызовы fsync(2)/fdatasync(2), причем последний аргумент определяет сам вызов - fsync или fdatasync. Не выполняет почти никаких действий за исключением преобразования файлового дескриптора в файловую структуру (file = fget(fd)) и сброса/установки семафора inode->i_sem. Файловая система Ext2, на сегодняшний день, игнорирует последний аргумент, передаваемый методу и выполняет одни и те же действия как для fsync(2) так и для fdatasync(2).


  • fasync: этот метод вызывается при изменении file->f_flags & FASYNC.


  • lock: специфичная для файловой системы часть механизма блокировки области файла POSIX fcntl(2). Единственная неувязка состоит в том, что этот метод вызывается перед независимой от типа файловой системы posix_lock_file(), если метод завершается успешно, а стандартный POSIX код блокировки терпит неудачу, то блокировка не будет снята на зависимом от типа файловой системы уровне..


  • readv: реализация системного вызова readv(2).


  • writev: реализация системного вызова writev(2).



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