这期内容当中小编将会给大家带来有关Linux中怎么实现虚拟内存,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。
Linux虚拟内存怎么实现
第一个例子:下面一段程序会打印出程序的pid(进程号)后挂起。
#include
#include
#include
intmain(){
printf("run`pmap%d`\n",getpid());
pause();
}
将上面代码保存成文件mem_munch.c然后运行下面程序编译并执行:
$gccmem_munch.c-omem_munch
$./mem_munch
run`pmap25681`
上面进程号是25681,可能你试验的结果会不太一样。
下面我们通过pmap命令来查看一下这个小程序的内存使用情况
$pmap25681
25681:./mem_munch
00000000004000004Kr-x--/home/user/mem_munch
00000000006000004Kr----/home/user/mem_munch
00000000006010004Krw---/home/user/mem_munch
00007fcf5af880001576Kr-x--/lib/x86_64-linux-gnu/libc-2.13.so
00007fcf5b1120002044K-----/lib/x86_64-linux-gnu/libc-2.13.so
00007fcf5b31100016Kr----/lib/x86_64-linux-gnu/libc-2.13.so
00007fcf5b3150004Krw---/lib/x86_64-linux-gnu/libc-2.13.so
00007fcf5b31600024Krw---[anon]
00007fcf5b31c000132Kr-x--/lib/x86_64-linux-gnu/ld-2.13.so
00007fcf5b51200012Krw---[anon]
00007fcf5b53900012Krw---[anon]
00007fcf5b53c0004Kr----/lib/x86_64-linux-gnu/ld-2.13.so
00007fcf5b53d0008Krw---/lib/x86_64-linux-gnu/ld-2.13.so
00007fff7efd8000132Krw---[stack]
00007fff7efff0004Kr-x--[anon]
ffffffffff6000004Kr-x--[anon]
total3984K
上面的结果是这个程序的内存使用情况,其实更确切的说是这个程序认为它使用内存的情况。从上面的结果我们能看到,当你访问libc库时,实际上是对内存地址00007fcf5af88000的访问,当你访问ld库时,实际上是对内存地址00007fcf5b31c000的访问。
上面的输出可能还比较抽象,下面我们修改一下上面的程序,我们在程序的堆和栈上各放一块数据。
#include
#include
#include
#include
intmain(){
inton_stack,*on_heap;
//局部变量是放在栈上的,所以on_stack的地址就是栈的初始地址
on_stack=42;
printf("stackaddress:%p\n",&on_stack);
//malloc的内存是在堆上分配的
on_heap=(int*)malloc(sizeof(int));
printf("heapaddress:%p\n",on_heap);
printf("run`pmap%d`\n",getpid());
pause();
}
编译运行:
$./mem_munch
stackaddress:0x7fff497670bc
heapaddress:0x1b84010
run`pmap11972`
然后再用pmap命令查看一下内存使用:
$pmap11972
11972:./mem_munch
00000000004000004Kr-x--/home/user/mem_munch
00000000006000004Kr----/home/user/mem_munch
00000000006010004Krw---/home/user/mem_munch
0000000001b84000132Krw---[anon]
00007f3ec4d980001576Kr-x--/lib/x86_64-linux-gnu/libc-2.13.so
00007f3ec4f220002044K-----/lib/x86_64-linux-gnu/libc-2.13.so
00007f3ec512100016Kr----/lib/x86_64-linux-gnu/libc-2.13.so
00007f3ec51250004Krw---/lib/x86_64-linux-gnu/libc-2.13.so
00007f3ec512600024Krw---[anon]
00007f3ec512c000132Kr-x--/lib/x86_64-linux-gnu/ld-2.13.so
00007f3ec532200012Krw---[anon]
00007f3ec534900012Krw---[anon]
00007f3ec534c0004Kr----/lib/x86_64-linux-gnu/ld-2.13.so
00007f3ec534d0008Krw---/lib/x86_64-linux-gnu/ld-2.13.so
00007fff49747000132Krw---[stack]
00007fff497bb0004Kr-x--[anon]
ffffffffff6000004Kr-x--[anon]
total4116K
这次多出了上面红色的一行内容,红色内容就是堆的起始位置:
0000000001b84000132Krw---[anon]
在我们程序运行的输出里也有一行红色的输出,这是这个地址在程序中的内存地址:
heapaddress:0x1b84010
这两个地址基本上是一样的,其中的anon是Anonymous的缩写,表明这段内存是没有文件映射的。
我们再看上面绿色的两行,与上面相对应,这两行分别是用pmap和应用程序看到的栈起始地址:
00007fff49747000132Krw---[stack]
stackaddress:0x7fff497670bc
上面说到的内存使用,都只是程序认为自己对内存的使用,实际上程序在分配内存是不知道系统内存的状态的。所以上面的输出都只是从程序自己的角度看到的内存使用状况。比如在上面的例子中,我们看到程序的内存地址空间是从0×0000000000400000到0xffffffffff600000的所有地址(而0xffffffffff600000到0×00007fffffffffffffff之间的地址是有特殊用处的,这里不多讲)。这样算下来,我们总共可以使用的内存空间有1千万TB。
但是实际上目前没有硬件能有1千万TB的物理内存。为什么操作系统会如此设计呢?原因有很多,可以看这里,但也正因此,我们可以使用远远超出物理内存大小的内存空间。
内存映射
内存映射的原理就是让操作系统将一个文件映射到一段内存中,然后在操作这个文件内存就可以像操作内存一样。比如我们创建一个完全内容随机的文件,然后将它用内存映射的方式映射到一段内存空间中。那么我们在这段内存中随便取一位就相当于取到了一个随机数。下面就让我们来做这个实验,先用下面命令生成一个内容随机的文件。
$ddif=/dev/urandombs=1024count=1000000of=/home/user/random
1000000+0recordsin
1000000+0recordsout
1024000000bytes(1.0GB)copied,123.293s,8.3MB/s
$ls-lhrandom
-rw-r--r--1useruser977M2011-08-2916:46random
然后我们用下面程序来将这个文件内容映射到内存,再从中取出随机数
#include
#include
#include
#include
#include
intmain(){
char*random_bytes;
FILE*f;
intoffset=0;
//open"random"forreading
f=fopen("/home/user/random","r");
if(!f){
perror("couldn'topenfile");
return-1;
}
//wewanttoinspectmemorybeforemappingthefile
printf("run`pmap%d`,thenpress",getpid());
getchar();
random_bytes=mmap(0,1000000000,PROT_READ,MAP_SHARED,fileno(f),0);
if(random_bytes==MAP_FAILED){
perror("errormappingthefile");
return-1;
}
while(1){
printf("randomnumber:%d(pressfornextnumber)",*(int*)(random_bytes+offset));
getchar();
offset+=4;
}
}
然后运行这个程序:
$./mem_munch
run`pmap12727`,thenpress
Linux虚拟内存怎么实现
下面我们通过一次次的按下回车键来从这个文件中读取随机数,按下几次后我们可以再通过pmap来查看其内存空间的情况:
$pmap12727
12727:./mem_munch
00000000004000004Kr-x--/home/user/mem_munch
00000000006000004Kr----/home/user/mem_munch
00000000006010004Krw---/home/user/mem_munch
000000000147d000132Krw---[anon]
00007fe261c6f000976564Kr--s-/home/user/random
00007fe29d61c0001576Kr-x--/lib/x86_64-linux-gnu/libc-2.13.so
00007fe29d7a60002044K-----/lib/x86_64-linux-gnu/libc-2.13.so
00007fe29d9a500016Kr----/lib/x86_64-linux-gnu/libc-2.13.so
00007fe29d9a90004Krw---/lib/x86_64-linux-gnu/libc-2.13.so
00007fe29d9aa00024Krw---[anon]
00007fe29d9b0000132Kr-x--/lib/x86_64-linux-gnu/ld-2.13.so
00007fe29dba600012Krw---[anon]
00007fe29dbcc00016Krw---[anon]
00007fe29dbd00004Kr----/lib/x86_64-linux-gnu/ld-2.13.so
00007fe29dbd10008Krw---/lib/x86_64-linux-gnu/ld-2.13.so
00007ffff29b2000132Krw---[stack]
00007ffff29de0004Kr-x--[anon]
ffffffffff6000004Kr-x--[anon]
total980684K
上面的输出和之前的大同小异,但是多出了上面红色的一行。这是我们上面的随机文件映射到内存中的内存。我们再使用pmap-x选项来查看一下程序的内存使用,会得到下面的内容,其中RSS(residentsetsize)列表示真实占用的内存。
pmap-x12727
12727:./mem_munch
AddressKbytesRSSDirtyModeMapping
0000000000400000040r-x--mem_munch
0000000000600000044r----mem_munch
0000000000601000044rw---mem_munch
000000000147d000044rw---[anon]
00007fe261c6f000040r--s-random
00007fe29d61c00002880r-x--libc-2.13.so
00007fe29d7a6000000-----libc-2.13.so
00007fe29d9a500001616r----libc-2.13.so
00007fe29d9a9000044rw---libc-2.13.so
00007fe29d9aa00001616rw---[anon]
00007fe29d9b000001080r-x--ld-2.13.so
00007fe29dba600001212rw---[anon]
00007fe29dbcc00001616rw---[anon]
00007fe29dbd0000044r----ld-2.13.so
00007fe29dbd1000088rw---ld-2.13.so
00007ffff29b200001212rw---[stack]
00007ffff29de000040r-x--[anon]
ffffffffff600000000r-x--[anon]
----------------------------------
totalkB980684508100
如果你的虚拟内存占用(上面的Kbytes列)都是0,不用担心,这是一个在Debian/Ubuntu系统上pmap-x命令的bug。最后一行输出的总占用量是正确的。
现在你可以看一下RSS那一列,这就是实际内存占用。在random文件上,你的程序实际上可以访问在00007fe261c6f000之前的数十亿字节的内存地址,但是只要你访问的地址超过4KB,那么操作系统就会去磁盘上查找内容。也就是说实际上只有4KB的物理内存被使用了。只有访问这4KB的东西时,才是真正的内存操作。其它部分虽然你使用的也是内存操作函数来访问它,但是由于它没有被加载到内存中,所以在这些内容被访问的时候,操作系统会先去磁盘读random中读取内容到内存中。
如果我们把程序再修改一下,修改成下面这样,让程序把整个random文件都访问一遍。
#include
#include
#include
#include
#include
intmain(){
char*random_bytes;
FILE*f;
intoffset=0;
//open"random"forreading
f=fopen("/home/user/random","r");
if(!f){
perror("couldn'topenfile");
return-1;
}
random_bytes=mmap(0,1000000000,PROT_READ,MAP_SHARED,fileno(f),0);
if(random_bytes==MAP_FAILED){
printf("errormappingthefile\n");
return-1;
}
for(offset=0;offset<1000000000;offset+=4){ inti=*(int*)(random_bytes+offset); //toshowwe'remakingprogress if(offset%1000000==0){ printf("."); } } //attheend,waitforsignalsowecancheckmem printf("\ndone,run`pmap-x%d`\n",getpid()); pause(); } 现在我们的pmap-x命令就会得到如下输出: $pmap-x5378 5378:./mem_munch AddressKbytesRSSDirtyModeMapping 0000000000400000044r-x--mem_munch 0000000000600000044r----mem_munch 0000000000601000044rw---mem_munch 0000000002271000044rw---[anon] 00007fc2aa33300009765640r--s-random 00007fc2e5ce000002920r-x--libc-2.13.so 00007fc2e5e6a000000-----libc-2.13.so 00007fc2e606900001616r----libc-2.13.so 00007fc2e606d000044rw---libc-2.13.so 00007fc2e606e00001616rw---[anon] 00007fc2e607400001080r-x--ld-2.13.so 00007fc2e626a00001212rw---[anon] 00007fc2e629000001616rw---[anon] 00007fc2e6294000044r----ld-2.13.so 00007fc2e6295000088rw---ld-2.13.so 00007fff037e600001212rw---[stack] 00007fff039c9000040r-x--[anon] ffffffffff600000000r-x--[anon] ---------------------------------- totalkB980684977072104
上述就是小编为大家分享的Linux中怎么实现虚拟内存了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。