29-fork 函数与文件共享

来源:互联网 发布:淘宝详情页英文素材 编辑:程序博客网 时间:2024/06/07 16:27

不知道大家考虑过这样的问题没:如果进程在 fork 之前打开了一些文件,那么 fork 完之后,这些文件的描述符是共享的,还是不共享的?

聪明的同学阅读了上篇《进程空间》的相关内容,脑子应该立即反应过来:父子进程的进程地址空间都是隔离的啊!所以打开的文件,应该也互不影响吧!

No! No! No! 很抱歉,上一篇我的确讲过进程空间是隔离的。为了循序渐近和压缩篇幅,我不得不相关内容挪到此篇。

进程 4GB 空间并不是完全隔离的。

实际上进程空间被分割为用户空间和内核空间。对于32 位 linux 来说,从 0-3GB 的空间是用户空间,从 3GB - 4GB 是内核空间。对于一个进程来说,是绝对无法读写内核空间的。

最重要的一点,或者精确一点,进程的用户空间是隔离的,而内核空间是共享的。看起来有点像下面的图。


这里写图片描述
图 1 进程的用户空间和内核空间

理解这一点也相当重要,这将为未来的进程通信带来可能!

1. 回忆描述符

对于一个进程来说,它所有打开的描述符,都会有记录。而且这些记录,保存该进程的 PCB 结构体中(PCB位于内核空间),该结构体有一个成员 struct file *flip[NR_OPEN],就保存了所有打开的文件(linux 0.11)。如图 2。


这里写图片描述
图2 进程打开的文件

有一点要注意的是,struct file 结构体中的 f_inode 并不是真的直接指向磁盘文件,这中间需要经过若干的步骤,不过为了方便起见和理解,这里直接指向了磁盘文件。

2. fork 后的样子

当图 2 所示的进程 fork 后,为变成下面这个样子。


这里写图片描述
图3 fork 后的两个进程打开的文件

这时候,struct file 中的 f_count 都会自增 1.

上图告诉我们的一个事实是,fork 完后的父子进程,共享 struct file 结构(因为该结构位于内核空间)。在《APUE》 这本书中,把 struct file 称为文件表。

3. 实验

理解了前面的内容后,不如做个实验看看是否真的是这样。下面这段程序在 fork 之前以写的方式创建了一个文件 test.txt。然后 fork 出的子进程立即向文件中写入“world”,然后睡眠5秒。而父进程在 fork 后睡眠3秒后向 test.txt 写入 "hello",并关闭描述符。子进程恢复后,又向 test.txt 文件中写入 "lalala"后关闭描述符,结束。

  • 代码
// forkwrite.c#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>int main() {  int fd = open("test.txt", O_WRONLY | O_CREAT, 0664);  if (fd == -1) {    perror("open");    return 1;  }  printf("I'm father\n");  printf("before fork\n");  pid_t pid = fork();  if (pid > 0) {    sleep(3);    printf("I'm father; I'm writing test.txt...\n");    write(fd, "hello", 5);     close(fd);  }  else if (pid == 0) {    printf("I'm child; I'm writing test.txt...\n");    write(fd, "world", 5);     sleep(5);    write(fd, "lalala", 6);     close(fd);  }  else {    perror("fork");    return 1;  }  return 0;  }
  • 编译
$ gcc forkwrite.c -o forkwrite
  • 运行
$ ./forkwrite
  • 结果

屏幕打印:

I'm fatherbefore forkI'm child; I'm writing test.txt...I'm father; I'm writing test.txt...

生成的 test.txt 文件内容:

worldhellolalala

4. 总结

  • 理解用户空间和内核空间
  • 知道内核空间是所有进程共享的
  • 加深对文件描述符的理解

除了打开的文件外,父进程的很多其他性质也会被子进程共享,比如各种 ID 号、当前工作目录、根目录、资源的限制、信号屏蔽字、进程环境、文件打开执行时关闭标志、共享存储段。

0 0
原创粉丝点击