linux进程内存映象解析

一、程序如何转化为进程

程序转化为进程一般有两个步骤:
1、内核会将程序从磁盘读入内存,为程序分配内存空间
2、内核会为进程保存PID以及相应的状态信息(保存在task_struct中),将进程放在运行队列中等待执行。
程序转变为进程以后就可以被操作系统调度程序执行了。

二、内存映象

内存映象指的是内核如何在内存中存放可执行程序。
在程序转化为进程的过程中,操作系统可直接将可执行程序复制到内存中,其分布状况如下:
linux进程内存映象解析_第1张图片
(1)栈的地址是由高地址向低地址生长的(这也就解释了为什么我们定义数组的时候要给定数组大小)
(2)堆的地址是有低地址向高地址生长的

三、虚拟地址与物理地址

结合子进程与父进程,我们可以将上图画的更详细:
linux进程内存映象解析_第2张图片
1、地址空间(在操作系统中是现实存在的,由地址空间结构体支持):指的是虚拟地址上的一个地址范围,按需要来获取。举个形象点的例子来说明:你活动的范围是西安,但是最终你真正落脚的只是交通大学419宿舍的2号床铺。也就是说整个西安你都可以访问,但最终属于你的只是你现在坐的那块固定大小的地方。
2、虚拟地址:每个进程有运行在一个属于自己的地址空间里面,这个地址空间就是虚拟地址(我们平时见到的也都是虚拟地址,但最终还是要映射到物理地址上去)。
3、MMU:内存管理单元(硬件),结合页表可以完成映射。

上面我们说虚拟地址是每个进程特有的空间,并且我们见到的和用的都是虚拟地址,现在我们通过一段来验证一下:

int g_val=100;
int main()
{
    pid_t id=fork();
    if(id<0)//fork error
    {
        printf("fork error\n");
        return 1;
    }
    else if(id==0)//child 
    {
        g_val=1000;
        printf("child ,pid:%d,ppid:%d,g_val:%d,&g_val:%p\n",getpid(),getppid(),g_val,&g_val);
    }
    else
    {
        sleep(3);
        printf("father ,pid:%d,ppid:%d,g_val:%d,&g_val:%p\n",getpid(),getppid(),g_val,&g_val);
    }
    return 0;

输出:
这里写图片描述
在子进程中将全局变量g_val的值改为1000;
在父进程中显示值为100,子进程中值为1000,并且两者的地址是相同的。

结合上面的图我们来解释一下这个现象:我们说子进程和父进程共享代码,但是数据并不是共享的,也就是说,他们的虚拟地址是一样的,但是物理地址就肯定不一样了。父进程根据自己的页表和MMU映射到一个物理地址,子进程也映射到一个地址。子进程改一个值并不影响父进程的值,所以就出现了上面变量地址相同而值不同的情况。

PS:父子进程数据私有化的实现:并不是立刻实现,而是当有进程对某个区域的数据进行写入的时候,实现对该进程空间的数据的私有化,私有化的实现技术是写时拷贝。这样做的目的是为了节省消耗,提高效率。

你可能感兴趣的:(Linux)