统计输出
top命令只是给出了一个进程大概占用了多少的内存,而pmap能更详细的给出内存都是被谁占用了。pmap命令输出的内容来自于/proc/[pid]/maps和/proc/[pid]/smaps这两个文件,第一个文件包含了每段的一个大概描述,而后一个文件包含了更详细的信息。
xxxxxxxxxx
101[root@666 ~]# less -N /proc/14224/
2arch_status coredump_filter io mountstats personality smaps_rollup timers
3attr/ cpuset limits net/ projid_map stack timerslack_ns
4autogroup cwd/ loginuid ns/ root/ stat uid_map
5auxv environ map_files/ numa_maps sched statm wchan
6cgroup exe maps oom_adj schedstat status
7clear_refs fd/ mem oom_score sessionid syscall
8cmdline fdinfo/ mountinfo oom_score_adj setgroups task/
9comm gid_map mounts pagemap smaps timens_offsets
10[root@666 ~]#
xxxxxxxxxx
271[root@666 ~]# yum whatprovides pmap
2
3procps-ng-3.3.10-28.el7.i686 : System and process monitoring utilities
4Repo : base
5Matched from:
6Filename : /usr/bin/pmap
7
8[root@666 ~]# rpm -ql procps-ng
9/usr/bin/free
10/usr/bin/pgrep
11/usr/bin/pkill
12/usr/bin/pmap
13/usr/bin/ps
14/usr/bin/pwdx
15/usr/bin/skill
16/usr/bin/slabtop
17/usr/bin/snice
18/usr/bin/tload
19/usr/bin/top
20/usr/bin/uptime
21/usr/bin/vmstat
22/usr/bin/w
23/usr/bin/watch
24/usr/lib64/libprocps.so.4
25/usr/lib64/libprocps.so.4.0.0
26/usr/sbin/sysctl
27
这里第一列是内存的起始地址,第二列是mapping的地址大小,第三列是这段内存的访问权限,最后一列是mapping到的文件。这里的地址都是虚拟地址,大小也是虚拟地址大小。
这里的输出有很多的[ anon ]行,表示在磁盘上没有对应的文件,这些一般都是可执行文件或者动态库里的bss段。当然有对应文件的mapping也有可能是anonymous,比如文件的数据段。
xxxxxxxxxx
2041
2# 通过以下示例可以看到nginx引用了哪些链接库、每个链接库占用了多少内存,以及总的内存占用。在最下方Total
3# ps -ef | grep nginx
4root 14224 1 0 Sep09 ? 00:00:00 nginx: master process /usr/local/nginx/nginx -c /usr/local/nginx/nginx.conf
5root 29926 28136 0 19:50 pts/0 00:00:00 grep --color=auto nginxfr
6[root@666 ~]# pmap 14224
714224: nginx: master process /usr/local/nginx/nginx -c /usr/local/nginx/nginx.conf
80000000000400000 4500K r-x-- nginx
90000000000a64000 196K r---- nginx
100000000000a95000 160K rw--- nginx
110000000000abd000 148K rw--- [ anon ]
120000000001a77000 4644K rw--- [ anon ]
130000000001f00000 3704K rw--- [ anon ]
1400007f1724340000 1024K rw-s- zero (deleted)
1500007f1724440000 2500K r--s- GeoLite2-Country.mmdb
1600007f17246b1000 128228K r--s- GeoIP2-City.mmdb
1700007f172c4ea000 88K r-x-- libresolv-2.17.so
18...
1900007f172c702000 8K rw--- [ anon ]
2000007f172c704000 24K r-x-- libnss_dns-2.17.so
21...
2200007f17348b5000 48K r-x-- libnss_files-2.17.so
23...
2400007f1734ac2000 24K rw--- [ anon ]
2500007f1734ac8000 384K r-x-- libpcre.so.1.2.0
26...
2700007f1734d2a000 8K r-x-- libXau.so.6.0.0
2800007f1734d2c000 2048K ----- libXau.so.6.0.0
2900007f1734f2c000 4K r---- libXau.so.6.0.0
3000007f1734f2d000 4K rw--- libXau.so.6.0.0
3100007f1734f2e000 176K r-x-- libgraphite2.so.3.0.1
32...
3300007f173515c000 1104K r-x-- libglib-2.0.so.0.5600.1
3400007f1735270000 2044K ----- libglib-2.0.so.0.5600.1
3500007f173546f000 4K r---- libglib-2.0.so.0.5600.1
3600007f1735470000 4K rw--- libglib-2.0.so.0.5600.1
3700007f1735471000 4K rw--- [ anon ]
3800007f1735472000 36K r-x-- libjbig.so.2.0
3900007f173547b000 2044K ----- libjbig.so.2.0
4000007f173567a000 4K r---- libjbig.so.2.0
4100007f173567b000 12K rw--- libjbig.so.2.0
4200007f173567e000 156K r-x-- libxcb.so.1.1.0
4300007f17356a5000 2044K ----- libxcb.so.1.1.0
4400007f17358a4000 4K r---- libxcb.so.1.1.0
4500007f17358a5000 4K rw--- libxcb.so.1.1.0
4600007f17358a6000 60K r-x-- libbz2.so.1.0.6
4700007f17358b5000 2044K ----- libbz2.so.1.0.6
4800007f1735ab4000 4K r---- libbz2.so.1.0.6
4900007f1735ab5000 4K rw--- libbz2.so.1.0.6
5000007f1735ab6000 16K r-x-- libuuid.so.1.3.0
5100007f1735aba000 2044K ----- libuuid.so.1.3.0
5200007f1735cb9000 4K r---- libuuid.so.1.3.0
5300007f1735cba000 4K rw--- libuuid.so.1.3.0
5400007f1735cbb000 156K r-x-- libexpat.so.1.6.0
5500007f1735ce2000 2048K ----- libexpat.so.1.6.0
5600007f1735ee2000 8K r---- libexpat.so.1.6.0
5700007f1735ee4000 4K rw--- libexpat.so.1.6.0
5800007f1735ee5000 108K r-x-- libfribidi.so.0.4.0
5900007f1735f00000 2044K ----- libfribidi.so.0.4.0
6000007f17360ff000 4K r---- libfribidi.so.0.4.0
6100007f1736100000 4K rw--- libfribidi.so.0.4.0
6200007f1736101000 624K r-x-- libharfbuzz.so.0.10705.0
6300007f173619d000 2044K ----- libharfbuzz.so.0.10705.0
6400007f173639c000 4K r---- libharfbuzz.so.0.10705.0
6500007f173639d000 4K rw--- libharfbuzz.so.0.10705.0
6600007f173639e000 84K r-x-- libgcc_s-4.8.5-20150702.so.1
6700007f17363b3000 2044K ----- libgcc_s-4.8.5-20150702.so.1
6800007f17365b2000 4K r---- libgcc_s-4.8.5-20150702.so.1
6900007f17365b3000 4K rw--- libgcc_s-4.8.5-20150702.so.1
7000007f17365b4000 932K r-x-- libstdc++.so.6.0.19
7100007f173669d000 2048K ----- libstdc++.so.6.0.19
7200007f173689d000 32K r---- libstdc++.so.6.0.19
7300007f17368a5000 8K rw--- libstdc++.so.6.0.19
7400007f17368a7000 84K rw--- [ anon ]
7500007f17368bc000 436K r-x-- libwebp.so.7.0.5
7600007f1736929000 2044K ----- libwebp.so.7.0.5
7700007f1736b28000 4K r---- libwebp.so.7.0.5
7800007f1736b29000 4K rw--- libwebp.so.7.0.5
7900007f1736b2a000 8K rw--- [ anon ]
8000007f1736b2c000 444K r-x-- libtiff.so.5.2.0
8100007f1736b9b000 2048K ----- libtiff.so.5.2.0
8200007f1736d9b000 4K r---- libtiff.so.5.2.0
8300007f1736d9c000 12K rw--- libtiff.so.5.2.0
8400007f1736d9f000 4K rw--- [ anon ]
8500007f1736da0000 1248K r-x-- libX11.so.6.3.0
8600007f1736ed8000 2048K ----- libX11.so.6.3.0
8700007f17370d8000 4K r---- libX11.so.6.3.0
8800007f17370d9000 20K rw--- libX11.so.6.3.0
8900007f17370de000 68K r-x-- libXpm.so.4.11.0
9000007f17370ef000 2044K ----- libXpm.so.4.11.0
9100007f17372ee000 4K r---- libXpm.so.4.11.0
9200007f17372ef000 4K rw--- libXpm.so.4.11.0
9300007f17372f0000 268K r-x-- libjpeg.so.62.1.0
9400007f1737333000 2048K ----- libjpeg.so.62.1.0
9500007f1737533000 4K r---- libjpeg.so.62.1.0
9600007f1737534000 4K rw--- libjpeg.so.62.1.0
9700007f1737535000 64K rw--- [ anon ]
9800007f1737545000 732K r-x-- libfreetype.so.6.14.0
9900007f17375fc000 2048K ----- libfreetype.so.6.14.0
10000007f17377fc000 28K r---- libfreetype.so.6.14.0
10100007f1737803000 4K rw--- libfreetype.so.6.14.0
10200007f1737804000 256K r-x-- libfontconfig.so.1.11.1
10300007f1737844000 2044K ----- libfontconfig.so.1.11.1
10400007f1737a43000 8K r---- libfontconfig.so.1.11.1
10500007f1737a45000 4K rw--- libfontconfig.so.1.11.1
10600007f1737a46000 16K r-x-- libraqm.so.0.700.0
10700007f1737a4a000 2044K ----- libraqm.so.0.700.0
10800007f1737c49000 4K r---- libraqm.so.0.700.0
10900007f1737c4a000 4K rw--- libraqm.so.0.700.0
11000007f1737c4b000 164K r-x-- libpng15.so.15.13.0
11100007f1737c74000 2048K ----- libpng15.so.15.13.0
11200007f1737e74000 4K r---- libpng15.so.15.13.0
11300007f1737e75000 4K rw--- libpng15.so.15.13.0
11400007f1737e76000 16K r-x-- libgpg-error.so.0.10.0
11500007f1737e7a000 2044K ----- libgpg-error.so.0.10.0
11600007f1738079000 4K r---- libgpg-error.so.0.10.0
11700007f173807a000 4K rw--- libgpg-error.so.0.10.0
11800007f173807b000 500K r-x-- libgcrypt.so.11.8.2
11900007f17380f8000 2044K ----- libgcrypt.so.11.8.2
12000007f17382f7000 4K r---- libgcrypt.so.11.8.2
12100007f17382f8000 12K rw--- libgcrypt.so.11.8.2
12200007f17382fb000 4K rw--- [ anon ]
12300007f17382fc000 1028K r-x-- libm-2.17.so
12400007f17383fd000 2044K ----- libm-2.17.so
12500007f17385fc000 4K r---- libm-2.17.so
12600007f17385fd000 4K rw--- libm-2.17.so
12700007f17385fe000 148K r-x-- liblzma.so.5.2.2
12800007f1738623000 2044K ----- liblzma.so.5.2.2
12900007f1738822000 4K r---- liblzma.so.5.2.2
13000007f1738823000 4K rw--- liblzma.so.5.2.2
13100007f1738824000 84K r-x-- libz.so.1.2.7
13200007f1738839000 2044K ----- libz.so.1.2.7
13300007f1738a38000 4K r---- libz.so.1.2.7
13400007f1738a39000 4K rw--- libz.so.1.2.7
13500007f1738a3a000 8K r-x-- libfreebl3.so
13600007f1738a3c000 2044K ----- libfreebl3.so
13700007f1738c3b000 4K r---- libfreebl3.so
13800007f1738c3c000 4K rw--- libfreebl3.so
13900007f1738c3d000 1808K r-x-- libc-2.17.so
14000007f1738e01000 2044K ----- libc-2.17.so
14100007f1739000000 16K r---- libc-2.17.so
14200007f1739004000 8K rw--- libc-2.17.so
14300007f1739006000 20K rw--- [ anon ]
14400007f173900b000 56K r-x-- libprofiler.so.0.4.14
14500007f1739019000 2048K ----- libprofiler.so.0.4.14
14600007f1739219000 4K r---- libprofiler.so.0.4.14
14700007f173921a000 4K rw--- libprofiler.so.0.4.14
14800007f173921b000 16K rw--- [ anon ]
14900007f173921f000 256K r-x-- libgd.so.3.0.8
15000007f173925f000 2048K ----- libgd.so.3.0.8
15100007f173945f000 20K r---- libgd.so.3.0.8
15200007f1739464000 128K rw--- libgd.so.3.0.8
15300007f1739484000 16K rw--- [ anon ]
15400007f1739488000 80K r-x-- libexslt.so.0.8.17
15500007f173949c000 2044K ----- libexslt.so.0.8.17
15600007f173969b000 4K r---- libexslt.so.0.8.17
15700007f173969c000 4K rw--- libexslt.so.0.8.17
15800007f173969d000 240K r-x-- libxslt.so.1.1.28
15900007f17396d9000 2048K ----- libxslt.so.1.1.28
16000007f17398d9000 4K r---- libxslt.so.1.1.28
16100007f17398da000 4K rw--- libxslt.so.1.1.28
16200007f17398db000 4K rw--- [ anon ]
16300007f17398dc000 1404K r-x-- libxml2.so.2.9.1
16400007f1739a3b000 2044K ----- libxml2.so.2.9.1
16500007f1739c3a000 32K r---- libxml2.so.2.9.1
16600007f1739c42000 8K rw--- libxml2.so.2.9.1
16700007f1739c44000 8K rw--- [ anon ]
16800007f1739c46000 48K r-x-- libsregex.so.0.0.1
16900007f1739c52000 2044K ----- libsregex.so.0.0.1
17000007f1739e51000 4K r---- libsregex.so.0.0.1
17100007f1739e52000 4K rw--- libsregex.so.0.0.1
17200007f1739e53000 20K r-x-- libmaxminddb.so.0.0.7
17300007f1739e58000 2044K ----- libmaxminddb.so.0.0.7
17400007f173a057000 4K r---- libmaxminddb.so.0.0.7
17500007f173a058000 4K rw--- libmaxminddb.so.0.0.7
17600007f173a059000 32K r-x-- libcrypt-2.17.so
17700007f173a061000 2044K ----- libcrypt-2.17.so
17800007f173a260000 4K r---- libcrypt-2.17.so
17900007f173a261000 4K rw--- libcrypt-2.17.so
18000007f173a262000 184K rw--- [ anon ]
18100007f173a290000 92K r-x-- libpthread-2.17.so
18200007f173a2a7000 2044K ----- libpthread-2.17.so
18300007f173a4a6000 4K r---- libpthread-2.17.so
18400007f173a4a7000 4K rw--- libpthread-2.17.so
18500007f173a4a8000 16K rw--- [ anon ]
18600007f173a4ac000 8K r-x-- libdl-2.17.so
18700007f173a4ae000 2048K ----- libdl-2.17.so
18800007f173a6ae000 4K r---- libdl-2.17.so
18900007f173a6af000 4K rw--- libdl-2.17.so
19000007f173a6b0000 136K r-x-- ld-2.17.so
19100007f173a7b2000 1024K rw-s- zero (deleted)
19200007f173a8b2000 80K rw--- [ anon ]
19300007f173a8cf000 4K rw-s- zero (deleted)
19400007f173a8d0000 4K rw--- [ anon ]
19500007f173a8d1000 4K r---- ld-2.17.so
19600007f173a8d2000 4K rw--- ld-2.17.so
19700007f173a8d3000 4K rw--- [ anon ]
19800007fffd249b000 132K rw--- [ stack ]
19900007fffd2568000 16K r---- [ anon ]
20000007fffd256c000 8K r-x-- [ anon ]
201ffffffffff600000 4K r-x-- [ anon ]
202total 246988K
203[root@666 ~]#
204
xxxxxxxxxx
281#这里$$代表当前bash的进程ID,下面只显示了部分输出结果
2dev@dev:~$ pmap $$
32805: bash
40000000000400000 976K r-x-- bash
500000000006f3000 4K r---- bash
600000000006f4000 36K rw--- bash
700000000006fd000 24K rw--- [ anon ]
80000000000be4000 1544K rw--- [ anon ]
9......
1000007f1fa0e9e000 2912K r---- locale-archive
1100007f1fa1176000 1792K r-x-- libc-2.23.so
1200007f1fa1336000 2044K ----- libc-2.23.so
1300007f1fa1535000 16K r---- libc-2.23.so
1400007f1fa1539000 8K rw--- libc-2.23.so
1500007f1fa153b000 16K rw--- [ anon ]
16......
1700007f1fa196c000 152K r-x-- ld-2.23.so
1800007f1fa1b7e000 28K r--s- gconv-modules.cache
1900007f1fa1b85000 16K rw--- [ anon ]
2000007f1fa1b8f000 8K rw--- [ anon ]
2100007f1fa1b91000 4K r---- ld-2.23.so
2200007f1fa1b92000 4K rw--- ld-2.23.so
2300007f1fa1b93000 4K rw--- [ anon ]
2400007ffde903a000 132K rw--- [ stack ]
2500007ffde90e4000 8K r---- [ anon ]
2600007ffde90e6000 8K r-x-- [ anon ]
27ffffffffff600000 4K r-x-- [ anon ]
28total 22464K
这里第一列是内存的起始地址,第二列是mapping的地址大小,第三列是这段内存的访问权限,最后一列是mapping到的文件。这里的地址都是虚拟地址,大小也是虚拟地址大小。
这里的输出有很多的[ anon ]行,表示在磁盘上没有对应的文件,这些一般都是可执行文件或者动态库里的bss段。当然有对应文件的mapping也有可能是anonymous,比如文件的数据段。关于程序的数据段和bss段的介绍请参考elf的相关资料。
上面可以看到bash、libc-2.23.so等文件出现了多行,但每行的权限不一样,这是因为每个动态库或者可执行文件里面都分很多段,有只能读和执行的代码段,有能读写的数据段,还有比如这一行“00007f1fa153b000 16K rw--- [ anon ]”,就是它上面一行libc-2.23.so的bss段。
[ stack ]表示进程用到的栈空间,而heap在这里看不到,因为pmap默认情况下不单独标记heap出来,由于heap是anonymous,所以从这里的大小可以推测出来,heap就是“0000000000be4000 1544K rw--- [ anon ]”。
其实从上面的结果根本看不出实际上每段占用了多少物理内存,要想看到RSS,需要使用-X参数,下面看看更详细的输出:
xxxxxxxxxx
301dev@dev:~$ pmap -X $$
22805: bash
3Address Perm Offset Device Inode Size Rss Pss Referenced Anonymous Shared_Hugetlb Private_Hugetlb Swap SwapPss Locked Mapping
400400000 r-xp 00000000 fc:00 390914 976 888 526 888 0 0 0 0 0 0 bash
5006f3000 r--p 000f3000 fc:00 390914 4 4 4 4 4 0 0 0 0 0 bash
6006f4000 rw-p 000f4000 fc:00 390914 36 36 36 36 36 0 0 0 0 0 bash
7006fd000 rw-p 00000000 00:00 0 24 24 24 24 24 0 0 0 0 0
800be4000 rw-p 00000000 00:00 0 1544 1544 1544 1544 1544 0 0 0 0 0 [heap] 这里是heap标记
9.....
107f1fa0e9e000 r--p 00000000 fc:00 136340 2912 400 83 400 0 0 0 0 0 0 locale-archive
117f1fa1176000 r-xp 00000000 fc:00 521726 1792 1512 54 1512 0 0 0 0 0 0 libc-2.23.so
127f1fa1336000 ---p 001c0000 fc:00 521726 2044 0 0 0 0 0 0 0 0 0 libc-2.23.so
137f1fa1535000 r--p 001bf000 fc:00 521726 16 16 16 16 16 0 0 0 0 0 libc-2.23.so
147f1fa1539000 rw-p 001c3000 fc:00 521726 8 8 8 8 8 0 0 0 0 0 libc-2.23.so
157f1fa153b000 rw-p 00000000 00:00 0 16 12 12 12 12 0 0 0 0 0
16......
177f1fa196c000 r-xp 00000000 fc:00 521702 152 144 4 144 0 0 0 0 0 0 ld-2.23.so
187f1fa1b7e000 r--s 00000000 fc:00 132738 28 28 9 28 0 0 0 0 0 0 gconv-modules.cache
197f1fa1b85000 rw-p 00000000 00:00 0 16 16 16 16 16 0 0 0 0 0
207f1fa1b8f000 rw-p 00000000 00:00 0 8 8 8 8 8 0 0 0 0 0
217f1fa1b91000 r--p 00025000 fc:00 521702 4 4 4 4 4 0 0 0 0 0 ld-2.23.so
227f1fa1b92000 rw-p 00026000 fc:00 521702 4 4 4 4 4 0 0 0 0 0 ld-2.23.so
237f1fa1b93000 rw-p 00000000 00:00 0 4 4 4 4 4 0 0 0 0 0
247ffde903a000 rw-p 00000000 00:00 0 136 24 24 24 24 0 0 0 0 0 [stack]
257ffde90e4000 r--p 00000000 00:00 0 8 0 0 0 0 0 0 0 0 0 [vvar]
267ffde90e6000 r-xp 00000000 00:00 0 8 4 0 4 0 0 0 0 0 0 [vdso]
27ffffffffff600000 r-xp 00000000 00:00 0 4 0 0 0 0 0 0 0 0 0 [vsyscall]
28===== ==== ==== ========== ========= ============== =============== ==== ======= ======
2922468 5084 2578 5084 1764 0 0 0 0 0 KB
30
xxxxxxxxxx
101
2[root@666 ~]# top -p$$
3Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
4%Cpu0 : 1.0 us, 1.0 sy, 0.0 ni, 98.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
5KiB Mem : 63.5/2036924 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ]
6KiB Swap: 0.0/2036924 [ ]
7
8PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
928136 root 20 0 116656 4760 3216 S 0.0 0.2 0:00.01 /bin/bash -i
10
https://tldp.org/LDP/tlk/mm/memory.html
在linux下,使用top,vmstat,free等命令查看系统或者进程的内存使用情况时,经常看到buff/cache memeory,swap,avail Mem等,他们都代表什么意思呢?这篇文章将来聊一聊Linux下的内存管理并解答这个问题。
讨论Linux下的内存管理其实就是讨论Linux下虚拟内存的实现方式,本人不是内核专家,所以这篇文章只会介绍一些概念性的东西,不会深入实现细节,有些地方描述的有可能不精确。
在早些时候,物理内存比较有限,人们希望程序可以使用的内存空间能超过实际物理内存,于是出现了虚拟内存的概念,不过随着时间的推移,虚拟内存的意义已经远远的超过了最初的想法。
虚拟内存是Linux管理内存的一种技术。它使得每个应用程序都认为自己拥有独立且连续的可用的内存空间(一段连续完整的地址空间),而实际上,它通常是被映射到多个物理内存段,还有部分暂时存储在外部磁盘存储器上,在需要时再加载到内存中来。
每个进程所能使用的虚拟地址大小和CPU位数有关,在32位的系统上,虚拟地址空间大小是4G,在64位系统上,是2^64=?(算不过来了)。而实际的物理内存可能远远小于虚拟地址空间的大小。
虚拟地址和进程息息相关,不同进程里的同一个虚拟地址指向的物理地址不一定一样,所以离开进程谈虚拟地址没有任何意义。
注意:网上很多文章将虚拟内存等同于交换空间,其实描述不够严谨,交换空间只是虚拟内存这张大蓝图中的一部分。
下面这张表很直观的表述了它们之间的关系
xxxxxxxxxx
221 进程X 进程Y
2+-------+ +-------+
3| VPFN7 |--+ | VPFN7 |
4+-------+ | 进程X的 进程Y的 +-------+
5| VPFN6 | | Page Table Page Table +-| VPFN6 |
6+-------+ | +------+ +------+ | +-------+
7| VPFN5 | +----->| .... |---+ +-------| .... |<---+ | | VPFN5 |
8+-------+ +------+ | +------+ | +------+ | | +-------+
9| VPFN4 | +--->| .... |---+-+ | PFN4 | | | .... | | | | VPFN4 |
10+-------+ | +------+ | | +------+ | +------+ | | +-------+
11| VPFN3 |--+ | | .... | | | +--->| PFN3 |<---+ +----| .... |<---+--+ | VPFN3 |
12+-------+ | | +------+ | | | +------+ | +------+ | +-------+
13| VPFN2 | +-+--->| .... |---+-+-+ | PFN2 |<------+ | .... | | | VPFN2 |
14+-------+ | +------+ | | +------+ +------+ | +-------+
15| VPFN1 | | | +----->| FPN1 | +----| VPFN1 |
16+-------+ | | +------+ +-------+
17| VPFN0 |----+ +------->| PFN0 | | VPFN0 |
18+-------+ +------+ +-------+
19 虚拟内存 物理内存 虚拟内存
20
21
22PFN(the page frame number): 页编号
当进程执行一个程序时,需要先从先内存中读取该进程的指令,然后执行,获取指令时用到的就是虚拟地址,这个地址是程序链接时确定的(内核加载并初始化进程时会调整动态库的地址范围),为了获取到实际的数据,CPU需要将虚拟地址转换成物理地址,CPU转换地址时需要用到进程的page table,而page table里面的数据由操作系统维护。
注意:Linux内核代码访问内存时用的都是实际的物理地址,所以不存在虚拟地址到物理地址的转换,只有应用层程序才需要。
为了转换方便,Linux将虚拟内存和物理内存都拆分为固定大小的页,x86的系统一般内存页大小是4K,每个页都会分配一个唯一的编号,这就是页编号(PFN).
从上面的图中可以看出,虚拟内存和物理内存的page之间通过page table进行映射。进程X和Y的虚拟内存是相互独立的,且page table也是独立的,它们之间共享物理内存。进程可以随便访问自己的虚拟地址空间,而page table和物理内存由内核维护。当进程需要访问内存时,CPU会根据进程的page table将虚拟地址翻译成物理地址,然后进行访问。
注意:并不是每个虚拟地址空间的page都有对应的Page Table相关联,只有虚拟地址被分配给进程后,也即进程调用类似malloc函数之后,系统才会为相应的虚拟地址在Page Table中添加记录,如果进程访问一个没有和Page Table关联的虚拟地址,系统将会抛出SIGSEGV信号,导致进程退出,这也是为什么我们访问野指针时会经常出现segmentfault的原因。换句话说,虽然每个进程都有4G(32位系统)的虚拟地址空间,但只有向系统申请了的那些地址空间才能用,访问未分配的地址空间将会出segmentfault错误。Linux会将虚拟地址0不映射到任何地方,这样我们访问空指针就一定会报segmentfault错误。
page table可以简单的理解为一个memory mapping的链表(当然实际结构很复杂),里面的每个memory mapping都将一块虚拟地址映射到一个特定的资源(物理内存或者外部存储空间)。每个进程拥有自己的page table,和其它进程的page table没有关系。
每个memory mapping就是对一段虚拟内存的描述,包括虚拟地址的起始位置,长度,权限(比如这段内存里的数据是否可读、写、执行), 以及关联的资源(如物理内存page,swap空间上的page,磁盘上的文件内容等)。
当进程申请内存时,系统将返回虚拟内存地址,同时为相应的虚拟内存创建memory mapping并将它放入page table,但这时系统不一定会分配相应的物理内存,系统一般会在进程真正访问这段内存的时候才会分配物理内存并关联到相应的memory mapping,这就是所谓的延时分配/按需分配。
每个memory mapping都有一个标记,用来表示所关联的物理资源类型,一般分两大类,那就是anonymous和file backed,在这两大类中,又分了一些小类,比如anonymous下面有更具体的shared和copy on write类型, file backed下面有更具体的device backed类型。下面是每个类型所代表的意思:
这种类型表示memory mapping对应的物理资源存放在磁盘上的文件中,它所包含的信息包括文件的位置、offset、rwx权限等。
当进程第一次访问对应的虚拟page的时候,由于在memory mapping中找不到对应的物理内存,CPU会报page fault中断,然后操作系统就会处理这个中断并将文件的内容加载到物理内存中,然后更新memory mapping,这样下次CPU就能访问这块虚拟地址了。以这种方式加载到内存的数据一般都会放到page cache中,关于page cache会在后面介绍到.
一般程序的可执行文件,动态库都是以这种方式映射到进程的虚拟地址空间的。
和file backed类似,只是后端映射到了磁盘的物理地址,比如当物理内存被swap out后,将被标记为device backed。
程序自己用到的数据段和堆栈空间,以及通过mmap分配的共享内存,它们在磁盘上找不到对应的文件,所以这部分内存页被叫做anonymous page。anonymous page和file backed最大的差别是当内存吃紧时,系统会直接删除掉file backed对应的物理内存,因为下次需要的时候还能从磁盘加载到内存,但anonymous page不能被删除,只能被swap out。
不同进程的Page Table里面的多个memory mapping可以映射到相同的物理地址,通过虚拟地址(不同进程里的虚拟地址可能不一样)可以访问到相同的内容,当一个进程里面修改内存的内容后,在另一个进程中可以立即读取到。这种方式一般用来实现进程间高速的共享数据(如mmap)。当标记为shared的memory mapping被删除回收时,需要更新物理page上的引用计数,便于物理page的计数变0后被回收。
copy on write基于shared技术,当读这种类型的内存时,系统不需要做任何特殊的操作,而当要写这块内存时,系统将会生成一块新的内存并拷贝原来内存中的数据到新内存中,然后将新内存关联到相应的memory mapping,然后执行写操作。Linux下很多功能都依赖于copy on write技术来提高性能,比如fork等。
通过上面的介绍,我们可以简单的将内存的使用过程总结如下:
当然缺页中断不是每次都会发生,只有系统觉得有必要延迟分配内存的时候才用的着,也即很多时候在上面的第3步系统会分配真正的物理内存并和memory mapping关联。
操作系统只要实现了虚拟内存和物理内存之间的映射关系,就能正常工作了,但要使内存访问更高效,还有很多东西需要考虑,在这里我们可以看看跟内存有关的一些其它概念以及它们的作用。
MMU是CPU的一个用来将进程的虚拟地址转换成物理地址的模块,简单点说,这个模块的输入是进程的page table和虚拟地址,输出是物理地址。将虚拟地址转换成物理地址的速度直接影响着系统的速度,所以CPU包含了这个模块用来加速。
上面介绍到,MMU的输入是page table,而page table又存在内存里面,跟CPU的cache相比,内存的速度很慢,所以为了进一步加快虚拟地址到物理地址的转换速度,Linux发明了TLB,它存在于CPU的L1 cache里面,用来缓存已经找到的虚拟地址到物理地址的映射,这样下次转换前先查一下TLB,如果已经在里面了就不需要调用MMU了.
由于实际情况下物理内存要比虚拟内存少很多,所以操作系统必须很小心的分配物理内存,以使内存的使用率达到最大化。一个节约物理内存的办法就是只加载当前正在使用的虚拟page对应的数据到内存。比如,一个很大的数据库程序,如果你只是用了查询操作,那么负责插入删除等部分的代码段就没必要加载到内存中,这样就能节约很多物理内存,这种方法就叫做物理内存页按需分配,也可以称作延时加载。
其实现原理很简单,就是当CPU访问一个虚拟内存页的时候,如果这个虚拟内存页对应的数据还没加载到物理内存中,则CPU就会通知操作系统发生了page fault,然后由操作系统负责将数据加载进物理内存。由于将数据加载进内存比较耗时,所以CPU不会等在那里,而是去调度其它进程,当它下次再调度到该进程时,数据已经在物理内存上了。
Linux主要使用这种方式来加载可执行文件和动态库,当程序被内核开始调度执行时,内核将进程的可执行文件和动态库映射到进程的虚拟地址空间,并只加载马上要用到的那小部分数据到物理内存中,其它的部分只有当CPU访问到它们时才去加载。
当一个进程需要加载数据到物理内存中,但实际的物理内存已经被用完时,操作系统需要回收一些物理内存中的page以满足当前进程的需要。
对于file backed的内存数据,即物理内存里面的数据来自于磁盘上的文件,那么内核将直接将该部分数据从内存中移除掉来释放出更多的内存,当下次有进程需要访问这部分数据时,再将它从磁盘上加载到内存中来。但是,如果这部分数据被修改过且没被写入文件,那这部分数据就变成了脏数据,脏数据不能被直接删掉,只能被移动到交换空间上去。(可执行文件和动态库文件不会被修改,但通过mmap+private的方式映射到内存的磁盘文件有可能被修改,这种方式映射的内存比较特殊,没修改之前是file backed,修改后但没有写回磁盘之前就变成了anonymous的)
对于anonymous的内存数据,在磁盘上没有对应的文件,这部分数据不能直接被删除,而是被系统移到交换空间上去。交换空间就是磁盘上预留的一块特殊空间,被系统用来临时存放内存中不常被访问的数据,当下次有进程需要访问交换空间上的数据时,系统再将数据加载到内存中。由于交换空间在磁盘上,所以访问速度要比内存慢很多,频繁的读写交换空间会带来性能问题。
关于swap空间的详细介绍请参考Linux交换空间
有了虚拟内存之后,进程间共享内存变得特别的方便。进程所有的内存访问都通过虚拟地址来实现,而每个进程都有自己的page tables。当两个进程共享一块物理内存时,只要将物理内存的页号映射到两个进程的page table中就可以了,这样两个进程就可以通过不同的虚拟地址来访问同一块物理内存。
从上面的那个图中可以看出,进程X和进程Y共享了物理内存页PFN3,在进程X中,PFN3被映射到了VPFN3,而在进程Y中,PFN3被映射到了VPFN1,但两个进程通过不同的虚拟地址访问到的物理内存是同一块。
page table里面的每条虚拟内存到物理内存的映射记录(memory mapping)都包含一份控制信息,当进程要访问一块虚拟内存时,系统可以根据这份控制信息来检查当前的操作是否是合法的。
为什么需要做这个检查呢?比如有些内存里面放的是程序的可执行代码,那么就不应该去修改它;有些内存里面存放的是程序运行时用到的数据,那么这部分内存只能被读写,不应该被执行;有些内存里面存放的是内核的代码,那么在用户态就不应该去执行它;有了这些检查之后会大大增强系统的安全性。
由于CPU的cache有限,所以TLB里面缓存的数据也有限,而采用了huge page后,由于每页的内存变大(比如由原来的4K变成了4M),虽然TLB里面的纪录数没变,但这些纪录所能覆盖的地址空间变大,相当于同样大小的TLB里面能缓存的映射范围变大,从而减少了调用MMU的次数,加快了虚拟地址到物理地址的转换速度。
为了提高系统性能,Linux使用了一些跟内存管理相关的cache,并且尽量将空闲的内存用于这些cache。这些cache都是系统全局共享的:
从上面的定义可以看出,page cache和buffer cache有重叠的地方,不过实际情况是buffer cache只缓存page cache不缓存的那部分内容,比如磁盘上文件的元数据。所以一般情况下和page cache相比,Buffer Cache的大小基本可以忽略不计。
当然,使用cache也有一些不好的地方,比如需要时间和空间去维护cache,cache一旦出错,整个系统就挂了。
有了上面介绍的知识,再来看看我们刚开始提出来的问题,以top命令的输出为例:
xxxxxxxxxx
21KiB Mem : 500192 total, 349264 free, 36328 used, 114600 buff/cache
2KiB Swap: 524284 total, 524284 free, 0 used. 433732 avail Mem
KiB Mem代表物理内存,KiB Swap代表交换空间,它们的单位都是KiB。
total、used和free没什么好介绍的,就是总共多少,然后用了多少,还剩多少。
buff/cached代表了buff和cache总共用了多少,buff代表buffer cache占了多少空间,由于它主要用来缓存磁盘上文件的元数据,所以一般都比较小,跟cache比可以忽略不计;cache代表page cache和其它一些占用空间比较小且大小比较固定的cache的总和,基本上cache就约等于page cache,page cache的准确值可以通过查看/proc/meminf中的Cached得到。由于page cache是用来缓存磁盘上文件内容的,所以占有空间很大,Linux一般会尽可能多的将空闲物理内存用于page cache。
avail Mem表示可用于进程下一次分配的物理内存数量,这个大小一般比free大一点,因为除了free的空间外,系统还能立即释放出一些空间来。
那么怎么判断当前内存使用情况出现了异常呢?有下面几点供参考:
查看全部内存信息 cat /proc/meminfo
查看进程地址空间信息
cat /proc/进程ip/maps
proc查看进程状态
cat /proc/进程ip/status
使用pmap -d选项
是一个进程获取信息时,获取指向自身进程id的指针,/proc/pid/,而无须查询。
xxxxxxxxxx
561[root@666 ~]# cat /proc/14224/status
2Name: nginxfrontend
3Umask: 0000
4State: S (sleeping)
5Tgid: 14224
6Ngid: 0
7Pid: 14224
8PPid: 1
9TracerPid: 0
10Uid: 0 0 0 0
11Gid: 0 0 0 0
12FDSize: 64
13Groups:
14NStgid: 14224
15NSpid: 14224
16NSpgid: 14224
17NSsid: 14224
18VmPeak: 379908 kB
19VmSize: 246984 kB
20VmLck: 0 kB
21VmPin: 0 kB
22VmHWM: 16672 kB
23VmRSS: 15544 kB
24RssAnon: 8556 kB
25RssFile: 6976 kB
26RssShmem: 12 kB
27VmData: 9556 kB
28VmStk: 132 kB
29VmExe: 4500 kB
30VmLib: 13608 kB
31VmPTE: 264 kB
32VmSwap: 0 kB
33HugetlbPages: 0 kB
34CoreDumping: 0
35THP_enabled: 1
36Threads: 1
37SigQ: 1/7859
38SigPnd: 0000000000000000
39ShdPnd: 0000000000000000
40SigBlk: 0000000000000000
41SigIgn: 0000000040001000
42SigCgt: 000000019c016a07
43CapInh: 0000000000000000
44CapPrm: 000000ffffffffff
45CapEff: 000000ffffffffff
46CapBnd: 000000ffffffffff
47CapAmb: 0000000000000000
48NoNewPrivs: 0
49Seccomp: 0
50Speculation_Store_Bypass: thread vulnerable
51Cpus_allowed: 1
52Cpus_allowed_list: 0
53Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
54Mems_allowed_list: 0
55voluntary_ctxt_switches: 8
56nonvoluntary_ctxt_switches: 3
[root@localhost 1]# cat /proc/4668/status Name: gam_server State: S (sleeping) SleepAVG: 88% Tgid: 31999 Pid: 31999 PPid: 1 TracerPid: 0 Uid: 0 0 0 0 Gid: 0 0 0 0 FDSize: 256 Groups: 0 1 2 3 4 6 10 VmSize: 2136 kB VmLck: 0 kB VmRSS: 920 kB VmData: 148 kB VmStk: 88 kB VmExe: 44 kB VmLib: 1820 kB VmPTE: 20 kB Threads: 1 SigQ: 1/2047 SigPnd: 0000000000000000 ShdPnd: 0000000000000000 SigBlk: 0000000000000000 SigIgn: 0000000000001006 SigCgt: 0000000210000800 CapInh: 0000000000000000 CapPrm: 00000000fffffeff CapEff: 00000000fffffeff
** cat /proc/4743/statm **611450 185001 883 18 0 593431 0
- size :- total program size (611450 X 4096/1024 = 2445800kB = 2388M)
- resident :- resident set size (185001 X 4096/1024 = 740004kB = 722M)
- share :- shared pages (883 X 4096 = 3532)
- trs :- text (code) (18 X 4096/1024 = 72kB = VmExe )
- drs :- data/stack
- lrs :- library (593431 X 4096/1024 = 2373724kB = VmData +VmStk)
- dt :- dirty pages
*补充 by wylhistory:*
*对于信号那几个段:*
*SigPnd: 0000000000000000 ShdPnd: 0000000000000000 SigBlk: 0000000000000000 SigIgn: 0000000000001006 SigCgt: 0000000210000800*
*其中前32位分别表示普通的信号,比如:*
*SigBlk: 0000000000002000 这里表示SIGALRM已经被阻塞了,*
*因为SIGALRM的值就是14;*
*而里面的Siglgn:1006就表示:*
*类型为2,3,13的信号被忽略了,*
*分别是:SIGINT,SIGQUIT,SIGPIPE,*
*记得从1开始而不是0,因为信号没有类型为0的信号;*
*后32位的一般不用管为实时信号;*
#分析实例
检查mysqld进程情况
ipcns
,mntns
,netns
,pidns
,userns
,和utsns
每种namespace均有对应的唯一的nid,相互之间不同。
xxxxxxxxxx
101[root@666 ~]# yum provides lsns
2Loaded plugins: fastestmirror, getpagespeed
3Loading mirror speeds from cached hostfile
4util-linux-2.23.2-65.el7.i686 : A collection of basic system utilities
5Repo : base
6Matched from:
7Filename : /usr/bin/lsns
8
9
10
lsns -- this NEW COMMAND lists information about all currently accessible namespaces or about the given namespace.
引 https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v2.28/v2.28-ReleaseNotes
xxxxxxxxxx
231[root@666 ~]# lsns
2NS TYPE NPROCS PID USER COMMAND
34026531836 pid 152 1 root /usr/lib/systemd/systemd --system --deserialize 17
44026531837 user 153 1 root /usr/lib/systemd/systemd --system --deserialize 17
54026531838 uts 152 1 root /usr/lib/systemd/systemd --system --deserialize 17
64026531839 ipc 152 1 root /usr/lib/systemd/systemd --system --deserialize 17
74026531840 mnt 96 1 root /usr/lib/systemd/systemd --system --deserialize 17
84026531860 mnt 1 14 root kdevtmpfs
94026531992 net 152 1 root /usr/lib/systemd/systemd --system --deserialize 17
104026532211 mnt 1 554 chrony /usr/sbin/chronyd
114026532212 mnt 2 577 root /usr/sbin/NetworkManager --no-daemon
124026532216 mnt 52 20093 root php-fpm: master process (/etc/php-fpm.conf)
134026532224 mnt 1 31192 root /usr/sbin/sshd -D -e
144026532225 uts 1 31192 root /usr/sbin/sshd -D -e
154026532226 ipc 1 31192 root /usr/sbin/sshd -D -e
164026532227 pid 1 31192 root /usr/sbin/sshd -D -e
174026532229 net 1 31192 root /usr/sbin/sshd -D -e
18[root@666 ~]#
19
20# NS namespace ID
21# TYPE Namespace Type
22# NPROCS Namespace Process ID
23#
xxxxxxxxxx
321# 查看进程的命名空间id
2
3[root@666 ~]# ls -alh /proc/self/ns
4total 0
5dr-x--x--x 2 root root 0 Sep 17 09:19 .
6dr-xr-xr-x 9 root root 0 Sep 17 09:19 ..
7lrwxrwxrwx 1 root root 0 Sep 17 09:19 cgroup -> cgroup:[4026531835]
8lrwxrwxrwx 1 root root 0 Sep 17 09:19 ipc -> ipc:[4026531839]
9lrwxrwxrwx 1 root root 0 Sep 17 09:19 mnt -> mnt:[4026531840]
10lrwxrwxrwx 1 root root 0 Sep 17 09:19 net -> net:[4026531992]
11lrwxrwxrwx 1 root root 0 Sep 17 09:19 pid -> pid:[4026531836]
12lrwxrwxrwx 1 root root 0 Sep 17 09:19 pid_for_children -> pid:[4026531836]
13lrwxrwxrwx 1 root root 0 Sep 17 09:19 time -> time:[4026531834]
14lrwxrwxrwx 1 root root 0 Sep 17 09:19 time_for_children -> time:[4026531834]
15lrwxrwxrwx 1 root root 0 Sep 17 09:19 user -> user:[4026531837]
16lrwxrwxrwx 1 root root 0 Sep 17 09:19 uts -> uts:[4026531838]
17[root@666 ~]# ls -alh /proc/$$/ns
18total 0
19dr-x--x--x 2 root root 0 Sep 17 09:11 .
20dr-xr-xr-x 9 root root 0 Sep 17 09:11 ..
21lrwxrwxrwx 1 root root 0 Sep 17 09:20 cgroup -> cgroup:[4026531835]
22lrwxrwxrwx 1 root root 0 Sep 17 09:20 ipc -> ipc:[4026531839]
23lrwxrwxrwx 1 root root 0 Sep 17 09:20 mnt -> mnt:[4026531840]
24lrwxrwxrwx 1 root root 0 Sep 17 09:11 net -> net:[4026531992]
25lrwxrwxrwx 1 root root 0 Sep 17 09:20 pid -> pid:[4026531836]
26lrwxrwxrwx 1 root root 0 Sep 17 09:20 pid_for_children -> pid:[4026531836]
27lrwxrwxrwx 1 root root 0 Sep 17 09:20 time -> time:[4026531834]
28lrwxrwxrwx 1 root root 0 Sep 17 09:20 time_for_children -> time:[4026531834]
29lrwxrwxrwx 1 root root 0 Sep 17 09:20 user -> user:[4026531837]
30lrwxrwxrwx 1 root root 0 Sep 17 09:20 uts -> uts:[4026531838]
31[root@666 ~]#
32
ps
现在具有用于与过程相关联的不同类型的命名空间的输出选择:ipcns
,mntns
,netns
,pidns
,userns
,和utsns
。对于这个问题,相关的是PID名称空间或pidns
。
因此,如果您想找出PID命名空间ID,例如pid 459:
xxxxxxxxxx
21# ps -h -o pidns -p 459
24026532661
xxxxxxxxxx
111
21 Executable programs or shell commands
32 System calls (functions provided by the kernel)
43 Library calls (functions within program libraries)
54 Special files (usually found in /dev)
65 File formats and conventions eg /etc/passwd
76 Games
87 Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7)
98 System administration commands (usually only for root)
109 Kernel routines [Non standard]
11
unshare - disassociate parts of the process execution context
unshare() allows a process (or thread) to disassociate parts of its execution context that are currently being shared with other processes (or threads). Part of the execution context, such as the mount namespace, is shared implicitly when a new process is created using fork(2) or vfork(2), while other parts, such as virtual memory, may be shared by explicit request when creating a process or thread using clone(2).
unshare()函数方法用于对已分配与其它进程共享的执行上下文进行解除关联。上下文包括,通过fork,vfork创建的mount 命名空间,及通过明确要求的虚拟内存。
https://man7.org/linux/man-pages/man2/unshare.2.html
fork - create a child process
派生--创建一个子进程。
fork() creates a new process by duplicating the calling process. The new process is referred to as the child process. The calling process is referred to as the parent process.
通过复制调用进程来创建一个新的进程。
新的进程一般称作子进程。
调用进程一般称作父进程。