Linux进程地址空间.

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸
在这里插入图片描述


以下结论只在Linux有效,其他系统类似

一、验证地址空间

这里的地址空间,指的就是进程地址空间

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int g_val=100;
int main()
{
	pid_t id = fork()	
	if(id==0)//这里就不做对进程创建失败的判断了
	{
		int cnt=0;
		while(1)
		{
			printf("i am child,pid:%d ,ppid:%d,g_val:%d,&g_val:%p\n",
			getpid(),getppid(),g_val,&g_val);
			sleep(1);
			cnt++;
			if(cnt==5)
			{
				g_val=200;//修改两个进程中其中一个的g_val来验证地址空间
				printf("child change g_val 100->200 success\n";
			}
		}
	}
	else
	{
		while(1)
		{
			printf("i am parent,pid:%d ,ppid:%d,g_val:%d,&g_val:%p\n",
			getpid(),getppid(),g_val,&g_val);			
			sleep(1);
		}
	}
	return 0;
}

在这里插入图片描述
我们发现,同一个地址,同时读取的时候出现了不同的值,这就告诉了我们,这里的地址绝对不是物理内存的地址,而是虚拟地址,所以,我们所说的地址,其实是虚拟地址

在这里插入图片描述

特定的硬件和地址空间就是这种对应关系:
如果在访问虚拟内存的时候,如果访问的那个空间对应的是内存,就访问内存,对应的是外设,就访问外设(以统一的视角看待不同的设备)

二、进程地址空间的分布

在这里插入图片描述
命令行参数环境变量之下是用户空间,大约3G,之上是内核空间,大约1G
其实在正文代码和初始化数据的中间还有一个区,是字符串常量区。

三、验证地址

#include <stdio.h>

int g_val=100;//已初始化
int g_unval;//未初始化
int main(int argc,char* argv[],char* env[])
{
	printf("code addr: %p\n",main);//函数是存在代码段的
	printf("Init global addr: %p\n",&g_val);
	printf("Uninit global addr: %p\n",&g_unval);
	char *heap_mem  =(char*)malloc(10);//堆
	printf("heap addr: %p\n",heap_mem);
	printf("stack addr: %p\n",&heap_mem);//指针变量是存在栈区的
	for(int i=0;i<argc;i++)
	{
		printf("argv[%d]: %p\n",i,argv[i]);
	}
	for(int i=0;env[i];i++)
	{
		printf("env[%d]: %p\n",i,env[i]);
	}
	return 0;
}

在这里插入图片描述
从上可以看出
1.正文代码并不是从000…000开始的
2.初始化数据和未初始化数据是在一起的
3.栈区和命令行参数环境变量大概是一起创建的,并且在栈区上面

在malloc(10)的时候 ,C标准库不仅仅是生成了10个字节,其实生成了更多,多出来的通常用来 描述这个堆的属性信息(被称作cookie数据),有了这些数据,free才知道我要free多少。

四、历史vs现代 计算机访问物理内存

1.历史

历史是直接访问物理内存的,内存是个硬件,只负责存和取,内存本身是可以被随时读写的,所以,如果内存中加载了多个进程,进程万一弄错了,就可能直接改掉其他进程的东西,而且,如果进程一是输入账号和密码的操作,那么进程二直接对那块内存进行读取就可获得,所以,这样访问物理内存是特别不安全的。(根本原因就是直接使用物理内存,没有软件来设置权限)

2.现代

1.每一个进程有一个PCB(task_struct)
2.操作系统给每一个进程创建了一个进程地址空间。(虚拟内存)
3.这个地址空间的编制认为是0x000…0 到 0xFF…FF的

虚拟地址空间(进程地址空间)上的地址称为虚拟地址,最终是会存到物理内存上的,系统存在一种映射机制,映射机制的核心工作就是把用户的代码,数据经过映射机制映射到物理内存当中,总之,凡是要访问物理内存,需要先进行映射,这就是把虚拟内存映射为物理地址的过程
在这里插入图片描述
我们就在CPU和物理内存之间引入了虚拟地址和映射机制,非法的行为都被拦截,非法就禁止映射,变相的保护了内存

五、其它问题

1.什么是进程地址空间

每个进程都有地址空间,地址空间的本质在内核中是一种数据结构 ,里面至少含有各个数据的划分

struct addr_room
{
	int code_start;
	int code_end;
	
	int init_start;
	int init_end;

	int unInit_start;
	int unInit_end;
	
	int heap_start;
	int heap_end();

	int stack_start;
	int stack_end;
	.....
	其他的属性
}

这个结构体叫做mm_struct
在PCB当中,就有一个struct mm_struct*mm来指向它自己的进程地址空间

2.映射关系谁来维护

1.映射关系是操作系统自己维护的
2.映射关系是一种表结构,称为(用户级)页表
3.地址空间和页表是每一个进程独一份的
4.只要保证,每一个进程的页表,映射的是物理内存的不同区域,就能做到不互相干扰,进而保证进程的独立性(就是拿一张表找到位置,存数据)

所以,子进程继承父进程,可以做到同一个地址,值不同,就是因为有地址空间的存在,子进程是会继承父进程的大部分属性的,其中就包括进程地址空间

3.写时拷贝

如果父子进程的g_val值是一样的话,在物理内存当中只会开辟一块空间,而当修改某一进程的g_val的时候,才会重新开辟另一块空间,这就叫做写时拷贝

4.编译的时候就已经有地址

其实可执行程序编译的时候就已经有地址了,地址空间不仅仅是OS要遵守,编译器也是要遵守的编译器在编译代码的时候,已经形成了各个区域,并且采用和Linux内核当中一样的编址方式,给每一个 变量,每一行代码都进行了编址,所以程序在编译的时候,每一个字段早已具有了一个虚拟地址(虚拟地址也是数据,加载的时候也要加载)。比如执行过程中的函数调用,就会根据这个地址去调用相应的函数(通过地址来标定逻辑)

链接动态库就是再链接的时候,把程序当中调用库中的函数的地址拷贝到可执行程序中。

当程序加载到内存的时候,每行代码,每个变量也就通过编译器形成的虚拟地址来映射到物理内存。
在这里插入图片描述

六、为什么要存在地址空间?

1.凡是非法的访问或者映射,操作系统都会识别到并终止这个进程
因为地址空间和页表是OS创建和维护的,也就意味着一定要在OS的监管之下进行访问,也便保护了物理内存中的合法数据,包括各个进程,以及内核的相关有效数据

进程崩溃的本质就是进程退出,是OS杀掉了这个进程

2.因为有地址空间的存在,有页表的映射的存在,所以我们可以对数据进行任意位置的加载(如果不是这样,而是直接访问内存,首先不考虑安全问题的情况下,我们还需要对内存碎片进行管理,这其实是特别不好弄的)

物理内存的分配就可以和进程的管理,做到没有关系(进程通过地址空间管理)
内存管理模块 vs 进程管理模块 ,做到解耦合,减少关联性,降低维护成本。

物理地址的申请内存,是由操作系统自动完成的,用户,进程,完全0感知
比如我们在new malloc的时候,本质上也是在虚拟内存上申请的空间
如果我申请了物理空间,不立马使用,就是浪费了空间
所以,因为有地址空间的存在,上层申请空间,其实是在地址空间上申请的。物理内存甚至可以一个字节都不给,当你真正对物理 地址空间进行访问的时候,才执行内存的相关管理算法,帮我申请内存,构建页表 映射关系,然后再让我进行内存的访问。这种虚拟内存分配空间,而物理内存没有分配的技术叫做缺页中断
其实就是延迟分配,用这种策略来提高整机效率

3.直接访问物理内存的话是乱序的,地址空间+页表的存在,可以将内存分布有序化!(通过映射,虚拟内存有序,物理内存依旧乱序),有了映射,保证映射到不同的地址,就可以很容易的做到进程独立性的实现

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猪皮兄弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值