• 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,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    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文件系统的根目录 */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    打开文件

    SYSCALL_DEFINE3
        ->do_sys_open
            ->do_sys_openat2
    
    • 1
    • 2
    • 3
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    分配文件描述符

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    解析文件路径

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

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

  • 相关阅读:
    OKHTTP断点续传
    简单的咖啡文化静态HTML网页设计作品 DIV布局咖啡馆文化网页模板代码 DW咖啡网站制作成品
    但凡项目团队有个“二舅”,项目交付哪用得着又焦又愁
    我的2022面试总结(已拿BAT头条网易滴滴亚马逊offer)
    Win10+MX350+CUDA10.2+Python3.9配置Detectron2
    pandas数据分析:pandas基础函数入门教程【w3school学习笔记】
    ICC2/innovus merge gds
    win11系统完全卸载Oracle11g图文详细步骤
    Swin Transformer目标检测实验——环境配置的步骤和避坑
    入职环境安装经验
  • 原文地址:https://blog.csdn.net/wangxiandou/article/details/126673054