fork函数的写时拷贝
#include
#include
#include
#include
#include
#include
int main()
{
char p = g;
int number = 11;
if(fork()==0) /*子进程*/
{
p = c; /*子进程对数据的修改*/
printf("p = %c , number = %d ",p,number);
exit(0);
}
/*父进程*/
number = 14; /*父进程对数据修改*/
printf("p = %c , number = %d ",p,number);
exit(0);
}
编译调试:
[gong@Gong-Computer cprogram]$ gcc -g TestWriteCopyTech.c -o TestWriteCopyTech
[gong@Gong-Computer cprogram]$ ./TestWriteCopyTech
p = g , number = 14 -----父进程打印内容
[gong@Gong-Computer cprogram]$ p = c , number = 11 -----子进程打印内容
原因分析:
由于存在企图进行写操作的部分,因此会发生写时拷贝过程,子进程中对数据的修改,内核就会创建一个新的物理内存空间。然后再次将数据写入到新的物理内存空间中。可知,对新的区域的修改不会改变原有的区域,这样不同的空间就区分开来。但是没有修改的区域仍然是多个进程之间共享。
fork函数的代码段基本是只读类型的,而且在运行阶段也只是复制,并不会对内容进行修改,因此父子进程是共享代码段,而数据段、Bss段、堆栈段等会在运行的过程中发生写过程,这样就导致了不同的段发生相应的写时拷贝过程,实现了不同进程的独立空间。
但是需要注意的是文件操作,由于文件的操作是通过文件描述符表、文件表、v-node表三个联系起来控制的,其中文件表、v-node表是所有的进程共享,而每个进程都存在一个独立的文件描述符表。父子进程虚拟存储空间的内容是大致相同的,父子进程是通过同一个物理区域存储文件描述符表,但如果修改文件描述符表,也会发生写时拷贝操作,只有这样才能保证子进程中对文件描述符的修改,不会影响到父进程的文件描述符表。例如close操作,因为close会导致文件的描述符的值发生变化,相当于发生了写操作,这是产生了写时拷贝过程,实现新的物理空间,然后再次发生close操作,这样就不会产生子进程中文件描述符的关闭而导致父进程不能访问文件。
测试函数:
#include
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
char c[3];
char *s = "TestFs";
fd = open("foobar.txt",O_RDWR,0);
if(fork()==0) //子进程
{
fd = 1;//stdout
write(fd,s,7);
exit(0);
}
//父进程
read(fd,c,2);
c[2]= ;
printf("c = %s",c);
exit(0);
}
[gong@Gong-Computer cprogram]$ gcc -g fileshare2.c -o fileshare2
[gong@Gong-Computer cprogram]$ ./fileshare2
c = fo ----foobar.txt中的内容
[gong@Gong-Computer cprogram]$ TestFs ---标准输出
原因分析:由于父子进程的文件描述符表是相同的,但是在子进程中对fd(文件描述符表中的项)进行了修改,这时会发生写时拷贝过程,内核在物理内存中分配一个新的页面存储子进程原文件描述符fd存在页面的内容,然后再进修写操作,实现将fd修改为1,也就是标准输出。但是父进程的fd并没有发生改变,还是与其他的子进程共享文件描述符表,因此仍然是对文件foobar.txt进行操作。
因此需要主要fork函数实质上是按着写时拷贝的方式实现文件的映射,并不是共享,写时拷贝操作使得内存的需求量大大的减少了,具体的写时拷贝实现,请参看非常经典的“深入理解计算机系统”的第622页。
评论