1.Linux中的文件
1.1 文件、文件描述符和文件表
Linux内核将一切视为文件,那么Linux的文件是什么呢?其既可以是事实上真正的物理文件,也可以是设备、管道,甚至还可以是一块内存。狭义的文件是指文件系统中的物理文件,而广义的文件是Linux管理的所有对象。这些广义的文件利用VFS机制,以文件系统的形式挂载在Linux内核中,对外提供一致的文件操作接口。
VFS机制:https://www.ibm.com/developerworks/cn/linux/l-vfs/
图解:https://blog.csdn.net/softgmx/article/details/82259567
https://blog.csdn.net/dkh63671763/article/details/54731150
从数值上看,文件描述符是一个非负整数,其本质就是一个句柄,所以也可以认为文件描述符就是一个文件句柄。那么何为句柄呢?一切对于用户透明的返回值,即可视为句柄。用户空间利用文件描述符与内核进行交互;而内核拿到文件描述符后,可以通过它得到用于管理文件的真正的数据结构。
使用文件描述符即句柄,有两个好处:一是增加了安全性,句柄类型对用户完全透明,用户无法通过任何hacking的方式,更改句柄对用的内部结果,比如Linux内核的文件描述符,只有内核才能通过该值得到相应的文件结构;二是增加了可扩展性,用户的代码只依赖于句柄的值,这样实际的结构的类型就可以随时发生变化,与句柄的映射关系也可以随时改变,这些变化都不回影响任何现有的用户代码。
Linux的每个进程都会维护一个文件表,以便维护该进程打开的文件信息,包括打开的文件的个数、每个文件的偏移量等信息。
1.2 内核文件表的实现
内核中进程对应的结构体是task_struct,进程的文件表保存在task_struct->files中。
下面看看files_struct是如何使用默认的fdtab和fd_array的,init是Linux的第一个进程,它的文件表是一个全局变量,代码如下:
init_files.fdt和init_files.fdtab.fd都分别指向了自己已有的成员变量,并以此作为一个默认值。后面的进程都是从init进程fork出来的。fork的时候会调用dup_fd,而在dup_fd中其代码结构如下:
初始化new_fdt,同样是为了让new_fdt和new_fdt->fd指向其本身的成员变量fdtab和fd_array。
/proc/pid/status为对应pid的进程的当前运行状态,其中FDSize值即为当前进程max_fds的值。
因此,初始状态下,files_struct、fdtable和files的关系如图所示。