linux内存管理机制

时间:2020-12-19 16:53:42

做了这么年的linux c开发,经常碰到各种内存问题。这里结合网上资料做一下总结。

一. 内存位置

 在C语言中,定义了4个内存区间:代码区;全局变量和静态变量区;局部变量区即栈区;动态存储区,即堆区;

linux内存管理机制

 

1. bss段 block started by symbol. 存储没有被初始化的全局和静态变量。 

2. data段 存储被初始化的全局和静态变量

3. rodata段 常量变量。 read only. 用cons来修饰

4. text段 代码段

5. stack栈 用来储存局部变量,函数的参数值,地址向下增长。

6. heap堆 被programmer控制,由malloc和free获取或释放。

 

二. 各种内存错误 

1. 内存泄漏memory leak  调用malloc后没有free,导致内存剩余容量越来越少。解决方法:

a. 通过少量的实践和适当的文本搜索,您能够快速验证平衡的 malloc() 和 free() 或者 new 和 delete 的源主体

b. 运用实时监测工具, 如valgrind

 

2.  缓冲区溢出buffer overlow. 在分配的内存外的读写数据 

   stack overflow: 实际上是一种buffer overflow. 以下是*的描述:

栈有自己的内存大小限制,栈大小由编程语言,机器架构,多线程等在程序开始决定的,当程序尝试使用超过栈空间的部分,就会出现overflow,程序崩溃。

stack overflow常用的导致方式是递归,重复调用自己,导致内存用尽

An example of infinite recursion in C

int foo() { return foo(); }

如果用了-O2/-O3,那么可能会有一些内部的优化使得程序不会有段错误)。一个递归例子:

 

 

int pow(int base, int exp) {

    if (exp > 0)

        return base * pow(base, exp - 1);

    else

        return 1;

}

int pow(int base, int exp) {

    return pow_accum(base, exp, 1);

}

 

int pow_accum(int base, int exp, int accum) {

    if (exp > 0)

        return pow_accum(base, exp - 1, accum * base);

    else

        return accum;

}

运行完结果对比:

   
pow(5, 4) 5 * pow(5, 3) 5 * (5 * pow(5, 2)) 5 * (5 * (5 * pow(5, 1))) 5 * (5 * (5 * (5 * pow(5, 0)))) 5 * (5 * (5 * (5 * 1))) 625 
pow(5, 4) pow_accum(5, 4, 1) pow_accum(5, 3, 5) pow_accum(5, 2, 25) pow_accum(5, 1, 125) pow_accum(5, 0, 625) 625 

左边的代码要保存很多中间变量,一旦exp太大,会发生overflow。右边只需要保存三个变量,不会产生overflow。这样迭代风格代码的转换,只需要把上一次的迭代结果放在下一次的迭代的input里面就可以解决。很有技巧!

另外一个,是因为函数里面的局部变量太大,如:

int foo() { double x[1048576]; } 

一个线程时,工作顺利。但是当多个线程时,程序崩溃, 这是因为多线程时stack空间比单线程小。

 

三. 内存碎片

分配的内存在内存空间内小而不连续。内存分配较小,并且分配的这些小的内存生存周期又较长,反复申请后将产生内存碎片的出现。如何避免:

a. 尽量少使用动态分配内存函数如malloc等。同时分配和释放要在同一函数里面。

b. 一次性分配一个很大的内存,自己做内存池,自己管理内存。

 

四. segmentation error 段错误。

段错误是指访问的内存超出了系统给的这个程序的内存空间。简单例子:

#include <stdio.h>
typedef struct TEST {
int a;
int b;
} test;
int main(){
 test *t = NULL;
 t->a = 12;
 return 0;
}

 运行完程序报segmentation error. 可以用gdb去debug. 如下图, 在compile的时候,加上-g. 运行时, gdb xxx(运行的程序)进入debug界面,run完之后,在backtrace,查出错的路径。

linux内存管理机制

小窍门: 在-O3优化中发生segmentation error, 复杂代码可能用gdb backtrace 找不到正确的位置,那么我们可以在大概的位置加上

#pragma opt_level = "O0"

强制它变成O0。