fork后发生了什么——文件篇

/ 0评 / 0

首先思考一个问题,我们知道,fork函数会复制父进程的所有打开的文件描述符给子进程(vfork不会复制),对于父进程打开的所有文件,子进程也可以通过句柄值相同的文件描述符进行读写。那么,父子进程同时操作一个文件,这个过程是并行独立,还是互相影响?父子进程同时操作一个文件时,是什么现象?

首先可以肯定的是,父子进程对同一个文件的写必须是同步起来的,否则还怎么保证文件最终数据的一致性,但是对于读操作,似乎并行是没有问题的,毕竟互不影响。事实究竟如何呢,我们通过一个父子进程同时读同一份文件的例子来看看知道了:

image.png

image.png

结果如图:

image.png

父子进程读是共享的一份偏移量,否则就会各自打出1~8

对于文件操作的两个基本函数,read函数和write函数都没有将偏移量作为参数,然而我们在使用这两个函数时,却每次都能接着上次调用结束后的位置继续读写,原因就是内核已经将偏移量记录在了与专门描述信息被打开文件的结构里,所以要弄明白父子进程同时对于文件操作的本质,就要研究父子进程到底是如何共用一个偏移量,我们看看源码中的流程


在fork中关于文件的调用:copy_files---->dup_fdcopy_files中会做一些判断:比如是否应该拷贝文件描述符,因为对于vforkpthread_create不需要dup_fd去进行文件描述符拷贝,当然文件描述符只是一个句柄,关键是与它关联的结构。        

        files_struct是PCB中与打开的文件相关联的结构        

image.png

dup_fd会去给子进程分配一个这样的结构,做一些初始化的赋值。看起来似乎file_structfdtable中有不少相似的字段,比如close_on_exec标志位、open_fds、以及file指针数组等,仔细对比下就能发现,file_struct存的是实例,fdtable里存的是指针。内核选取long类型作为位图来记录文件的打开情况,这样做是出于考虑到进程不会打开太多文件,long算是一个经验值,因此当打开的文件小于64(32位平台为32)时files_struct自带的两个位图和一个fd_array就足够了,于是可以用这这些去初始化fdtable实例,如下

image.png

此时fdtable指针还并未指向自带的fdtable实例,原因就是还要检查64个够不够,当打开的文件大于64个时,自带的结构不能满足了,就要由alloc_fdtable来进行行后序分配,alloc_fdtable函数会直接分配一个新的fdtable,并分配其中的两个位图和fd_array数组,如图:

image.png

这时才会去真正确定fdtable真正的指向,接下就是把父进程中对应的两个位图与一个file结构的指针拷贝到fd_array中来,现在就可以发现,父子进程一共享file结构的实例,拷贝的是file的指针:

image.png

其中file结构如下,我们的答案就在这

image.png

现在对于如何共享偏移量就非常的清晰了

发表评论

电子邮件地址不会被公开。 必填项已用*标注