仮想ファイルシステムシリーズ(一)--概要

13314 ワード

linuxの仮想ファイルシステムはアプリケーションにインタフェース層を提供し、統一的なインタフェースを使用して下位デバイスとファイルを操作することができ、linuxのいわゆる「万物すべてのファイル」の考えを実現した.各ファイルに含まれる操作は異なります.たとえば、ディレクトリファイルにはlookup関数が含まれていますが、通常のファイルはありません.デバイスファイルはioctlで最下位のデバイスを操作できます.異なるファイルシステムの実現方式も異なり、ネットワークファイルシステムはネットワークを通じてファイルの読み書き、キャッシュ同期などを実現することができる.各種ファイルシステムのために、各種ファイルに統一操作を提供するために、linuxが与えた方法はすべてのファイルシステムのすべてのサポートを抽象化する方法であり、特定のファイルシステムに対して、自身がサポートする機能を実現するだけで、抽象操作のサブセットとして機能する.カーネルが関数を使用する前に、このファイルシステムがサポートされているかどうかを確認します.つまり、この関数が空ではないかどうかを確認します.カーネルエラーがサポートされていないか、デフォルトで処理されていない場合は、サポートされている場合は、特定のファイルシステムの関数を呼び出します.
linuxの仮想ファイルシステムは、inode、file、addressの3つの一般的なオペレーティングモデルを抽象化します.space.inode関連の操作新規ファイル、削除、ファイル属性、ディレクトリ検索などの操作、file関連の操作処理ファイルの読み書きなど、address_spaceはファイル関連で、各ファイルにaddressが1つあります.mapping,address_spaceに関する操作は、メモリ管理システムとのインタラクションに使用されます.
struct inode_operations {
	struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
	void * (*follow_link) (struct dentry *, struct nameidata *);
	int (*permission) (struct inode *, int);
	struct posix_acl * (*get_acl)(struct inode *, int);

	int (*readlink) (struct dentry *, char __user *,int);
	void (*put_link) (struct dentry *, struct nameidata *, void *);

	int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
	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 *,int);
	int (*rmdir) (struct inode *,struct dentry *);
	int (*mknod) (struct inode *,struct dentry *,int,dev_t);
	int (*rename) (struct inode *, struct dentry *,
			struct inode *, struct dentry *);
	void (*truncate) (struct inode *);
	int (*setattr) (struct dentry *, struct iattr *);
	int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
	int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
	ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
	ssize_t (*listxattr) (struct dentry *, char *, size_t);
	int (*removexattr) (struct dentry *, const char *);
	void (*truncate_range)(struct inode *, loff_t, loff_t);
	int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
		      u64 len);
} ____cacheline_aligned;

struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	int (*readdir) (struct file *, void *, filldir_t);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, loff_t, loff_t, int datasync);
	int (*aio_fsync) (struct kiocb *, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	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);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
	int (*setlease)(struct file *, long, struct file_lock **);
	long (*fallocate)(struct file *file, int mode, loff_t offset,
			  loff_t len);
};

struct address_space_operations {
	int (*writepage)(struct page *page, struct writeback_control *wbc);
	int (*readpage)(struct file *, struct page *);

	/* Write back some dirty pages from this mapping. */
	int (*writepages)(struct address_space *, struct writeback_control *);

	/* Set a page dirty.  Return true if this dirtied it */
	int (*set_page_dirty)(struct page *page);

	int (*readpages)(struct file *filp, struct address_space *mapping,
			struct list_head *pages, unsigned nr_pages);

	int (*write_begin)(struct file *, struct address_space *mapping,
				loff_t pos, unsigned len, unsigned flags,
				struct page **pagep, void **fsdata);
	int (*write_end)(struct file *, struct address_space *mapping,
				loff_t pos, unsigned len, unsigned copied,
				struct page *page, void *fsdata);

	/* Unfortunately this kludge is needed for FIBMAP. Don't use it */
	sector_t (*bmap)(struct address_space *, sector_t);
	void (*invalidatepage) (struct page *, unsigned long);
	int (*releasepage) (struct page *, gfp_t);
	void (*freepage)(struct page *);
	ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
			loff_t offset, unsigned long nr_segs);
	int (*get_xip_mem)(struct address_space *, pgoff_t, int,
						void **, unsigned long *);
	/* migrate the contents of a page to the specified target */
	int (*migratepage) (struct address_space *,
			struct page *, struct page *);
	int (*launder_page) (struct page *);
	int (*is_partially_uptodate) (struct page *, read_descriptor_t *,
					unsigned long);
	int (*error_remove_page)(struct address_space *, struct page *);
};

上位レベルのアプリケーションは仮想ファイルシステムとのみ対話するため、特定のファイルシステムに対してどのように自身に関連するファイルシステムの操作を仮想ファイルシステムに追加しますか?例えば、ext 2ファイルシステムについて、彼がどのように自分のfile_をoperations,inode_operations, address_space_operationsはinodeとfile構造に入れますか?
最初の感覚はinodeとfileオブジェクトが生成されたときに、上記の3つの操作オブジェクトの参照を解決することです.カーネルはパス検索時にinodeオブジェクト(たとえば/media/usb/document)を生成します.カーネルは階層ごとの解析ディレクトリを生成し、ルートファイルシステムの下でmediaディレクトリを検索します.ディレクトリがキャッシュにない場合は、下位ファイルシステムから呼び出しinode_をロードします.operations->lookup、mediaの下でusbを検索し、usbからdocumentを検索すると、ディレクトリと対応するinodeオブジェクトがキャッシュされます.上からinodeオブジェクトの生成は、親ディレクトリの特定のファイルシステムの関数lookupを呼び出してサブディレクトリ項目を検索します.ext 2ファイルシステムのUディスクを/media/usbディレクトリにマウントすると、/media/usbディレクトリからdocumentを呼び出すべきext 2ファイルシステムのルートディレクトリのinode_を検索します.operations->lookup操作document操作を検索し、ファイルシステムのルートディレクトリのinode_を解決する方法operationsの引用は?この参照を解決すれば、前例では/media/usbのext 2ファイルシステムがサブディレクトリのinode_を解決するoperations,file_operationsとaddress_space_operationsの参照はext 2のルートディレクトリのinode_を通過することができます.operations->lookupで解決します.したがって、ファイルシステムのルートディレクトリは、自身に関連するすべての操作のエントリであり、ルートディレクトリの関連操作を設定すれば、このファイルシステムをvfsに統合することができる.
ルートディレクトリの関連オブジェクトをどのように設定するかは、ファイルシステムのマウントから説明する必要があります.特定のファイルシステムは、マウント時にのみvfsレイヤとアクティブに相互操作し、自身の関連構造を対応する構造に配置し、他の場合はvfsによって特定のファイルシステムの関連操作をアクティブに使用します.
各ファイルシステムはsuper_に対応しています.blockオブジェクト.このオブジェクトは、ファイルシステムのマウント時にカーネルによって生成されます.
struct super_block {
	struct list_head	s_list;		/* Keep this first */
	dev_t			s_dev;		/* search index; _not_ kdev_t */
	unsigned char		s_dirt;
	unsigned char		s_blocksize_bits;
	unsigned long		s_blocksize;
	loff_t			s_maxbytes;	/* Max file size */
	struct file_system_type	*s_type;
	const struct super_operations	*s_op;
	const struct dquot_operations	*dq_op;
	const struct quotactl_ops	*s_qcop;
	const struct export_operations *s_export_op;
	unsigned long		s_flags;
	unsigned long		s_magic;
	struct dentry		*s_root;
	struct rw_semaphore	s_umount;
	struct mutex		s_lock;
	int			s_count;
	atomic_t		s_active;
#ifdef CONFIG_SECURITY
	void                    *s_security;
#endif
	const struct xattr_handler **s_xattr;

	struct list_head	s_inodes;	/* all inodes */
	struct hlist_bl_head	s_anon;		/* anonymous dentries for (nfs) exporting */
#ifdef CONFIG_SMP
	struct list_head __percpu *s_files;
#else
	struct list_head	s_files;
#endif
	/* s_dentry_lru, s_nr_dentry_unused protected by dcache.c lru locks */
	struct list_head	s_dentry_lru;	/* unused dentry lru */
	int			s_nr_dentry_unused;	/* # of dentry on lru */

	/* s_inode_lru_lock protects s_inode_lru and s_nr_inodes_unused */
	spinlock_t		s_inode_lru_lock ____cacheline_aligned_in_smp;
	struct list_head	s_inode_lru;		/* unused inode lru */
	int			s_nr_inodes_unused;	/* # of inodes on lru */

	struct block_device	*s_bdev;
	struct backing_dev_info *s_bdi;
	struct mtd_info		*s_mtd;
	struct list_head	s_instances;
	struct quota_info	s_dquot;	/* Diskquota specific options */

	int			s_frozen;
	wait_queue_head_t	s_wait_unfrozen;

	char s_id[32];				/* block device name which contents the super_block. Informational name */
	u8 s_uuid[16];				/* UUID */

	void 			*s_fs_info;	/* special filesystem information which the generic filesystem model does not content. Filesystem private info */
	fmode_t			s_mode;

	/* Granularity of c/m/atime in ns.
	   Cannot be worse than a second */
	u32		   s_time_gran;

	/*
	 * The next field is for VFS *only*. No filesystems have any business
	 * even looking at it. You had been warned.
	 */
	struct mutex s_vfs_rename_mutex;	/* Kludge */

	/*
	 * Filesystem subtype.  If non-empty the filesystem type field
	 * in /proc/mounts will be "type.subtype"
	 */
	char *s_subtype;

	/*
	 * Saved mount options for lazy filesystems using
	 * generic_show_options()
	 */
	char __rcu *s_options;
	const struct dentry_operations *s_d_op; /* default d_op for dentries */

	/*
	 * Saved pool identifier for cleancache (-1 means none)
	 */
	int cleancache_poolid;

	struct shrinker s_shrink;	/* per-sb shrinker handle */
};

赤い色が表示されているのはこのファイルシステムのルートで、一般的には'/'で、ファイルシステムがマウントされている間にカーネルがsuper_を生成します.blockオブジェクト、ファイルシステムをマウントするとき、共通のdo_new_mountはnamespaceを呼び出します.c/mount_fs(kernel 3.2.1)操作:
struct dentry *
mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
{
	struct dentry *root;
	struct super_block *sb;
	char *secdata = NULL;
	int error = -ENOMEM;

	。。。。。

	root = type->mount(type, flags, name, data); //           mount  。。
	if (IS_ERR(root)) {
		error = PTR_ERR(root);
		goto out_free_secdata;
	}
	sb = root->d_sb;
	。。。。。
}

linuxは、ブロックデバイスをマウントするmount_など、さまざまなデバイスタイプに共通のマウント操作を定義します.bdev、コールバック関数で自分のsuper_を注文できますblock、例えばext 2のext 2_fill_super_block
static int ext2_fill_super(struct super_block *sb, void *data, int silent)
{

.............
	struct inode *root;
	root = ext2_iget(sb, EXT2_ROOT_INO);
	if (IS_ERR(root)) {
		ret = PTR_ERR(root);
		goto failed_mount3;
	}
	if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) {
		iput(root);
		ext2_msg(sb, KERN_ERR, "error: corrupt root inode, run e2fsck");
		goto failed_mount3;
	}

	sb->s_root = d_alloc_root(root);
.........
}

ext2_igetは様々なファイルタイプのoperationsの参照を解決し、ext 2ファイルシステムのinode_operations->lookupは、ファイルシステムに関連する操作の参照を解決するために使用されます.
struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
{
	。。。。
	if (S_ISREG(inode->i_mode)) { //      
		inode->i_op = &ext2_file_inode_operations;
		if (ext2_use_xip(inode->i_sb)) {
			inode->i_mapping->a_ops = &ext2_aops_xip;
			inode->i_fop = &ext2_xip_file_operations; //file_operations   , open    file  
		} else if (test_opt(inode->i_sb, NOBH)) {
			inode->i_mapping->a_ops = &ext2_nobh_aops;
			inode->i_fop = &ext2_file_operations;
		} else {
			inode->i_mapping->a_ops = &ext2_aops;
			inode->i_fop = &ext2_file_operations;
		}
	} else if (S_ISDIR(inode->i_mode)) { //      
		inode->i_op = &ext2_dir_inode_operations;
		inode->i_fop = &ext2_dir_operations;
		if (test_opt(inode->i_sb, NOBH))
			inode->i_mapping->a_ops = &ext2_nobh_aops;
		else
			inode->i_mapping->a_ops = &ext2_aops;
	} else if (S_ISLNK(inode->i_mode)) { //      
		if (ext2_inode_is_fast_symlink(inode)) {
			inode->i_op = &ext2_fast_symlink_inode_operations;
			nd_terminate_link(ei->i_data, inode->i_size,
				sizeof(ei->i_data) - 1);
		} else {
			inode->i_op = &ext2_symlink_inode_operations;
			if (test_opt(inode->i_sb, NOBH))
				inode->i_mapping->a_ops = &ext2_nobh_aops;
			else
				inode->i_mapping->a_ops = &ext2_aops;
		}
	} else { //         
		inode->i_op = &ext2_special_inode_operations;
		if (raw_inode->i_block[0])
			init_special_inode(inode, inode->i_mode,
			   old_decode_dev(le32_to_cpu(raw_inode->i_block[0])));
		else 
			init_special_inode(inode, inode->i_mode,
			   new_decode_dev(le32_to_cpu(raw_inode->i_block[1])));
	}
	。。。。。。
}

したがって、ルートディレクトリの様々な参照が解決されると、ルートディレクトリのinode_operations->loopup操作で検索されたディレクトリとファイルに関する操作のすべての参照はext 2_を使用できます.iget自動解決.これにより、関連する操作が与えられ、vfsに分類される限り、すべてのものをファイルシステムの範疇にまとめることができる.これにより、vfs上ですべてのアプリケーションが動作し、下位層を無視することが実装される.