linux进程系列(3)父子进程变量虚拟内存地址相同但变量值不同的问题
2016-11-13 17:40
351 查看
首先,我们来看一个实例:
process_fork3.c
/*
* process_fork3.c
*
* Created on: 2016-11-13
* Author: river
*/
/*
* process_fork.c
*
* Created on: 2016-11-11
* Author: river
*/
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
//全局变量
int g_v = 30;
int main()
{
int a_v = 30;//局部变量
static int s_v = 30;//静态变量
pid_t pid;
pid = fork();//创建子进程
//在fork之后会运行2个进程(父进程、子进程)
if(pid < 0)
{
perror("fork error");
}
else if(pid > 0)
{//父进程(在父进程中fork返回的是子进程的pid)
//父进程执行的代码...
g_v = 40;
a_v = 40;
s_v = 40;
printf("I am parent process pid is %d\n", getpid());
printf("g_v: %p, a_v: %p, s_v: %p\n\n\n", &g_v, &a_v, &s_v);
}
else
{//子进程(在子进程中fork返回的是0)
//子进程执行的代码...
printf("kkkpid: %d, g_v:%d, a_v:%d, s_v:%d\n", getpid(), g_v, a_v, s_v);//父子进程都执行
g_v = 50;
a_v = 50;
s_v = 50;
printf("I am child process pid is %d\n", getpid());
printf("g_v: %p, a_v: %p, s_v: %p\n\n\n", &g_v, &a_v, &s_v);
}
printf("pid: %d, g_v:%d, a_v:%d, s_v:%d\n\n\n", getpid(), g_v, a_v, s_v);//父子进程都执行
sleep(1);//睡眠1s使得父子进程可以交替运行
exit(0);
}
上面的例子,我们定义3个变量,分别是全局变量、局部变量、静态变量,并都赋予初始值30,在父进程中都修改为40,并打印三个变量的地址,在子进程中都修改为50,并打印三个变量的地址,最后父子进程分别打印三个变量的值。
程序运行输出结果为:
I am parent process pid is 21268
g_v: 0x804a034, a_v: 0xbf8e1528, s_v: 0x804a038
pid: 21273, g_v:30, a_v:30, s_v:30
pid: 21268, g_v:40, a_v:40, s_v:40
I am child process pid is 21273
g_v: 0x804a034, a_v: 0xbf8e1528, s_v: 0x804a038
pid: 21273, g_v:50, a_v:50, s_v:50
从上面的输出结果我们可以看到父进程的ID是21268,子进程的ID是21273,我们用绿色标示了父进程的输出信息,蓝色标示了子进程的输出信息。
对于pid: 21273, g_v:30, a_v:30, s_v:30这一句的解释是 在程序运行开始我们将三个变量都初始化为30,因此子进程在没有对三个变量进行修改之前,我们打印的三个变量的值是从父进程中继承而来的数值(注意父进程是在fork之后对三个变量修改为40的,所以父进程拷贝到子进程中的三个变量的值仍然是父进程初始化为30的值),这也就验证了上一篇文章中提到的子进程会遗留父进程中的数据。
我们会看到这样一个现象:
I am parent process pid is 21268
g_v: 0x804a034, a_v: 0xbf8e1528, s_v: 0x804a038
I am child process pid is 21273
g_v: 0x804a034, a_v: 0xbf8e1528, s_v: 0x804a038
父进程变量的地址与子进程变量的地址是一样的,也就是说不同的进程的变量却有相同的虚拟地址(注:程序中变量的地址都是虚拟地址,而非物理地址)。原因是内核会为每个进程分配4G的虚拟地址空间,这4G的虚拟地址空间地址分布都是一样的,由于子进程虚拟地址空间的数据都是从父进程中拷贝而来的,都是一样的,因此相同的数据在4G的虚拟地址空间中的分布也是一样的。那为什么相同的虚拟地址(变量地址)最终会得到不同的变量值呢?原因是虽然虚拟地址分布是一样的,但是由于相同的虚拟地址映射到不同的物理地址,所以我们才会得到不同的变量值。
下面的示图描了这一点:
从上图可以看到,虽然父进程与子进程中三个变量虚拟地址是一样的,但是映射到不同的物理地址就得到了不同的数值。
process_fork3.c
/*
* process_fork3.c
*
* Created on: 2016-11-13
* Author: river
*/
/*
* process_fork.c
*
* Created on: 2016-11-11
* Author: river
*/
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
//全局变量
int g_v = 30;
int main()
{
int a_v = 30;//局部变量
static int s_v = 30;//静态变量
pid_t pid;
pid = fork();//创建子进程
//在fork之后会运行2个进程(父进程、子进程)
if(pid < 0)
{
perror("fork error");
}
else if(pid > 0)
{//父进程(在父进程中fork返回的是子进程的pid)
//父进程执行的代码...
g_v = 40;
a_v = 40;
s_v = 40;
printf("I am parent process pid is %d\n", getpid());
printf("g_v: %p, a_v: %p, s_v: %p\n\n\n", &g_v, &a_v, &s_v);
}
else
{//子进程(在子进程中fork返回的是0)
//子进程执行的代码...
printf("kkkpid: %d, g_v:%d, a_v:%d, s_v:%d\n", getpid(), g_v, a_v, s_v);//父子进程都执行
g_v = 50;
a_v = 50;
s_v = 50;
printf("I am child process pid is %d\n", getpid());
printf("g_v: %p, a_v: %p, s_v: %p\n\n\n", &g_v, &a_v, &s_v);
}
printf("pid: %d, g_v:%d, a_v:%d, s_v:%d\n\n\n", getpid(), g_v, a_v, s_v);//父子进程都执行
sleep(1);//睡眠1s使得父子进程可以交替运行
exit(0);
}
上面的例子,我们定义3个变量,分别是全局变量、局部变量、静态变量,并都赋予初始值30,在父进程中都修改为40,并打印三个变量的地址,在子进程中都修改为50,并打印三个变量的地址,最后父子进程分别打印三个变量的值。
程序运行输出结果为:
I am parent process pid is 21268
g_v: 0x804a034, a_v: 0xbf8e1528, s_v: 0x804a038
pid: 21273, g_v:30, a_v:30, s_v:30
pid: 21268, g_v:40, a_v:40, s_v:40
I am child process pid is 21273
g_v: 0x804a034, a_v: 0xbf8e1528, s_v: 0x804a038
pid: 21273, g_v:50, a_v:50, s_v:50
从上面的输出结果我们可以看到父进程的ID是21268,子进程的ID是21273,我们用绿色标示了父进程的输出信息,蓝色标示了子进程的输出信息。
对于pid: 21273, g_v:30, a_v:30, s_v:30这一句的解释是 在程序运行开始我们将三个变量都初始化为30,因此子进程在没有对三个变量进行修改之前,我们打印的三个变量的值是从父进程中继承而来的数值(注意父进程是在fork之后对三个变量修改为40的,所以父进程拷贝到子进程中的三个变量的值仍然是父进程初始化为30的值),这也就验证了上一篇文章中提到的子进程会遗留父进程中的数据。
我们会看到这样一个现象:
I am parent process pid is 21268
g_v: 0x804a034, a_v: 0xbf8e1528, s_v: 0x804a038
I am child process pid is 21273
g_v: 0x804a034, a_v: 0xbf8e1528, s_v: 0x804a038
父进程变量的地址与子进程变量的地址是一样的,也就是说不同的进程的变量却有相同的虚拟地址(注:程序中变量的地址都是虚拟地址,而非物理地址)。原因是内核会为每个进程分配4G的虚拟地址空间,这4G的虚拟地址空间地址分布都是一样的,由于子进程虚拟地址空间的数据都是从父进程中拷贝而来的,都是一样的,因此相同的数据在4G的虚拟地址空间中的分布也是一样的。那为什么相同的虚拟地址(变量地址)最终会得到不同的变量值呢?原因是虽然虚拟地址分布是一样的,但是由于相同的虚拟地址映射到不同的物理地址,所以我们才会得到不同的变量值。
下面的示图描了这一点:
从上图可以看到,虽然父进程与子进程中三个变量虚拟地址是一样的,但是映射到不同的物理地址就得到了不同的数值。
相关文章推荐
- fork()后,父子进程变量地址相同?
- fork之后父子进程虚拟地址(逻辑地址)相同的问题
- linux 守护进程 环境变量的问题
- linux 反汇编分析变量地址,直接修改二进制文件中变量值
- 创建子进程时变量的地址与父进程一样而数值不一样的问题
- Linux 进程虚拟内存的问题
- Linux中fork()函数详解 父子进程变量的关系
- linux进程系列(4)父子进程的缓存方式写文件
- C语言学习4: 函数返回值与传入参数,关于函数值传递和类型隐性转换,变量不同的作用域,static变量,多文件编译例如两个C文件,显示函数调用语句跳转,递归,斐波那契数列,多文件编译相同变量的问题。
- php 变量 【PHP_EOL】 解决不同环境(win,linux,unix)下的按回车分隔问题
- 【linux草鞋应用编程系列】_2_ 环境变量和进程控制
- Linux 软件系列之八——父子进程
- Linux 父子进程终止的先后顺序不同产生不同的后果
- Linux 父子进程在没有同步时出现的问题
- Linux下父子进程的全局变量
- linux不同用户环境变量问题
- fork()后 父子进程变量地址一致
- AES加密算法在linux下相同输入每次加密结果都不同的问题
- 父子进程变量的地址一样,但值不一样
- linux系统下 fork()系统调用: 关于父子进程缓存问题的小坑