聊聊文件系统的原理

发布时间 - 2025-07-15 00:00:00    点击率:

文件系统在操作系统中扮演着关键角色,为用户和应用程序提供了对文件进行操作的基础能力。在linux内核中,进程管理、内存管理、网络协议栈、文件系统被视为内核的四大核心模块。文件系统在内核中分为vfs(虚拟文件系统)和实际文件系统(如ext4)。vfs作为一种工厂设计模式的抽象层,向外提供标准的posix语义层;而实际文件系统则实现了特定文件功能的磁盘文件系统。具体结构如下图所示:

文件系统的IO协议栈中,应用程序如果以dio方式读写文件,请求首先经过内核的vfs,然后到达实际文件系统的处理函数,接着请求进入设备映射,最后传递到块设备的IO层。在这里,调度算法和IO优化也会进行处理。块设备IO层处理完毕后,请求直接到达磁盘的驱动层,这一层通过调用IO命令对磁盘进行读写操作。

在Linux中,设计理念是“一切皆文件”,这种理念在文件系统设计中得到了充分体现。内核将目录视为文件来处理。文件的inode中存储的是文件对应的数据块索引和数据,而目录的inode则存储该目录下的文件的inode和文件名称,虽然都是inode,但存储的数据不同。文件系统中的所有目录名称和文件名称存在于vfs层(仅为内存结构展示),这个结构以struct dentry表示,而文件或目录以struct inode表示。

每个打开的文件在内核进程中以文件描述符存在,每个进程维护一个数组,这个数组的下标就是返回给应用的文件描述符,数组中的每个元素对应的是struct filestruct file中保存了struct dentry,而struct dentry中包含文件的inode信息。具体关系如下:

vfs层提供标准文件操作的函数接口,具体的文件操作函数由实际文件系统提供。针对应用程序访问文件系统,比如执行echo "aaa" > 1.txt命令,cat命令会先根据服务目录查找文件,然后读取1.txt的文件数据,这涉及到部分POSIX函数。整个echo命令在文件系统层面(vfs和实际文件系统)会经历lookup->open->write->close的过程。接下来将重点分析write语义的函数,以内核4.18为例进行分析。

实际文件系统会定义针对文件或目录的相关操作函数,每个inode会有const struct inode_operations *i_opconst struct file_operations *i_fop,实际文件系统的相关操作函数会在__ext4_iget中为对应的inode进行赋值。以下是针对ext4本地文件系统的示例代码:

// 这里是以 ext4本地文件系统为例
const struct inode_operations ext4_dir_inode_operations = {
    // 文件创建函数
    .create = ext4_create,
    // 查找函数
    .lookup = ext4_lookup,
    /****** 省略其他的定义的函数*****/
};

const struct file_operations ext4_file_operations = { .llseek = ext4_llseek, // 读函数 .read_iter = ext4_file_read_iter, // 写函数 .write_iter = ext4_file_write_iter, .unlocked_ioctl = ext4_ioctl,

ifdef CONFIG_COMPAT

.compat_ioctl = ext4_compat_ioctl,
#endif
.mmap = ext4_file_mmap,
.mmap_supported_flags = MAP_SYNC,
// open函数对应实际文件系统的函数
.open = ext4_file_open,
.release = ext4_release_file,
.fsync = ext4_sync_file,
.get_unmapped_area = thp_get_unmapped_area,
.splice_read = generic_file_splice_read,
.splice_write = iter_file_splice_write,
.fallocate = ext4_fallocate,

};

当用户程序发起write的POSIX语义时,请求会进入内核的ksys_write->vfs_write->ext4_file_write_iter来完成这个write操作。整体流程如下:

以下是ksys_writevfs_writevfs_write的示例代码:

// write函数进入内核态的ksys_write
// fd是已经打开的文件描述符,buf是需要写入的数据,count是写入的长度
ssize_t ksys_write(unsigned int fd, const char user *buf, size_t count) {
// f 代表打开的文件和打开文件的flag
struct fd f = fdget_pos(fd);
ssize_t ret = -EBADF;
// 如果打开的文件为空,则退出
if (f.file) {
// 获取文件读写的位置
loff_t pos = file_pos_read(f.file);
// 进入vfs_write的函数,接着处理文件写操作
ret = vfs_write(f.file, buf, count, &pos);
if (ret >= 0)
file_pos_write(f.file, pos);
fdput_pos(f);
}
return ret;
}

// vfs_write包装了ext4_file_write_iter 函数 ssize_t vfs_write(struct file file, const char __user buf, size_t count, loff_t *pos) { ssize_t ret; // 判断文件是否写入,如果不能则返回错误码 if (!(file->f_mode & FMODE_WRITE)) return -EBADF; if (!(file->f_mode & FMODE_CAN_WRITE)) return -EINVAL; if (unlikely(!access_ok(VERIFY_READ, buf, count))) return -EFAULT; // 检查文件系统可写区域以及锁检查 ret = rw_verify_area(WRITE, file, pos, count); if (!ret) { if (count > MAX_RW_COUNT) count = MAX_RW_COUNT; file_start_write(file); // 调用ext4_file_write_iter 开始写数据 ret = __vfs_write(file, buf, count, pos); if (ret > 0) { fsnotify_modify(file); add_wchar(current, ret); } inc_syscw(current); file_end_write(file); } return ret; }

// vfs_write是包装函数 ssize_t vfs_write(struct file file, const char __user p, size_t count, loff_t *pos) { if (file->f_op->write) return file->f_op->write(file, p, count, pos); else if (file->f_op->write_iter) return new_sync_write(file, p, count, pos); else return -EINVAL; }

// 把用户需要写入的数据封装为struct iovec,然后把这个iovec和fd对应的struct file,传递给时间文件系统的函数,进行文件写入。 static ssize_t new_sync_write(struct file filp, const char __user buf, size_t len, loff_t ppos) { struct iovec iov = { .iov_base = (void __user )buf, .iov_len = len }; struct kiocb kiocb; struct iov_iter iter; ssize_t ret;

init_sync_kiocb(&kiocb, filp);
kiocb.ki_pos = *ppos;
iov_iter_init(&iter, WRITE, &iov, 1, len);
ret = call_write_iter(filp, &kiocb, &iter);
BUG_ON(ret == -EIOCBQUEUED);
if (ret youjiankuohaophpcn 0)
    *ppos = kiocb.ki_pos;
return ret;

}

// call_write_iter是直接调用f_op->write_iter函数,这里对应的是 ext4_file_write_iter,到了这里虚拟文件系统层基本已经结束,进入实际文件系统的调用过程。 static inline ssize_t call_write_iter(struct file file, struct kiocb kio, struct iov_iter *iter) { return file->f_op->write_iter(kio, iter); }


# unix  # linux  # 操作系统  # access  # echo  # Static  # const  # 接口  #   # Struct  # 算法  # 文件系统  # 的是  # 应用程序  # 为例  # 都是  # 这一  # 在这里  # 会有  # 也会  # 其他的 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: Win11怎么关闭专注助手 Win11关闭免打扰模式设置【操作】  Java Adapter 适配器模式(类适配器,对象适配器)优缺点对比  JavaScript模板引擎Template.js使用详解  晋江文学城电脑版官网 晋江文学城网页版直接进入  家族网站制作贴纸教程视频,用豆子做粘帖画怎么制作?  如何在IIS7上新建站点并设置安全权限?  详解Android——蓝牙技术 带你实现终端间数据传输  使用豆包 AI 辅助进行简单网页 HTML 结构设计  详解Oracle修改字段类型方法总结  Laravel如何实现API速率限制?(Rate Limiting教程)  百度输入法全感官ai怎么关 百度输入法全感官皮肤关闭  html5如何实现懒加载图片_ intersectionobserver api用法【教程】  微信小程序 canvas开发实例及注意事项  如何在云虚拟主机上快速搭建个人网站?  Laravel的契約(Contracts)是什么_深入理解Laravel Contracts与依赖倒置  Laravel路由Route怎么设置_Laravel基础路由定义与参数传递规则【详解】  如何基于云服务器快速搭建网站及云盘系统?  Laravel用户认证怎么做_Laravel Breeze脚手架快速实现登录注册功能  微博html5版本怎么弄发语音微博_语音录制入口及时长限制操作【教程】  laravel怎么实现图片的压缩和裁剪_laravel图片压缩与裁剪方法  桂林网站制作公司有哪些,桂林马拉松怎么报名?  HTML 中如何正确使用模板变量为元素的 name 属性赋值  如何在IIS中新建站点并解决端口绑定冲突?  如何实现建站之星域名转发设置?  利用python获取某年中每个月的第一天和最后一天  在线教育网站制作平台,山西立德教育官网?  C语言设计一个闪闪的圣诞树  如何正确下载安装西数主机建站助手?  美食网站链接制作教程视频,哪个教做美食的网站比较专业点?  javascript中数组(Array)对象和字符串(String)对象的常用方法总结  三星、SK海力士获美批准:可向中国出口芯片制造设备  创业网站制作流程,创业网站可靠吗?  网站制作大概多少钱一个,做一个平台网站大概多少钱?  如何在建站之星绑定自定义域名?  Laravel怎么解决跨域问题_Laravel配置CORS跨域访问  个人摄影网站制作流程,摄影爱好者都去什么网站?  如何在云主机快速搭建网站站点?  谷歌浏览器如何更改浏览器主题 Google Chrome主题设置教程  Laravel控制器是什么_Laravel MVC架构中Controller的作用与实践  JS实现鼠标移上去显示图片或微信二维码  如何在景安服务器上快速搭建个人网站?  Python文本处理实践_日志清洗解析【指导】  如何选择可靠的免备案建站服务器?  Laravel辅助函数有哪些_Laravel Helpers常用助手函数大全  如何在IIS中新建站点并配置端口与IP地址?  高性能网站服务器配置指南:安全稳定与高效建站核心方案  Android自定义控件实现温度旋转按钮效果  html文件怎么打开证书错误_https协议的html打开提示不安全【指南】  Swift中switch语句区间和元组模式匹配  网站建设整体流程解析,建站其实很容易!