Linux内核中的overlay文件系统

一、简介

Docker 内核实现容器的功能用了linux 内核中的三个特性 Namespace、Cgroup、UnionFs,今天我们来说一下UnionFs。

linux UnionFs 实现的是overlay 文件系统

OverlayFs 文件系统分为三层,

lower 是只读层

Upper 是可读写

Merged 是 lower 和Upper 合并的目录

挂载方式可以使用mount 命令挂载:

mount -t overlay overlay -o lowerdir=lower1:lower2,upperdir=upper,workdir=work merged

二、源码分析

1.挂载overlay 设备初始化

当我们使用

mount -t overlay overlay -o lowerdir=lower1:lower2,upperdir=upper,workdir=work merged

linux 内核层,overlay 结构体声明类型

static struct file_system_type ovl_fs_type = {
	.owner		= THIS_MODULE,
	.name		= "overlay",
	.fs_flags	= FS_USERNS_MOUNT,
	.mount		= ovl_mount,
	.kill_sb	= kill_anon_super,
};

当我们使用overlay设备的时候,会触发结构体上挂载的mount函数指针,这个函数触发linux内核中的ovl_mount

static struct dentry *ovl_mount(struct file_system_type *fs_type, int flags,
				const char *dev_name, void *raw_data)
{
	return mount_nodev(fs_type, flags, raw_data, ovl_fill_super);
}

核心是使用ovl_fill_super,填充overlay 文件系统的超级块,申请一个ovl_fs,然后填充到

sb->s_fs_info = ofs;

详细代码:

static int ovl_fill_super(struct super_block *sb, void *data, int silent)
{
	struct path upperpath = { };
	struct dentry *root_dentry;
	struct ovl_entry *oe;
	struct ovl_fs *ofs;
	struct ovl_layer *layers;
	struct cred *cred;
	char *splitlower = NULL;
	unsigned int numlower;
	int err;

    // 如果当前用户的namespace不是超级块的ns那么返回错误 -EIO
	err = -EIO;
	if (WARN_ON(sb->s_user_ns != current_user_ns()))
		goto out;

    // 目录操作结构体赋值
	sb->s_d_op = &ovl_dentry_operations;

	err = -ENOMEM;
    // 申请ovl_fs,并且对ovl_fs进行填充
	ofs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL);
	if (!ofs)
		goto out;

	err = -ENOMEM;
	ofs->creator_cred = cred = prepare_creds();
	if (!cred)
		goto out_err;

	/* Is there a reason anyone would want not to share whiteouts? */
	ofs->share_whiteout = true;

	ofs->config.index = ovl_index_def;
	ofs->config.uuid = true;
	ofs->config.nfs_export = ovl_nfs_export_def;
	ofs->config.xino = ovl_xino_def();
	ofs->config.metacopy = ovl_metacopy_def;
    // 装载选项
	err = ovl_parse_opt((char *) data, &ofs->config);
	if (err)
		goto out_err;

	err = -EINVAL;
	if (!ofs->config.lowerdir) {
		if (!silent)
			pr_err("missing 'lowerdir'\n");
		goto out_err;
	}

	err = -ENOMEM;
	splitlower = kstrdup(ofs->config.lowerdir, GFP_KERNEL);
	if (!splitlower)
		goto out_err;

	err = -EINVAL;
	numlower = ovl_split_lowerdirs(splitlower);
	if (numlower > OVL_MAX_STACK) {
		pr_err("too many lower directories, limit is %d\n",
		       OVL_MAX_STACK);
		goto out_err;
	}

	err = -ENOMEM;
	layers = kcalloc(numlower + 1, sizeof(struct ovl_layer), GFP_KERNEL);
	if (!layers)
		goto out_err;

	ofs->layers = layers;
	/* Layer 0 is reserved for upper even if there's no upper */
	ofs->numlayer = 1;

	sb->s_stack_depth = 0;
	sb->s_maxbytes = MAX_LFS_FILESIZE;
	atomic_long_set(&ofs->last_ino, 1);
	/* Assume underlying fs uses 32bit inodes unless proven otherwise */
	if (ofs->config.xino != OVL_XINO_OFF) {
		ofs->xino_mode = BITS_PER_LONG - 32;
		if (!ofs->xino_mode) {
			pr_warn("xino not supported on 32bit kernel, falling back to xino=off.\n");
			ofs->config.xino = OVL_XINO_OFF;
		}
	}

	/* alloc/destroy_inode needed for setting up traps in inode cache */
	sb->s_op = &ovl_super_operations;

	if (ofs->config.upperdir) {
		struct super_block *upper_sb;

		err = -EINVAL;
		if (!ofs->config.workdir) {
			pr_err("missing 'workdir'\n");
			goto out_err;
		}

		err = ovl_get_upper(sb, ofs, &layers[0], &upperpath);
		if (err)
			goto out_err;

		upper_sb = ovl_upper_mnt(ofs)->mnt_sb;
		if (!ovl_should_sync(ofs)) {
			ofs->errseq = errseq_sample(&upper_sb->s_wb_err);
			if (errseq_check(&upper_sb->s_wb_err, ofs->errseq)) {
				err = -EIO;
				pr_err("Cannot mount volatile when upperdir has an unseen error. Sync upperdir fs to clear state.\n");
				goto out_err;
			}
		}

		err = ovl_get_workdir(sb, ofs, &upperpath);
		if (err)
			goto out_err;

		if (!ofs->workdir)
			sb->s_flags |= SB_RDONLY;

		sb->s_stack_depth = upper_sb->s_stack_depth;
		sb->s_time_gran = upper_sb->s_time_gran;
	}
	oe = ovl_get_lowerstack(sb, splitlower, numlower, ofs, layers);
	err = PTR_ERR(oe);
	if (IS_ERR(oe))
		goto out_err;

	/* If the upper fs is nonexistent, we mark overlayfs r/o too */
	if (!ovl_upper_mnt(ofs))
		sb->s_flags |= SB_RDONLY;

	if (!ofs->config.uuid && ofs->numfs > 1) {
		pr_warn("The uuid=off requires a single fs for lower and upper, falling back to uuid=on.\n");
		ofs->config.uuid = true;
	}

	if (!ovl_force_readonly(ofs) && ofs->config.index) {
		err = ovl_get_indexdir(sb, ofs, oe, &upperpath);
		if (err)
			goto out_free_oe;

		/* Force r/o mount with no index dir */
		if (!ofs->indexdir)
			sb->s_flags |= SB_RDONLY;
	}

	err = ovl_check_overlapping_layers(sb, ofs);
	if (err)
		goto out_free_oe;

	/* Show index=off in /proc/mounts for forced r/o mount */
	if (!ofs->indexdir) {
		ofs->config.index = false;
		if (ovl_upper_mnt(ofs) && ofs->config.nfs_export) {
			pr_warn("NFS export requires an index dir, falling back to nfs_export=off.\n");
			ofs->config.nfs_export = false;
		}
	}

	if (ofs->config.metacopy && ofs->config.nfs_export) {
		pr_warn("NFS export is not supported with metadata only copy up, falling back to nfs_export=off.\n");
		ofs->config.nfs_export = false;
	}

	if (ofs->config.nfs_export)
		sb->s_export_op = &ovl_export_operations;

	/* Never override disk quota limits or use reserved space */
	cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);

	sb->s_magic = OVERLAYFS_SUPER_MAGIC;
	sb->s_xattr = ofs->config.userxattr ? ovl_user_xattr_handlers :
		ovl_trusted_xattr_handlers;
	sb->s_fs_info = ofs;
	sb->s_flags |= SB_POSIXACL;
	sb->s_iflags |= SB_I_SKIP_SYNC;

    // 把 overlay 文件系统的根目录设置到 upperDir里
	err = -ENOMEM;
    // 创建root的inode并且指向新建的inode对象root_inode
	root_dentry = ovl_get_root(sb, upperpath.dentry, oe);
	if (!root_dentry)
		goto out_free_oe;

	mntput(upperpath.mnt);
	kfree(splitlower);

	sb->s_root = root_dentry;

	return 0;

out_free_oe:
	ovl_entry_stack_free(oe);
	kfree(oe);
out_err:
	kfree(splitlower);
	path_put(&upperpath);
	ovl_free_fs(ofs);
out:
	return err;
}

操作overlay 文件系统的目录操作结构体实现:

static const struct dentry_operations ovl_dentry_operations = {
	.d_release = ovl_dentry_release,
	.d_real = ovl_d_real,
	.d_revalidate = ovl_dentry_revalidate,
	.d_weak_revalidate = ovl_dentry_weak_revalidate,
};

数据结构图:

参考网址:

Linux源码剖析——OverlayFS 源码分析_linux overlay-CSDN博客

2、描述符操作结构体 

如果你做过kernel module ,读过linux设计实现.就很容易理解了

描述符操作结构体定义:

const struct file_operations ovl_dir_operations = {
	.read		= generic_read_dir,
	.open		= ovl_dir_open,
	.iterate	= ovl_iterate,
	.llseek		= ovl_dir_llseek,
	.fsync		= ovl_dir_fsync,
	.release	= ovl_dir_release,
};

当我们使用linux 系统调用打开overlay 设备文件的时候会触发操作结构体的函数,

open 函数:

static int ovl_dir_open(struct inode *inode, struct file *file)
{
	struct path realpath;
	struct file *realfile;
	struct ovl_dir_file *od;
	enum ovl_path_type type;

	od = kzalloc(sizeof(struct ovl_dir_file), GFP_KERNEL);
	if (!od)
		return -ENOMEM;

	type = ovl_path_real(file->f_path.dentry, &realpath);
	realfile = ovl_dir_open_realfile(file, &realpath);
	if (IS_ERR(realfile)) {
		kfree(od);
		return PTR_ERR(realfile);
	}
	od->realfile = realfile;
	od->is_real = ovl_dir_is_real(file->f_path.dentry);
	od->is_upper = OVL_TYPE_UPPER(type);
	file->private_data = od;

	return 0;
}

struct ovl_dir_file {
	bool is_real; // 是否需要合并
	bool is_upper; // 是否需要从upper读取
	struct ovl_dir_cache *cache; // 缓存目录
	struct list_head *cursor; // 遍历游标
	struct file *realfile; // 真实文件
	struct file *upperfile; // overlay 里 在upper目录所在位置
};

这里主要做的操作是初始化ovl_dir_file,并且把他挂载到万能指针private_data中。

读的操作是通过getdents,我们看迭代器:

static int ovl_iterate(struct file *file, struct dir_context *ctx)
{
	struct ovl_dir_file *od = file->private_data;
	struct dentry *dentry = file->f_path.dentry;
	struct ovl_cache_entry *p;
	const struct cred *old_cred;
	int err;

	old_cred = ovl_override_creds(dentry->d_sb);
	if (!ctx->pos)
		ovl_dir_reset(file);

    //是否需要读取真实路径
	if (od->is_real) {
        // 不需要合并直接读取真实路径
		/*
		 * If parent is merge, then need to adjust d_ino for '..', if
		 * dir is impure then need to adjust d_ino for copied up
		 * entries.
		 */
		if (ovl_xino_bits(dentry->d_sb) ||
		    (ovl_same_fs(dentry->d_sb) &&
		     (ovl_is_impure_dir(file) ||
		      OVL_TYPE_MERGE(ovl_path_type(dentry->d_parent))))) {
			err = ovl_iterate_real(file, ctx);
		} else {
			err = iterate_dir(od->realfile, ctx);
		}
		goto out;
	}

    // 创建目录缓存
	if (!od->cache) {
		struct ovl_dir_cache *cache;

		cache = ovl_cache_get(dentry);
		err = PTR_ERR(cache);
		if (IS_ERR(cache))
			goto out;

		od->cache = cache;
		ovl_seek_cursor(od, ctx->pos);
	}

    // 直接把合并后的目录缓存,遍历返回用户层
	while (od->cursor != &od->cache->entries) {
		p = list_entry(od->cursor, struct ovl_cache_entry, l_node);
		if (!p->is_whiteout) {
			if (!p->ino) {
				err = ovl_cache_update_ino(&file->f_path, p);
				if (err)
					goto out;
			}
		}
		/* ovl_cache_update_ino() sets is_whiteout on stale entry */
		if (!p->is_whiteout) {
			if (!dir_emit(ctx, p->name, p->len, p->ino, p->type))
				break;
		}
		od->cursor = p->l_node.next;
		ctx->pos++;
	}
	err = 0;
out:
	revert_creds(old_cred);
	return err;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/188596.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[AutoSAR 存储] 汽车智能座舱的存储需求

公知及经验整理&#xff0c;原创保护&#xff0c;禁止转载。 专栏 《AutoSAR 存储》 <<<< 返回总目录 <<<< 1 智能座舱的发展&#xff1a; 1.1 发展历史 车辆信息娱乐系统的发展可以分为三个阶段。 机械化阶段 在上世纪90年代&#xff0c;车辆仪表盘…

⑥【bitmap 】Redis数据类型: bitmap [使用手册]

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ Redis bitmap ⑥Redis bitmap 基本操作命令1. …

【前端】数据行点击选择

前言 【前篇文章】说了,我们公司的核心价值就是让人越来越懒,能怎么便捷就怎么便捷,主打一个简单实用又快捷,为了实现这个目标,我看成这个列表陷入了深思在想,要不要子表的数据加载在点击这个行时,就可以展示数据,这样就不用每次都要点那个小圆圈啦。 查资料 这显然…

卷积神经网络(Inception-ResNet-v2)交通标志识别

文章目录 一、前言二、前期工作1. 设置GPU&#xff08;如果使用的是CPU可以忽略这步&#xff09;2. 导入数据3. 查看数据 二、构建一个tf.data.Dataset1.加载数据2. 配置数据集 三、构建Inception-ResNet-v2网络1.自己搭建2.官方模型 五、设置动态学习率六、训练模型七、模型评…

VUE限制文件上传大小和上传格式

<el-form-item label"图片&#xff1a;" prop"tempImagePath"><el-uploadclass"upload"accept"image/jpeg":show-file-list"false"list-type"picture-card":headers"{ token: token}":action&…

postman自动化运行接口测试用例

做过接口测试的人&#xff0c;应该都知道postman &#xff0c;我们在日常的时候都可以利用postman做接口测试&#xff0c;我们可以把接口的case保存下来在collection里面&#xff0c;那么可能会有这样的需求&#xff0c;我们怎么把collection的用例放到jenkins中定时执行呢&…

2023-11-24 LeetCode每日一题(统计和小于目标的下标对数目)

2023-11-24每日一题 一、题目编号 2824. 统计和小于目标的下标对数目二、题目链接 点击跳转到题目位置 三、题目描述 给你一个下标从 0 开始长度为 n 的整数数组 nums 和一个整数 target &#xff0c;请你返回满足 0 < i < j < n 且 nums[i] nums[j] < targe…

2023 最新 PDF.js 在 Vue3 中的使用

因为自己写业务要定制各种 pdf 预览情况&#xff08;可能&#xff09;&#xff0c;所以采用了 pdf.js 而不是各种第三方封装库&#xff0c;主要还是为了更好的自由度。 一、PDF.js 介绍 官方地址 中文文档 PDF.js 是一个使用 HTML5 构建的便携式文档格式查看器。 pdf.js 是社区…

文献速递:人工智能在新生儿重症监护室:现在是时候了

人工智能在新生儿重症监护室&#xff1a;现在是时候了 01 文献速递介绍 文章介绍了AI的多学科特性&#xff0c;包括计算机科学、数学、神经科学和哲学。AI的目标是通过各种计算和算法技术创建智能机器。尽管早期对人类水平AI的预测未能实现&#xff0c;但对AI的期待仍然强烈…

PC端从零搭建微信自动回复机器人(一)基础框架搭建及源码

由于工作需要&#xff0c;最近一年一直在研究和使用C#&#xff0c;加上最近工作上有做微信机器人的需要&#xff0c;在已经对接、调试稳定之后&#xff0c;将项目的源码分享给大家&#xff0c;传递开源精神。 一、环境依赖 1、开发工具&#xff1a;Vistual Studio 2022 2、Ne…

java学习part11继承

1.类的继承 继承为了让类之间建立联系&#xff0c;同时复用代码。 子类和父类的同名函数构成重写&#xff0c;能覆盖&#xff0c;除非用super.xx()调。 同名属性不会覆盖&#xff0c;而是并存&#xff0c;用super.xx调。 2.子类初始化 子类会自动调用父类无参构造super() 3.重…

RuntimeError: CUDA error: device-side assert triggered

背景&#xff1a; 使用SAGEConv卷积层的图神经网络&#xff0c;网络架构如下 原因&#xff1a; 我在卷积层之前改变了特征矩阵的维度&#xff0c;原本为[172,1,32] 现在改为了 [172,2,32]。导致了特征矩阵x在进行 “x x.squeeze(1)” 操作时并没有将第二向量值去除&#xff08…

国标交流充电桩接口和直流充电桩接口介绍

1、背景 与传统油车相比&#xff0c;纯电车有太多的优势&#xff0c;但是纯电需要考虑充电时间的长短以及电池的使用寿命。然而相比较而言&#xff0c;混动有好多的备选方案比如插电式、增程式等&#xff0c;除了满足比电车较远的续航外&#xff0c;充电等待时间大大缩短。 在…

ElasticSearch01

ElasticSearch 版本&#xff1a;7.8 学习视频&#xff1a;尚硅谷 笔记&#xff1a;https://zgtsky.top/ ElasticSearch介绍 Elaticsearch&#xff0c;简称为es&#xff0c; es是一个开源的高扩展的分布式全文检索引擎&#xff0c;它可以近乎实时的存储、检索数据&#xff1b…

C++实现十大排序算法

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…

基于Flutter的图片浏览器的实现

一 效果展示&#xff1a; 1. 图片展示&#xff1a; 2.混色&#xff0c;平铺&#xff0c;拉伸&#xff0c;原图展示 二 实验准备&#xff1a; 1.在包结构中创建images包来存放我们用到的图片&#xff0c;在pubspec.yaml中声明路径&#xff1a; 2. 检查虚拟机是否正常运行&…

数字孪生智慧校园 Web 3D 可视化监测

当今&#xff0c;智慧校园发展阶段亟需推动信息可视化建设与发展&#xff0c;将大数据、云计算、可视化等高新技术相融合&#xff0c;为校园师生创造科学智能的学习环境&#xff0c;并实现教学资源最大化和信息服务智能化。帮助学校更好地应用校园可视化技术&#xff0c;提升校…

rfc4301- IP 安全架构

1. 引言 1.1. 文档内容摘要 本文档规定了符合IPsec标准的系统的基本架构。它描述了如何为IP层的流量提供一组安全服务&#xff0c;同时适用于IPv4 [Pos81a] 和 IPv6 [DH98] 环境。本文档描述了实现IPsec的系统的要求&#xff0c;这些系统的基本元素以及如何将这些元素结合起来…

Jensen不等式

如果是正数&#xff0c;并且它们的和等于1&#xff0c;f是凸函数&#xff0c;那么&#xff1a; 也可表述为&#xff1a; 即x期望的凸函数值小于等于x凸函数值的期望

【数据库】聊聊一颗B+树 可以存储多少数据

我们知道数据库使用的数据结构是B树&#xff0c;但是B树可以存储多少数据呢&#xff0c;在面试中也是经常会问的问题&#xff0c;所以我们从根上理解这个问题。 操作系统层面 数据都是存储在磁盘中的&#xff0c;而磁盘中的数据都是以最新单位扇区进行分割。一个扇区的大小是…