关于虚拟内存的分析

来源:互联网 发布:汪涵 知乎 编辑:程序博客网 时间:2024/05/16 17:00

好久没更新博客了,最近被虚拟内存给搞糊涂了,翻书 google差不多半个月吧,争取搞出点头绪来,但是基础知识不多说,大家看书看看什么是虚拟空间吧,这篇文章重在是一个进程所占虚拟空间的分析


先看top

top - 18:34:57 up 4 days,  4:20,  4 users,  load average: 0.00, 0.00, 0.00
Tasks: 114 total,   1 running, 113 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.2%us,  0.2%sy,  0.0%ni, 99.7%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   4051424k total,  3612920k used,   438504k free,   200040k buffers
Swap:  8385920k total,        0k used,  8385920k free,  3114324k cached


在这里重点介绍的是Swap,当物理内存空间不够用的时候,进程的虚拟空间的内容会放到硬盘上的swap分区中,

[root@web-dev-146 ~]# cat /proc/swaps
Filename                                Type            Size    Used    Priority
/dev/sda3                               partition       8385920 0       -2

很明显分了8g的大小也就是硬盘分区来存放进程虚拟空间内容数据

对于共享内存映射情况,缺页异常处理程序首先在swap   cache中寻找目标页(符合address_space以及偏移量的物理页),如果找到,则直接返回地址;如果没有找到,则判断该页是否在交换区(swap   area),如果在,则执行一个换入操作;如果上述两种情况都不满足,处理程序将分配新的物理页面,并把它插入到page   cache中。进程最终将更新进程页表。 

一会通过实例我们来分析swap cache的作用

swap cached中的大小我们通过

free来看

             total       used       free     shared    buffers     cached
Mem:       4051424    3613052     438372          0     200072    3114368
-/+ buffers/cache:     298612    3752812
Swap:      8385920          0    8385920

说明读取文件占用了这么大的物理空间,用于缓存已经打开的文件

倒没必要释放操作系统会根据需要多少内存就从cache释放多少内存,所以不必操心,但是通过我们的例子你可以看到swap cache是不断增长的那是因为我们用到了文件映射,并且去读文件的内容

可以通过echo 3 > /proc/sys/vm/drop_caches 手动释放

写了一个c程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>

int main()
{
int fd;
int i;
if ( (fd = open("/home/mongodb/shard200/fs_media_recommend.8", O_RDWR|O_CREAT, S_IRWXU)) < 0){
   printf("open file wrong!");
   return 0;
}

struct stat file_stat;
if ( fstat( fd, &file_stat) < 0 )
{
   printf(" fstat wrong");
   return 0;
}

int *start_fp;
if( ( start_fp = (int *)mmap(NULL, 2146435072, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 )) == MAP_FAILED)
{
   printf("mmap wrong");
   return 0;

}
for(i=0;i<10000000;i++){
printf("%d\n", start_fp[i]);
}
/*msync( start_fp, file_stat.st_size, MS_ASYNC);

if ( munmap( start_fp, file_stat.st_size ) < 0 )
{
   printf("munmap wrong");
   return 0;
}*/
sleep(100000);
}


其中我们用到了mmap文件映射,故意sleep这么久我们可以分析下进程的虚拟地址的信息

top - 18:43:07 up 4 days,  4:28,  4 users,  load average: 0.15, 0.05, 0.02
Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
Cpu0  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu1  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   4051424k total,  3612804k used,   438620k free,   200052k buffers
Swap:  8385920k total,        0k used,  8385920k free,  3114332k cached
 Cumulative time On
  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  SWAP CODE DATA COMMAND                                                 
16955 root      16   0 2050m  25m  25m R  0.0  0.6   0:25.84 2.0g    4  120 test9                                                   

当运行改程序,会发现进程的res在不断增大,但是VIRT和SWAP以及上面的Swap没有丝毫变化

大家可以试着去掉我从映射读取内容那块代码,可以见到一上来VIRT和SWAP就已经确定了大小,该大小是在进程加载后分配了虚拟空间,当然并不是真的分了2g这么大的空间,因为虚拟空间对应的Swap并没有发生变化,说明虚拟空间内容没有写到磁盘中,其实我们看下进程虚拟空间的分析

[root@web-dev-146 ~]# ps -ef | grep test9
root     16968  9017 19 18:47 pts/2    00:00:01 ./test9
root     16970 13123  0 18:47 pts/1    00:00:00 grep test9
[root@web-dev-146 ~]# cat /proc/16968/maps
00400000-00401000 r-xp 00000000 08:02 6305715                            /root/test9
00600000-00601000 rw-p 00000000 08:02 6305715                            /root/test9
3c5a400000-3c5a41c000 r-xp 00000000 08:02 1405202                        /lib64/ld-2.5.so
3c5a61b000-3c5a61c000 r--p 0001b000 08:02 1405202                        /lib64/ld-2.5.so
3c5a61c000-3c5a61d000 rw-p 0001c000 08:02 1405202                        /lib64/ld-2.5.so
3c5a800000-3c5a94d000 r-xp 00000000 08:02 1405203                        /lib64/libc-2.5.so
3c5a94d000-3c5ab4d000 ---p 0014d000 08:02 1405203                        /lib64/libc-2.5.so
3c5ab4d000-3c5ab51000 r--p 0014d000 08:02 1405203                        /lib64/libc-2.5.so
3c5ab51000-3c5ab52000 rw-p 00151000 08:02 1405203                        /lib64/libc-2.5.so
3c5ab52000-3c5ab57000 rw-p 3c5ab52000 00:00 0
2ad9f31db000-2ad9f31dc000 rw-p 2ad9f31db000 00:00 0
2ad9f31ed000-2ad9f31ef000 rw-p 2ad9f31ed000 00:00 0
2ad9f31ef000-2ada730ef000 rw-s 00000000 08:05 60686353                   /home/mongodb/shard200/fs_media_recommend.8
2ada730ef000-2ada730f0000 rw-p 2ada730ef000 00:00 0
7fffd6e4f000-7fffd6e64000 rw-p 7ffffffea000 00:00 0                      [stack]
ffffffffff600000-ffffffffffe00000 ---p 00000000 00:00 0                  [vdso]


这就是该进程对应的虚拟地址空间,可以很清楚的看到虚拟空间对应了我代码里面文件的映射关系,大小是多少呢

16968:   ./test9
0000000000400000      4K r-x--  /root/test9
0000000000600000      4K rw---  /root/test9
0000003c5a400000    112K r-x--  /lib64/ld-2.5.so
0000003c5a61b000      4K r----  /lib64/ld-2.5.so
0000003c5a61c000      4K rw---  /lib64/ld-2.5.so
0000003c5a800000   1332K r-x--  /lib64/libc-2.5.so
0000003c5a94d000   2048K -----  /lib64/libc-2.5.so
0000003c5ab4d000     16K r----  /lib64/libc-2.5.so
0000003c5ab51000      4K rw---  /lib64/libc-2.5.so
0000003c5ab52000     20K rw---    [ anon ]
00002ad9f31db000      4K rw---    [ anon ]
00002ad9f31ed000      8K rw---    [ anon ]
00002ad9f31ef000 2096128K rw-s-  /home/mongodb/shard200/fs_media_recommend.8
00002ada730ef000      4K rw---    [ anon ]
00007fffd6e4f000     84K rw---    [ stack ]
ffffffffff600000   8192K -----    [ anon ]
 total          2107968K

很清楚吧,大小就是我代码里面映射的大小,大家可以通过一下更深刻了解一下虚拟空间的信息

[root@web-dev-146 ~]# cat /proc/16968/status
Name:   test9
State:  S (sleeping)
SleepAVG:       98%
Tgid:   16968
Pid:    16968
PPid:   9017
TracerPid:      0
Uid:    0       0       0       0
Gid:    0       0       0       0
FDSize: 256
Groups: 0 1 2 3 4 6 10
VmPeak:  2099776 kB
VmSize:  2099776 kB
VmLck:         0 kB
VmHWM:     39440 kB
VmRSS:     39440 kB
VmData:       36 kB
VmStk:        84 kB
VmExe:         4 kB
VmLib:      1444 kB
VmPTE:       116 kB
StaBrk: 1ede2000 kB
Brk:    1ede2000 kB
StaStk: 7fffd6e63850 kB
Threads:        1
SigQ:   1/36864
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: 0000000000000000
CapInh: 0000000000000000
CapPrm: 00000000fffffeff
CapEff: 00000000fffffeff
Cpus_allowed:   00000000,00000000,00000000,00000000,00000000,00000000,00000000,0000000f
Mems_allowed:   00000000,00000001

我们可以看到虚拟空间是2099776kB大小,这和我们top该进程对应的数据是一致的,

上面的例子为了说明virt虚拟内存大小并不是其实占用了物理内存后者磁盘空间的大小,这个大小的确定是由于进程在物理内存中放了一份映射表,没分配一个进程总是又这么一个映射数据结构即

加载关于进程虚拟地址空间的页面时,一系列的vm_area_struct将自动生成,每一个vm_area_struct描述进程的一部分,如执行代码、数据等。Linux虚拟存储管理支持了多数标准的虚拟内存操作,如读取、关闭、共享、缺页等。一旦vm_area_struct结构生成,就可以通过该结构中的指向vm_operation_struct的指针进行虚拟内存操作了。

如图3所示,为虚存管理数据结构之间的关系。

虚存管理数据结构之间的关系

图 3        虚拟存储管理的数据结构关系

这个结构引用一下网上的,这个结构是不可避免的是真正占用内存空间的当然不会太大,它只记录虚拟空间分配内容中的头和尾的信息,所以占用虚拟空间大小一算就知道了

刚才我们提到过VIRT放到SWAP分区的所以对于该进程刚开始初始化的时候我们发现VIRT和SWAP空间大小一致,都是虚拟的,

但是当我们读取映射文件的内容时,这时候通过页面中断,我们映射文件的内容会加载到真正的物理内存中,就是我们上面做的实验,我们会发现物理内存RES 在不断的增加,但是由于有一些内容已经导入到了物理内存中,所以操作系统会对SWAP磁盘空间进行释放,看下面

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  SWAP CODE DATA COMMAND                                                 
 9193 root      15   0 3974m 3.0g  640 S  0.0 19.0  28:50.33 933m  328 3.9g redis-server      

这个时候我们发现VIRT = SWAP+RES的大小,很容易理解,既然你物理内存已经存放了数据,当然我要空出那些没必要的冗余空间用来作别的          


先到这我会把想到的继续补充进去                         







原创粉丝点击