29-fork 函数与文件共享
2016-12-19 16:07
218 查看
不知道大家考虑过这样的问题没:如果进程在 fork 之前打开了一些文件,那么 fork 完之后,这些文件的描述符是共享的,还是不共享的?
聪明的同学阅读了上篇《进程空间》的相关内容,脑子应该立即反应过来:父子进程的进程地址空间都是隔离的啊!所以打开的文件,应该也互不影响吧!
No! No! No! 很抱歉,上一篇我的确讲过进程空间是隔离的。为了循序渐近和压缩篇幅,我不得不相关内容挪到此篇。
进程 4GB 空间并不是完全隔离的。
实际上进程空间被分割为用户空间和内核空间。对于32 位 linux 来说,从 0-3GB 的空间是用户空间,从 3GB - 4GB 是内核空间。对于一个进程来说,是绝对无法读写内核空间的。
最重要的一点,或者精确一点,进程的用户空间是隔离的,而内核空间是共享的。看起来有点像下面的图。
![](http://img.blog.csdn.net/20161219154206255?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcTEwMDc3Mjk5OTE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
图 1 进程的用户空间和内核空间
理解这一点也相当重要,这将为未来的进程通信带来可能!
![](http://img.blog.csdn.net/20161219154857320?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcTEwMDc3Mjk5OTE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
图2 进程打开的文件
有一点要注意的是,struct file 结构体中的 f_inode 并不是真的直接指向磁盘文件,这中间需要经过若干的步骤,不过为了方便起见和理解,这里直接指向了磁盘文件。
![](http://img.blog.csdn.net/20161219155207884?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcTEwMDc3Mjk5OTE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
图3 fork 后的两个进程打开的文件
这时候,struct file 中的 f_count 都会自增 1.
上图告诉我们的一个事实是,fork 完后的父子进程,共享 struct file 结构(因为该结构位于内核空间)。在《APUE》 这本书中,把 struct file 称为文件表。
代码
编译
运行
结果
屏幕打印:
生成的 test.txt 文件内容:
知道内核空间是所有进程共享的
加深对文件描述符的理解
除了打开的文件外,父进程的很多其他性质也会被子进程共享,比如各种 ID 号、当前工作目录、根目录、资源的限制、信号屏蔽字、进程环境、文件打开执行时关闭标志、共享存储段。
聪明的同学阅读了上篇《进程空间》的相关内容,脑子应该立即反应过来:父子进程的进程地址空间都是隔离的啊!所以打开的文件,应该也互不影响吧!
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 father before fork I'm child; I'm writing test.txt... I'm father; I'm writing test.txt...
生成的 test.txt 文件内容:
worldhellolalala
4. 总结
理解用户空间和内核空间知道内核空间是所有进程共享的
加深对文件描述符的理解
除了打开的文件外,父进程的很多其他性质也会被子进程共享,比如各种 ID 号、当前工作目录、根目录、资源的限制、信号屏蔽字、进程环境、文件打开执行时关闭标志、共享存储段。
相关文章推荐
- Linux socket 初步
- Linux Kernel 4.0 RC5 发布!
- linux lsof详解
- linux 文件权限
- Linux 执行数学运算
- 10 篇对初学者和专家都有用的 Linux 命令教程
- Linux 与 Windows 对UNICODE 的处理方式
- Ubuntu12.04下QQ完美走起啊!走起啊!有木有啊!
- 解決Linux下Android开发真机调试设备不被识别问题
- 运维入门
- 运维提升
- Linux 自检和 SystemTap
- Ubuntu Linux使用体验
- c语言实现hashmap(转载)
- Linux 信号signal处理机制
- linux下mysql添加用户
- Scientific Linux 5.5 图形安装教程