Hi,您好,欢迎来到西安盛图软件科技有限公司!

Linux 如何进行内存分配

发布时间:2023-10-19 16:48:15

虚拟内存管理回顾

在 Linux 操作系统中,虚拟地址空间的内部又被分为内核空间和用户空间两部分,不同位数的系统,地址空间的范围也不同。比如最常见的 32 位和 64 位系统,如下所示:



通过这里可以看出:


再来说说,内核空间与用户空间的区别:


虽然每个进程都各自有独立的虚拟内存,但是每个虚拟内存中的内核地址,其实关联的都是相同的物理内存。这样,进程切换到内核态后,就可以很方便地访问内核空间内存。



我们看看用户空间分布的情况,以 32 位系统:



通过这张图你可以看到,用户空间内存,从低到高分别是 6 种不同的内存段:

申请内存的两种方式

申请内存空间一般就两种方法,一种是 malloc,另一种是 mmap 映射空间。 在使用 malloc()分配内存的时候,可能系统调用 brk(),也可能调用 mmap()。

malloc 的调用规律

1. 即分配一块小型内存(小于或等于 128kb),malloc()会调用 brk 函数将 堆顶 指针向高地址移动,获得新的内存空间。

2. 当分配一块大型内存(大于 128kb),mmap() 系统调用中「私有匿名映射」的方式,在文件映射区分配一块内存

申请内存过程图

需要注意的是,malloc() 分配的是虚拟内存。


如果分配后的虚拟内存没有被访问的话,虚拟内存是不会映射到物理内存的,这样就不会占用物理内存了。

只有在访问已分配的虚拟地址空间的时候,操作系统通过查找页表,发现虚拟内存对应的页没有在物理内存中,就会触发缺页中断,然后操作系统会建立虚拟内存和物理内存之间的映射关系。


缺页中断就是要访问的页不在主存,需要操作系统将其调入主存后再进行访问。在这个时候,被内存映射的文件实际上成了一个分页交换文件。

malloc 申请的内存,free 释放内存会归还给操作系统吗

mmap 和 brk 分配内存的区别

mmap 来分配内存的问题

mmap 分配的内存每次释放的时候,都会归还给操作系统,于是每次 mmap 分配的虚拟地址都是缺页状态的,然后在第一次访问该虚拟地址的时候,就会触发缺页中断。


也就是说,频繁通过 mmap 分配的内存话,不仅每次都会发生运行态的切换,还会发生缺页中断(在第一次访问虚拟地址后),这样会导致 CPU 消耗较大。


为了改进这两个问题,malloc 通过 brk() 系统调用在堆空间申请内存的时候,由于堆空间是连续的,所以直接预分配更大的内存来作为内存池,当内存释放的时候,就缓存在内存池中。


等下次在申请内存的时候,就直接从内存池取出对应的内存块就行了,而且可能这个内存块的虚拟地址与物理地址的映射关系还存在,这样不仅减少了系统调用的次数,也减少了缺页中断的次数,这将大大降低 CPU 的消耗。

只使用 brk 来分配内存的问题

前面我们提到通过 brk 从堆空间分配的内存,并不会归还给操作系统,那么我们那考虑这样一个场景。

如果我们连续申请了 10k,20k,30k 这三片内存,如果 10k 和 20k 这两片释放了,变为了空闲内存空间,如果下次申请的内存小于 30k,那么就可以重用这个空闲内存空间。



但是如果下次申请的内存大于 30k,没有可用的空闲内存空间,必须向 OS 申请,实际使用内存继续增大。


因此,随着系统频繁地 malloc 和 free ,尤其对于小块内存,堆内将产生越来越多不可用的碎片,导致“内存泄露”。而这种“泄露”现象使用 valgrind 是无法检测出来的。


所以,malloc 实现中,充分考虑了 brk 和 mmap 行为上的差异及优缺点,默认分配大块内存 (128KB) 才使用 mmap 分配内存空间。


上一篇:嵌入式系统和C语言
下一篇:学习方法|学习嵌入式的十个小技巧