【Linux】进程地址空间了解

研究背景是
1、kernel 2.6.32
2、32位平台下

1、什么是进程地址空间
  • 在了解进程地址空间是我们先了解 程序地址空间
    我们都知道计算机的内存的大小是固定的也就是我们主板上面插得内存条,CPU可以直接进行寻址,物理内存的容量是固定的,但是寻址的空间取决于CPU地址线的数量。在32位平台下,我们的内存的可寻址线性地址空间大小为(2^23)4G,一般的分配就是按照1:3的分配方式,用户享有3G用户空间,内核独有1G的内核空间;
    内存分布

  • 那么问题来了!如果说我们自己的用户空间是固定的只有3G,那么按照常识我们平时在电脑上运行那么多程序,不但没有出现内存沾满的问题,还能够同时安全运行呢?在这里我们应该了解:一个程序在被编译完成时是一个存储在硬盘中的二进制文件,等到要执行的时候被操作系统调入内存当中分配相应的空间并且在内存中必须具有连续的内存地址空间才能够顺利运行,等到完成时将空间资源由操作系统回收
    程序运行时内存
    从上图的分析可以看出如果内存时固定的,那么直接能运行的程序也就是有限的并且这也不符合我们平时使用电脑运行多个任务的情景。程序指令所访问的内存地址就是物理内存地址鉴于这种直接加载到内存的机制必定产生相对应得问题:
    1、当多个程序需要运行时,必须保证这些程序用到的内存总量要小于计算机实际的物理内存的大小。
    2、内存使用效率低;内存空间不足,就需要将其他程序展示拷贝到硬盘当中,然后将新的程序装入内存。然而由于大量的数据装入装出,内存的使用效率会非常低,并程序运行的效率比较低下。
    3、进程地址空间不连续,由于程序是直接访问物理内存的,所以每一个进程都可以修改其他进程的内存数据,设置修改内核地址空间中的数据,所以有些恶意程序可以随意修改别的进程,就会造成一些破坏,造成内存碎片化难以管理。
    4、程序运行的地址不确定;因为内存地址是随机分配的,所以程序运行的地址也是不正确的

  • 那么为什么现在的计算机可以同时多个的运行程序?
    我们先看一段linux父子进程的代码代码:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int val = 0;
int main()
{
	pid_t id = fork(); //获取返回值
	if(id < 0)
	{
		perror("fork");
		return 0;
	}
	else if(id == 0)
	{
		//child
		printf("child[%d]:%d:%p\n",getpid(),val,&val);
	}
	else
	{
		//parent
		printf("paren[%d]:%d:%p\n",getpid(),val,&val);
	}
	sleep(3);
	return 0;
}

输出的结果:
在这里插入图片描述
从结果我们可以看出子进程创建成功,这时就相当于有了两个进程但是输出来的变量值和地址是一样的。这说明了什么?因为子进程是以父进程位模板创建的,父子并没有对变量进行改变。但是我们在看:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int val = 0;
int main()
{
	pid_t id = fork(); //获取返回值
	if(id < 0)
	{
		perror("fork");
		return 0;
	}
	else if(id == 0)
	{
		//child
		val = 100;
		printf("child[%d]:%d:%p\n",getpid(),val,&val);
	}
	else
	{
		//parent
		sleep(3);
		printf("paren[%d]:%d:%p\n",getpid(),val,&val);
	}
	sleep(3);
	return 0;
}

输出结果为:
在这里插入图片描述
从图中我们也就可以看出此时彼变量值不相同,但是地址都相同。由此我们可以得出结论:
1、变量的内容不一样就说明父子进程输出的变量绝不是同一个变量
2、但是地址一样,说明这肯定就不是物理内存地址
3、那么就可以顺势推出在linux下的地址都不是物理内存地址而是一种虚拟的内存地址
父子进程创建过程:
1、创建PCB(进程控制块)
2、拷贝父进程PCB中的数据(拥有相同的虚拟地址空间,相同的页表)
3、父子进程映射同一块物理内存,等到子进程修改的时候采薇子进程重新开辟空间(体现进程独立性)
结论:到这里我们借由上面父子进程的代码验证可以看出,程序在运行的时候使用的是一种虚拟的内存地址,而不是真正的物理内存地址,这也正是为什么计算机能够同时执行多个程序而不会出现内存不够用和程序因内存而不能执行问题的原因了;不管物理内存大小但是在进程的眼内它的空间都是4G(这取决于32位)这些都有操作系统管理

2、进程地址空间的深入了解

进程地址空间的实现其实就是:分页&虚拟地址空间(结构体mm_struct) 来实现的。

  • 什么是分页式&虚拟地址空间?
    虚拟地址空间就是:操作系统为进程所描述的一个假的地址空间,目的就是为了让进程认为自己拥有一块连续的线性的完整的地址空间;但实际上一个进程使用的内存并非是连续的存储,而是通过页表映射了虚拟地址于物理地址之间的关系;让进程通过页表获取物理地址,进而实现数据的离散式存储;

  • 这样做的意义是什么?
    1、提高的内存的利用率:物理内存的离散存储解决上面直接装入物理内存造成空间碎片化和利用率较低的问题。
    2、页表可以通过进行内存访问控制:页表可以对每一个虚拟地址进行权限标记
    3、提高了进程的独立性:每个进程都只能访问自己虚拟地址映射的物理内存

在这里插入图片描述
从上图我们可以看出这就是其实现的原理,父子进程的地址相同其实就是虚拟地址相同,但是变量值不同的原因内容不同其实是被映射到了不同的物理地址和页表进行不同地址空间的访问权限来实现的!

  • 虚拟内存地址怎么样映射到物理内存地址呢?
  • 这是就要了解分页&虚拟地址空间(结构体mm_struct)中的分页。
    页表的概念:
    1、页表是一个特殊的数据结构,放在内存空间的页表区/2、每一个进程都有一个页表,PCB表中有指针指向页表
    2、页表用来存放逻辑地址于物理地址的对应关系,是否映射,是否缓存
    3、页表的每一个表项分为俩部分,第一部分记录此页是否在物理内存上,第二部分记录物理内存页的地址(如果在的话)
    4、当进程访问某个虚拟地址,去查页表的时候如果发现对应的数据不在物理内存中,则发送缺页异常。
    缺页异常处理过程:将进程需要的数据从磁盘中拷贝到物理内存里,如果物理内存已经满了,没有空地方了,那就找一个页覆盖,当然如果被覆盖的页曾经被修改过,就需将此页写回到磁盘
    页表的状态:
    1、如果页表的有效位置为1,那么就说明虚拟地址存储的内存存储在物理页当中
    2、如果页表的有效位置为0,那么就说明虚拟地址存储的内存没有存储在物理页中,发生了缺页异常,需要处理这个异常
    页表的工作原理:
    1、当cpu想访问父进程的g_val的值的时候,先根据其虚拟地址找到虚拟页,根据页表,找出页表中g_val对应的位置,查看该页表的表项是否有效,有效则为1 ,DRMA换命中,根据物理页号找到物理页中的内容,返回
    2、若为无效(0),则参数缺页异常,调用内核缺页异常处理程序,内核会选择一个无理由作为牺牲页,将该页的内容刷新到磁盘空间,然后将g_val映射到该物理页上面,然后页表中的该表项有效位置1,第二位存放对应物理内存页的地址内容缺页处理完成后,返回中断前的指令,重新指向,此时缓存命中,执行1
    3、将找到的内容映射到告诉缓存当中,CPU从告诉缓存中获取该值,结束;

虚拟地址的工作原理:
1、系统将会为每一个进程创建一个4G大小的虚拟地址空间,他们通过页表于物理内存相互映射,在虚拟地址内存空间上看起来是一个连续的4G内存空间,但是在物理内存上却并非是连续的,这取决去物理空间是否有足够的空间,反正不管内存空间地址是否连续,在每一个进程看来他们自己都有4G的内存空间。这也正是虚拟内存地址的作用。

以上就是我对进程这块的进程地址空间的了解,
如有漏洞或者意见,请您不吝赐教。必然感激万分!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值