Linux内存管理
一、进程映像
进程在内存空间中的布局就是进程映像,从低地址到高地址依次为:
二、虚拟内存
首先要理解一点就是操作系统不能把物理内存直接分配给进程使用,如果操作系统把物理内存直接分配给进程,这样就会面临严重的安全问题,进程可以根据自己获取到的内存地址,对指针进行运算从而访问其它进程的内存空间进行破坏,甚至影响操作系统的安全,所以操作系统的设计引入的虚拟内存的概念。
1、什么是虚拟内存:
虚拟内存是操作系统内核为了对进程地址空间进行管理( process address space management)而精心设计的一个逻辑意义上的内存空间概念。
在32位系统下,操作系统规定每个进程拥有4G的虚拟内存,但这4G的内存空间并不能直接使用,也就是操作系统给进程画的饼,当进程真的需要使用内存来存储数据时,操作系统会把一部分的虚拟内存与物理内存进行映射,映射后的虚拟内存才能正常使用。
2、用户空间与内核空间:
每个进程的4G虚拟内存,根据使用者的不同分为两个部分。
(1)用户空间:
[0x00000000,0xC0000000)部分,有3GB大小,在经过操作系统的映射后,在应用程序中使用,程序中不能直接访问内核空间中的代码和数据,但可以通过系统调用进入内核态,间接地与系统内核交互。
(2)内核空间:
[0xC0000000,0xFFFFFFFF]部分,有1GB大小,只有操作系统才能使用,里面存储着操作系统为该进程服务或者与该进程交互所需要的相关数据,内核空间由操作系统内核进行管理,不会随进程切换而改变。
用户空间对应进程,进程一切换,用户空间即随之变化,每个进程的用户空间完全独立。不同进程之间交换虚拟内存地址是毫无意义的,所以下面两个程序无法完成数据传递。
3、进程使用虚拟内存好处有哪些:
1、操作系统为每个进程分配一个4G虚拟内存空间,可以对进程进行隔离,避免进程之间互相影响、破坏。
2、操作系统把4G虚拟内存划分为用户态和内核态,可以对进程和操作系统进行隔离,避免进程对操作系统的影响破坏。
3、操作系统还可以把硬盘上的文件与虚拟内存进行映射,当物理内存不够用时,还可以使用硬盘代替,虽然速度慢了些。
三、内存映射
1、自动映射:
当程序执行时,操作系统把它加载到内存形成进程后,会自动给该进程进行text、data、bss、stack、命令行参数表、环境变量表内存映射。
2、手动映射:
在程序中首次使用malloc申请内存时,此时malloc手里没有堆内存可分配,也就是没有映射过的内存可供分配,malloc会向操作系统申请映射33页虚拟内存,这33页内存就归malloc管理了,之后再向malloc申请内存时,malloc会从这映射好的33页内存中分配给调用者,而malloc函数底层调用了操作系统API接口完成了映射操作。
如果程序要使用堆内存不多,达不到33页,那么使用malloc申请内存就会造成内存浪费,可以直接调用了操作系统API接口,进行更精确的内存映射。
四、Linux系统内存管理API
遵循POSIX标准的内存管理API:
#include <unistd.h>
// brk和sbrk在内部维护一个指针p,指向当前堆内存一个字节的下一个位置地址。
void *sbrk(intptr_t increment);
功能:根据参数来调整p的位置(p+/-increment),既可以映射虚拟内存也可以取消映射。
返回值:调整前的p的位置。
int brk(void *addr);
功能:根据指针参数修改p的位置(把p设置为addr)。
返回值:成功返回0,失败返回-1。
// 注意:brk和sbrk都可以单独分配/释放内存,但一般配合使用,sbrk用于分配,brk用于释放。
// 注意:brk和sbrk只能用于对虚拟内存映射,无法做到精细化管理。
例如:计算出100到100000所有的素数,存储到内存中。
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <math.h>
bool is_prime(int num)
{
int end = sqrt(num);
for(int i=2; i<=end; i++)
{
if(0 == num % i)
return false;
}
return true;
}
int main(int argc,const char* argv[])
{
int* base = sbrk(0);
for(int i=100; i<10000; i++)
{
if(is_prime(i))
{
int* p = sbrk(4);
*p = i;
}
}
int *p = base, *end = sbrk(0);
while(p < end)
{
printf("%d ",*p++);
}
printf("\n");
brk(base);
return 0;
}
Linux系统的内存管理:
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
功能:
1、用户空间的虚拟内存与物理内存建立映射关系。
2、用户空间的虚拟内存与文件建立映射关系。
3、对无关联进程提供共享内存空间。
addr:想要映射虚拟内存的首地址,如果是NULL则操作系统自动计算。
length:要映射的字节数
prot:映射的内存权限
PROT_EXEC 执行权限
PROT_READ 读权限
PROT_WRITE 写权限
PROT_NONE 无权限
flags:
MAP_SHARED 共享映射,映射的内容对其它进程是可见的,对共享区的写入,相当于输出到文件。
MAP_PRIVATE 私有映射,映射的内容对其它进程不可见的
MAP_DENYWRITE 拒绝其它文件写入操作
MAP_ANON 映射的是内存而非文件
fd:文件描述符,类似于文件指针FILE
offset:映射文件时使用到的偏移值
返回值:映射成功后的虚拟内存地址,如果失败返回值为0xffffffff
int munmap(void *addr, size_t length);
功能:取消映射
addr:映射内存的首地址
length:内存的字节数
返回值:成功返回0,失败返回-1。
// 注意:系统的内存映射是以页为单位的(4096byte)