Linux文件系统

Linux文件系统

用户层面

在linux系统中,一切皆文件,除了通常所说的狭义的文件(文本文件和二进制文件)以外,目录、设备、套接字和管道等都是文件
->在存储设备上组织文件的方法,包括数据结构和访问方法
->按照某种文件系统类型格式化的一块存储介质。就是我们常说的在某个目录下载或卸载文件系统
->内核中负责管理和储存文件的模块,即文件系统模块

在这里插入图片描述

用户空间层面

  1. 一个存储设备上的文件系统,只有挂载到内存中目录树的某个目录下,进程才能访问这个系统,系统调用mount命令 mount -t fstype device dir,把文件系统挂载到某个目录下
  2. 系统调用umount用来卸载某个目录下挂载的文件系统,可以执行命令 mount dir,来卸载文件系统
  3. 使用open打开文件
  4. 使用close关闭文件
  5. 使用read读文件
  6. 使用lseek设置文件偏移

硬件层面

外部存储设备分为块设备、闪存和NVDIMM

  1. 机械硬盘
  2. 闪存类块设备

内核空间层面

内核支持多种文件系统类型,为了对用户程序提供统一的文件操作接口,为了使不同的文件系统实现能够共存,内核实现了一个抽象层,称为虚拟文件系统(Virtual File System,VFS),文件系统分为4种:

  1. 块设备文件系统,存储设备是机械硬盘和固态硬盘等块设备,通常块设备文件系统EXT和btrfs
  2. 闪存文件系统,存储设备是NAND闪存和NOR闪存,常用JFFS2、UBIFS
  3. 内存文件系统,文件在内存种,断电以后文件丢失,常用内存文件系统tmpfs
  4. 伪文件系统,是假的文件系统。如:sockfs、proc、sysfs、hugetlbfs、cgroup

虚拟文件系统的数据结构

  1. 超级块
  2. 挂载描述符
  3. 文件系统类型
  4. 索引节点
  5. 目录项

在这里插入图片描述
在这里插入图片描述

挂载文件系统

命令 mount -t fstype [-o options] device dir 执行流程如下
在这里插入图片描述

  1. 调用user_path,根据目录名称找到挂载描述符和dentry实例
  2. 调用get_fs_type,根据文件系统类型的名称查找file_system_type实例
  3. 调用alloc_vfsmnt,分配挂载描述符
  4. 调用文件系统类型的挂载方法,读取并且解析超级快
  5. 把挂载描述符添加到超级块的挂载实例链表中
  6. 把挂载描述符加入散列表
  7. 把挂载描述符加入父亲的孩子链表

挂载根文件系统

注册rootfs文件系统
init/do_mounts.c

struct file_system_type rootfs_fs_type = {
	.name		= "rootfs",
	.init_fs_context = rootfs_init_fs_context,
	.kill_sb	= kill_litter_super,
};
static void __init init_mount_tree(void)
{
	struct vfsmount *mnt;
	struct mount *m;
	struct mnt_namespace *ns;
	struct path root;
    /* 挂接rootfs文件系统 */
	mnt = vfs_kern_mount(&rootfs_fs_type, 0, "rootfs", NULL);
	if (IS_ERR(mnt))
		panic("Can't create rootfs");

	ns = alloc_mnt_ns(&init_user_ns, false); /* 创建第一个挂载命名空间 */
	if (IS_ERR(ns))
		panic("Can't allocate initial namespace");
	m = real_mount(mnt);
	m->mnt_ns = ns;
	ns->root = m;
	ns->mounts = 1;
	list_add(&m->mnt_list, &ns->list);
	init_task.nsproxy->mnt_ns = ns; /* 设置0号线程的挂载命名空间 */
	get_mnt_ns(ns);

	root.mnt = mnt;
	root.dentry = mnt->mnt_root;
	mnt->mnt_flags |= MNT_LOCKED;

	set_fs_pwd(current->fs, &root); /* 把0号线程的当前工作目录设置为rootfs文件系统的根目录 */
	set_fs_root(current->fs, &root); /* 把0号线程的根目录设置为rootfs文件系统的根目录 */
}

打开文件

SYSCALL_DEFINE3
    ->do_sys_open
        ->do_sys_openat2
static long do_sys_openat2(int dfd, const char __user *filename,
			   struct open_how *how)
{
	struct open_flags op;
	int fd = build_open_flags(how, &op); /* 把标志位分类为打开标志位、访问模式、意图和查找标志位,保存到结构体open_flags中 */
	struct filename *tmp;

	if (fd)
		return fd;

	tmp = getname(filename); /* 把文件路径从用户空间的缓冲区复制都内核空间的缓冲区 */
	if (IS_ERR(tmp))
		return PTR_ERR(tmp);

	fd = get_unused_fd_flags(how->flags); /* 分配文件描述符 */
	if (fd >= 0) {
		struct file *f = do_filp_open(dfd, tmp, &op); /* 解析文件路径并得到文件的索引节点,创建文件的一个打开实例,把打开实例关联都索引节点 */
		if (IS_ERR(f)) {
			put_unused_fd(fd);
			fd = PTR_ERR(f);
		} else {
			fsnotify_open(f); /* 通告打开文件事件,进程可以使用inotify监视文件系统的事件 */
			fd_install(fd, f); /* 把打开文件的打开实例添加到进程的打开文件表中 */
		}
	}
	putname(tmp);
	return fd;
}

分配文件描述符

static int alloc_fd(unsigned start, unsigned end, unsigned flags)
{
	struct files_struct *files = current->files;
	unsigned int fd;
	int error;
	struct fdtable *fdt;

	spin_lock(&files->file_lock);
repeat:
	fdt = files_fdtable(files);
	fd = start;
	if (fd < files->next_fd) /* 开始尝试分配文件描述符 */
		fd = files->next_fd;

	if (fd < fdt->max_fds) /* 如果fd小于打开文件描述符表的大小,那么在打开文件描述符位图中查找一个空闲的文件描述符 */
		fd = find_next_fd(fdt, fd);

	/*
	 * N.B. For clone tasks sharing a files structure, this test
	 * will limit the total number of files that can be opened.
	 */
	error = -EMFILE; /* 如果进程打开的文件数量达到限制,那么返回-EMFILE */
	if (fd >= end)
		goto out;

	error = expand_files(files, fd); /* 如果当前的打开的文件表已经分配完文件描述符,那么扩大打开文件表 */
	if (error < 0)
		goto out;

	/*
	 * If we needed to expand the fs array we
	 * might have blocked - try again.
	 *//* 如果打开的文件表被扩大了,那么重新尝试分配文件描述符 */
	if (error)
		goto repeat;

	if (start <= files->next_fd)
		files->next_fd = fd + 1; /* 记录下次分配文件描述开始尝试的位置 */

	__set_open_fd(fd, fdt); /* 在文件描述符位图中记录fd已被分配 */
	if (flags & O_CLOEXEC)
		__set_close_on_exec(fd, fdt);
	else
		__clear_close_on_exec(fd, fdt);
	error = fd;
#if 1
	/* Sanity check */
	if (rcu_access_pointer(fdt->fd[fd]) != NULL) {
		printk(KERN_WARNING "alloc_fd: slot %d not NULL!\n", fd);
		rcu_assign_pointer(fdt->fd[fd], NULL);
	}
#endif

out:
	spin_unlock(&files->file_lock);
	return error;
}

解析文件路径

struct file *do_filp_open(int dfd, struct filename *pathname, const struct open_flags *op)
{
	struct nameidata nd;
	int flags = op->lookup_flags;
	struct file *filp;

	set_nameidata(&nd, dfd, pathname, NULL);
	filp = path_openat(&nd, op, flags | LOOKUP_RCU);
	if (unlikely(filp == ERR_PTR(-ECHILD)))
		filp = path_openat(&nd, op, flags);
	if (unlikely(filp == ERR_PTR(-ESTALE)))
		filp = path_openat(&nd, op, flags | LOOKUP_REVAL);
	restore_nameidata();
	return filp;
}

系统调用读/写文件
在这里插入图片描述
以ext4文件系统为例
ex4_file_read_iter
在这里插入图片描述
ext4_dio_write_iter
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值