关于Linux的内存之我的总结

关于Linux的内存之我的总结

说来惭愧,作为一名Linux应用层软件开发人员,应当对Linux的分配、布局等方式了然于胸,而我索然在之前有所了解,但总觉得是云里雾里的,没有真正搞清楚。现在也算是一个总结吧,可能还会有错误,后续把《深入理解Linux内核》这本书学习后,再来修改。

1、应用程序的内存布局

大家都知道,一个Linux的应用程序,拥有代码段(.text段)、数据段(.data段)、BSS段、堆、栈这五个段。
其中,数据段和BSS段的区别是,数据段中存放的是已经初始化过的全局变量和静态变量,而BSS段中放的是未经初始化的全局变量和静态变量。当有应用程序加载到内存后,发现BSS段中有数据,则会在分配一块堆空间,将BSS段中的数据移动至堆里,并将其全部赋值为0。
有下面这个小例子:
在这里插入图片描述在这里插入图片描述

在这里插入图片描述在这里插入图片描述
上图和下图当中,唯一的区别就是上图没有把test[1024000]这个数组给赋值,而下图将数组赋值了,但编译出来的大小可以看到,这两个程序的大小正好是1024000字节,也就是这个数组的大小。这说明了数据段是会在编译出来的程序中占用空间,而BSS段则不会。

2、进程的具体内存分布

首先见下图(网上找了张):
在这里插入图片描述
大家都知道,Linux的每个进程,在32位的情况下,拥有4G的虚拟内存。其中0-3G是属于用户态内存,而3-4G是内核态的内存。
如上图可以看出,从0地址往上增加的过程中,首先存放的是只读段数据,比如刚才所说的程序段、常量数据等,然后就到了数据段和BSS段的可读写区域。在可读写数据区域的上面,是堆区域,而最靠近内核态内存区域的是栈空间。堆和栈空间之间的区域,是文件映射区域。
关于上面的描述,我见到过一个面试题,记录在这里:

int main (int argc, char **argv)
{
	int a;
	static int b;
	void *c = malloc(100);
	const char d = 1;
}

求问,由&a, &b, c, &d排个大小顺序。
根据前面看到的进程内存布局可以知道,a是存在于栈空间的,它的地址属于最高,也就是说&a为最大,而b处于可读写的数据段中,c处于堆空间中,&d处于只读数据区域。
所以&a > c > &b > &d。

3、内存分配方式

Linux在运行的时候,用户态程序使用malloc来进行内存分配。而malloc是一个C库实现的程序,他的底层分为两种情况:
在malloc小于128KB以内的内存时,系统调用brk()或sbrk()进行内存分配,此时内存是在堆中往上增加。此时不需要陷入到内核进行内存分配,性能最强。但也有缺点:那就是会出现泄漏的问题,具体是因为,以brk方式分配内存,只是把堆的指针向上移动得到新的虚拟内存。当多次分配内存后,想释放中间的内存,由于上面的内存还在使用中,故不可释放,只是将其标记为可用,下次还需要申请时,会从此处分配。当中间内存的上面区域被释放后,此时该内存才会真正被释放掉。所以有时候会看到,申请内存或释放内存后,VSZ并没有增大或者减小。而当多次申请和释放小内存时,会有内存泄漏问题,而此时是无法使用valgrind工具检测到。
在malloc大于128KB以内的内存时,系统其实是调用malloc()来进行内存的分配,此时内存分配区域处于栈和堆中间的文件映射区。这种内存分配的好处就是每次申请和释放,都能做到真正的内存操作,而不会造成内存碎片问题。缺点就是,每次分配时,需要陷入到内核态,进行内存分配,性能开销比较大。
另外要说明的是,malloc分配内存时,是先分配好虚拟内存,建立好页表而已,当应用程序需要使用到此块内存时,陷入到内核态,建立起真正的虚拟内存到真实物理内存的映射关系。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值