上一篇说到ext2文件系统删除文件是逻辑删除,对应文件系统的unlink操作,其实就是解除磁盘目录项与inode关系,同时删除目录项(也是逻辑删除,与前一项合并)。那么,同样删除目录跟删除文件类似,ext2也是做了一次unlink操作。详细的删除目录流程如下:
- ① 检查目录是否为空(除了.和..之外,是否还有其它文件),如果目录不空有文件,返回-ENOTEMPTY,无法删除(相关函数:ext2_empty_dir())
- ② 如果目录为空,调用ext2_unlink()函数解除link
上一篇说到ext2文件系统删除文件是逻辑删除,对应文件系统的unlink操作,其实就是解除磁盘目录项与inode关系,同时删除目录项(也是逻辑删除,与前一项合并)。那么,同样删除目录跟删除文件类似,ext2也是做了一次unlink操作。详细的删除目录流程如下:
前面几篇博客讲了创建文件、创建目录、查找文件,接下来说一下ext2文件系统是如何删除文件,ext2文件系统删除文件,并没有在物理设备上擦除文件,只是做了一个unlink操作,所谓的逻辑删除。unlink的具体实现,就是解除目录项与inode关系,这样目录项就不会关联到inode,就无法查找到文件,达到了删除的目的。unlink的具体的流程如下:
在打开文件、列举文件或者stat()操作时,用户端给的是文件路径信息,内核文件系统需要根据路径,查找到对应的inode,这样才能生成file对象,才能进行后续的读写等操作。所以,文件查找是一个基础能力,是其他文件操作的前置条件。
所谓查找文件,就是找到路径对应的dentry项,通过dentry项找到对应inode信息,有了inode才能对文件进行其他操作。文件查找时,优先在dcache缓存中查找,如果没有找到,需要到磁盘上进行遍历。
在dcache中查找,是VFS层面的基本动作,dcache如果没有找到,VFS就会调用具体文件系统的lookup()函数,到磁盘上进行遍历。以ext2文件系统为例,lookup具体流程如下:
创建文件是文件系统的基本操作,之前在介绍Linux文件系统VFS时,说过创建文件会调用inode操作表中的create()函数,那create函数具体应该如何实现呢?
文件系统在创建文件时,VFS会调用父目录目录的create()函数,在这个函数中要完成具体的文件创建,以ext2文件系统为例,ext2注册的创建文件的函数指针是ext2_create,在ext2文件系统创建文件,一般包括以下几个步骤:
上一篇介绍了ext2注册文件系统注册的流程,其中提到在注册文件系统时,给VFS传递了file_system_type结构体,在这个结构体中,包含了挂载函数指针ext2_mount(),用于挂载的回调操作。
在ext2_mount()函数实现中,调用了VFS的mount_bdev()来挂载块设备,这个mount_bdev()函数最后一个参数,是一个函数指针,VFS使用此函数完成超级块的构造和填充,而超级块是文件系统核心数据结构,它的构造是构建文件系统主要流程,本文就重点梳理一下超级块的构造,以下是mount_bdev()函数原型:
dentry *mount_bdev(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data,
int (*fill_super)(struct super_block *, void *, int));
继续阅读“02 ext2如何构造超级块super_block” struct file是Linux文件系统中的一个重要数据结构,用于表示进程打开的文件,也就是文件对象,struct file是已打开的文件在内存中的表示,存储了与文件操作和状态相关的信息。
通过file对象就可以获取当前文件的目录项和索引节点,通过files_struct这个纽带,就把进程了和文件系统对接起来了。那么文件系统给进程呈现的是什么信息呢?
继续阅读“010 Linux文件系统数据结构详解:文件对象struct file”前面介绍了超级块super_block、索引节点inode、目录项dentry等核心数据结构,接下来介绍文件系统挂载相关的mount和vfsmount结构体。
文件系统挂载就是将一个文件系统安装到全局文件结构树的一个分支上,文件系统挂载能力是VFS对外提供的统一的接口,实际挂载时会调用具体文件系统的实现(mount函数),这个具体实现逻辑是注册文件系统时,通过文件系统类型file_system_type结构体传递给VFS。挂载文件系统,VFS会回调具体文件系统的挂载函数mount(),mount()函数会构造超级块和根dentry,函数的返回值就是根dentry指针。
struct file_system_type {
const char *name;
int fs_flags;
int (*init_fs_context)(struct fs_context *);
const struct fs_parameter_spec *parameters;
struct dentry *(*mount) (struct file_system_type *, int,
const char *, void *);
void (*kill_sb) (struct super_block *);
struct module *owner;
struct file_system_type * next;
struct hlist_head fs_supers;
......
};
继续阅读“009 Linux文件系统数据结构详解:挂载点mount和vfsmount” 前两篇博客看了超级块和索引节点的数据结构,接下来研究一下目录项dentry的结构。目录项设计的目的是构建完整的目录树,这样可以快速找到文件对应的inode,帮助VFS完成文件操作。具体过程如下:
用户程序在调用open()、stat()、chmod()等函数时,传递文件路径信息到VFS,VFS需要根据文件路径找到对应的inode,所以VFS用文件名和目录项缓存一层一层比对,直到找到对应的目录项,进而获取inode信息。如果在缓存中没有命中,VFS将会新创建文件的inode和对应的目录项,并且持久化到磁盘上。
继续阅读“008 Linux文件系统数据结构详解:目录项dentry”上一篇介绍了虚拟文件系统VFS的基本数据结构,包括:文件系统类型file_system_type、超级块super_block、索引节点inode、目录项dentry和文件对象file。本篇文章着重看一下与进程相关的数据结构,包括files_struct和fs_struct,两者通过task_struct(进程)结构体粘结起来,通过fs_struct可以获取文件系统、挂载点、根dentry等信息,通过files_struct可以打开的文件对象,进而获取目录项、索引节点,最后和物理文件建立联系。
继续阅读“004 虚拟文件系统VFS与进程相关的数据结构”Linux支持多种文件系统,这些具体的文件系统并不是直接都挂载到系统中的,实际上是按需挂载的,这些文件系统只有挂载到系统中,VFS才能够真正管理起来,否则如果只是注册,不会产生任何文件系统实例。
文件系统挂载(安装)后,将会产生多个数据结构实例,挂载过程实际上就是构造这些数据结构实例的过程。Linux在文件系统的设计中,汲取了Unix的设计思想,抽象出四个基本数据结构:超级块(super_block),索引节点(inode),目录项(directory entry)和文件对象(file)