看了以前的关于Linux内存管理和进程调度的文章,相比读者们应该对Linux有了大体的了解,本文的主题是Linux虚拟文件系统。闲话少说,开始!node
1.软连接和硬连接的区别缓存
咱们知道文件都有文件名与数据,数据分两部分:用户数据 (user data) 与元数据 (metadata)。用户数据,即文件数据块 (data block),数据块是记录文件真实内容的地方;而元数据则是文件的附加属性,如文件大小、建立时间、全部者等信息。在 Linux 中,元数据中的 inode 号(inode 是文件元数据的一部分但其并不包含文件名,inode 号即索引节点号)才是文件的惟一标识而非文件名。文件名仅是为了方便人们的记忆和使用,系统或程序经过 inode 号寻找正确的文件数据块安全
为解决文件的共享使用,Linux 系统引入了两种连接:硬连接 (hard link) 与软连接(又称符号连接,即 soft link 或 symbolic link)。连接为 Linux 系统解决了文件的共享使用,还带来了隐藏文件路径、增长权限安全及节省存储等好处。若一个 inode 号对应多个文件名,则称这些文件为硬连接。硬连接就是同一个文件使用了多个别名网络
因为硬连接是有着相同 inode 号仅文件名不一样的文件,所以硬连接存在如下几点特性:数据结构
inode 号仅在各文件系统下是惟一的,当 Linux 挂载多个文件系统后将出现 inode 号重复的现象,所以硬连接建立时不可跨文件系统socket
软连接与硬连接不一样,若文件用户数据块中存放的内容是另外一文件的路径名的指向,则该文件就是软链接。软连接就是一个普通文件,只是数据块内容有点特殊。软连接有着本身的 inode 号以及用户数据块。所以软连接的建立与使用没有相似硬连接的诸多限制:ide
2.Linux VFS函数
Linux 有着极其丰富的文件系统,大致上可分以下几类:操作系统
实现以上这些文件系统并在 Linux 下共存的基础就是 Linux VFS(Virtual File System 又称 Virtual Filesystem Switch),即虚拟文件系统。VFS 做为一个通用的文件系统,抽象了文件系统的四个基本概念:文件、目录项 (dentry)、索引节点 (inode) 及挂载点,其在内核中为用户空间层的文件系统提供了相关的接口。VFS 实现了 open()、read() 等系统调并使得 cp 等用户空间程序可跨文件系统。VFS 真正实现了上述内容中:在 Linux 中除进程以外一切皆是文件。设计
Linux VFS 存在四个基本对象:超级块对象 (superblock object)、索引节点对象 (inode object)、目录项对象 (dentry object) 及文件对象 (file object)。超级块对象表明一个已安装的文件系统;索引节点对象表明一个文件;目录项对象表明一个目录项,如设备文件 event5 在路径 /dev/input/event5 中,其存在四个目录项对象:/ 、dev/ 、input/ 及 event5。文件对象表明由进程打开的文件。为文件路径的快速解析,Linux VFS 设计了目录项缓存(Directory Entry Cache,即 dcache)。
3.文件的打开过程
open()系统调用的过程以下:
1.查看system-wide open-file table(系统打开文件表)中是否有该文件,即查看该文件是否已经被其余进程打开了
2.若是存在,那么该进程会在本身的per-process open-file table(进程打开文件表)中,创建一个项目,指向system-wide open-file table中的该文件
3.若是不存在,则须要根据file name在directory中查找该file,一般directory中的部份内容在cache中,这样能够加快搜索速度。
4.一旦文件被找到,那么FCB(file control block)文件控制块会被复制到system-wide open-file table中,该表不只仅保存FCB,并且记录每一个文件被多少个进程打开
5.接下来,在per-process open-file table(进程打开文件表)中,简直一个entry,指向进程打开文件表中该项目
当进程close()一个文件时:
1.该进程的per-process open-flle table中的对应项会被删除,系统打开表中的该文件计数器会减1
2.若是系统打开表中的计算为0,那么删除该文件项
4.inode的理解
操做系统读取硬盘的时候,不会一个个扇区地读取,这样效率过低,而是一次性连续读取多个扇区,即一次性读取一个"块"(block)。这种由多个扇区组成的"块",是文件存取的最小单位。"块"的大小,最多见的是4KB,即连续八个 sector组成一个 block。
文件数据都储存在"块"中,那么很显然,咱们还必须找到一个地方储存文件的元信息,好比文件的建立者、文件的建立日期、文件的大小等等。这种储存文件元信息的区域就叫作inode,中文译名为"索引节点"。
inode包含文件的元信息,具体来讲有如下内容:
* 文件的字节数
* 文件拥有者的User ID
* 文件的Group ID
* 文件的读、写、执行权限
* 文件的时间戳,共有三个:ctime指inode上一次变更的时间,mtime指文件内容上一次变更的时间,atime指文件上一次打开的时间。
* 连接数,即有多少文件名指向这个inode
* 文件数据block的位置
除了文件名之外的全部文件信息,都存在inode之中
每一个inode都有一个号码,操做系统用inode号码来识别不一样的文件。
表面上,用户经过文件名,打开文件。实际上,系统内部这个过程分红三步:首先,系统找到这个文件名对应的inode号;其次,经过inode号,获取inode信息;最后,根据inode信息,找到文件数据所在的block,读出数据。
目录(directory)也是一种文件,目录文件的结构很是简单,就是一系列目录项(dirent)的列表。每一个目录项,由两部分组成:所包含文件的文件名,以及该文件名对应的inode号码。
数据块寻址
inode中记录了文件数据块的位置,有三种寻址方式:direct blocks直接指向数据块;single indirect指向一个block,该block中为数据块的指针;double indirect,两级block
Linux系统篇-文件系统&虚拟文件系统(很是重要!)
5.文件描述符
在Linux系统中一切皆能够当作是文件,文件又可分为:普通文件、目录文件、连接文件和设备文件。文件描述符(file descriptor)是内核为了高效管理已被打开的文件所建立的索引,其是一个非负整数(一般是小整数),用于指代被打开的文件,全部执行I/O操做的系统调用都经过文件描述符。程序刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误。若是此时去打开一个新的文件,它的文件描述符会是3。POSIX标准要求每次打开文件时(含socket)必须使用当前进程中最小可用的文件描述符号码
文件描述符是系统的一个重要资源,虽说系统内存有多少就能够打开多少的文件描述符,可是在实际实现过程当中内核是会作相应的处理的,通常最大打开文件数会是系统内存的10%(以KB来计算)(称之为系统级限制)
6.文件描述符和打开文件之间的关系
每个文件描述符会与一个打开文件相对应,同时,不一样的文件描述符也会指向同一个文件。相同的文件能够被不一样的进程打开也能够在同一个进程中被屡次打开。系统为每个进程维护了一个文件描述符表,该表的值都是从0开始的,因此在不一样的进程中你会看到相同的文件描述符,这种状况下相同文件描述符有可能指向同一个文件,也有可能指向不一样的文件。具体状况要具体分析,要理解具体其概况如何,须要查看由内核维护的3个数据结构。
1. 进程级的文件描述符表
2. 系统级的打开文件描述符表
3. 文件系统的i-node表
进程级的描述符表的每一条目记录了单个文件描述符的相关信息。
1. 控制文件描述符操做的一组标志。(目前,此类标志仅定义了一个,即close-on-exec标志)
2. 对打开文件句柄的引用
内核对全部打开的文件的文件维护有一个系统级的描述符表格(open file description table)。有时,也称之为打开文件表(open file table),并将表格中各条目称为打开文件句柄(open file handle)。一个打开文件句柄存储了与一个打开文件相关的所有信息,以下所示:
1. 当前文件偏移量(调用read()和write()时更新,或使用lseek()直接修改)
2. 打开文件时所使用的状态标识(即,open()的flags参数)
3. 文件访问模式(如调用open()时所设置的只读模式、只写模式或读写模式)
4. 与信号驱动相关的设置
5. 对该文件i-node对象的引用
6. 文件类型(例如:常规文件、套接字或FIFO)和访问权限
7. 一个指针,指向该文件所持有的锁列表
8. 文件的各类属性,包括文件大小以及与不一样类型操做相关的时间戳
在进程A中,文件描述符1和30都指向了同一个打开的文件句柄(标号23)。这多是经过调用dup()、dup2()、fcntl()或者对同一个文件屡次调用了open()函数而造成的。
进程A的文件描述符2和进程B的文件描述符2都指向了同一个打开的文件句柄(标号73)。这种情形多是在调用fork()后出现的(即,进程A、B是父子进程关系),或者当某进程经过UNIX域套接字将一个打开的文件描述符传递给另外一个进程时,也会发生。再者是不一样的进程独自去调用open函数打开了同一个文件,此时进程内部的描述符正好分配到与其余进程打开该文件的描述符同样。
此外,进程A的描述符0和进程B的描述符3分别指向不一样的打开文件句柄,但这些句柄均指向i-node表的相同条目(1976),换言之,指向同一个文件。发生这种状况是由于每一个进程各自对同一个文件发起了open()调用。同一个进程两次打开同一个文件,也会发生相似状况。
7. 总结
1. 因为进程级文件描述符表的存在,不一样的进程中会出现相同的文件描述符,它们可能指向同一个文件,也可能指向不一样的文件
2. 两个不一样的文件描述符,若指向同一个打开文件句柄,将共享同一文件偏移量。所以,若是经过其中一个文件描述符来修改文件偏移量(由调用read()、write()或lseek()所致),那么从另外一个描述符中也会观察到变化,不管这两个文件描述符是否属于不一样进程,仍是同一个进程,状况都是如此。
3. 要获取和修改打开的文件标志(例如:O_APPEND、O_NONBLOCK和O_ASYNC),可执行fcntl()的F_GETFL和F_SETFL操做,其对做用域的约束与上一条颇为相似。
4. 文件描述符标志(即,close-on-exec)为进程和文件描述符所私有。对这一标志的修改将不会影响同一进程或不一样进程中的其余文件描述符