1.Linux进程内存分布
进程内存分布如上图:
[1]Linux没有采用分段机制,逻辑地址和虚拟地址是一个概念。所谓虚拟地址,就是物理地址的映射。虚拟内存开始时不对应任何内存,直接使用会引发段错误,不进入内核就接触不到物理内存地址,只会接触到虚拟内存地址。虚拟内存地址必须映射物理内存(或者硬盘上的文件)以后才能存储数据 (数据存储在物理内存上,打印地址为虚拟内存地址)。而内存分配其实就是虚拟内存地址映射物理内存的过程,内存回收则是解除映射关系的过程。 虚拟内存中,0~3G是用户空间,3~4G是内核空间。通常所讲内存地址,其实都不是真正意义上的物理内存的地址,而是虚拟内存地址。
[2]内核空间中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据。
[3]例如,32位系统中,创建了一个进程,则当前进程拥有32位的虚拟地址空间(32位系统只能表示到4G),因为其中有3G的用户内存,所以如果用malloc申请内存时,最大申请的总长度可以接近3G,因为此时并不涉及物理内存。而如果malloc后对内存进行操作,例如memset为0,因为此时需要访问物理内存,所以会产生一个缺页异常,把数据写入到物理内存。因此这时候申请的总长度就是当前可用的物理内存。
2.用户空间
进程的代码和数据存放于用户空间,对于任何一个进程,都会涉及到不同的内存段。
此处应当这样理解:Linux系统把内存分为内核空间和用户空间,用户空间给进程使用。另外不能和某个具体语言的编译器内存分区混淆,意思就是提到用户空间,就是以下的分段管理,具体到某个编程语言的内存处理是在这个之上的。
[1]用户空间的内存被分为几个段(图中自下向上说明):
代码段: 代码段是用来存放可执行文件的操作指令,也就是说是它是可执行程序在内存中的镜像。很明显这部分内存是只读的。
数据段: 数据段用来存放可执行文件中已初始化(非0)的全局变量。如果初始化为0,则会被放到BSS段。
BSS段: BSS段包含了程序中未初始化全局变量,在内存中bss段全部置零。这意思就是创建一个为初始化的全局变量会被置0。
堆: 堆用于存放进程运行中被动态分配的内存段。使用malloc分配的内存就是从堆上面分配。
mmap段: 这个用于mmap系统调用的内存分配。
栈: 栈存放程序临时创建的局部变量。
BSS段和数据段区别:全局变量如果初始化为非0,则放到数据段,如果未初始化或者初始化为0,则放到BSS段。
另外注意的是statis类型的变量,即使在函数内声明,也属于全局变量。
[2]进程的内存空间就是在上面那些段中使用内存。可以使用cat /proc/<pid>/maps命令,查看进程使用的内存区域地址:
3.内核空间
4.内存映射mmap
[1]原理:调用mmap后,在虚拟空间中分配一段连续的内存,此时只是逻辑映射,并没有分配物理内存。当访问这段空间时,产生缺页中断,这时候是首先在swap页面中查找,如果没找到,则将硬盘的文件读到物理内存中。
[2]mmap主要有两种用法,第一个是父子进程之间共享内存的作用。另一个是把磁盘文件映射到进程的虚拟地址空间。
5.top命令
VIRT:一个进程所使用的虚拟内存的总数。它包括共享库、共享内存、栈、堆,所有已申请的总内存空间。VIRT也就是虚拟内存。
RES:进程正在使用的实际物理内存,不包括SWAP。
SHR:共享内存大小。这个是物理内存。
DATA:栈内存+堆内存。