Linux 下malloc的探究

前几天看《操作系统:精髓与设计原理》的虚拟存储系统的时候想到个问题,一个程序是否可以无限制的分配内存空间。今天刚好有点时间,就写了个小程序试了试。

#include<stdio.h>
#include<stdlib.h>

int mian()
{
    int *p;
    int i=0;
    while(1)
    {
        p=(int *)malloc(sizeof(char)*1024*1024);
        i++;
        printf("%dM\n",i);
    }

}


运行的结果让我和我的小伙伴都惊呆了 。。。居然能分配100G+。。并且还能继续分配。。当时就吓尿了啊。。。

哥这破二手电脑也就只有40G的内存啊。。怎么能分配这么多呢?
 
于是哥又想到了内存泄漏,可能每次内存如果没有指针去指向它的话可能就直接释放了。
 
于是

 p=(int *)malloc(sizeof(char)*1024*1024*(i+1));

居然还是和原来一样!!!看来原来C学C的时候还是学的太浅了啊。。

然后又想到是不是没有使用那一块内存,所以gcc直接优化掉了。

于是就在分配内存下面增加了一条语句

memset(p,i,sizeof(char)*1024*1024)

分配语句改成第一次的样子。

这次总算是正常了。前800M都是很快出来。估计是可以直接用内存的原因(只有1G内存)。

但是后面的就出来很慢了。大概一秒也就刷5到6行的样子。个人猜测可能是用到了虚拟内存的缘故,不断的IO操作,让速度慢了很多。

到1200M在左右的时就基本就不能动了。。所以只好乖乖的等待被内核干掉了。

大概8分钟左右的样子,程序出现了Out of memory的字样。提示说结束进程或者牺牲掉孩子(英语不好。。)。

然后下面两排提示杀掉了进程,行尾显示:total -vm 2728124KB  anon -rss 869736KB  file -rss 88KB

最后排就显示个killed.

于是问题又来了。这些数字是个什么意思呢。。

total 意思估计就是总的大小吧。已经分配了的磁盘+内存空间大小。

anon的意思就没这么容易看出来了。不过从之前的观察来看,似乎是内存使用的大小。

file 这个如果是文件的长度,似乎不太对头啊。估计是内核分配给程序的初始空间大小吧。

查了下swap的大小,2015.99M=2064373.76k。用total-anon差不多180000+K的样子,还没有把磁盘交换区占满,估计是还有其他程序也占用了吧。

弄了半天,malloc分配的空间,好像除了物理限制,似乎没有给设定一个最大分配的值啊。果断用man来查查看。

man这个东西还是好啊,又学了很多东西。

第一段:

 malloc这个函数通常是在程序内存堆中用sbrk这个函数来分配空间。并且是有限制的,在MMAP_THRESHOLD中设置了上限,默认大小是128K,可以用mallopt()来调整。

超过128K之后则用mmap来分配内存空间,并且不受RLIMIT_DATA(通过getrlimit设置)的影响。那RLIMIT_DATA是个什么呢?下面是百度上的解释。

一个进程的数据段最大字节长度。数据段中初始化数据、非初始化数据以及堆的总和。当调用函数brk动态改变一个进程的数据段大小时,若失败,errno值将被设置为ENOMEM。

真是越看越觉得自己了解的少啊。。为什么要设置这个RLIMIT_DATA呢。。为何mmap可以不受RLIMIT_DATA影响呢。。以后再慢慢学吧。

第二段:

一般glibc库中是默认会自动设置错误信息的。如果自己写一个当然就不一定了(废话。。)。另外,还有一个MALLOC_CHECK_可以用来设置检查错误的强度。

貌似设置成0就可以无限分配内存了?估计电脑会直接挂掉吧。。。

第三段(malloc有BUG!):

如果没有内存分配的时候,malloc会返回一个non-NULL(估计是个NULL),并不保证这块内存可以使用。然后系统会了解到内存没有了,还会通过OOM来乱杀进程。

但是刚才看OOM的资料时说,OOM会给每个进程打分,分最高的会被杀掉。这么看来。。这个BUG是不是就没有了。。

不过在多进程的情况下,可能在刚换到另一进程的时候,这个进程又分配了一点空间和之前那个准备杀掉的进程一样。这样就可能杀掉其他无关进程了。

当然这样的情况还与kill设置有关,最后还是得看OOM怎么实现的。

最后,sbrk和brk还有mmap也是内存分配的函数,只是没有malloc用的那么方便。mmap直接映射到内存,效率估计比操作数据段的brk和sbrk要快得多吧。