Linux有一个著名的理念:一切皆文件,在Linux中,可以把任何东西都理解为文件,这是因为Linux文件包含很多类型:
- 普通文件,包括文本文件和二进制文件;
- 目录文件,文件目录本身其实就是一个目录类型的文件,可以通过vim等文本编辑器直接打开,但只有操作系统内核才能直接写目录文件;
- 块特殊文件,提供对设备(如磁盘)带缓冲的访问;
- 字符特殊文件:提供对设备不带缓冲的访问;
- 管道:用于进程间网络通讯的管道文件;
- 套接字:用于进程间通讯的套接字文件,包括网络套接字和本地套接字;
- 符号链接(软链接):与硬链接相对应,指链接到硬链接的文件。
Linux文件系统将硬盘分为扇区,通常一个扇区的大小为512Byte,多个扇区组成一个块,块的大小为4k,这与我们在虚拟地址与物理地址转换中介绍的页框大小相同。文件数据以块为单位储存,块中的文件信息通过i-node节点来管理。i-node节点中记录了文件相关的信息,这些信息可以通过stat()函数或stat命令来访问,stat()函数调用后返回一个stat结构体,具体结构见下面的代码:
struct stat{
mode_t st_mode; //文件类型和访问权限
ino_t st_ino; //i-node节点号
dev_t st_dev; //文件系统设备号
dev_t st_rdev; //特殊文件设备号
nlink_t st_nlink; //链接数
uid_t st_uid; //用户ID
gid_t st_gid; //组ID
off_t st_size; //普通文件大小
struct timespec st_atime; //最近访问时间
struct timespec st_mtime; //最近修改时间
struct timespec st_ctime; //最近i-node修改时间
blksize_t st_blksize; //IO块块大小
blkcnt_t st_blocks; //数据在硬盘块中的位置
}
下图是一段文件系统的放大图:
图中的目录块记录了一个文件的节点编号和文件名,当我们修改一个文件的文件名时,文件系统将构造一个新的目录项,删除原来的目录项。i-node会记录链接的目录块数量,当我们删除i-node中记录的目录块时,链接数将减1,当链接数为0时,才能释放磁盘中的数据。这能解释为什么我们在打开一个文件时可以进行删除文件的操作,而文件实际上并不会被删除。同时,需要注意目录项只能指向同一文件系统中的i-node。
下面来看一个实际的例子,下图是一个空目录,我们先通过stat命令查看当前i-node(Linux中,”.”表示当前目录,”…”表示上一级目录):
接着我们新建目录testdir:
再通过stat命令查看此时的i-node:
可以看到此时硬链接数增加了1,这是因为testdir的”…”链接到了当前目录的”.”上,新建的testdir的链接数为2,事实上,任意空目录的链接数都为2,包括命名这个目录的目录和”.”两个链接。上面的情况用图片表示如下:
下面是进入testdir的链接情况,根据上面的分析很容易可以推出链接数的来源。
软链接和硬链接
硬链接指直接链接到i-node的链接,因此上面我们讨论的都属于硬链接。Linux中还有一类链接被称为软链接,软链接类似于windows中的快捷方式,以指针方式指向硬链接。
引入软链接主要是为了解决下面两个问题:
- 硬链接要求数据和链接在同一文件系统中;
- 创建硬链接需要超级用户权限;
软链接没有上面两个限制,任何用户都可以创建指向目录的软链接。使用函数处理软链接时,需要注意函数是否跟随软链接,即函数是否会跟随软链接处理指向的文件,还是处理软链接本身。如果函数跟随软链接,可能会导致循环,比如下面的模型:
此时软链接指向自己的父目录。这种文件分布可以用下图表示:
当我们使用函数打印这个目录,如果函数跟随软链接,将陷入循环,errno被设置为ELOOP。
总结
本文介绍了Linux文件系统的基本储存结构和硬链接软链接,后续将借助前面介绍的虚拟地址空间进一步介绍文件在进程中打开后的属性。