前言
提示:以下是本篇文章正文内容,下面案例可供参考
一、空间分布
二、栈和堆的特点
(1)栈堆相对而生,堆是向上增长的,栈是向下增长的。
验证:堆是向上增长的
这里我们看到申请的堆,依次进行申请,堆的地址是向上增长的。
栈:向下增长(32位机器和64位机器)
验证:
这里为什么说要32位机器。对64位机器来说,栈是向下增长的,这个说法也许是不正确的。
至于为什么,这个为什么?64位对内存的分布进行了改进。下面打印的结果和32位地址结果相反
三.物理内存和虚拟内存
上面我们提到内存的空间分布是真实的物理内存吗?
我们创建子进程,父子进程代码共享,当一方发生写入时,就会发生写实拷贝,父子进程数据独立。
这里我们发现,父子进程打印的值是不同的,这里我们可以理解,但是地址为什么是一样的,一个内存地址一样,值不一样,说明这个不是真正的物理内存,这个是虚拟地址。
1.地址空间和区域划分
我们知道地址空间的大小是4GB,操作系统将这个4GB根据每个进程的需要进行分配,每个进程都对所分配的空间独立使用权,比如栈,堆中的一部分。地址空间就是大蛋糕,区域划分就是分蛋糕,分配的对象就是进程。
操作系统要对进程地址空间进行管理,在linux中地址空间就是一个内核数据结构:struc mm_struct{}。进程的task_struct里有个指针,指向这个struct mm_struct{}.
这个里面描述了,可以使用的地址空间的范围。起始地址和结束地址之间的都是可以被使用的区域。
虚拟地址如何转换为物理内存?通过页表进行映射,这个过程是操作系统帮我们做的。
2.为什么要有虚拟地址空间
1.可以让申请的物理内存分布,通过虚拟地址让无序变为有序。
2.可以有效的进行安全检查,页表中其实还有权限字段,规定了数据是否可读可写。字符常量区权限就是只读,如果发生修改的转化,编译器识别到就会显示错误。
3.将进程管理和内存管理进行解耦。
内存管理是操作系统帮我们做的,申请内存(分配),填充数据或者加载程序(是否有内容)。一个进程如果要对物理内存进行访问,首先要通过虚拟地址,在cpu中寄存器CR3中存储者页表的物理地址,硬件MMU进行虚拟到物理转化的过程中,先要通过页表字段查看内存是否分配和内存是否有内容,没内容或者被分配,这个进程就会被停止访问内存,将操作权限转交给操作系统,这时操作系统就会进行内存的分配和填充数据或者程序,将虚拟内存映射到分配的物理内存,这个过程也叫缺页中断,分配填充完,进程就可以继续访问。进程不参与内存管理,操作系统管理内存分配。
总结:一个进程有自己的页表,自己的空间地址,有自己的task_struct.进程的独立性通过页表的虚拟地址映射物理地址来体现。进程的切换也是不影响其他进程,因为进程的上下文数据在切换时就会被进程带走包括寄存器的内容,等到进程重新被调度,恢复上下文数据包括寄存器中的进程原来的内容。
3.如何理解父子进程变量地址相同而值不同
fok()创建子进程,子进程要以父进程为模板拷贝页表,地址空间。如果一方发生写入的时候,操作系统就会修改页表重新将物理内存映射到虚拟地址上,这也为什么上面的同一个变量打印的虚拟地址是相同的,值是不一样的。真实的物理地址我们是看不到,c++/或者c,我们可以看到的都是虚拟地址。