持续创作,加速成长!这是我参与「日新计划 6 月更文挑战」的第23天。

Docker底层有三驾马车,NamespaceCGroupUnionFS(联合文件系统)UnionFSDocker镜像的基础。

UnionFS(联合文件系统)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修数据结构题库改作为枸杞一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。

UnionFSDocke数据结构c语言版严蔚敏第二版答案r镜像的基础,镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可数据结构c语言版第二版课后答案以制作各种具体的应用镜像。由于 Linuxdockerhub 下有多种的UnionFS(如AUFSOverlay缓存英文FSBtrfs等),所以我们以实现相对简单的OverlayFS作为分析对象。

OverlayFS使用

我们先来看看OverlayFS基本原理(图片来源于网络):

容器技术-Overlay 文件系统浅析

overlayfs-map

从上图可知,OverlayFS文件系统主要有三个角色,lowerdirupperdirmergedlowerdir是只读层,用户不能修改这个层的文件;upperdir是可读写缓存是什么意思层,用户能够修改工资超过5000怎么扣税这个层的文件;而merged是合并层,把lowerdir层和upperdir层的文件合并展示。

使用OverlayFS前需要进行挂载操作,挂载OverlayFS文件系统的基本命令如下:

$mount-toverlayoverlay-olowerdir=lower1:lower2,upperdir=upper,workdir=workmerged

参数-t表示挂载的文件系接口卡统类型,这里设置为overlay表示文件系统类型为Overl缓存视频变成本地视频ayFS,而参数-o指定的是lowe公司让员工下班发手机电量截图rdirupperdocker安装dirworkdir,最后的merged目录工商银行就是最终的挂载点目录。下面说明一下-o参数几个目录的作用:

  1. lowerdir:指定用户需要挂载的l龚俊ower层目录,指定多个目录可以使用:来分隔(最大支持5数据结构题库00层)。

  2. upperdir:指定用数据结构户需要挂载的upper层目录。

  3. workdir:指定文件系统的工作基础目录,挂载后内容会被清空,且在使用过程中其内容用户不可见。

OverlayFS实现原理

下面我们开始分析OverlayFS的实现原理。

OverlayFS文件系统的作用是合并upper目录和low工龄差一年工资差多少er目录的中的内容,如果upp工龄越长退休金越多吗er目录与lower目录接口是什么同时存在同一文件或目录,那么Overdocker菜鸟教程layFS文件系统怎么处理呢?

  1. 如果upperlower目录下同时数据结构有哪些存在同一文件,那么按upper目录的文件为准。比如upperlower目录下同时存在文件a.txt,那么按upper目录工商银行a.txt文件为Docker准。

  2. 如果upperlowe工资超过5000怎么扣税r目录下同时存在同一目录,那么把upper目录与lower目录的内容合并起来。比如upperlower目录下同时存在目录test,那么把upper缓存是什么意思录下的test目录中的内容与lower目录下的test目录中的内容合并起来。

为了简单起见,本文使用的是Linux 3.18.3版本,此版本的OverlayFS文件系统只支持一层的lower目录,所以简化了多层lower合并的逻辑。

OverlayFS文件系统挂载

前面介绍过挂载OverlayFS文件系统的命令,挂载OverlayFS文件系统会触发系统调用接口卡sys_mount(),而sys_mount()会执行虚拟文件系统的通用挂载过程,如申请缓存是什么意思和初始化超级块对象(super block)接口英文可参考:虚拟文件系统)。然后调用具体文件系统的fill_super()接口来填充超级块对象,对于OverlayFS文件系统而言,最公积金终会调缓存视频在手机哪里找ovl_fill_super()函数来填充超级块对象

我们来分析一下ovl_fill_super()缓存视频怎样转入相册函数的主要部分:

staticintovl_fill_super(structsuper_block*sb,void*data,intsilent){structpathlowerpath;structpathupperpath;structpathworkpath;structinode*root_inode;structdentry*root_dentry;structovl_entry*oe;...oe=ovl_alloc_entry();//新建一个ovl_entry对象...//新建一个inode对象root_inode=ovl_new_inode(sb,S_IFDIR,oe);//新建一个dentry对象,并且指向新建的inode对象root_inoderoot_dentry=d_make_root(root_inode);...oe->__upperdentry=upperpath.dentry;//指向upper目录的dentry对象oe->lowerdentry=lowerpath.dentry;//指向lower目录的dentry对象root_dentry->d_fsdata=oe;//保存ovl_entry对象到新建dentry对象的d_fsdata字段中...sb->s_root=root_dentry;//保存新建的dentry对象到超级块的s_root字段中...return0;}

ovl_fill_super()函数主要完成以下几个步骤:

  1. 调用ovl_alloc_entry()创建一个ovl_e数据结构c语言版ntry对象oe

  2. 调用ovl_new_inode()创建一个inode对象root_inod数据结构教程第5版李春葆答案e

  3. 调用d_make接口和抽象类的区别_root()创建一个数据结构dentry对象root_dentry,并且将其指向root_inode

  4. oe__upperdentry字段指向upper目录的dentry,而将lo工龄越长退休金越多吗werdentry字段指向lower目录的dentry

  5. root_dentryd_fsdata字段指向oe

  6. 超级块对象s_root字段指向root_dentry

最后,其各个数据结构的关系如下图:

容器技术-Overlay 文件系统浅析

overlay接口和抽象类的区别fs-relation

在上面的代码中出现的ovl_entry结构用于记录OverlayFS文件系统中某个文件或者目录所在的真实位置,由于OverlayFS文件系统是一个联合文件系统,并不是真正存数据结构c语言版在于磁盘的文件系统,所以在OverlayFS文件系统中的文件都要指向真实文件系统中的位置。

ovl_entry数据结构与算法构就是用来指向真实文件系统的位置,其定义如下:

structovl_entry{structdentry*__upperdentry;structdentry*lowerdentry;structovl_dir_cache*cache;union{struct{u64version;boolopaque;};structrcu_headrcu;};};

下面解宫颈癌析一下ovl_entry结构各个字段的作用:

  1. __upperdentry:如果文件存在于upper目录中,那么指向此文件公积金的dentry对象。

  2. lowerdentry:如果文件存在于lower目录中,那接口和抽象类的区别么指向此文件的dentry对象。

  3. cache:如果指向的目录,那么数据结构实验报告缓存此目录的文件接口卡列表。

  4. version:用于记录此ovl_entry结构的版本。

  5. opaque:此文件或目录是否被隐藏。

__upperdentrylowe数据结构知识点总结rdentr接口yovl_edocker常用命令ntry结构比较重要的两个字段,一个指向文件所在upper目录中的dentry对象,另外一个指向文件所在lower目录中的dentry对象,如下图:

容器技术-Overlay 文件系统浅析

overlayfs-mount

OverlayFS文件系统中,每个文件或目录都由一个ovl_entry结构管理。如果我们把dentry结构当成是文件或目录的接口crc错误计数实体,那么__uppe接口测试rdentry指向的就是文件或目录所在upper目录中的实体,而lowerdentry指向的就是文件或目录所在lower目录的实体docker菜鸟教程

读取OverlayFS文件数据结构系统的目录

一般来说,我们调用ls命令读取目录的列表时,会触发内存以下过程:

  1. 调用openat()系统调用打开目录。

  2. 调用getdents()系统调用读取目录的列表。

打开目录

o工龄差一年工资差多少pen(接口文档)系统调用最终会调用具体文件系统的open(接口文档)方法来打开文件接口卡,对于OverlayFS文件系统调用的是ovl_di缓存的视频在哪r_open()函数,其实现如下:

staticintovl_dir_open(structinode*inode,structfile*file){structpathrealpath;structfile*realfile;structovl_dir_file*od;enumovl_path_typetype;//申请一个ovl_dir_file对象od=kzalloc(sizeof(structovl_dir_file),GFP_KERNEL);if(!od)return-ENOMEM;type=ovl_path_real(file->f_path.dentry,&realpath);realfile=ovl_path_open(&realpath,file->f_flags);if(IS_ERR(realfile)){kfree(od);returnPTR_ERR(realfile);}//初始化ovl_dir_file对象INIT_LIST_HEAD(&od->cursor.l_node);od->realfile=realfile;od->is_real=(type!=OVL_PATH_MERGE);od->is_upper=(type!=OVL_PATH_LOWER);od->cursor.is_cursor=true;file->private_data=od;//保存到file对象的private_data字段中return0;}

ovl_dir_open()函数主要完成的工作包括:

  1. 创建一个缓存文件夹名称ovl_dir_file对象od

  2. 调用ovl_path_real()函数分析当前文件接口类型或目录所属的类型:

  3. 如果是一个目录,并且upper目录和lower宫颈癌目录同时存在,那么返回OVL_数据结构教程第5版李春葆答案PATH_MERGE,表示需要接口类型对目录进行合并。

  4. 如果是一个目录,并且只存在于upper目录中。或者是一个文件,并且存docker容器在于upper目录中docker常用命令,那么返回OVL_PATH_UPPER,表接口自动化示从upper目录中读取。

  5. 否则返回OVL_PATH_LOWER,表示从lower目录中读取。

  6. ovl_dir_file对象保存到 file 对象的private_data字段中。

ovldocker命令_dir_fi数据结构知识点总结le对象用于描述缓存OverlayFS文件系统的目工商银行录或文件的信息,其定义如下:

structovl_dir_file{boolis_real;boolis_upper;structovl_dir_cache*cache;structovl_cache_entrycursor;structfile*realfile;structfile*upperfile;};structovl_dir_cache{longrefcount;u64version;structlist_headentries;};

ovl_dir_file对象各个字段的含义如下:

  1. is_r接口卡eal:如不需要合并,设置为true。

  2. is_upper:是否需要从upper目录中读取。

  3. c缓存视频合并appa工龄差一年工资差多少che:用于缓存目录的文件列表。

  4. cursor:用dockerhub于迭代目录列表时的游标。

  5. rea缓存英文l接口类型file:真实文件或目录的dentr接口英文y对象。

  6. upperfile:指向文件或目录所在upper目录中的dentry对象。

读取目录列表

读取目录中的文件列表是通过getdents()系统调用,而getdents()系统调用最终会调用具体文件系统的iterate(数据结构有哪些)接口,对于OverlayFSgoogle件系统而言,调用的就是ovl_iterate()函数。其实现如下:

staticintovl_iterate(structfile*file,structdir_context*ctx){structovl_dir_file*od=file->private_data;structdentry*dentry=file->f_path.dentry;if(!ctx->pos)ovl_dir_reset(file);if(od->is_real)//如果不需要合并,直接调用iterate_dir()函数读取真实的目录列表即可returniterate_dir(od->realfile,ctx);if(!od->cache){//如果还没有创建缓存对象structovl_dir_cache*cache;cache=ovl_cache_get(dentry);//读取合并后目录的文件列表,并且缓存起来if(IS_ERR(cache))returnPTR_ERR(cache);od->cache=cache;//保持缓存对象ovl_seek_cursor(od,ctx->pos);//移动游标}while(od->cursor.l_node.next!=&od->cache->entries){//遍历合并后的目录中的文件列表structovl_cache_entry*p;p=list_entry(od->cursor.l_node.next,structovl_cache_entry,l_node);if(!p->is_cursor){if(!p->is_whiteout){if(!dir_emit(ctx,p->name,p->len,p->ino,p->type))//写到用户空间的缓冲区中break;}ctx->pos++;}list_move(&od->cursor.l_node,&p->l_node);//移动到下一个文件}return0;}

ovl_iterate()函数的主要工作有以下几个步骤:

  1. 如果不需要合并目录(就是i缓存是什么意思s_real为true),那么直接调用iterate_dir()函数读取真实的目录列表。

  2. 如果ovl_dir_file对象的缓存没有被创建,那么调用ovl_cache_get()创建缓存对象,ovl_cache_get()除了创建缓存对象外,还会读取合并后的目录中的文件列表,并保存到缓存对象的entries链表中。

  3. 遍历合并后的目录中的文件列表,并把文件列表写到用户空间的缓存中,这docker是干什么的样用户就可以获取合并后的文件列表。

我们主要来分析一下怎么通过ovl_cach工龄越长退休金越多吗e_get()函数来读取合并后的目录中的接口文档文件列表:

staticstructovl_dir_cache*ovl_cache_get(structdentry*dentry){intres;structovl_dir_cache*cache;...cache=kzalloc(sizeof(structovl_dir_cache),GFP_KERNEL);if(!cache)returnERR_PTR(-ENOMEM);cache->refcount=1;INIT_LIST_HEAD(&cache->entries);res=ovl_dir_read_merged(dentry,&cache->entries);...cache->version=ovl_dentry_version_get(dentry);ovl_set_dir_cache(dentry,cache);returncache;}

ovl_cache_get()缓存英文数首先创建一个ovl_dir_cac接口和抽象类的区别he缓存对象,并且调用ovl_dir_read_merged()函数读取合并目录的文件列表,ovl_di接口和抽象类的区别r_read_merged()函数实现如下:

staticintovl_dir_read_merged(structdentry*dentry,structlist_head*list){interr;structpathlowerpath;structpathupperpath;structovl_readdir_datardd={.ctx.actor=ovl_fill_merge,.list=list,.root=RB_ROOT,.is_merge=false,};ovl_path_lower(dentry,&lowerpath);//获取lower目录ovl_path_upper(dentry,&upperpath);//获取upper目录if(upperpath.dentry){//如果upper目录存在,读取upper目录中的文件列表err=ovl_dir_read(&upperpath,&rdd);if(err)gotoout;if(lowerpath.dentry){err=ovl_dir_mark_whiteouts(upperpath.dentry,&rdd);if(err)gotoout;}}if(lowerpath.dentry){//如果lower目录存在,读取lower目录中的文件列表list_add(&rdd.middle,rdd.list);rdd.is_merge=true;err=ovl_dir_read(&lowerpath,&rdd);list_del(&rdd.middle);}out:returnerr;}

ovl_dir_read_merged()函数的实现比较简单,调用了ovl_dir_read()函数读取lowerupper目录中的文件列表,并保存到list参数中。

这里有个问题,就是如果lower目录和u接口卡pper目录同时存在相同的文件怎办?

在调用ovl_dir_read()函数读取lowerupper目录中的文件列表时会缓存视频在手机哪里找调用ovl_fill_merge()函数过滤相同的文件。过滤操作通过红黑树来实现,过滤过程如下:

  1. 读取upper目录中的文件列表,保存到list列表中,并且保存到红黑树中接口crc错误计数

  2. 读取lower目录中的文google件列表,查询红黑树中是否已经存在此文件,如果存在,那么跳过此文件,否则添加到list列表中