首页 > 其他分享 >深入理解mv

深入理解mv

时间:2022-10-10 18:24:09浏览次数:45  
标签:文件 理解 文件系统 mv 深入 操作 inode 目录

 

=====================================================以下来自:https://blog.51cto.com/baidutech/743731=========================

mv操作深入浅出

业务背景:存在两个进程,进程A生成文件file,然后将其mv到一个新的文件fileA,进程B在需要的时候会取读取文件fileA。如果在进程B读取文件的时候,进程A在进行mv的操作,那么这个操作是否对进程B读取文件生成影响呢?会产生什么结果?
如下图所示:

 

 在阐述该问题之前,首先讲解下mv的操作和fopen()函数等对文件的操作的基本原理。那么首先需要初步的了解下Linux的文件系统中所涉及到的一些基础知识,比如说Linux文件系统组件的体系结构,VFS, i节点,元数组等概念。

1.Linux文件系统体系结构

如图1为Linux文件系统组件的体系结构:

 

 

其中用户空间包含一些应用程序(例如,文件系统的使用者)和 GNU C 库(glibc),它们为文件系统调用(打开、读取、写和关闭)提供用户接口。系统调用接口的作用就像是交换器,它将系统调用从用户空间发送到内核空间中的适当端点。
VFS 是底层文件系统的主要接口。这个组件导出一组接口,然后将它们抽象到各个文件系统,各个文件系统的行为可能差异很大。有两个针对文件系统对象的缓存(inode 和 dentry)。它们缓存最近使用过的文件系统对象。文件系统实现(比如 ext2、JFS 等等)导出一组通用接口,供 VFS 使用。缓冲区缓存会缓存文件系统和相关块设备之间的请求。例如,对底层设备驱动程序的读写请求会通过缓冲区缓存来传递。这就允许在其中缓存请求,减少访问物理设备的次数,加快访问速度(缓存的目的)。以最近使用(LRU)列表的形式管理缓冲区缓存。注意,可以使用 sync 命令将缓冲区缓存中的请求发送到存储媒体(迫使所有未写的数据发送到设备驱动程序,进而发送到存储设备)。具体的信息可以参考IBM develop的《Linux 文件系统剖析》,链接为 http://www.ibm.com/developerworks/cn/linux/l-linux-filesystem/

Linux文件系统下如何读取文件:
VFS采用了一组数据结构来描述文件系统,这些数据有超级块、inode、dentry和数据块。Linux版本较多,文件系统也不同,但是对于linux系统,其基本结构还是一致的,都会包含引导块、超级快,目录项i节点表、数据区等几个部分。
1) 引导块:位于文件卷最开始的第一扇区,这512字节是文件系统的引导代码,为根文件系统所特有,其他文件系统这512字节为空。
2) 超级块:位于文件系统第二扇区,紧跟引导块之后,用于描述本文件系统的结构。如i节点长度、文件系统大小等。
3) 目录项:Unix所有文件均存放于目录中,目录本身也是一个文件。目录存放文件的机制如下: 首先,目录文件本身也象普通文件一样,占用一个索引节点; 其次,由这个索引节点得到目录内容的存放位置; 再次,从其内容中取出一个个的文件名和它对应的节点号,从而访问一个文件。
4) i节点:i节点表存放在超级块之后,其长度是由超级块中的s_isize字段决定的,其作用是用来描述文件的属性、长度、属主、属组、数据块表等
Linux会为每一个文件分配一个唯一的inode节点。而dentry是实现了文件名和inode编号的映射,当然还有其他的功能。在linux中,文件的文件名、文件属性、文件内容是分别存储的:文件名存放在目录项(即dentry)中,文件属性存放在inode中,文件内容存放在数据块中。Linux在查找操作文件系统中的文件时,首先先读取超级块信息,找到文件名对应的inode,然后根据inode找到磁盘中的文件,进而根据inode中的信息来完成文件的各种操作。也就是说,Linux通过inode来寻找磁盘中的文件,而不是通过文件名来寻找的。这就是Linux操作文件时的一个大致过程,当然具体情况要比这复杂。
inode的结构:
• inode 编号
• 用来识别文件类型,以及用于 stat C 函数的模式信息
• 文件的链接数目
• 属主的 UID
• 属主的组 ID (GID)
• 文件的大小
• 文件所使用的磁盘块的实际数目
• 最近一次修改的时间
• 最近一次访问的时间
• 最近一次更改的时间
需要注意的是:inode本身并不记录文件名,而是记录文件的相关的属性(在上文提到过的那些属性),文件名则记录在目录所属的块区域。正因为这个原因,使得如果Linux读取一个文件的内容,就要先由根目录/获取该文件的上层目录所在的inode,再由该目录所记录的的文件关联性获取该文件的inode,最后通过inode内提供的块指针来获取最终的文件内容。
可以看到i节点中包含了大多数于文件有关的信息:文件的类型,文件的访问权限,文件所占用的数据块的指针等。接下来我们可以认识下这个常听说的文件的inode节点,并且阐述mv操作对文件的inode影响。
如图2为磁盘、分区和文件系统的结构图

 

 

通用文件模型由下列对象类型组成:
• 超级块(superblock)对象: 存放系统中已安装文件系统的有关信息。对于基于磁盘的文件系统,这类对象通常对应于存放在磁盘上的文件系统控制块,也就是说,每个文件系统都有一个超级块对象.
• 索引节点(inode)对象: 存放关于具体文件的一般信息。对于基于磁盘的文件系统,这类对象通常对应于存放在磁盘上的文件控制块(FCB),也就是说,每个文件都有一个索引节点对象。每个索引节点对象都有一个索引节点号,这个号唯一地标识某个文件系统中的指定文件。
• 目录项(dentry)对象: 存放目录项与对应文件进行链接的信息。VFS把每个目录看作一个由若干子目录和文件组成的常规文件。例如,在查找 路径名/tmp/test时 , 内核为 根目录“/ ”创建一个目录项对象, 为根目录下的 tmp项创建一个第二级目录项对象,为 /tmp 目录下的test项创建一个第三级目录项对象。
• 文件(file)对象: 存放打开文件与进程之间进行交互的有关信息。这类信息仅当进程访问文件期间存在于内存中详细信息可以参考《UNIX环境高级编程》的第四章《文件和目录》和《深入分析Linux内核源码》。

2. mv在同一个分区之内是执行的rename的操作,不会更改i节点的信息。


首先可以通过常用的命令,如ls -li,stat等命令来认识下inode节点。
2.1)首先可以通过,df命令来查看磁盘的分区,如下操作:
//用df -i来查一下磁盘空间
# df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 7913472 126186 7787286 2% /
2.2)然后可以通过ls -li查看i节点的信息,如以下操作中会在第一列中显示文件的inode的编号。
//ls -li来查一个文件的inumber
#ls -i /bin/ping
1032194 -rwsr-xr-x 1 root root 33272 Apr 14 2006 /bin/ping
2.3)进一步,可以通过stat来查文件的信息。该显示的信息会比较多,其中stat结构中大多数的信息都是来自i节点,只有两项数据是存放在目录项当中:文件名和i节点的编号。
# stat /bin/ping
File: `/bin/ping'
Size: 33272 Blocks: 80 IO Block: 4096 regular file
Device: 801h/2049d Inode: 1032194 Links: 1
Access: (4755/-rwsr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2009-08-04 11:13:08.000000000 +0800
Modify: 2006-04-14 21:16:26.000000000 +0800
Change: 2009-07-01 14:04:44.000000000 +0800

2.4)mv对inode的影响:
如果mv命令的目标和源文件所在的分区相同:
1)使用新文件名建立目录项(dentry),将新文件名称对应到inode 编号;
2)解除与旧目录项的链接;
需要注意的是:该操作对inode表没有影响(除时间戳),对数据的位置也没有影响,不移动任何数据。只需要构造一个现有i节点的新目录项,并解除和旧目录项的链接。在实践中也能得知:不论文件有多大,执行mv的时间都是“瞬间”完成。
2.5)查看到i节点的信息并未改变:
通过ls –li或者stat filename来查看具体的信息,如实际的例子:如下图3所示,通过mv操作之后,并不会改变i节点的信息。

 

 

从上述图中可以得出,mv操作并不改变i节点的编号,并且其实现是通过rename的机制来实现的。

2.8) 通过mv的源码来查看其信息。可以查看(coreutils-8.9)的源码。
mv操作是针对cp_options这个结构体,其中该结构体中的move_mode决定了方式。即判断是否在一个分区内,如下示意图为调用的信息。
在mv.c中movefile->do_move->copy,在movefile中传入了cp_options的结构体,并通过move_mode来决定是进行rename还是read和write的方式。

结论:由以上对inode的实践,并通过strace来追踪mv的实现机制、mv的源码,可以得出在同一个分区中,mv实际进行的是rename操作。接下来本文将讲述rename的实现。

 

 

3. rename是一种原子操作


“Rename是一种原子操作”,如果要将这个问题讲述清楚,则需要讲到的是“Linux文件系统中元数据的加锁机制与组织方式”。
在Linux系统中,需要对元数据进行加锁,元数据操作是一种事务操作,需要满足原子性,一致性,独立性和持久性。为了解决这种元数据操作带来的一致性的问题和多个元数据操作的交互和重叠的问题,采用加锁的方式。
如:操作1,在目录a下创建了b, 递增目录 a 的 nlink 值,操作2删除目录a,如果并发的进行就会出问题,但是如果通过加锁的方式,先对a进行加锁,再解锁,就是一种串行的执行,则不会出现问题。
如图6元数据操作的死锁所示:如果此时系统中没有相应的加锁机制对元数据操作进行互斥,那么当操作①创建了对象 b 以后,接下来操作②有可能就将目录 a 删除了,当操作①要递增目录 a 的 nlink 值时,就会发现没有可操作的对象了,于是操作出错。

 

为了解决这个问题:Linux规定同一时间只能有一个rename的操作,当然这个是针对同一个文件系统内的,该实现机制也是通过加锁来实现的。具体信息可以参考IBM develop上的《Linux文件系统中元数据的加锁机制与组织方式》,链接为: http://www.ibm.com/developerworks/cn/linux/l-cn-fsmeta/

继续看mv的操作实现原理,如图8所示,解释刚才出现的问题:

 

 

总结mv操作的实现过程:
1) 如果通过stat返回与此命名文件有关的信息结构,lstat函数类似于stat,但是当命名的文件是一个符号链接时,lstat返回该符号链接的有关信息,而不是由该符号链接引用文件的信息。
2) 通过access文件b(W_OK),查看文件b是否有写入的权限。对于access,它会检查是否可以读/写某一已存在的文件。参数mode有几种情况组合, R_OK,W_OK,X_OK 和F_OK。R_OK,W_OK与X_OK用来检查文件是否具有读取、写入和执行的权限。由于access只作权限的核查,并不理会文件形态或文件内容。
3) 进行rename的重命名操作。
注意:rename操作可以根据所操作的为文件还是目录项,分为几种情况,由于本文的业务背景,目前只是讨论所操作的为文件的情况,所以不将详细阐述,具体的可以参考《UNIX环境高级编程》中第四章的内容,对于rename操作,其原型为:
int rename(const char * oldname,const char * newname)
需要说明的是:如果newname已经存在,且为文件的,则先将该目录项删除,然后将oldname更名为newname,若newname不存在,则只需要进行更名即可

 

 

 

 

 

 

 

参考转载:

https://blog.51cto.com/baidutech/743731

标签:文件,理解,文件系统,mv,深入,操作,inode,目录
From: https://www.cnblogs.com/rebrobot/p/16776718.html

相关文章

  • 我对软件工程的理解(之前写在“文章”一栏中,故再发一次)
     作为一个化学专业的学生,我对软件工程的理解还比较粗浅,辅修计算机是认为在当今时代计算机不仅是一门科学,也是一项强大的工具。我认为软件工程致力于构建有效、实用、高......
  • 初识内存中的数据——由浅入深理解程序的底层实现原理(一)
    引言:要想成为一名合格的开发者,掌握计算机系统工作原理是必须的,而在学这些之前应具有一门编程语言(汇编最好)的基础和一些计算机底层基础。本篇,我将从零开始一步步地探究高级......
  • 利用MVC设计模式构建GUI(PyQt5版)
    今天介绍一个PyQt5中利用MVC设计模式构建GUI的例子,这个案例来源于《MATLAB面向对象编程——从入门到设计模式(第2版)》第7章内容,关于存取款的GUI工具设计,详情请参考127~160页......
  • 基础知识 | 目标检测中Anchor的认识及理解
    近期好多同学在私信让我说一些基础性的知识。好多入门的同学在纠结Anchor的设置,而且部分同学私信,可不可以把这个基础知识详细说一次,今天就单独开一次小课,一起来学习FasterR......
  • 深入理解css 笔记(3)
    过去经常将网页的根元素字号设置为0.625em或者62.5%。这样是为了将全局的font-size改成10px。这样设计师希望字号14px时,只需要写成1.4rem就好了。还使用了相对单......
  • epoll和ractor的粗浅理解
    我们继续上篇的文章继续更新我们的代码。首先就是介绍一下epoll的三个函数。epoll_createepoll_ctlepoll_wait如何去理解这3个函数,我是这样去理解这个函数,就像我们......
  • js闭包理解
    js闭包其实就是一句话闭包变量就是函数对象的属性例1functionf1(){varn=999;functionf2(){n++;alert(n);}returnf2;}varresult=f1();result();r......
  • 关于对JS-面向对象-的理解
    最近看了《你不知道的JS上卷》这本书,写下自己的一点感悟!类首先,关于类,这是一种设计模式。JS是一门真正面对对象的语言为什么这样说呢?像JAVA这种众所周知的面对对象的语......
  • 深入理解Spark:核心思想与源码分析 pdf
    高清扫描版下载链接:https://pan.baidu.com/s/1HkOYrJjosNWRo0QLgQM8uA点击这里获取提取码 ......
  • 深入RabbitMQ pdf
    高清扫描版下载链接:https://pan.baidu.com/s/1rF5SIH173eSOxWbhz1tk1A点击这里获取提取码 ......