简述:Unix/Linux内存管理

1、底层结构程序员

        采用三层结构,实际使用中能够方便映射到两层或者三层结构,以适用不一样的硬件结构。最下层的申请内存函数get_free_page。之上有三种类型的内存分配函数:
函数

        1.kmalloc类型。内核进程使用,基于切片(slab)技术,用于管理小于内存页的内存申请。思想出发点和应用层的内存缓冲池同出一辙。但它针对内核结构,特别处理应用场景固定,不考虑释放。spa

        2.vmalloc类型。内核进程使用。用于申请不连续内存。
code

        3.brk/mmap类型。用户进程使用。malloc/free实现的基础。
对象

2、内存管理的相关函数图进程

        STL  ->  内存自动分配和自动回收(C++)
内存

           |
开发

        C++  ->  new分配内存,delete回收内存
字符串

           |
get

          C  ->  malloc分配内存,free回收内存

           |

        Unix 系统函数 ->  sbrk/brk  分配和回收内存

           |

        Unix底层系统函数  ->  mmap/munmap分配回收

                                                                                        (用户层)

----------------------------------------------------------------------------

                                                                                        (内核层)

        Unix内核函数  kmalloc/vmalloc/get_free_page

3、进程与内存

        a.全部进程(执行的程序)都必须占用必定数量的内存

        b.对任何一个普通进程来说,它都会涉及到5种不一样的数据段,其内存空间划分为:

                1.代码区    ——    存放代码/函数,也就是说它是可执行程序的内存中的镜像。(只读)

                2.全局区    ——    保存全局变量,static局部变量。

                3.BSS段     ——    未初始化的全局变量,BSS段在main函数执行以前会自动清零

                4.栈区       ——    局部变量,包括函数的形参,栈区内存自动分配和自动回收。

                5.堆区       ——    程序员本身管理的区域,malloc/free操做的都是堆区。

                6.只读常量区    ——    存放字符串常量和const修饰的全局变量

                注:只读常量区和代码区很是接近,有些书把只读常量区和代码区合并为代码区。

        c.进程如何组织这些区域?

                从小到大次序:代码区、只读常量区、全局区、BSS段、堆区、栈区

                堆区在离前面四个区不远的地址空间开始,从小到大分配,栈区从3G开始,从大到小分配。主要为了不堆区和栈区重叠。

        d.查看内存分配

                Linux把一切都看作成文件,内存也能够在文件中查看。每一个进程都在/proc目录下有一个对应的子目录,以进程ID做为子目录名。进程ID是系统对进程的标识。能够用ps-aux命令查看进程。

                cat /proc/进程ID/maps    能够查看当前进程的内存状况。

4、虚拟内存管理技术

        Linux使用了虚拟内存地址。每一个Linux中的进程都有0~4G的虚拟内存地址,就是0~4G的数字。虚拟内存地址在开始时只是一个数字,不对应任何的内存。虚拟内存地址必须先映射一段物理内存或硬盘上的文件才能被使用。所谓的分配内存其实就是让虚拟内存地址映射一段物理内存。若是使用没有映射的虚拟内存地址就会引起段错误。

        程序员所操做的内存地址都是虚拟内存地址,看不到物理内存地址。

        0~4G的虚拟内存地址中,0~3G是用户使用,叫作“用户空间”,3G~4G是内核使用的,叫作“内核空间”。用户空间不能直接使用内核空间,但能够经过内核空间提供的一些函数(系统调用)访问内核空间。

        注:内存管理的基本单位是4096 byte (4K),叫内存页。内存的映射和回收都是之内存页做为基本单位。

5、进程内存管理

        进程内存管理的对象是进程线性地址空间上的内存镜像,这些内存镜像其实就是进程使用的虚拟内存区域(memory region)。进程虚拟空间是个32或64位的“平坦”(独立的连续区间)地址空间(空间的具体大小取决于体系结构)。要统一管理这么大的平坦空间可绝非易事,为了方便管理,虚拟空间被划分为许多大小可变的(但必须是4096byte的整数倍数)内存区域,这些区域在进程线性地址中像停车位同样有序排列。这些区域的划分原则是“将访问属性一致的地址空间存放在一块儿”,所谓访问属性一致无非是指“可读、可写、可执行等”。

6、物理内存管理(页管理)

        Linux内核管理物理内存是经过分页机制实现的,它将整个内存划分红无数4K(在i386体系结构中)大小页,从而分配和回收内存的基本单位即是内存页了。利用分页管利用助于灵活分配内存地址,由于分配时没必要要求必须有大块的连续内存,系统能够东一页、西一页的凑出所须要的内存供进程使用。虽然如此,可是实际上系统使用内存仍是倾向于分配连续的内存块,由于分配连续内存时,页表不须要修改,所以能下降刷新率(频繁刷新会很大增长访问速度)。

7、brk/sbrk的虚拟内存管理

        void *sbrk(int size);

            size = 0    返回sbrk/brk上次的末尾地址,表明取当前的位置,

            size > 0    分配内存空间,返回上次的末尾地址,表明分配size字节的内存,

            size < 0    释放空间,表明回收size字节内存。

        int brk(void* ptr);

            直接修改访问的有效范围的末尾地址,释放空间造成一个完整的page,则该页映射被解除

            返回:0    分配成功

                      -1    分配失败

        经验:sbrk在分配内存上简单,brk在释放内存上简单。所以,开发大多数使用sbrk分配内存,使用brk释放内存。

8、系统底层的内存映射(mmap/munmap)

#include <sys/mman.h>
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *start, size_t length);

        参数公共部分:

            start:指向欲映射的内存起始地址,一般设为 NULL,表明让系统自动选定地址,映射成功后返回该地址。

            length:表明将文件中多大的部分映射到内存。    映射空间大小。建议4k倍数,不是4K倍数,自动对齐。

        mmap独有部分:

            prot:映射区域的保护方式。能够为如下几种方式的组合:

                1.PROT_EXEC 映射区域可被执行

                2.PROT_READ 映射区域可被读取    

                3.PROT_WRITE 映射区域可被写入

                4.PROT_NONE 映射区域不能存取

            flags:影响映射区域的各类特性。在调用mmap()时必需要指定MAP_SHARED 或MAP_PRIVATE。

                1.MAP_FIXED 若是参数start所指的地址没法成功创建映射时,则放弃映射,不对地址作修正。一般不鼓励用此标志。

                2.MAP_SHARED对映射区域的写入数据会复制回文件内,并且容许其余映射该文件的进程共享。

                3.MAP_PRIVATE 对映射区域的写入操做会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域做的任何修改都不会写回原来的文件内容。

                4.MAP_ANONYMOUS创建匿名映射。此时会忽略参数fd,不涉及文件,并且映射区域没法和其余进程共享。

                    5.MAP_DENYWRITE只容许对映射区域的写入操做,其余对文件直接写入的操做将会被拒绝。

                    6.MAP_LOCKED 将映射区域锁定住,这表示该区域不会被置换(swap)。

            fd:要映射到内存中的文件描述符。若是使用匿名内存映射时,即flags中设置了MAP_ANONYMOUS,fd设为-1。有些系统不支持匿名内存映射,则可使用fopen打开/dev/zero文件,而后对该文件进行映射,能够一样达到匿名内存映射的效果。

            offset:文件映射的偏移量,一般设置为0,表明从文件最前方开始对应,offset必须是分页大小的整数倍。

        返回值:

            若映射成功则返回映射区的内存起始地址,不然返回MAP_FAILED(-1),错误缘由存于errno 中。

9、errno错误代码

           1.EBADF 参数fd 不是有效的文件描述词

            2.EACCES 存取权限有误。若是是MAP_PRIVATE 状况下文件必须可读,使用MAP_SHARED则要有PROT_WRITE以及该文件要能写入。

            3.EINVAL 参数start、length 或offset有一个不合法。

            4.EAGAIN 文件被锁住,或是有太多内存被锁住。

            5.ENOMEM 内存不足。

相关文章
相关标签/搜索