1、关于子进程

首先我们要知道,当利用fork函数创建出一个子进程之后,子进程会复制父进程的虚拟地址空间,也就是说子进程会复制父进程的数据段。那么接下来我们就要验证子进程与父进程能否共享全局变量。


2、验证思路

很简单,我们只需要定义一个全局变量,这父进程中修改该全局变量,在子进程中读取该全局变量。如果子进程中读取的全局变量的值没有改变,那么就证明父子进程不共享全局变量。
代码:

//验证父子进程能否共享全局变量
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

int gloab = 10;
int main()
{	
	
	//创建子进程
	//pid_t fork(void)
	pid_t pid = fork();
	if(pid<0)
	{
		perror("fork error");
		return -1;
	}
	else if(pid>0)//父进程
	{
		printf("father: pid == [%d]\n",getpid());
		gloab++;
	}
	else if(pid==0) //子进程
	{	
		sleep(10); //保证父进程先执行
		printf("child: pid==[%d]\n",getpid());
		printf("gloab==[%d]\n",gloab);
	}
	printf("after fork\n");
	return 0;
}

注意
在子进程前要加sleep()函数来保证父进程先执行。因为父子进程的顺序是随机的,取决于谁先占到cpu的使用权。如果子进程先执行的话,那么读出来的gloab肯定就是10了,这样会影响我们的验证。

运行结果
可以看到在子进程中变量gloab并没有改变。那么是否可以说明父子进程不共享全局变量呢?
接下来在父子进程中我们打印下gloab的地址:

...
  	else if(pid>0)//父进程
	{
		printf("father: pid == [%d]\n",getpid());
		printf("%p\n",&gloab);
	}
	else if(pid==0) //子进程
	{	
		sleep(10); //保证父进程先执行
		printf("child: pid==[%d]\n",getpid());
		printf("%p\n",&gloab);
	}
...

alt
可以看到打印的gloab地址都一样,都是0x555555558010。为什么父子进程打印出来的全局变量地址一样呢?
原因在于这个地址是虚拟地址,而因为子进程的创建是复制了父进程的虚拟地址空间的,因为这两个变量的虚拟地址也是一样的。(我们打印变量的地址都是虚拟地址,物理内存地址是不能够直接访问的)


图解父子进程内存

alt
比如我们创建了一个全局变量g_val。如果父进程对其进行修改,那么在真正的物理内存中会再拷贝一份g_val的副本。父进程中修改的其实是副本。这时候子进程再去访问g_val。访问的仍然是物理内存中原来的g_val的值。那如果子进程也对g_val进行修改呢?毫无疑问内存会再拷贝一份g_val的副本。


结论

  • 父子进程不能共享全局变量。
  • 但是如果父子进程只是对全局变量做读操作,则父子进程在内存***享同一份全局变量。
  • 如果父子进程中的任何一个对变量做修改操作,会在内存中拷贝一份副本,然后在这个副本上进行修改,修改完成后再通过MMU(内存管理单元)映射回去。