Linux系统下的进程地址、内存分区、映射机制

1、Linux进程地址空间分布

通常程序访问的是虚拟内存,虚拟内存映射到物理内存的一小部分。
在Linux系统中,虚拟内存默认为4G的大小。每个进程都有独立的4G内存地址空间。
请添加图片描述
请添加图片描述

int main()
{
	char s[] = "hello world";	//s数组位于栈区,复制了一份字符串到数组里
	char *s = "hello world";	//s指针位于栈区,字符串位于常量区
	char *s = malloc(128); 	//分配的128字节区域位于堆区
}

代码段(text):存放CPU执行的机器指令,代码区是可共享,并且是只读的。
数据区
.rodata段:只读数据段,常量数据
.data段: 存放已初始化的全局变量、静态变量(全局和局部)
.bss段:存放的是未初始化的全局变量和静态变量
堆区:堆向上生长,由malloc()函数分配的内存块,使用free()函数来释放内存,堆的申请释放工作由程序员控制,但容易产生内存泄漏。
栈区:栈向下生长,由编译器自动分配释放,存放函数的参数值、返回值和局部变量,在程序运行过程中实时分配和释放,栈区由操作系统自动管理,无须程序员手动管理。

补充:往上还存在命令行参数及环境变量。
全局变量在程序的整个运行过程中存在。
局部变量的生命周期从其定义开始,到函数执行结束时自动销毁。

栈与堆的区别:
1、管理方式不同:堆的申请malloc()与释放free()由程序员来完成,栈由系统编译器自动分配
2、空间大小不同:堆空间大于栈空间
3、栈在内存中连续分配,不会产生碎片。堆的频繁申请可能造成内存空间的不连续性,产生大量碎片
4、增长方式不同:栈向内存地址减小的方向增长,堆则相反
5、分配效率不同:栈的申请效率通常高于堆,计算机在底层提供寄存器存放栈的地址,压栈出栈有专门的指令;堆由c函数库提供,动态内存分配需要有一定的算法去寻找申请足够大的地址空间。

内存错误情况:
1、指针没有指向一块合法的内存,
2、指针没有初始化地址。

int * p;  //1、野指针
char *p = NULL; //2、空指针
*p = 100//以上两种情况,都会引起段错误

3、指针分配的内存太小
4、内存越界:向缓冲区写入超出其大小的数据,可能导致覆盖相邻的内存区域,引发未定义的行为或安全漏洞
5、内存泄漏:动态分配的内存未被释放,导致程序持续占用内存,最终耗尽系统资源
6、内存释放后,继续使用该指针

objdump -h查看程序的各个段

使用任意一个可执行的**.c语言程序即可,首先编译为.o**文件。

gcc -c main.c

运行,显示结果如下图

objdump -h main.o

请添加图片描述

3、内存映射机制

在单片机中(无MMU)或者未开启MMU时的控制器中,CPU直接通过物理地址来访问外设和内存。
启动MMU(Memory Management Unit),即内存管理单元,硬件器件。作用是将CPU发出的虚拟地址转换为应用程序的物理地址访问外设和内存。

Linux内核会把物理地址会划分为一个个4K的块,也称为页,会对每个块进行编号处理,并用Struct page这个结构体来管理块(页)的状态信息,并对每一页进行编号 PFN(Page Frame Number)。

下面是Linux系统下两级页表映射机制解析图所示
在这里插入图片描述

在32位操作系统中,程序员首先给出的是逻辑地址,然后通过分段机制转换为线性地址,再将线性地址通过分页机制转换为物理地址
CPU发出的线性地址(虚拟地址),以0000000011 0000000100 0001 0000 0000为例解析,分为3个部分,第一部分是目录Directory,第二部分是页表,第三部分是偏移量

首先取出第一部分的值为3索引去查看页目录表,找到页目录项3,里面存放着页表3的地址。每个进程都有一个页目录表,表里有1024页目录项,页目录项的地址保存至cr3寄存器中,每一个页目录项又指向相应的页表,所以通过第一项能得到页表的地址,一个页表也是1024项
其次,第二部分以table的值为4索引找到页表里对应的页表4项的内容,项的内容保存的是实际的物理块号4的,找到对应的4K物理块起始地址0x1000 4000(基地址)。
最后,第三部分通过offset的偏移量256和页表3里面的页表项4的地址相加来找到最终物理地址0x1000 4256,完成内存映射。

总结:通过前几位来找到页目录表中的项数来确定二级页表,再通过中间几位找到页表在的页表项来确定基地址,再通过后几位来确定偏移地址

通常程序访问的是虚拟内存,虚拟内存映射到物理内存的一小部分
在Linux系统中,虚拟内存默认为4G的大小。每个进程都有独立的4G内存地址空间。4G指的是可用范围为4G,不一定能占满,所以能映射到的物理内存实际上很少,只是让进程误以为自己独占4G内存。
进程中分为用户空间和内核态
在这里插入图片描述

总的来说,虚拟地址的映射涉及到整个进程的地址空间,而不是特定变量或数据结构内的偏移地址映射。
值得注意的是,操作系统为每个运行的进程分配独立的虚拟地址空间,每个进程都有自己的页表
每个进程的虚拟地址其实是不一样的,当不同的进程里,如定义int a = 10;假设其地址都是0x1234,访问相同的虚拟地址时,它们实际上在各自的页表中进行地址映射。
进程里的变量的地址是相对于进程的虚拟地址空间的偏移量,所以不同进程里的相同虚拟地址,如0x1234所映射的物理地址是不相同的。

不同进程间虚拟地址对应物理内存的映射情况
在这里插入图片描述
以上仅为个人见解,如有问题,欢迎讨论!
补充
一级页表中每一个表项用来设置1M 的空间,对于整个4G空间,需要4096个页表项。
一级页表映射,MMU根据虚拟地址的[31:20]确定是哪一个一级页表项,后[19:0]确定偏移地址。
换页机制:当物理内存不足时,操作系统会将部分不常用的页写入磁盘上的交换空间(swap space),释放出物理内存用于其他进程。需要时,这些页会从磁盘重新加载到物理内存中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值