Linux内核vfs

一、通用文件系统模型对象

 1、超级块对象 struct super_block

/**
 * 超级块对象
 */
struct super_block {
	/**
	 * 指向超级块链表的指针
	 */
	struct list_head	s_list;		/* Keep this first */
	/**
	 * 设备标识符
	 */
	dev_t			s_dev;		/* search index; _not_ kdev_t */
	/**
	 * 以字节为单位的块大小
	 */
	unsigned long		s_blocksize;
	/**
	 * 基本块设备驱动程序中的以字节为单位的块大小。
	 */
	unsigned long		s_old_blocksize;
	/**
	 * 以位为单位的块大小
	 */
	unsigned char		s_blocksize_bits;
	/**
	 * 脏标志
	 */
	unsigned char		s_dirt;
	/**
	 * 文件的最大长度
	 */
	unsigned long long	s_maxbytes;	/* Max file size */
	/**
	 * 文件系统类型。
	 */
	struct file_system_type	*s_type;
	/**
	 * 超级块方法
	 */
	struct super_operations	*s_op;
	/**
	 * 磁盘限额处理方法
	 */
	struct dquot_operations	*dq_op;
	/**
	 * 磁盘限额管理方法
	 */
 	struct quotactl_ops	*s_qcop;
	/**
	 * NFS使用的输出操作
	 */
	struct export_operations *s_export_op;
	/**
	 * 安装标志
	 */
	unsigned long		s_flags;
	/**
	 * 文件系统的魔数
	 */
	unsigned long		s_magic;
	/**
	 * 文件系统根目录的目录项
	 */
	struct dentry		*s_root;
	/**
	 * 卸载时使用的信号量
	 */
	struct rw_semaphore	s_umount;
	/**
	 * 保护超级块使用的信号量
	 */
	struct semaphore	s_lock;
	/**
	 * 引用计数
	 */
	int			s_count;
	/**
	 * 对超级块的索引节点进行同步的标志
	 */
	int			s_syncing;
	/**
	 * 对超级块的已安装文件系统进行同步的的标志
	 */
	int			s_need_sync_fs;
	/**
	 * 次引用计数。
	 */
	atomic_t		s_active;
	/**
	 * 超级块安全数据结构
	 */
	void                    *s_security;
	/**
	 * 超级块扩展属性结构的指针
	 */
	struct xattr_handler	**s_xattr;

	/**
	 * 所有索引节点链表
	 */
	struct list_head	s_inodes;	/* all inodes */
	/**
	 * 脏索引节点链表
	 */
	struct list_head	s_dirty;	/* dirty inodes */
	/**
	 * 等等写入磁盘的索引节点链表
	 */
	struct list_head	s_io;		/* parked for writeback */
	/**
	 * 匿名目录项链表,用于NFS
	 */
	struct hlist_head	s_anon;		/* anonymous dentries for (nfs) exporting */
	/**
	 * 文件对象链表
	 */
	struct list_head	s_files;

	/**
	 * 指向块设备驱动程序描述符的指针
	 */
	struct block_device	*s_bdev;
	/**
	 * 相同文件类型的超级块对象链表。
	 */
	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];				/* Informational name */

	/**
	 * 指向特定文件系统的超级块信息的指针。各文件系统自定义。
	 * 对ext2来说,是指向一个ext2_sb_info类型的结构。
	 */
	void 			*s_fs_info;	/* Filesystem private info */

	/*
	 * The next field is for VFS *only*. No filesystems have any business
	 * even looking at it. You had been warned.
	 */
	/**
	 * 当VFS通过目录重命名文件时使用的信号量。
	 */
	struct semaphore s_vfs_rename_sem;	/* Kludge */

	/* Granuality of c/m/atime in ns.
	   Cannot be worse than a second */
	/**
	 * c/m/atime的时间戳粒度。
	 */
	u32		   s_time_gran;
};

      存放文件系统相关信息,对于基于磁盘的文件系统该对象对应存放在磁盘上的文件系统控制块;  --》通过链表进行管理,需要回写对磁盘保证数据一致性。

超级块对象的申请:

以ext2为例:

ext2文件系统分配sb的方法:

从ext2文件系统的超级块链表查找与块设备对应的超级块,查找到则直接返回,未查找到则进行申请超级块对象 

static struct super_block *ext2_get_sb(struct file_system_type *fs_type,
	int flags, const char *dev_name, void *data)
{
	/**
	 * get_sb_bdev分配并初始化一个适合于磁盘文件系统的超级块。
	 * ext2_fill_super从ext2磁盘分区读取磁盘超级块。
	 * 对特殊文件系统来说,有一个get_sb_pseudo,get_sb_single及get_sb_nodev。
	 */
	return get_sb_bdev(fs_type, flags, dev_name, data, ext2_fill_super);
}

/**
 * 一般磁盘文件的get_sb函数。
 */
struct super_block *get_sb_bdev(struct file_system_type *fs_type,
	int flags, const char *dev_name, void *data,
	int (*fill_super)(struct super_block *, void *, int))
{
	struct block_device *bdev;
	struct super_block *s;
	int error = 0;

	/**
	 * 打开块设备。获得块设备描述符的指针。
	 */
	bdev = open_bdev_excl(dev_name, flags, fs_type);
	if (IS_ERR(bdev))
		return (struct super_block *)bdev;

	/*
	 * once the super is inserted into the list by sget, s_umount
	 * will protect the lockfs code from trying to start a snapshot
	 * while we are mounting
	 */
	down(&bdev->bd_mount_sem);
	/**
	 * 搜索文件系统的超级块对象链表。如果找到一个与块设备相关的超级块,则返回它的地址,
	 * 否则分配并初始化一个新对象并插入到文件系统链表和超级块全局链表中。
	 */
	s = sget(fs_type, test_bdev_super, set_bdev_super, bdev);
	up(&bdev->bd_mount_sem);
	if (IS_ERR(s))
		goto out;

	if (s->s_root) {/* 不是新的超级块,说明文件系统已经安装。 */
		if ((flags ^ s->s_flags) & MS_RDONLY) {/* 与以前的装载有冲突 */
			up_write(&s->s_umount);
			deactivate_super(s);/* 关闭设备并返回 */
			s = ERR_PTR(-EBUSY);
		}
		goto out;
	} else {
		char b[BDEVNAME_SIZE];

		/**
		 * 复制安装标志。及其他文件系统相关的值。
		 */
		s->s_flags = flags;
		strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id));
		s->s_old_blocksize = block_size(bdev);
		sb_set_blocksize(s, s->s_old_blocksize);
		/**
		 * 每个文件系统传入的fill_super不一样,调用它来访问磁盘上的超级块信息,并填充超级块对象的其他字段。
		 */
		error = fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
		if (error) {
			up_write(&s->s_umount);
			deactivate_super(s);
			s = ERR_PTR(error);
		} else {
			s->s_flags |= MS_ACTIVE;
			bdev_uevent(bdev, KOBJ_MOUNT);
		}
	}

	return s;

out:
	close_bdev_excl(bdev);
	return s;
}

对新申请的超级块对象与磁盘块设备建立关系:该方法将申请sb->s_fs_info结构体,该结构体记录超级块对象对应的磁盘缓冲区地址,而缓冲区与磁盘存储空间对应


/**
 * 从ext2磁盘分区中读取磁盘超级块。
 */
static int ext2_fill_super(struct super_block *sb, void *data, int silent)
{
	struct buffer_head * bh;
	struct ext2_sb_info * sbi;
	struct ext2_super_block * es;
	struct inode *root;
	unsigned long block;
	unsigned long sb_block = get_sb_block(&data);
	unsigned long logic_sb_block;
	unsigned long offset = 0;
	unsigned long def_mount_opts;
	int blocksize = BLOCK_SIZE;
	int db_count;
	int i, j;
	__le32 features;

	/**
	 * 分配一个ext2超级块ext2_sb_info,并放到超级块的s_fs_info
	 */
	sbi = kmalloc(sizeof(*sbi), GFP_KERNEL);
	if (!sbi)
		return -ENOMEM;
	sb->s_fs_info = sbi;
	memset(sbi, 0, sizeof(*sbi));

	/*
	 * See what the current blocksize for the device is, and
	 * use that as the blocksize.  Otherwise (or if the blocksize
	 * is smaller than the default) use the default.
	 * This is important for devices that have a hardware
	 * sectorsize that is larger than the default.
	 */
	blocksize = sb_min_blocksize(sb, BLOCK_SIZE);
	if (!blocksize) {
		printk ("EXT2-fs: unable to set blocksize\n");
		goto failed_sbi;
	}

	/*
	 * If the superblock doesn't start on a hardware sector boundary,
	 * calculate the offset.  
	 */
	if (blocksize != BLOCK_SIZE) {
		logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize;
		offset = (sb_block*BLOCK_SIZE) % blocksize;
	} else {
		logic_sb_block = sb_block;
	}

	/**
	 * 调用bread在缓冲区页中分配一个缓冲区和缓冲区首部。然后从磁盘读入超级块存放在缓冲区。从块设备对应的磁盘获取超级块的缓存区地址,该缓冲区与磁盘对应存储空间进行映射。
	 */
	if (!(bh = sb_bread(sb, logic_sb_block))) {     
		printk ("EXT2-fs: unable to read superblock\n");
		goto failed_sbi;
	}
	/*
	 * Note: s_es must be initialized as soon as possible because
	 *       some ext2 macro-instructions depend on its value
	 */
	es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
	/**
	 * 将缓冲区首部地址存放起来。
	 */
	sbi->s_es = es;
	sb->s_magic = le16_to_cpu(es->s_magic);

	if (sb->s_magic != EXT2_SUPER_MAGIC)
		goto cantfind_ext2;

	/* Set defaults before we parse the mount options */
	def_mount_opts = le32_to_cpu(es->s_default_mount_opts);
	if (def_mount_opts & EXT2_DEFM_DEBUG)
		set_opt(sbi->s_mount_opt, DEBUG);
	if (def_mount_opts & EXT2_DEFM_BSDGROUPS)
		set_opt(sbi->s_mount_opt, GRPID);
	if (def_mount_opts & EXT2_DEFM_UID16)
		set_opt(sbi->s_mount_opt, NO_UID32);
	if (def_mount_opts & EXT2_DEFM_XATTR_USER)
		set_opt(sbi->s_mount_opt, XATTR_USER);
	if (def_mount_opts & EXT2_DEFM_ACL)
		set_opt(sbi->s_mount_opt, POSIX_ACL);
	
	if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_PANIC)
		set_opt(sbi->s_mount_opt, ERRORS_PANIC);
	else if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_RO)
		set_opt(sbi->s_mount_opt, ERRORS_RO);

	sbi->s_resuid = le16_to_cpu(es->s_def_resuid);
	sbi->s_resgid = le16_to_cpu(es->s_def_resgid);
	
	if (!parse_options ((char *) data, sbi))
		goto failed_mount;

	sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
		((EXT2_SB(sb)->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ?
		 MS_POSIXACL : 0);

	if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV &&
	    (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) ||
	     EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) ||
	     EXT2_HAS_INCOMPAT_FEATURE(sb, ~0U)))
		printk("EXT2-fs warning: feature flags set on rev 0 fs, "
		       "running e2fsck is recommended\n");
	/*
	 * Check feature flags regardless of the revision level, since we
	 * previously didn't change the revision level when setting the flags,
	 * so there is a chance incompat flags are set on a rev 0 filesystem.
	 */
	features = EXT2_HAS_INCOMPAT_FEATURE(sb, ~EXT2_FEATURE_INCOMPAT_SUPP);
	if (features) {
		printk("EXT2-fs: %s: couldn't mount because of "
		       "unsupported optional features (%x).\n",
		       sb->s_id, le32_to_cpu(features));
		goto failed_mount;
	}
	if (!(sb->s_flags & MS_RDONLY) &&
	    (features = EXT2_HAS_RO_COMPAT_FEATURE(sb, ~EXT2_FEATURE_RO_COMPAT_SUPP))){
		printk("EXT2-fs: %s: couldn't mount RDWR because of "
		       "unsupported optional features (%x).\n",
		       sb->s_id, le32_to_cpu(features));
		goto failed_mount;
	}

	blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);

	/* If the blocksize doesn't match, re-read the thing.. */
	if (sb->s_blocksize != blocksize) {
		brelse(bh);

		if (!sb_set_blocksize(sb, blocksize)) {
			printk(KERN_ERR "EXT2-fs: blocksize too small for device.\n");
			goto failed_sbi;
		}

		logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize;
		offset = (sb_block*BLOCK_SIZE) % blocksize;
		bh = sb_bread(sb, logic_sb_block);
		if(!bh) {
			printk("EXT2-fs: Couldn't read superblock on "
			       "2nd try.\n");
			goto failed_sbi;
		}
		es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
		sbi->s_es = es;
		if (es->s_magic != cpu_to_le16(EXT2_SUPER_MAGIC)) {
			printk ("EXT2-fs: Magic mismatch, very weird !\n");
			goto failed_mount;
		}
	}

	sb->s_maxbytes = ext2_max_size(sb->s_blocksize_bits);

	if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV) {
		sbi->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
		sbi->s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
	} else {
		sbi->s_inode_size = le16_to_cpu(es->s_inode_size);
		sbi->s_first_ino = le32_to_cpu(es->s_first_ino);
		if ((sbi->s_inode_size < EXT2_GOOD_OLD_INODE_SIZE) ||
		    (sbi->s_inode_size & (sbi->s_inode_size - 1)) ||
		    (sbi->s_inode_size > blocksize)) {
			printk ("EXT2-fs: unsupported inode size: %d\n",
				sbi->s_inode_size);
			goto failed_mount;
		}
	}

	sbi->s_frag_size = EXT2_MIN_FRAG_SIZE <<
				   le32_to_cpu(es->s_log_frag_size);
	if (sbi->s_frag_size == 0)
		goto cantfind_ext2;
	sbi->s_frags_per_block = sb->s_blocksize / sbi->s_frag_size;

	sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);
	sbi->s_frags_per_group = le32_to_cpu(es->s_frags_per_group);
	sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);

	if (EXT2_INODE_SIZE(sb) == 0)
		goto cantfind_ext2;
	sbi->s_inodes_per_block = sb->s_blocksize / EXT2_INODE_SIZE(sb);
	if (sbi->s_inodes_per_block == 0)
		goto cantfind_ext2;
	sbi->s_itb_per_group = sbi->s_inodes_per_group /
					sbi->s_inodes_per_block;
	sbi->s_desc_per_block = sb->s_blocksize /
					sizeof (struct ext2_group_desc);
	sbi->s_sbh = bh;
	sbi->s_mount_state = le16_to_cpu(es->s_state);
	sbi->s_addr_per_block_bits =
		log2 (EXT2_ADDR_PER_BLOCK(sb));
	sbi->s_desc_per_block_bits =
		log2 (EXT2_DESC_PER_BLOCK(sb));

	if (sb->s_magic != EXT2_SUPER_MAGIC)
		goto cantfind_ext2;

	if (sb->s_blocksize != bh->b_size) {
		if (!silent)
			printk ("VFS: Unsupported blocksize on dev "
				"%s.\n", sb->s_id);
		goto failed_mount;
	}

	if (sb->s_blocksize != sbi->s_frag_size) {
		printk ("EXT2-fs: fragsize %lu != blocksize %lu (not supported yet)\n",
			sbi->s_frag_size, sb->s_blocksize);
		goto failed_mount;
	}

	if (sbi->s_blocks_per_group > sb->s_blocksize * 8) {
		printk ("EXT2-fs: #blocks per group too big: %lu\n",
			sbi->s_blocks_per_group);
		goto failed_mount;
	}
	if (sbi->s_frags_per_group > sb->s_blocksize * 8) {
		printk ("EXT2-fs: #fragments per group too big: %lu\n",
			sbi->s_frags_per_group);
		goto failed_mount;
	}
	if (sbi->s_inodes_per_group > sb->s_blocksize * 8) {
		printk ("EXT2-fs: #inodes per group too big: %lu\n",
			sbi->s_inodes_per_group);
		goto failed_mount;
	}

	if (EXT2_BLOCKS_PER_GROUP(sb) == 0)
		goto cantfind_ext2;
	sbi->s_groups_count = (le32_to_cpu(es->s_blocks_count) -
				        le32_to_cpu(es->s_first_data_block) +
				       EXT2_BLOCKS_PER_GROUP(sb) - 1) /
				       EXT2_BLOCKS_PER_GROUP(sb);
	db_count = (sbi->s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
		   EXT2_DESC_PER_BLOCK(sb);
	/**
	 * 分配一个数组,用于存放缓冲区首部指针。每个组描述符一个。
	 */
	sbi->s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL);
	if (sbi->s_group_desc == NULL) {
		printk ("EXT2-fs: not enough memory\n");
		goto failed_mount;
	}
	percpu_counter_init(&sbi->s_freeblocks_counter);
	percpu_counter_init(&sbi->s_freeinodes_counter);
	percpu_counter_init(&sbi->s_dirs_counter);
	bgl_lock_init(&sbi->s_blockgroup_lock);
	/**
	 * 分配一个字节数据,并将其存到s_debts中,用于创建索引节点。
	 */
	sbi->s_debts = kmalloc(sbi->s_groups_count * sizeof(*sbi->s_debts),
			       GFP_KERNEL);
	if (!sbi->s_debts) {
		printk ("EXT2-fs: not enough memory\n");
		goto failed_mount_group_desc;
	}
	memset(sbi->s_debts, 0, sbi->s_groups_count * sizeof(*sbi->s_debts));
	/**
	 * 重复调用__bread分配缓冲区,并从磁盘读入包含ext2组描述符的块。
	 */
	for (i = 0; i < db_count; i++) {
		block = descriptor_loc(sb, logic_sb_block, i);
		sbi->s_group_desc[i] = sb_bread(sb, block);
		if (!sbi->s_group_desc[i]) {
			for (j = 0; j < i; j++)
				brelse (sbi->s_group_desc[j]);
			printk ("EXT2-fs: unable to read group descriptors\n");
			goto failed_mount_group_desc;
		}
	}
	if (!ext2_check_descriptors (sb)) {
		printk ("EXT2-fs: group descriptors corrupted!\n");
		db_count = i;
		goto failed_mount2;
	}
	sbi->s_gdb_count = db_count;
	get_random_bytes(&sbi->s_next_generation, sizeof(u32));
	spin_lock_init(&sbi->s_next_gen_lock);
	/*
	 * set up enough so that it can read an inode
	 */
	sb->s_op = &ext2_sops;
	sb->s_export_op = &ext2_export_ops;
	sb->s_xattr = ext2_xattr_handlers;
	root = iget(sb, EXT2_ROOT_INO);
	sb->s_root = d_alloc_root(root);
	if (!sb->s_root) {
		iput(root);
		printk(KERN_ERR "EXT2-fs: get root inode failed\n");
		goto failed_mount2;
	}
	if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) {
		dput(sb->s_root);
		sb->s_root = NULL;
		printk(KERN_ERR "EXT2-fs: corrupt root inode, run e2fsck\n");
		goto failed_mount2;
	}
	if (EXT2_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL))
		ext2_warning(sb, __FUNCTION__,
			"mounting ext3 filesystem as ext2\n");
	ext2_setup_super (sb, es, sb->s_flags & MS_RDONLY);
	percpu_counter_mod(&sbi->s_freeblocks_counter,
				ext2_count_free_blocks(sb));
	percpu_counter_mod(&sbi->s_freeinodes_counter,
				ext2_count_free_inodes(sb));
	percpu_counter_mod(&sbi->s_dirs_counter,
				ext2_count_dirs(sb));
	return 0;

cantfind_ext2:
	if (!silent)
		printk("VFS: Can't find an ext2 filesystem on dev %s.\n",
		       sb->s_id);
	goto failed_mount;

failed_mount2:
	for (i = 0; i < db_count; i++)
		brelse(sbi->s_group_desc[i]);
failed_mount_group_desc:
	kfree(sbi->s_group_desc);
	kfree(sbi->s_debts);
failed_mount:
	brelse(bh);
failed_sbi:
	sb->s_fs_info = NULL;
	kfree(sbi);
	return -EINVAL;
}

2、索引节点对象 struct inode

     存放具体文件的信息,基于磁盘的文件系统该对象对应存放在磁盘上的文件控制块,每个索引节点对象对应文件系统中的一个文件;---》通过链表,包括未使用、正在使用、已使用链表进行管理,同时也通过inode_hashtable的双向哈希链表进行管理

3、文件对象 struct file

     进程打开文件系统中的文件后在内核内存中申请该对象用于对文件系统中的文件进行控制,存放在内存中;----》从名为filep的slab分配器缓存中进行申请对象,每个进程都需要通过该对象去访问文件系统中的文件,其在磁盘上无对应的映象;

4、目录项对象 struct dentry

    Linux内核的内存中存放目录项与文件的链接信息的对象,对应磁盘文件系统的目录组织相关信息,从dentry_cache的slab分配器高速缓存中进行获取对象;--》目录项对象在磁盘无对应的映像;

二、目录项高速缓存  》》当目录项对象在内存中创建后,在内存中进行保留

由目录项对象集合和对目录项进行快速索引的散列表组成;

由于目录项高速缓存需要访问相关索引节点,故对应索引节点也需要进行缓存;

所有未使用的目录项对象都保存在LRU双向链表,往链表头加对象,当内存不够时从链表尾部删除元素;

当目录项指向文件的硬链接全部删除时,设置为负状态,并将其入未使用链表,在需要删除目录项缓存时,将负状态的目录项向链表尾部移动

三、与进程相关的文件

1、进程描述符的fs字段(struct fs_struct)

      记录进程与文件系统交互的相关信息,比如进程工作目录、进程根目录,挂载的文件系统、访问文件的权限;

2、进程描述符的files字段 (struct files_struct)

     对进程打开的文件进行管理的字段,包括文件对象指针数组、打开的文件个数、当前使用的最大文件描述符等;

三、文件类型

        内核给每个安装的特殊文件系统分配一个虚拟块设备,主设备号为0,次设备号为任意值;

        除网络和磁盘文件系统以外的文件系统称为特殊文件系统,普通文件系统和特殊文件系统均通过文件系统类型注册来实现,用file_system_type对象来表示,其成员fs_supers记录该文件系统类型的超级块对象链表的头,注册的对象添加到file_systems链表;

      

四、文件系统处理

1、根文件系统:在内核引导阶段直接安装,并拥有系统初始化脚本和最基本的系统程序;

2、每个文件系统都有自己的根目录,也即文件系统的安装点;

3、已安装文件系统的根目录会将父文件系统的安装点目录原来的内容隐藏,即同一安装点上的新安的文件系统将隐藏前一个安装的文件系统,但是使用先前安装下的文件和目录的进程可以继续使用,当安装被删除时,先前的安装重新可见;

五、命名空间

        每个进程可拥有自己的已安装文件系统树,即进程的命名空间,由进程描述符的namespace字段描述;通常大多数进程共享init进程的命名空间;

        当进程安装卸载一个文件系统时,仅修改自己的命名空间,所做的修改对共享同一命名空间的所有进程都可见;

       namespace结构体字段通过struct vfsmount *root字段记录进程命名空间根目录的安装的文件系统;通过struct list_head list链表记录安装在进程命名空间内所有的文件系统描述符;

六、文件系统安装

        同一文件系统可被多次安装,但是仅有一个超级块对象;

        文件系统描述符vfsmount:记录安装点和安装标记,要安装文件系统与其他已安装文件系统的关系;

        vfsmount的保存:

            1、存放在由vfsmount地址和安装点目录项对象的地址的索引散列表中;

            2、进程的namespace的list字段记录所有属于此命名空间的已安装文件系统的vfsmount双向链表的头部,vfsmount的mnt_list字段记录其在链表中的相邻元素的指针;

            3、对每个已安装的文件系统,通过双向循环链表记录其所有已安装的子文件系统,链表头部存放在已安装文件系统描述符的mnt_mounts字段,mnt_child字段存放指向相邻元素的地址;


/**
 * 文件系统安装点
 */
struct vfsmount
{
	/**
	 * 用于散列表链表的指针。
	 */
	struct list_head mnt_hash;
	/**
	 * 指向父文件系统,这个文件系统安装在其上。
	 */
	struct vfsmount *mnt_parent;	/* fs we are mounted on */
	/**
	 * 安装点目录节点。
	 */
	struct dentry *mnt_mountpoint;	/* dentry of mountpoint */
	/**
	 * 指向这个文件系统根目录的dentry。
	 */
	struct dentry *mnt_root;	/* root of the mounted tree */
	/**
	 * 该文件系统的超级块对象。
	 */
	struct super_block *mnt_sb;	/* pointer to superblock */
	/**
	 * 包含所有文件系统描述符链表的头
	 */
	struct list_head mnt_mounts;	/* list of children, anchored here */
	/**
	 * 已安装文件系统链表头。通过此字段将其加入父文件系统的mnt_mounts链表中。
	 */
	struct list_head mnt_child;	/* and going through their mnt_child */
	/**
	 * 引用计数器,禁止文件系统被卸载。
	 */
	atomic_t mnt_count;
	/**
	 * mount标志
	 */
	int mnt_flags;
	/**
	 * 如果文件系统标记为过期,就设置这个标志。
	 */
	int mnt_expiry_mark;		/* true if marked for expiry */
	/**
	 * 设备文件名。
	 */
	char *mnt_devname;		/* Name of device e.g. /dev/dsk/hda1 */
	/**
	 * 已安装文件系统描述符的namespace链表指针?
	 * 通过此字段将其加入到namespace的list链表中。
	 */
	struct list_head mnt_list;
	/**
	 * 文件系统到期链表指针。
	 */
	struct list_head mnt_fslink;	/* link in fs-specific expiry list */
	/**
	 * 进程命名空间指针
	 */
	struct namespace *mnt_namespace; /* containing namespace */
};

七、安装普通文件系统

       在已有的文件系统上安装新文件系统;

       通过系统调用mount实现:

      1、查找指定的文件系统安装点的目录项对象;

          查找每一级目录的目录项对象(先从目录项缓存中查找,未查找到从磁盘块设备进行查找),从目录项对象查找下一级目录对应的目录项(调用__d_lookup通过hash值索引目录对应的子目录高速缓存链表,遍历该链表比较目录名查找下一级目录项对象结构体,若为查找到则调用real_lookup从块设备查找磁盘对应的目录项对象),然后继续循环

      2、申请vfsmount结构体;

      3、申请超级块对象,并将超级块对象记录到文件系统的类型fd_supers链表,vfsmount的mnt_sb链表、进程namespace的已安装文件系统链表、父文件系统链表

八、安装根文件系统

1、首先安装基于内存的rootfs文件系统(由于在此之前还没有文件系统,故不能通过文件系统索引实际存储介质的块设备对应的目录去安装基于实际存储介质的实际文件系统,需要等到start_kernel初始化vfs安装根文件系统后,起init进程,在init进程中调用init_call时进行设备初始化后再进行实际文件系统的安装),该文件系统不可卸载,而实际根文件系统可以卸载。

2、根据内核的启动参数root指定的设备文件名,从注册的文件系统类型调用get_sb来获取块设备对应的sb对象,若成功获取到,则为要使用的文件系统类型,将该文件系统类型进行安装。

九、文件锁

文件锁通过struct file_lock结构体进行管理,文件对应的inode的struct file_lock *i_flock成员链表记录所有的文件锁,进程进行加锁操作时,检查该链表上是否有其他互斥的文件锁,然后判断是否进行加锁,若加锁成功则将file_lock添加到该链表,若加锁失败且进程可阻塞则将进程添加到lock->fl_wait队列进程被唤醒时再将其加入到文件锁链表,否则释放file_lock对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

picaso77

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值