了解内存分配

时间:2019-02-14 17:52:52

标签: c memory linux-kernel mmap

我试图了解“内存的工作原理”。据我了解,在调用mmap创建MAP_ANONYMOUS映射时的操作系统(在我的情况下为Linux)将创建:

  

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

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

据我所知,当CPU尝试访问尚未在页表中的内存页面时,触发mmap的页面错误时会发生到物理内存的实际映射。

OS捕获页面错误并在页面目录中创建一个条目。

如果我mmap使用了一些匿名内存(但没有触摸任何页面),然后其他进程耗尽了所有物理内存,然后尝试使用一个,就会发生什么 $.ajax({ type: "GET", url: "dummysite.com", crossDomain: true, success: function (data) { // do something with server response data outputElm.innerHTML += data; }, error: function (err) { // handle error logic here console.log(err); } }); 版的页面中(我已禁用交换功能)?

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

2 个答案:

答案 0 :(得分:2)

如果您没有足够的可用内存,则使用 mmap (MAP_ANONYMOUS)或 malloc 不会发生任何变化, mmap 返回{{1 }}和 malloc 返回 NULL

如果我使用该程序:

MAP_FAILED

我在具有1Gb内存和100Mo交换空间的raspberrypi上,因为我在SO

上,所以 chromium 已使用该内存。

#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; } 给出:

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

750很大,但是

pi@raspberrypi:/tmp $ ./a.out 750
ko

使用的内存( top 等)不能反映600Mo,因为我没有在其中进行读/写操作

pi@raspberrypi:/tmp $ ./a.out 600 & [1] 1525 pi@raspberrypi:/tmp $ ok 给出:

proc/meminfo

我可以再次做

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

即使对于内存+交换而言,总数太大,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

如果我在%1的内存中写入内容,然后停止它,那么我在闪存上做了很多交换工作

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 $ %1 ./a.out 600 done ^Z [1]+ stopped ./a.out 600 给出

/proc/meminfo

%1仍在等待 getchar ,如果我对%2进行相同操作,则可以正常工作,但实际上是因为进程%1消失了(外壳上没有消息)

如果我 malloc (给程序第二个参数),则行为相同。


另请参阅What is the purpose of MAP_ANONYMOUS flag in mmap system call?

答案 1 :(得分:1)

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

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

mmap(2)使用文件来填充用于内存段的页面的初始内容。但是它还有更多功能...它为该段分配的页面使用普通的虚拟内存,并在内核需要其中某些页面时将其换出。由于有一个文件可以存储页面内容,因此无需交换它,只需将页面的内容写入文件中的适当位置即可。就像附加了相同共享内存段的其他进程一样,同一页面映射到两个进程上,当一个进程写入该页面时,另一个进程立即看到该页面。同样,如果某些进程读取或写入文件,因为所使用的块与读取磁盘文件的块相同,则它将要看到的数据就是两个进程共享的相同数据。就是这样。

内核通过这种机制节省了很多交换,并且这还使内核能够丢弃程序的文本段的某些部分,而不必将其交换到辅助设备中(因为它们已经存在于辅助设备中)。该程序的文件的文本段)

当你说

  

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

如果您没有触摸任何页面,则可能两个页面都尚未被实际映射,只有资源准备使用,但尚未分配。当您在这些页面之一上出错(例如,为了阅读,您保证不会触摸它们)是映射到实际内存页面的页面,但是磁盘备份(或该磁盘的交换空间)实际上在文件中,而不是在交换设备中。该页面实际上也是用于存储来自磁盘驱动程序的数据的磁盘块(更确切地说是一组磁盘块),因此没有使用同一数据的多个副本。

编辑

匿名mmap(2)可能还会使用磁盘块(在某些默认磁盘单元中)。因此,即使在不使用交换设备的情况下,也可能允许将mmap(2)与映射到磁盘inode的虚拟空间一起使用。我没有检查过,但是旧的unix管道以这种方式工作。可以使用一个临时索引节点(没有在目录中分配条目,例如在打开进程的情况下删除文件)。