我试图了解“内存的工作原理”。据我了解,在调用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应该触发页面错误,然后尝试在页面目录中创建一个条目。但是由于没有剩余物理内存,所以将无法这样做...
答案 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管道以这种方式工作。可以使用一个临时索引节点(没有在目录中分配条目,例如在打开进程的情况下删除文件)。