Chinaunix

标题: mmap内存占用问题 [打印本页]

作者: 印随    时间: 2008-04-02 14:08
标题: mmap内存占用问题
这两天在折腾 mmap 函数

函数原型
        void *mmap(void *start, size_t length, int prot, int flags,  int fd, off_t offset);


映射一个1G 的文件的时候, offset = 0,
当 length = 100 M ,系统剩余内存 72 M
当 length = 1000M,系统剩余内存为71 M

几乎对系统内存没有影响。查看内存的时候我把程序sleep掉,然后用free查看

什么原因?
作者: flw    时间: 2008-04-02 14:11
mmap 只占用地址空间,不占用内存。
作者: 印随    时间: 2008-04-02 14:17
mmap具体怎么实现的
我看过unix网络编程 卷2
没有讲到实现的原理
作者: flw    时间: 2008-04-02 14:20
原帖由 印随 于 2008-4-2 14:17 发表
mmap具体怎么实现的
我看过unix网络编程 卷2
没有讲到实现的原理

mmap 是内核实现的,相当于 read/write 的语法糖。
没什么神奇的地方。
作者: 印随    时间: 2008-04-02 14:22
flw:
先谢过了
我原来的Blog是在wordpress上的,
想转移到chinaunix上,有没有合适的“搬家工具”?
作者: flw    时间: 2008-04-02 14:25
原帖由 印随 于 2008-4-2 14:22 发表
flw:
先谢过了
我原来的Blog是在wordpress上的,
想转移到chinaunix上,有没有合适的“搬家工具”?

没怎么用过 blog。
作者: Sorehead    时间: 2008-04-02 14:35
让机器人替你做就行了。
作者: 思一克    时间: 2008-04-02 14:53
malloc也不占内存.

你分配1M, 和分配1G看也有你的效果.

当向内存中写东西时候才真正占用.


原帖由 印随 于 2008-4-2 14:08 发表
这两天在折腾 mmap 函数

函数原型
        void *mmap(void *start, size_t length, int prot, int flags,  int fd, off_t offset);


映射一个1G 的文件的时候, offset = 0,
当 length = 100 M ,系统 ...

作者: 印随    时间: 2008-04-02 15:41
原帖由 思一克 于 2008-4-2 14:53 发表
malloc也不占内存.

你分配1M, 和分配1G看也有你的效果.

当向内存中写东西时候才真正占用.



mmap也一样吗?

1G的内存地址我都访问过,但系统内存也没有大的变化

实际上是有变化的,在我的系统中,内存占用一直增长,直到剩余内存为十几M的时候停了

[ 本帖最后由 印随 于 2008-4-3 09:59 编辑 ]
作者: 思一克    时间: 2008-04-02 15:50
无论如何, 你向内存写东西了(比如每页开始写几个BYTES), 就必须有物理内存分配了.
你可以用LOCK标志再测试. 如果不用, 可能被交换出去的.

原帖由 印随 于 2008-4-2 15:41 发表

mmap也一样吗?

1G的内存地址我都访问过,但系统内存也没有大的变化

作者: flw    时间: 2008-04-02 15:51
原帖由 思一克 于 2008-4-2 15:50 发表
无论如何, 你向内存写东西了(比如每页开始写几个BYTES), 就必须有物理内存分配了.

不会吧?
应该是不占内存才对。
作者: 思一克    时间: 2008-04-02 16:16
读写的对象一定是物理内存呀. 否则往那里读写?

MMAP的内容物理内存可能在cached(?我没有具体确认).

原帖由 flw 于 2008-4-2 15:51 发表

不会吧?
应该是不占内存才对。

作者: flw    时间: 2008-04-02 16:26
原帖由 思一克 于 2008-4-2 16:16 发表
读写的对象一定是物理内存呀. 否则往那里读写?

MMAP的内容物理内存可能在cached(?我没有具体确认).


那个和 malloc 性质不同的吧。
作者: 思一克    时间: 2008-04-02 16:36
大致相同.

原帖由 flw 于 2008-4-2 16:26 发表

那个和 malloc 性质不同的吧。

作者: flw2    时间: 2008-04-02 16:37
mmap的不占用物理内存,内核会检查到缺页,然后知道是mmap文件的,所以会进行文件操作,具体细节忘了
如果是普通内存,就像版主说的,当写的时候缺页导致物理内存的分配
作者: 思一克    时间: 2008-04-02 16:42
就是mmap和malloc都是取得虚内存, 使用时候通过缺页获得物理内存的.

原帖由 flw2 于 2008-4-2 16:37 发表
mmap的不占用物理内存,内核会检查到缺页,然后知道是mmap文件的,所以会进行文件操作,具体细节忘了
如果是普通内存,就像版主说的,当写的时候缺页导致物理内存的分配

作者: flw2    时间: 2008-04-02 16:47
原帖由 思一克 于 2008-4-2 16:42 发表
就是mmap和malloc都是取得虚内存, 使用时候通过缺页获得物理内存的.


恩是,但是版主忽略了mmap的fd参数,如果没有fd是类似的,malloc很可能都是用mmap(无fd)实现的
作者: 思一克    时间: 2008-04-02 16:48
不是可能(无论有无fd)而是malloc本来就要靠mmap实现.

原帖由 flw2 于 2008-4-2 16:47 发表

恩是,但是版主忽略了mmap的fd参数,如果没有fd是类似的,malloc很可能都是用mmap(无fd)实现的

作者: flw2    时间: 2008-04-02 16:54
原帖由 思一克 于 2008-4-2 16:48 发表
不是可能(无论有无fd)而是malloc本来就要靠mmap实现.


这也未必吧
不过mmap对文件映射的时候确实是不像普通的mmap然后写一样耗费内存的

#include <stdio.h>
#include <stdlib.h>
int main()
{
        char *p;
        int i = 0;
        /* just for strace */
        write(1,"",0);

        while(i++ < 100)
                p  = malloc(4096);
        sleep(111);
}



strace ./a.out

....
munmap(0xb7f7b000, 82039)               = 0
write(1, "", 0)                         = 0
brk(0)                                  = 0x804a000
brk(0x806c000)                          = 0x806c000
brk(0x808d000)                          = 0x808d000
brk(0x80ae000)                          = 0x80ae000
brk(0x80cf000)                          = 0x80cf000
rt_sigprocmask(SIG_BLOCK, [CHLD], [], = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL}, = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, = 0
nanosleep({111, 0},  <unfinished ...>
作者: 思一克    时间: 2008-04-02 16:57
LINUX下是这样的,
你malloc很小空间时候, 是靠brk实现的.
你malloc很大(比如128M以上的时候)是靠mmap实现.

brk和mmap都是系统调用.

所以我说malloc本来就是用mmap实现就是这意思.

原帖由 flw2 于 2008-4-2 16:54 发表

这也未必吧
不过mmap对文件映射的时候确实是不像普通的mmap然后写一样耗费内存的

#include
#include
int main()
{
        char *p;
        int i = 0;
        /* just for strace */
        ...

作者: flw2    时间: 2008-04-02 17:02
原帖由 思一克 于 2008-4-2 16:57 发表
LINUX下是这样的,
你malloc很小空间时候, 是靠brk实现的.
你malloc很大(比如128M以上的时候)是靠mmap实现.

brk和mmap都是系统调用.

所以我说malloc本来就是用mmap实现就是这意思.


对,因为地址空间部分被占用
作者: zx_wing    时间: 2008-04-02 17:16
原帖由 flw2 于 2008-4-2 16:37 发表
mmap的不占用物理内存,内核会检查到缺页,然后知道是mmap文件的,所以会进行文件操作,具体细节忘了
如果是普通内存,就像版主说的,当写的时候缺页导致物理内存的分配

占用的,它是把文件的page cache映射到应用程序的地址空间,当然就占用物理内存,这个还是shared的情况。
如果是private的mmap,写的时候还会单独为该进程分配内存的。
作者: flw2    时间: 2008-04-02 17:21
原帖由 zx_wing 于 2008-4-2 17:16 发表

占用的,它是把文件的page cache映射到应用程序的地址空间,当然就占用物理内存,这个还是shared的情况。
如果是private的mmap,写的时候还会单独为该进程分配内存的。

那写完一个G的文件之后消耗的物理内存有多少(1G的比例)
作者: flw    时间: 2008-04-02 17:22
原帖由 zx_wing 于 2008-4-2 17:16 发表

占用的,它是把文件的page cache映射到应用程序的地址空间,当然就占用物理内存,这个还是shared的情况。
如果是private的mmap,写的时候还会单独为该进程分配内存的。

这种占用自然是有的,只是不讨论它罢了。
就好像 printf 还要占用一点点内存一样,就这个意思。
作者: 思一克    时间: 2008-04-02 17:24
这个可以测试. 系统不一定是同时使用1G. 需要用个程序实验出.

原帖由 flw2 于 2008-4-2 17:21 发表

那写完一个G的文件之后消耗的物理内存有多少(1G的比例)

作者: flw2    时间: 2008-04-02 17:25
原帖由 flw 于 2008-4-2 17:22 发表

这种占用自然是有的,只是不讨论它罢了。
就好像 printf 还要占用一点点内存一样,就这个意思。

不管你说错了还是对了,但是我也是这们想的

作者: zx_wing    时间: 2008-04-02 17:26
原帖由 flw2 于 2008-4-2 17:21 发表

那写完一个G的文件之后消耗的物理内存有多少(1G的比例)

不会,几个页而已。
比如你先写0-4k这个部分,分配一个页。4k-8k,分配一个页,8k-12k分配一个页。12k-16k,这个时候内核可能就已经开始回收内存了,你前面几个页会被内核回收回去。所有通常不会用到多大内存的。
作者: zx_wing    时间: 2008-04-02 17:28
原帖由 flw 于 2008-4-2 17:22 发表

这种占用自然是有的,只是不讨论它罢了。
就好像 printf 还要占用一点点内存一样,就这个意思。

哦,那可能是我是理解错你们的意思了
作者: flw    时间: 2008-04-02 17:28
原帖由 flw2 于 2008-4-2 17:25 发表

不管你说错了还是对了,但是我也是这们想的

所以我一开始说 mmap 不占用内存,主要是强调它是文件操作。
后来思一克说 malloc 就是用 mmap 来实现的,这我不太清楚,所以就缄口不言了。
作者: flw2    时间: 2008-04-02 17:28
原帖由 zx_wing 于 2008-4-2 17:26 发表

不会,几个页而已。
比如你先写0-4k这个部分,分配一个页。4k-8k,分配一个页,8k-12k分配一个页。12k-16k,这个时候内核可能就已经开始回收内存了,你前面几个页会被内核回收回去。所有通常不会用到多大内存的。

恩,我还以为你又要来一堆KERNEL的东西...

作者: zx_wing    时间: 2008-04-02 17:30
原帖由 flw2 于 2008-4-2 17:28 发表

恩,我还以为你又要来一堆KERNEL的东西...

汗,看来我经常把问题复杂化了
作者: flw2    时间: 2008-04-02 17:33
原帖由 flw 于 2008-4-2 17:28 发表

所以我一开始说 mmap 不占用内存,主要是强调它是文件操作。
后来思一克说 malloc 就是用 mmap 来实现的,这我不太清楚,所以就缄口不言了。


对,因为malloc要尽可能使用用户态的地址空间,而堆的延升迟早会遇到障碍(所以只用brk是不能用完3G的,这至少是需要mmap的其中一个原因)
我只记得内核会检查到缺页,缺页异常会检查具体的原因,然后进行相关操作,但是凭印象是不会要太多内存的
作者: flw    时间: 2008-04-02 17:37
原帖由 flw2 于 2008-4-2 17:33 发表

对,因为malloc要尽可能使用用户态的地址空间,而堆的延升迟早会遇到障碍(所以只用brk是不能用完3G的,这至少是需要mmap的其中一个原因)
我只记得内核会检查到缺页,缺页异常会检查具体的原因,然后进行 ...

这样讲的话,通常小的 malloc 还都不用 mmap 的吧。
除非特别大了。
作者: flw2    时间: 2008-04-02 17:44
原帖由 flw 于 2008-4-2 17:37 发表

这样讲的话,通常小的 malloc 还都不用 mmap 的吧。
除非特别大了。

其实我没有完整的看过malloc,所以不知道是不是小的就用brk。brk失败就mmap
但是内核的brk和mmap都是搜索进程的地址空间树,然后摘个出来
由于brk的特殊性,可能比较容易一些,
它们的区别细节在ULK上有,zw_wing给咱们解释一下吧
作者: zx_wing    时间: 2008-04-02 17:47
原帖由 flw2 于 2008-4-2 17:44 发表

其实我没有完整的看过malloc,所以不知道是不是小的就用brk。brk失败就mmap
但是内核的brk和mmap都是搜索进程的地址空间树,然后摘个出来
由于brk的特殊性,可能比较容易一些,
它们的区别细节在ULK上有, ...

我不知道这个的,也没看过malloc的代码
我也是看到思一克说大于128M要用mmap时才知道malloc会用它,之前我也一直以为malloc都用brk实现的
作者: flw2    时间: 2008-04-02 17:50

cat /proc/self/maps

xuchm@xuchm:/test$ cat /proc/self/maps
08048000-0804c000 r-xp 00000000 08:03 1303058    /bin/cat
....
上面这个就是128MB吧
作者: 思一克    时间: 2008-04-02 17:53
不一定是128M界限.

你可以编个小程序, 分配256M, 前后打印出BRK数值, 你会看到不变化了. 因为是直接用mmap了, 没有用brk.

原帖由 zx_wing 于 2008-4-2 17:47 发表

我不知道这个的,也没看过malloc的代码
我也是看到思一克说大于128M要用mmap时才知道malloc会用它,之前我也一直以为malloc都用brk实现的

作者: flw2    时间: 2008-04-02 17:56
原帖由 思一克 于 2008-4-2 17:53 发表
不一定是128M界限.

你可以编个小程序, 分配256M, 前后打印出BRK数值, 你会看到不变化了. 因为是直接用mmap了, 没有用brk.


我试了,是没有brk
作者: zx_wing    时间: 2008-04-02 17:58
原帖由 思一克 于 2008-4-2 17:53 发表
不一定是128M界限.

你可以编个小程序, 分配256M, 前后打印出BRK数值, 你会看到不变化了. 因为是直接用mmap了, 没有用brk.


你这么一说我倒有点印象,好像你以前的一个帖子给了这么个实验程序的
作者: 思一克    时间: 2008-04-02 18:01
是的. 我也找不到那贴子了.

就是分配128M(?)以上的直接调用mmap做的, 小的用brk做.

原帖由 zx_wing 于 2008-4-2 17:58 发表

你这么一说我倒有点印象,好像你以前的一个帖子给了这么个实验程序的

作者: yuanchuang    时间: 2008-04-02 21:36

作者: Alligator27    时间: 2008-04-02 22:08
这是Linux Redhat allocator作者的website: http://www.malloc.de/en/

其中关于mmap的注释:
DEFAULT_MMAP_THRESHOLD       default: 256K
      Also settable using mallopt(M_MMAP_THRESHOLD, x)
  The request size threshold for using MMAP to directly service a
  request. Requests of at least this size that cannot be allocated
  using already-existing space will be serviced via mmap.  (If enough
  normal freed space already exists it is used instead.)  Using mmap
  segregates relatively large chunks of memory so that they can be
  individually obtained and released from the host system. A request
  serviced through mmap is never reused by any other request (at least
  not directly; the system may just so happen to remap successive
  requests to the same locations).  Segregating space in this way has
  the benefits that: Mmapped space can always be individually released
  back to the system, which helps keep the system level memory demands
  of a long-lived program low.  Also, mapped memory doesn't become
  `locked' between other chunks, as can happen with normally allocated
  chunks, which means that even trimming via malloc_trim would not
  release them.  However, it has the disadvantage that the space
  cannot be reclaimed, consolidated, and then used to service later
  requests, as happens with normal chunks.  The advantages of mmap
  nearly always outweigh disadvantages for "large" chunks, but the
  value of "large" may vary across systems.  The default is an
  empirically derived value that works well in most systems. You can
  disable mmap by setting to MAX_SIZE_T.


brk/sbrk 是mmap的特例。

mmap映射一个1G 的文件,最终会用1G的RAM。(direct IO 例外)。
作者: 思一克    时间: 2008-04-02 22:14
mmap映射一个1G 的文件,最终会用1G的RAM。

对的.
实际和malloc一样的.

你mmap一个1G文件, 用内存是0.
然后, 你开始将每一PAGE(1G/4096个总共)的开始都写上一个字符, 写完后, 1G物理内存用掉了.
马上free -m会看到效应.





原帖由 Alligator27 于 2008-4-2 22:08 发表
这是Linux Redhat allocator作者的website: http://www.malloc.de/en/

其中关于mmap的注释:


brk/sbrk 是mmap的特例。

mmap映射一个1G 的文件,最终会用1G的RAM。(direct IO 例外)。

作者: 印随    时间: 2008-04-03 10:10
我的系统是ubuntu,物理内存为1G

我刚才做了一个试验,不管是读还是写
映射1G的文件,最终会(大概)占用1G RAM(剩余内存直线减小,到最后会有一个小回升,可能是系统发现内存不够用,把一些临时占用的内存释放)

,系统剩余内存一直减少到十几M的状态,十几M剩余内存保持不变

这十几M内存为什么不被占用?(在我的系统中是13M)
作者: 思一克    时间: 2008-04-03 10:16
系统会保留最小的物理内存用做基本的工作.

实在不够了就交换出.

再不够了就杀死某些倒霉的进程.


原帖由 印随 于 2008-4-3 10:10 发表
我的系统是ubuntu,物理内存为1G

我刚才做了一个试验,不管是读还是写
映射1G的文件,最终会(大概)占用1G RAM(剩余内存直线减小,到最后会有一个小回升,可能是系统发现内存不够用,把一些临时占用的内存 ...

作者: 印随    时间: 2008-04-03 10:19
原帖由 思一克 于 2008-4-3 10:16 发表
系统会保留最小的物理内存用做基本的工作.

实在不够了就交换出.

再不够了就杀死某些倒霉的进程.




某些倒霉进程?是不是在指那些僵尸进程
作者: 思一克    时间: 2008-04-03 10:22
不是. 杀死的是正在运行的普通进程. 千万不要让机器到这地步.

这个功能在LINUX中叫OOM KILLER (out of memory 杀手).

原帖由 印随 于 2008-4-3 10:19 发表


某些倒霉进程?是不是在指那些僵尸进程

作者: 印随    时间: 2008-04-03 10:27
那我想在程序中一直要访问一个1G的文件,
有没有好办法?
我的办法是1:一次性全部映射(缺点是内存占用太大)
                    2:分页映射(页面管理繁琐)
作者: 思一克    时间: 2008-04-03 10:28
不需要用mmap. 就是用seek, read, write就可以了.

原帖由 印随 于 2008-4-3 10:27 发表
那我想在程序中一直要访问一个1G的文件,
有没有好办法?
我的办法是1:一次性全部映射(缺点是内存占用太大)
                    2:分页映射(页面管理繁琐)

作者: flw2    时间: 2008-04-03 11:11
是我理解有问题

int main(int argc, char* argv[])
{
        int fd = open("./futex",O_RDWR|O_CREAT, 0777);
        int fd2 = open("./futex2",O_RDWR|O_CREAT, 0777);

        lseek(fd,G,SEEK_SET);
        write(fd, "0", 1);

        lseek(fd2,G,SEEK_SET);
        write(fd2, "0", 1);
        char *base = mmap(NULL,G,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        char *base2 = mmap(NULL,G,PROT_READ|PROT_WRITE,MAP_SHARED,fd2,0);
        char *p;

        for (p = base; p < base + G; p += 4096)
                *p = 'a';

        for (p = base2; p < base + G; p += 4096)
                *p = 'a';

        return 0;
}
现象是总是占用内存在700M左右,应该是系统会从它占用的内存中回收
作者: Sorehead    时间: 2008-04-03 11:32
原帖由 思一克 于 2008-4-3 10:16 发表
系统会保留最小的物理内存用做基本的工作.

实在不够了就交换出.

再不够了就杀死某些倒霉的进程.




不知道版主是否了解这个杀死细节,能讲解一下其机制或算法嘛。
谢谢!
作者: sjh_311    时间: 2008-04-03 11:33
mark
作者: flw    时间: 2008-04-03 11:47
原帖由 flw2 于 2008-4-3 11:11 发表
是我理解有问题

int main(int argc, char* argv[])
{
        int fd = open("./futex",O_RDWR|O_CREAT, 0777);
        int fd2 = open("./futex2",O_RDWR|O_CREAT, 0777);

        lseek(fd,G,SEE ...

你的物理内存总大小是多少?
我觉得这个 700M 似乎和内存条大小有关。
我这里只有 210M。
我是 256M 的内存。
而且,我发现起十个八个本程序一点儿关系都没有,
占用的内存总大小维持不变。

因此,我觉得这个只是 OS 的一种优化,mmap 是可以不占内存的,但是如果有空闲内存,就会给它。
malloc 似乎做不到这一点吧?malloc 只能往 swap 分区换页,因此总大小会受到 swap 的限制,
但是 mmap 不会。我的 swap 也只有 256M。
作者: flw    时间: 2008-04-03 11:49
top - 11:47:44 up 2 days, 17:31,  4 users,  load average: 9.53, 6.80, 3.21
Tasks:  72 total,   4 running,  68 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.3%us, 16.4%sy,  0.0%ni,  0.0%id, 79.5%wa,  3.7%hi,  0.0%si,  0.0%st
Mem:    256572k total,   252184k used,     4388k free,      272k buffers
Swap:   329292k total,    62964k used,   266328k free,   232712k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
9668 flw       20   0 2049m 136m 136m R  4.6 54.7   0:08.18 tmmap
9581 flw       20   0 2049m  44m  44m R  4.3 17.8   0:33.24 tmmap
9584 flw       20   0 2049m  43m  43m R  3.3 17.4   0:16.42 tmmap

作者: flw    时间: 2008-04-03 11:53
晕~
起第四个的时候,EBUS 了。
  1. flw@debian:~/cis$ ../study/tmmap
  2. 总线错误 (core dumped)
  3. flw@debian:~/cis$
复制代码



哦,我知道了,总线错误是因为我的硬盘满了,文件操作失败了,并不是内存不够了。
我换个大硬盘的机器试试。
我感觉应该是十个八个一两百个问题都不大。

[ 本帖最后由 flw 于 2008-4-3 11:58 编辑 ]
作者: flw    时间: 2008-04-03 12:10
草草的做个总结吧,然后去吃饭:

1,malloc 和 mmap 极其相似,它们俩都不消耗物理内存,但是如果物理内存有富余的话,就会给它们用。
2,当物理内存紧张时(比如别的程序又需要申请一大笔内存),就会换页,将暂时用不到的页面交换到磁盘上去。
malloc 是交换到 swap(包括 swap 分区和 swap file),但是 mmap 则是交换到文件里去。

推论:
1,swap 分区总是有限的,因此所有进程总共可以 malloc 的内存总是有限的,
   但是 mmap 则只要磁盘空间足够,总是能成功,且不影响其它应用程序的正常运行(【重要】也就是不会随机 drop 倒霉进程)。
2,考虑只读的情况,mmap 不会比 malloc 更慢,因为它们俩的策略是相同的,都是优先使用物理内存,缺页时从磁盘上 load 页面。

[ 本帖最后由 flw 于 2008-4-3 12:22 编辑 ]
作者: flw2    时间: 2008-04-03 13:02
out_of_memory的策略不知道
__alloc_pages 里面在一定条件下会调用 out_of_memory函数,它会根据一些策略选择要杀死的进程
to flw: 我的是1G的 swap我不知道,我没有使用swap,也没看过相关资料
我觉得先不用考虑swap. 一旦用户态进程缺页, 那么可能有很多原因,比如至少有如下的几个:

1. 访问的地址不属于用户空间(内核能得到出错的线性地址,然后得知),那么将得到一个SIGSVEG信号
2. 访问的地址属于用户空间,但是这个地址所在的页还没有分配页框(比如分配一个大数组,然后写就会遇到这个情况),那是要分配物理地址,然后让该进程的对应页表项更新,继续执行缺页代码,就不会再缺页了
3. 地址空间内,但是检查到被交换出去了? 然后调入,这同样需要分配物理内存
4. 其它

内核在分配内存的时候最底层的来自__alloc_pages, 这个函数肯定不一定能成功,在不成功的时候就可能会杀死进程, ULK上说了具体的步骤,分析了这个函数,这个函数也有人分析过,虽然步骤很多,但是分析后还是比代码强

flw真执着啊,赞一个
作者: 印随    时间: 2008-04-03 13:13
munmap是 释放映射空间

为什么我循环调用mmap,munmap   剩余内存还是一直在减小?
作者: flw2    时间: 2008-04-03 13:15
原帖由 印随 于 2008-4-3 13:13 发表
munmap是 释放映射空间

为什么我循环调用mmap,munmap   剩余内存还是一直在减小?

一直?什么时候恢复了吗
作者: flw    时间: 2008-04-03 13:16
原帖由 印随 于 2008-4-3 13:13 发表
munmap是 释放映射空间

为什么我循环调用mmap,munmap   剩余内存还是一直在减小?

啥叫“剩余内存”?
作者: 印随    时间: 2008-04-03 13:18
剩余内存我指的是   free  mem,  



分段映射1G文件,实现分段访问,  每段为1M
for()
{
     mmap();
     访问;
     munmap();
}

过程是我把1G文件完全访问一遍,
free -m    发现内存就基本耗尽


我认为不应该是这样的,mmap始终只在使用1M内存

[ 本帖最后由 印随 于 2008-4-3 13:25 编辑 ]
作者: flw    时间: 2008-04-03 13:24
许多年以前,这个论坛流行一句很自负很自豪很牛逼的话:
内存就是让用的……

[ 本帖最后由 flw 于 2008-4-3 13:26 编辑 ]
作者: 印随    时间: 2008-04-03 13:26
原帖由 flw 于 2008-4-3 13:24 发表
几年前,这个论坛流行一句很自负很自豪很牛逼的话:
内存就是让用的……



我认为不应该是这样的,mmap始终只在使用1M内存



决定不用mmap了,还是文件IO简单,虽然麻烦一点

[ 本帖最后由 印随 于 2008-4-3 13:39 编辑 ]
作者: mingyanguo    时间: 2008-04-03 13:42
即使unmap,那些物理内存也不一定立即释放,如果你想释放物理内存,可以看看madvise(2)
作者: 思一克    时间: 2008-04-03 15:06
所以,尽量不要用mmap映射大文件. 尤其是不要用在常驻内存的程序中.
作者: flw    时间: 2008-04-03 15:07
原帖由 思一克 于 2008-4-3 15:06 发表
所以,尽量不要用mmap映射大文件. 尤其是不要用在常驻内存的程序中.

分段映射呢?
比如每次只映射一小部分。
作者: flw2    时间: 2008-04-03 15:15
原帖由 flw 于 2008-4-3 15:07 发表

分段映射呢?
比如每次只映射一小部分。


我的理解是没关系, 那段英文也提到了,说可以分段释放

很多不好daemon通常会有个buf,然后越来越大
char data[10M];
然后保持最大在10M,如果超过就删除某些旧的数据, 但是这个后果就是某段时间过后,内存占用大于10M,即使daemon只是偶尔需要10M
所以可以mmap一个,然后释放,这样就可把物理内存真正的归还给系统,在需要时增加一些,对于一些偶尔要求大内存块,但是通常却不需要的daemon很好
作者: 思一克    时间: 2008-04-03 15:16
分段, 是可以的, 但是意义也不是很大.

事实上MMAP和普通的read, write不是什么时候都有速度优势的. 因为文件最终要写磁盘, 而磁盘是外部设备, 速度的关键在这里.

如真想用, 对于大的文件(比物理内存大许多的), 应该考虑分段.

反正一次不分段可能是不良的程序, 和malloc一个巨大内存然后不断随机存取一样占用内存.

如果读写的仅仅是文件中的一小块地方, 不分段也可以, 对于没有读写的地方,mmap后并没有真正使用物理内存

原帖由 flw 于 2008-4-3 15:07 发表

分段映射呢?
比如每次只映射一小部分。

作者: flw    时间: 2008-04-03 15:25
原帖由 思一克 于 2008-4-3 15:16 发表
如果读写的仅仅是文件中的一小块地方, 不分段也可以, 对于没有读写的地方,mmap后并没有真正使用物理内存

但是占地址空间呀。
因此不分段的话,程序就只能处理不超过 2G 的文件,比如 19 个 100M,或者 5 个 390M。
我的 FTP 上传程序目前就有这个限制。

另外我觉得我使用 mmap 一方面是可能会快一点,另一方面主要还是觉得编码会简单些(我是多线程共享句柄)。
当然了,如果分段的话,编码简单这个优势就不是很明显了。
作者: 印随    时间: 2008-04-03 15:33
采用分段
简单使用munmap,  释放一个分段, 好像没有什么效果
不能释放内存

[ 本帖最后由 印随 于 2008-4-3 15:34 编辑 ]
作者: 思一克    时间: 2008-04-03 15:50
和malloc类似, 你free之后物理内存未必是立即归还系统的.

我实验了, 将整个的全部映射都munmap后还是归还的.

flw考虑的有道理.

原帖由 印随 于 2008-4-3 15:33 发表
采用分段
简单使用munmap,  释放一个分段, 好像没有什么效果
不能释放内存

作者: flw2    时间: 2008-04-03 15:56
如果munmap还不归还,那就没法归还了,就算内核暂时不回收,那么它也是属于内核管理的
作者: 印随    时间: 2008-04-03 16:14
我刚才写了个例子
main()
{
    映射1G;
    遍历访问1G内存;

    munmap();

    打印“释放完毕”;
    sleep(100000);
}
当我看到   “释放完毕”  的时候,
等待5分钟后,由free -m   可以看出,  内存没有释放
当kill掉这个程序后,内存就马上被释放,  

内核是怎么处理这个过程的?

[ 本帖最后由 印随 于 2008-4-3 16:16 编辑 ]
作者: solegoose    时间: 2008-04-03 16:34
mmap不真正的占有内存,要说占有,也是内核中的一小点内存。malloc最终使用brk或者是mmap,具体的界限我不清楚。
大文件映射成一段,和映射成多段,我看不出来有什么区别,除了映射2G以上文件外,知道其中区别的朋友请多多指教,
至于内存在munmap后不释放,这个和缓冲有关。内核不会把不用的内存页立刻释放。可以参考《情景分析》,里面很详细的说明了。
作者: eagle518    时间: 2008-04-03 16:38
我想lz的现象应该是linux cache机制引起。
作者: 思一克    时间: 2008-04-03 16:38
释放的. 你用system("cat /proc/self/status")
中的VMRSS的数值.

free -m不一定准确.


Name:   a.out
State:  S (sleeping)
SleepAVG:       78%
Tgid:   24446
Pid:    24446
PPid:   23055
TracerPid:      0
Uid:    0       0       0       0
Gid:    0       0       0       0
FDSize: 256
Groups: 0
VmPeak:     1532 kB
VmSize:     1444 kB
VmLck:         0 kB
VmHWM:       320 kB
VmRSS:       320 kB
VmData:       20 kB
VmStk:        84 kB
VmExe:         4 kB
VmLib:      1308 kB
VmPTE:        12 kB
Threads:        1
SigQ:   0/4095
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000010000
SigIgn: 0000000000000006
SigCgt: 0000000000000000
CapInh: 0000000000000000
CapPrm: 00000000fffffeff
CapEff: 00000000fffffeff
Cpus_allowed:   00000001
Mems_allowed:   1
361 80 67 1 0 26 0
j = 111134 k = 24446
src = 0x77e3f000
0 c = 0x77e40000 0xb7e3f000
4096 c = 0x78e40000 0xb7e3f000
8192 c = 0x79e40000 0xb7e3f000
12288 c = 0x7ae40000 0xb7e3f000
16384 c = 0x7be40000 0xb7e3f000
20480 c = 0x7ce40000 0xb7e3f000
24576 c = 0x7de40000 0xb7e3f000
28672 c = 0x7ee40000 0xb7e3f000
32768 c = 0x7fe40000 0xb7e3f000
36864 c = 0x80e40000 0xb7e3f000
40960 c = 0x81e40000 0xb7e3f000
45056 c = 0x82e40000 0xb7e3f000
49152 c = 0x83e40000 0xb7e3f000
53248 c = 0x84e40000 0xb7e3f000
57344 c = 0x85e40000 0xb7e3f000
61440 c = 0x86e40000 0xb7e3f000
65536 c = 0x87e40000 0xb7e3f000
69632 c = 0x88e40000 0xb7e3f000
73728 c = 0x89e40000 0xb7e3f000
77824 c = 0x8ae40000 0xb7e3f000
81920 c = 0x8be40000 0xb7e3f000
86016 c = 0x8ce40000 0xb7e3f000
90112 c = 0x8de40000 0xb7e3f000
94208 c = 0x8ee40000 0xb7e3f000
98304 c = 0x8fe40000 0xb7e3f000
102400 c = 0x90e40000 0xb7e3f000
106496 c = 0x91e40000 0xb7e3f000
110592 c = 0x92e40000 0xb7e3f000
Name:   a.out
State:  R (running)
SleepAVG:       68%
Tgid:   24446
Pid:    24446
PPid:   23055
TracerPid:      0
Uid:    0       0       0       0
Gid:    0       0       0       0
FDSize: 256
Groups: 0
VmPeak:  1050024 kB
VmSize:  1050024 kB
VmLck:         0 kB
VmHWM:    444908 kB
VmRSS:    444908 kB
VmData:       24 kB
VmStk:        84 kB
VmExe:         4 kB
VmLib:      1308 kB
VmPTE:       452 kB
Threads:        1
SigQ:   0/4095
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000010000
SigIgn: 0000000000000006
SigCgt: 0000000000000000
CapInh: 0000000000000000
CapPrm: 00000000fffffeff
CapEff: 00000000fffffeff
Cpus_allowed:   00000001
Mems_allowed:   1
262506 111227 111213 1 0 27 0
Name:   a.out
State:  R (running)
SleepAVG:       68%
Tgid:   24446
Pid:    24446
PPid:   23055
TracerPid:      0
Uid:    0       0       0       0
Gid:    0       0       0       0
FDSize: 256
Groups: 0
VmPeak:  1050024 kB
VmSize:     1448 kB
VmLck:         0 kB
VmHWM:    444908 kB
VmRSS:       372 kB
VmData:       24 kB
VmStk:        84 kB
VmExe:         4 kB
VmLib:      1308 kB
VmPTE:        12 kB
Threads:        1
SigQ:   0/4095
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000010000
SigIgn: 0000000000000006
SigCgt: 0000000000000000
CapInh: 0000000000000000
CapPrm: 00000000fffffeff
CapEff: 00000000fffffeff
Cpus_allowed:   00000001
Mems_allowed:   1
362 93 79 1 0 27 0
作者: Alligator27    时间: 2008-04-03 21:24
楼上几位说得对, Linux 允许所有的内存作file cache.

munmap 释放进程空间+物理内存。如果是匿名mmap,释放的物理内存在 "free -m" 的 "free" 列;如果是普遍文件mmap,释放的物理内存在 "free -m" 的 "cached" 列。cached memory 属准释放态物理内存。当系统的free memory list size小于pages_low,swap daemon启动,先偷换cached memory。

传统清除cache的方法是运行一个小程序,狂分配内存,迫使swap daemon偷换cached memory。程序退出,内存回到free list.

Linux 2.6.16+, 支持drop_caches
  1. $echo n > /proc/sys/vm/drop_caches
  2.     n=1, dentries/inodes cache
  3.     n=2, pagecache + 1
  4.     n=3, all cached objects
复制代码

[ 本帖最后由 Alligator27 于 2008-4-6 23:24 编辑 ]
作者: vupiggy    时间: 2008-04-04 11:40
原帖由 solegoose 于 2008-4-3 09:34 发表
malloc最终使用brk或者是mmap,具体的界限我不清楚。


#define DEFAULT_MMAP_THRESHOLD_MIN (128 * 1024)
#define DEFAULT_MMAP_THRESHOLD_MAX (8 * 1024 * 1024 * sizeof(long))

如果不修改的话, malloc一个128K以上的区域就动用mmap了, 要提升brk到很高, 可以尝试malloc小块内存很多次.
作者: fish-fly    时间: 2008-04-04 12:23
我是来学习的
作者: It'sGifted    时间: 2008-04-04 21:43
受教受教,不是很懂。
作者: littlemonk    时间: 2008-04-06 11:29
搬个板凳,边吃饭边受教.
作者: anders0913    时间: 2008-04-06 11:44
原帖由 印随 于 2008-4-2 15:41 发表

mmap也一样吗?

1G的内存地址我都访问过,但系统内存也没有大的变化

实际上是有变化的,在我的系统中,内存占用一直增长,直到剩余内存为十几M的时候停了



这个是不是和系统实现有关系,不解啊
作者: 金圭钟    时间: 2008-04-10 00:35
malloc mmap都不占内存的把
作者: luren04    时间: 2008-04-12 18:29
学习了
作者: xuanhan863    时间: 2008-04-17 13:21
进程调用mmap()时,只是在进程空间内新增了一块相应大小的缓冲区,并设置了相应的访问标识,但并没有建立进程空间到物理页面的映射。因此,第一次访问该空间时,会引发一个缺页异常。
作者: yananfu    时间: 2008-04-17 16:50
mmap返回用户虚拟地址,在内核中并没有分配相应的页面。
你可以尝试一下访问偏移为10M或更大的时候 在看一下内存使用情况
作者: xinglp    时间: 2008-04-17 19:46
在一个从网络上接收文件的程序中,网络这边用的是非阻塞的I/O. 每次要从网络读取数据的时候,先映射文件(块),完了之后munmap,这样由于网络I/O非阻塞,占用内存的时间就很少了.当然每次都要映射/解除映射,CPU占用高一些.
不过好多地方对于有写操作的情况不建议使用mmap.
作者: wellhope    时间: 2008-06-21 23:07
提示: 作者被禁止或删除 内容自动屏蔽
作者: redor    时间: 2008-06-22 09:56
原帖由 印随 于 2008-4-2 14:08 发表
这两天在折腾 mmap 函数

函数原型
        void *mmap(void *start, size_t length, int prot, int flags,  int fd, off_t offset);


映射一个1G 的文件的时候, offset = 0,
当 length = 100 M ,系统 ...



mmap使用的内存分配策略是缺页补充,也就是你使用哪一页,系统会自动分配这个页给你,你不使用的页基本就是稀疏状态...所以占用内存不是你的size的大小,弄明白这个就会明白为啥offset要是page的整数倍 .....




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2