文件系统
在unix文件系统是unix系统的心脏部分,提供了层次结构的目录和文件.文件系统将磁盘空间划分为每1024个字节为一组,称为块(也有用
512字节)
为一块的.编号从0到整个磁盘空间的最大块数.
在unix当中,之前我们在文件描述符 文件表项 v节点表项符当中多次提到了i节点,但是我们想了解i节点就必须了解磁盘空间当中的文件
系统,所
以
我们来介绍unix文件系统的基本结构. 同时了解i节点和指向i节点的目录之间的区别是非常重要的.
我们可以把一个磁盘分
为一个或多个分区,
每个分区可以包含一个文件系统. i节点是固定长度的记录项,它包括有关文件的大部分信息.
我们可以看到图上每个分区都是一个文件系统,其实在磁盘当中分的更细,分区中每个柱面其实都是一个小的文件系统,这里的文件系统分为:
>超级块: 文件系统当中的第一块被称为超级块,这个块存放文件系统本身的结构信息,比如每个区域的大小,未被使用的磁盘块的信息.
>i节点: 超级块的下一部分就是i节点表,文件系统中的每一个文件都在该表中都对应一个i节点. i节点是固定长度的记录项,它包含有关文件的
大部分信息. Linux文件系统使用 索引节点i 来记录文件信息,索引节点是一个结构,用固定长度. 它包含了一个文件的长度,创建以及修改时间
,权限,所述关系,找到磁盘存储位置的指针等等信息.
>数据区: 文件的内容保存在这个区域中,磁盘上所有块的大小都相同,如果文件所包含了超过一个快的内容,则文件内容存放在多个磁盘块中,
并把磁盘块的分配情况记录在文件的i节点中的磁盘序列表中.
一个文件系统当中分有许许多多的柱面题,如果我们仔细观察每一个柱面题当中的i节点和数据库
部分,那么它的结构就是下图这样的结构:
记住unix/Linux下一切皆文件,目录也是文件! 我们来分析这个结构: 每一个目录块和数据块都有自己唯一指定的i节点. 但是一个i节
点可能接受
多个文件或者多个目录(同一个i节点不可能有文件和目录共同指向). 所以一个i节点里面存储关于自己对应文件或者目录的相
应信息.你只需要知道
你的i节点编号就能找到相应的文件信息.
一个i节点只能存储一个文件或者目录的信息. 并且不能多个目录公用一个
i节点,因为这样可能会导致环
形链接,导致文件遍历出错.
还
有一个
问题,看下图:
在图中有两个目录块当中的文件指向同一个i节点.每一个i节点中都有一个链接计数,其值是指向该i节点的目录项数(或者文件计数).只
有当链接计
数减少至0时,
才可删除文
件. 这就是为什么"解除对一个文件的链接"操作并不总是意味着"释放该文件占用的磁盘块"的原因.
这也是为什么删除一
个目录项的函数
被称之为
unlink而不是delete的原因. 在stat结构中,链接计数包含在st_nlink成员中,其基本系
统
数据类型为nlink_t,这种链
接类型称为
硬链
接. 其中
POSIX.1常量LINK_MAX指定了一个文件链接数的最大值.
另一种链接类型称为符号链接,符号链接文件的实际内容包含了该符号链接所指向的文件的名字. 在下面的例子中,目录项中的文件名是
三个字符
字符串lib,而在该文件中包含了7个字节的数据user/lib:
1rwxrwxrwx 1 root 7 sep 25 07:14 lib->urs/lib
该i节点中的文件类型是S_IFLNK,于是系统知道这是一个符号链接(软连接).
i节点中包含了文件有关的所有信息:文件类型,文件访问权限位,文件长度和指向文件数据块的指针等. stat结构中的大多数信息都是取
自i节点。
只有两项重要数据存放在目录项中:文件名和i节点编号.i节点编号的数据类型是ino_t.
因为目录项中的i节点编号指向同一文件系统中的相应i节点,一个目录项不能指向另外一个文件系统的i节点. 这也就是为什么in命令,
不能跨越文
件系统的原因. 我们说明了普通文件的链接计数概念,但是对于目录文件的链接计数字段又如何呢? 假定我们再工作目录
中构造了一个新目录:
$mkdir testdir
我们现在观察这个图,该图显式的显示出了.(当前目录)和..(上层目录)项. 编号2549的i节点,其类型字段表示它是一个目录,链接计数
为2.任
何一个叶目录的链接的链接计数总为2,数值2来自于命名该目录(testdir)的目录项以及该目录的.项. 编号为1267的i节点,我
们就叫他hehedir,其
类型字段表示它是一
个目录,链接计数大于或等于3. 它大于或等于3的原因是,至少有三个目录项指向他. 第一个是
命名hehedir的目录,这个很容
易
理解,当你创建了testdir之后,hehedir就会有一项链接testdir,同理创建hehedir的上层目录肯定也
会链接hehedir的.
第二个
是在
该目录中的.
项,第三个是在其子目录testdir中的..项. 注意,在父目录中的每一个子目录都使该父目录的
链
接计数增加1.
如上所述,任何一个文件可以有多
个目录项指向其i节点.
创建一个指向现有文件的链接的方法是使用link函数或者linkat函数.
int link(const char* oldpath,const char* newpath);
int linkat(int efd,const char *oldpath,int nfd,const char* newpath,int flag);
两个都是成功返回0,出错返回-1. 创建新目录项和增加链接计数应该是一个原子操作的 虽然POSIX.1允许实现支持跨越文件系统的链接,
但是大多
数实现要求现有的和新建的两个路径名在同一个文件系统中.如果实现支持创建指向一个目录的硬链接,那么也极限于超级用户
其理由是这样做可
能会在文件系统中形成循环.大多数处理文件系统的实用程序,大多数处理文件系统的实用程序都不能出路这种情况.
因此很多文件系统不允许对目
录的硬链接. 为了删除一个现有的目录项,可以调用unlink函数.
int unlink(const char* pathname);
int unlinkat(int fd,const char* pathname,int flag)
这两个函数会删除目录项,并且将由pathname所引用文件的链接计数减一. 如果对该文件还有其他链接,则仍可通过其他链接访问该文件
的数据.
如果出错对文件不进行任何更改. 我们前面已经提到过,为了解除对文件的链接,必须对包含该目录项的目录具有写和执行权限.
如果对该目录设
置了粘着位,则对该目录必须拥有写权限,并且具备下面三个条件之一:
1.拥有该文件
2.拥有该目录
3.具有超级用户.
只有当链接计数为0的时候,该文件内容才可以被删除. 还有就是当一个进程打开这个文件,其内容也不能删除. 关闭一个文件时,内核首
先检查打
开该文件的进程个数; 如果这个技术达到0,内核再去检查其链接计数; 如果链接计数也为0,那么就删除该文件的内容.