Linux操作系统进程与文件的关系

2,039 阅读3分钟

本文重点介绍一下Linux操作系统进程(线程)与文件描述符、文件的关系,具体到内核部分就是task_struct、files_struct、file和inode的关系。

我们在Linux用户态开发都清楚,打开一个文件之后会返回一个文件描述符,而且每个进程打开文件的数量是有限的。这个具体是什么什么原因?

如果我们深入到Linux操作系统的内核,就会知道其中的奥秘。在Linux操作系统,每个用户态的进程在内核态都有一个对应的内核进程(线程),这个在内核中通过task_struct结构体标识,内核通过其实现对进程的调度。而在内核中对于文件的访问则是通过file和inode结构体实现的,其中包含这访问文件的关键信息(例如访问偏移)和方法(例如读写文件操作)。

fd=open(“/home/zhf/zhf/c_prj/itworld123.com”,O_RDWR);

进程与文件的关系

如下图是典型的进程与文件的关系图,图中进程打开了两个不同的文件。在进程结构体(task_struct)中有一个files_struct成员,其中保存这一个数组,这个数组的偏移量就是文件描述符,而其中的成员则是file结构体的指针。这样,通过用户态的整型的文件描述符可以很方便的找到管理文件的结构体(file)进而实现对文件的操作。本文为了方便说明,对files_struct结构体进行了简化处理,实际上该结构提要复杂很多。

图1 单进程概图

进程(task_struct)与文件结构体(file)的关系清楚了,那么文件结构体又是怎么来的,它跟inode的关系是什么样的呢?如上图所示,每一个file都有一个对应的inode的结构体。两种其实都对应着一个磁盘上的具体文件,但又有差异。file其实对应这一个打开文件的实例,而inode一一对应一个磁盘文件。也就是说一个磁盘文件对应的file在内存中可能有多份,而inode则只会有一份。后续我们会详细解释具体实现。

父子进程与文件的关系

我们知道在Linux中进程都存在一些父子关系,而且子进程会继承父进程的很多内容。那么如果我们fork出一个子进程,此时子进程会继承父进程文件相关的内容。如果我们使用的是fork系统调用,此时子进程会创建一个新的files_struct实例,并将父进程的内容迁移过来,这里说迁移,而不是拷贝,其原因是并不是原封不懂的内存拷贝,而是会做一些处理。比如父进程中对file结构体的指向,在子进程中也会指向,且文件描述符一致,同时会增加file结构体实例的引用计数,确保使用关系的正确性。

还有一种情况,Linux操作系统提供了另外一种创建子进程的方法,也就是clone方法。通过该方法创建进程时可以指定子进程可以不继承父进程的那些内容。如果此时传入了CLONE_FILES参数,则不会进行files_struct结构体的迁移,而只是增加一个该结构体的引用计数。也就是父进程和子进程中的files指针指向相同的内存区域,此时整个关系如下图所示。

图3 进程clone关系图

先到这里,后续本文会继续讨论file结构体与inode及磁盘数据的关系。