File Descriptor -> Open File Description -> Inode -> Block


Inode和Block

在Linux系统里“一切皆文件”,文件存储于文件系统。Linux文件系统里包含了两个基本的区块inode和block。这儿搬运一下《鸟哥Linux私房菜》里的图: inode

简而言之:

  • 文件的内容由若干块block组成,每块block的大小可以是1K、2K和4K等;
  • inode就是用来索引文件block的区块,此外还记录了文件的一些信息,用stat系统调用函数可以查看这些信息。
  • 对于大文件,则是用block块替代inode作索引,甚至作多级索引。

值得一提的还有,多个不同文件可以指向同一个inode,表面上这些文件的绝对路径是不同,但其本质是一体的,这就是硬链接。不过目录不能做硬链接,我的理解是如果目录可以做硬链接,这样就能构造出一个循环的目录路径,系统在挂载文件系统的时候不能很好的处理,所以就禁止目录做硬链接。

不妨就把inode当作文件的本身,而inode指向的block存放文件的内容。


File Descriptor 和 Open File Description

对文件的基本操作有open(2)close(2)这两个系统调用函数。

1
int open(const char *pathname, int flags);

1
int close(int fd);

open()函数打开一个文件并返回一个文件描述符(file descriptor),而close()函数则用来关闭指定的文件描述符。从一个整体的角度去看,文件描述符就表示了被打开的那个文件,指向了文件的inode。

事实上,在man page里对于open()close()函数的描述里除了提到了file descriptor,还提到了一个叫open file description的名词。为了方便观看,下面的描述中将file descriptor简称为FD,而open file description简称为OFD

  • open()调用成功后,会在一个系统全局的表中创建一个OFD的项,这个数据结构除了有指向打开文件的inode信息,还有文件打开的状态标识和文件当前偏移量等信息。同时在进程表中会创建一个FD项,其值取为当前进程还未用到的最小非负整数,并指向那个OFD项。
  • FD项与OFD项是多对一的关系。利用dupdup2函数以及fork出来的子进程可以复制FD,这些复制的文件描述符FD所指的OFD项不变。
  • close()调用成功后,会移除进程内维护的指定的FD项,并且如果其指向的OFD项只剩该FD这最后一个指向,那么这个孤立的OFD项也会被移除。

于是乎,进程调用open()打开一个文件,在进程内维护的是file descriptor这一项,其指向系统内核维护的open file description项,而该项又指向文件系统的inode

另外呢,观察进程打开的文件描述符可以用如下命令;

1
2
3
ls -l /proc/PID/fd/ # 列出进程号为PID的进程打开的文件描述符
ls -l /proc/self/fd/ # 列出当前进程打开的文件描述符
ls -l /dev/fd/ # 同上,这是一个指向上面那个目录的符号链接


最后

这四者的关系从整体上可以用这张图来表示(参考自APUE): File Descriptor->Open File Description->Inode->Block