在linux系统中,一切皆文件,除了通常所说的狭义的文件(文本文件和二进制文件)以外,目录、设备、套接字和管道等都是文件
->在存储设备上组织文件的方法,包括数据结构和访问方法
->按照某种文件系统类型格式化的一块存储介质。就是我们常说的在某个目录下载或卸载文件系统
->内核中负责管理和储存文件的模块,即文件系统模块
外部存储设备分为块设备、闪存和NVDIMM
内核支持多种文件系统类型,为了对用户程序提供统一的文件操作接口,为了使不同的文件系统实现能够共存,内核实现了一个抽象层,称为虚拟文件系统(Virtual File System,VFS),文件系统分为4种:
命令 mount -t fstype [-o options] device dir 执行流程如下
注册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