C 理解内存分配

C 理解内存分配,c,memory,linux-kernel,mmap,C,Memory,Linux Kernel,Mmap,我试图理解“记忆是如何工作的”。据我所知,在调用mmap创建MAP\u ANONYMOUS映射时,操作系统(在我的例子中是Linux)会创建: mmap()在虚拟地址中创建一个新映射 调用进程的空间 据我所知,进程的虚拟地址空间可能超过可用的实际物理内存 另外,据我所知,当CPU试图访问尚未在页表中的内存页时触发页错误时,实际映射到物理内存 操作系统捕获页面错误并在页面目录中创建一个条目 如果我mmaped了一些匿名内存(但没有触及任何页面),然后其他进程耗尽了所有物理内存,然后我尝试使用其中一

我试图理解“记忆是如何工作的”。据我所知,在调用
mmap
创建
MAP\u ANONYMOUS
映射时,操作系统(在我的例子中是Linux)会创建:

mmap()
在虚拟地址中创建一个新映射 调用进程的空间

据我所知,进程的虚拟地址空间可能超过可用的实际物理内存

另外,据我所知,当
CPU
试图访问尚未在页表中的内存页时触发页错误时,实际映射到物理内存

操作系统捕获页面错误并在页面目录中创建一个条目

如果我
mmap
ed了一些匿名内存(但没有触及任何页面),然后其他进程耗尽了所有物理内存,然后我尝试使用其中一个页面
mmap
ed(我已禁用交换),该怎么办

CPU应该触发页面错误,然后尝试在页面目录中创建一个条目。但是,由于没有剩余的物理内存,它将无法这样做…

使用mmap(MAP\u ANONYMOUS)或malloc在您的情况下不会改变任何内容,如果您没有足够的可用内存,mmap将返回
MAP\u FAILED
且malloc返回NULL

如果我使用该程序:

#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char ** argv)
{
  int n = atoi(argv[1]);
  void * m;

  if (argc == 1) {
    m = mmap(NULL, n*1024*1024, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);

    if (m == MAP_FAILED) {
      puts("ko");
      return 0;
    }
  }
  else {
    m = malloc(n*1024*1024);
    if (m == 0) {
      puts("ko");
      return 0;
    }
  }

  puts("ok");
  getchar();

  char * p = (char *) m;
  char * sup = p + n*1024*1024;

  while (p < sup) {
    *p = 0;
    p += 512;
  }

  puts("done");
  getchar();

  return 0;
}
如果我这样做:

pi@raspberrypi:/tmp $ ./a.out 750
ko
750太大了,但是

pi@raspberrypi:/tmp $ ./a.out 600 &
[1] 1525
pi@raspberrypi:/tmp $ ok
使用的内存(顶部等)不反映600Mo,因为我不在其中读/写

proc/meminfo
给出:

MemTotal:         949448 kB
MemFree:          295008 kB
MemAvailable:     633560 kB
Buffers:           39296 kB
Cached:           360372 kB
SwapCached:            0 kB
Active:           350416 kB
Inactive:         260960 kB
Active(anon):     191976 kB
Inactive(anon):    41908 kB
Active(file):     158440 kB
Inactive(file):   219052 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:        102396 kB
SwapFree:         102396 kB
Dirty:               352 kB
Writeback:             0 kB
AnonPages:        211704 kB
Mapped:           215924 kB
Shmem:             42304 kB
Slab:              24528 kB
SReclaimable:      12108 kB
SUnreclaim:        12420 kB
KernelStack:        2128 kB
PageTables:         5676 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      577120 kB
Committed_AS:    1675164 kB
VmallocTotal:    1114112 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB
CmaTotal:           8192 kB
CmaFree:            6796 kB
MemTotal:         949448 kB
MemFree:          282860 kB
MemAvailable:     626016 kB
Buffers:           39432 kB
Cached:           362860 kB
SwapCached:            0 kB
Active:           362696 kB
Inactive:         260580 kB
Active(anon):     199880 kB
Inactive(anon):    41392 kB
Active(file):     162816 kB
Inactive(file):   219188 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:        102396 kB
SwapFree:         102396 kB
Dirty:               624 kB
Writeback:             0 kB
AnonPages:        220988 kB
Mapped:           215672 kB
Shmem:             41788 kB
Slab:              24788 kB
SReclaimable:      12296 kB
SUnreclaim:        12492 kB
KernelStack:        2136 kB
PageTables:         5692 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      577120 kB
Committed_AS:    2288564 kB
VmallocTotal:    1114112 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB
CmaTotal:           8192 kB
CmaFree:            6796 kB
MemTotal:         949448 kB
MemFree:          282532 kB
MemAvailable:     626112 kB
Buffers:           39432 kB
Cached:           359980 kB
SwapCached:            0 kB
Active:           365200 kB
Inactive:         257736 kB
Active(anon):     202280 kB
Inactive(anon):    38320 kB
Active(file):     162920 kB
Inactive(file):   219416 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:        102396 kB
SwapFree:         102396 kB
Dirty:                52 kB
Writeback:             0 kB
AnonPages:        223520 kB
Mapped:           212600 kB
Shmem:             38716 kB
Slab:              24956 kB
SReclaimable:      12476 kB
SUnreclaim:        12480 kB
KernelStack:        2120 kB
PageTables:         5736 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      577120 kB
Committed_AS:    2876612 kB
VmallocTotal:    1114112 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB
CmaTotal:           8192 kB
CmaFree:            6796 kB
我能再次做到

pi@raspberrypi:/tmp $ ./a.out 600 &
[2] 7088
pi@raspberrypi:/tmp $ ok

pi@raspberrypi:/tmp $ jobs
[1]-  stopped                 ./a.out 600
[2]+  stopped                 ./a.out 600
pi@raspberrypi:/tmp $ 
即使总数对于内存+交换来说也太大,
/proc/meminfo
给出:

MemTotal:         949448 kB
MemFree:          295008 kB
MemAvailable:     633560 kB
Buffers:           39296 kB
Cached:           360372 kB
SwapCached:            0 kB
Active:           350416 kB
Inactive:         260960 kB
Active(anon):     191976 kB
Inactive(anon):    41908 kB
Active(file):     158440 kB
Inactive(file):   219052 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:        102396 kB
SwapFree:         102396 kB
Dirty:               352 kB
Writeback:             0 kB
AnonPages:        211704 kB
Mapped:           215924 kB
Shmem:             42304 kB
Slab:              24528 kB
SReclaimable:      12108 kB
SUnreclaim:        12420 kB
KernelStack:        2128 kB
PageTables:         5676 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      577120 kB
Committed_AS:    1675164 kB
VmallocTotal:    1114112 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB
CmaTotal:           8192 kB
CmaFree:            6796 kB
MemTotal:         949448 kB
MemFree:          282860 kB
MemAvailable:     626016 kB
Buffers:           39432 kB
Cached:           362860 kB
SwapCached:            0 kB
Active:           362696 kB
Inactive:         260580 kB
Active(anon):     199880 kB
Inactive(anon):    41392 kB
Active(file):     162816 kB
Inactive(file):   219188 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:        102396 kB
SwapFree:         102396 kB
Dirty:               624 kB
Writeback:             0 kB
AnonPages:        220988 kB
Mapped:           215672 kB
Shmem:             41788 kB
Slab:              24788 kB
SReclaimable:      12296 kB
SUnreclaim:        12492 kB
KernelStack:        2136 kB
PageTables:         5692 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      577120 kB
Committed_AS:    2288564 kB
VmallocTotal:    1114112 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB
CmaTotal:           8192 kB
CmaFree:            6796 kB
MemTotal:         949448 kB
MemFree:          282532 kB
MemAvailable:     626112 kB
Buffers:           39432 kB
Cached:           359980 kB
SwapCached:            0 kB
Active:           365200 kB
Inactive:         257736 kB
Active(anon):     202280 kB
Inactive(anon):    38320 kB
Active(file):     162920 kB
Inactive(file):   219416 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:        102396 kB
SwapFree:         102396 kB
Dirty:                52 kB
Writeback:             0 kB
AnonPages:        223520 kB
Mapped:           212600 kB
Shmem:             38716 kB
Slab:              24956 kB
SReclaimable:      12476 kB
SUnreclaim:        12480 kB
KernelStack:        2120 kB
PageTables:         5736 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      577120 kB
Committed_AS:    2876612 kB
VmallocTotal:    1114112 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB
CmaTotal:           8192 kB
CmaFree:            6796 kB
如果我在%1的内存中写入,然后停止写入,我在闪存上有很多交换操作

pi@raspberrypi:/tmp $ %1
./a.out 600

done
^Z
[1]+  stopped                 ./a.out 600
现在几乎没有可用交换,也几乎没有可用内存,
/proc/meminfo
给出

MemTotal:         949448 kB
MemFree:           33884 kB
MemAvailable:      32544 kB
Buffers:             796 kB
Cached:            66032 kB
SwapCached:        66608 kB
Active:           483668 kB
Inactive:         390360 kB
Active(anon):     462456 kB
Inactive(anon):   374188 kB
Active(file):      21212 kB
Inactive(file):    16172 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:        102396 kB
SwapFree:           3080 kB
Dirty:                96 kB
Writeback:             0 kB
AnonPages:        740984 kB
Mapped:            61176 kB
Shmem:             29288 kB
Slab:              21932 kB
SReclaimable:       9084 kB
SUnreclaim:        12848 kB
KernelStack:        2064 kB
PageTables:         7012 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      577120 kB
Committed_AS:    2873112 kB
VmallocTotal:    1114112 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB
CmaTotal:           8192 kB
CmaFree:            6796 kB
%1仍在等待getchar,如果我对%2执行相同的操作,它会工作,但实际上是因为进程%1消失了(shell上没有消息)

如果I malloc(为程序提供第二个参数),则行为相同



另请参见

首先,如果禁用交换(不添加任何交换分区),这并不意味着不使用交换。阅读下面的内容

您可以在没有任何交换辅助空间的情况下运行系统,但这并不意味着您没有使用虚拟内存。不能禁用虚拟内存,虚拟内存是实现
mmap(2)
syscall的基本概念

mmap(2)
使用文件填充它用于内存段的页面的初始内容。但它做的更多。。。它使用普通的虚拟内存来分配该段的页面,并在内核需要这些页面时将它们交换出去。由于有一个文件来存储页面内容,您不需要交换它,只需将页面内容写入文件中的适当位置即可。当另一个进程连接了相同的共享内存段时,同一页映射到两个进程上,当一个进程写入该页时,另一个进程立即看到它。另外,如果某个进程读取或写入文件,因为所使用的块与读取磁盘文件的块相同,那么它将看到的数据与两个进程共享的数据相同。这就是它的工作原理

内核通过这种机制节省了大量的交换,并且允许内核能够丢弃程序的部分文本段,而无需将它们交换到辅助设备(因为它们已经在程序的文件文本段中)

当你说

如果我移动了一些匿名内存(但没有触摸任何页面),会发生什么

如果您没有触摸任何页面,那么它们可能都没有被实际映射,只有资源已准备好使用,但尚未分配。当您在其中一个页面(例如,对于读取,您承诺不触摸它们)上出错时,该页面映射到实际内存页面,但磁盘备份(或其交换空间)实际上在文件中,而不是在交换设备中。该页实际上也是用于存储来自磁盘驱动程序的数据的磁盘块(更准确地说是磁盘块集),因此没有使用相同数据的多个副本

编辑
匿名
mmap(2)
可能还使用磁盘块(在某些默认磁盘单元中)。因此,即使您不使用交换设备,也可能允许您使用映射到磁盘inode的虚拟空间的
mmap(2)
。我没有检查这个,但是旧的unix管道是这样工作的。可以使用临时inode(没有在目录中分配条目,就像打开进程的已擦除文件一样)。

问题是物理内存不足,而不是虚拟内存不足。@Barmar是的,最好不要在没有swapRight的情况下谈论“虚拟”,他没有看到这一点。但它可以弹出映射到可执行文件中纯段的内存。@Barmar我删除了“virtual”一词,因为您的标记PTE包含的信息表明没有与VM页关联的物理内存,它必须在页面错误发生时创建。Google OOM killer@PSkocik它嵌入到内核中了吗?@SomeName是的。这是一种内核机制,当内核内存过多,内存变得急需,但没有足够的物理内存来支持承诺时,它会选择受害者。@SomeName IDK about VM_ACCOUNT。mmap不会填充(=预故障)整个映射,除非您使用MAP_populate进行mmap。在主页上,我不是这方面的专家。我真的尽量避免。我喜欢禁用Overmit,请放心,OOM杀手永远不会触及我的任何进程DHi感谢您的详细回复!我有点困惑。既然内核已经准备好了这个交换区域,为什么我们需要自己创建交换空间呢?这两种交换机制有什么区别?谢谢不,我不是说你用你自己的交换。。。我要说的是,很多资源都是原创的