問題文
ある日、「Free」ではメモリがほとんど使い果たされているのに、top/ps コマンドではユーザー モード アプリケーションがあまりにも多くのメモリ領域を占有していることがわからない場合は、カーネル モジュールにメモリ リークが発生している可能性があります。
質問が確認されました
- すべてのアプリケーションが占有するメモリ U をカウントします(単位は K):
$ grep Pss /proc/[1-9]*/smaps | awk '{合計+=$2}; END {合計を印刷}'
1721106
- 合計システム メモリ T 、空きメモリ F 、共有メモリS 、キャッシュ C を表示します。
$ フリー -h
使用可能な無料共有バフ/キャッシュの合計
メモリ: 125G 95G 4.2G 4.0G 26G 25G
スワップ: 9.4G 444M 8.9G
$ cat /proc/meminfo
メモリ合計: 131748024 kB
メムフリー: 4229544 kB
利用可能なメモリ: 26634796 kB
バッファ: 141416 kB
キャッシュ済み: 24657800 kB
スワップキャッシュ済み: 198316 kB
アクティブ: 7972388 kB
非アクティブ: 19558436 kB
アクティブ(アノン): 4249920 kB
非アクティブ(アノン): 2666784 kB
アクティブ(ファイル): 3722468 kB
非アクティブ(ファイル): 16891652 kB
避けられない: 0 KB
ロックされた: 0 kB
スワップ合計: 9830396 kB
スワップフリー: 9375476 kB
ダーティ: 80 KB
ライトバック: 0 KB
アノンページ: 2601440 kB
マッピング済み: 71828 kB
シュメム: 4185096 kB
スラブ: 2607824 kB
S再利用可能: 2129004 kB
S未回収: 478820 kB
カーネルスタック: 29616 kB
ページテーブル: 45636 KB
NFS_不安定: 0 kB
バウンス: 0 KB
ライトバック温度: 0 KB
コミットリミット: 75704408 kB
コミット済み_AS: 14023220 kB
Vmalloc合計: 34359738367 kB
Vmalloc使用済み: 529824 kB
Vmallocチャンク: 34292084736 kB
ハードウェアが破損しています: 0 KB
AnonHugePages: 260096 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
巨大なページサイズ: 2048 kB
DirectMap4k: 125688 kB
DirectMap2M: 3973120 kB
DirectMap1G: 132120576 kB
- 通常の状況では、次の式になるはずです ( K はカーネルによって占有されるメモリであり、スワップ メモリはここでは無視されます)。
T = U + K + S + C + F
これから、カーネルが使用するメモリ Kを計算できます。
原理分析
経験上、一般にメモリ リークやメモリ不足が発生するコードは、メモリの解放を頻繁に要求する部分であるはずです。
カーネル内で頻繁に解放が要求される可能性のあるメモリには、次のようなものがあります。
- カーネルは、task_struct 、inodeなどのデータ構造を管理します。これらのコードは通常、多くのテストを受けており、問題が発生する可能性はほとんどありません。
- カーネルIOサブシステムまたはドライバー (ブロック デバイス用のBIO 、ネットワーク プロトコル スタック用のSKB 、ストレージ ネットワーク デバイス ドライバーなど)。
ここで問題が発生する可能性が最も高いのは、ストレージまたはネットワーク デバイスのドライバです。
現場での分析
Linux カーネルは階層的なメモリ管理方法を使用しており、各層は異なる問題を解決します。主要な部分は下から上に次のとおりです。
- 物理メモリ管理は主にメモリのレイアウトと属性を記述するために使用され、主にNode 、Zone 、Pageの 3 つの構造を含み、メモリをPage単位で管理できます。
- バディメモリ管理は、主に外部断片化の問題を解決するために、get_free_pagesなどの関数を使用してPageのN乗単位で解放を申請します。
- スラブメモリ管理は、主に内部断片化の問題を解決するために、ユーザーが指定したサイズに従ってメモリをバッチで適用できます (最初にオブジェクト キャッシュ プールを作成する必要があります)。
- カーネル キャッシュ オブジェクトの場合は、Slab を使用していくつかの固定サイズのキャッシュを事前に割り当て、kmallocやvmallocなどの関数を使用してバイト単位でメモリを解放します。
次に、まずメモリがリークしているレベルを確認する必要があります (追記: ラージ ページ メモリ、ページ キャッシュ、ブロック キャッシュなど、関連するメモリ管理テクノロジが多数あり、それらはすべてこれらのレベルのメモリに適用されます) . は重要ではないため、ここではすべて無視されます。)
- バディのメモリ使用量を表示します。
$ cat /proc/buddyinfo
ノード 0、ゾーン DMA 0 1 1 0 2 1 1 0 0 1 3
ノード 0、ゾーン DMA32 3222 6030 3094 3627 379 0 0 0 0 0 0
ノード 0、ゾーン 通常 13628 0 0 0 0 0 0 0 0 0 0
ノード 1、ゾーン 通常 73167 165265 104556 17921 2120 144 1 0 0 0 0
$ cat /proc/buddyinfo | awk '{sum=0;for(i=5;i<=NF;i++) sum+=$i*(2^(i-5))};{total+=sum/256};{print $1 " " $2 " " $3 " " $4 "\t : " sum/256 "M"} END {print "total\t\t\t : " total "M"}'
ノード 0、ゾーン DMA : 14.5234M
ノード 0、ゾーン DMA32 : 245.07M
ノード 0、ゾーン 通常 : 53.2344M
ノード 1、ゾーン 通常 : 3921.41M
合計 : 4234.24M
ここから、Buddy が合計でどれだけのメモリを割り当てているかがわかります。
- スラブのメモリ使用量を表示します。
$ スラブトップ -o
アクティブ / オブジェクト合計 (使用率) : 3522231 / 6345435 (55.5%)
アクティブ / 合計スラブ (使用率) : 148128 / 148128 (100.0%)
アクティブ/合計キャッシュ (使用率) : 74 / 107 (69.2%)
アクティブ / 合計サイズ (% 使用) : 1297934.98K / 2593929.78K (50.0%)
最小/平均/最大オブジェクト: 0.01K / 0.41K / 15.88K
OBJS アクティブ使用 OBJ サイズ スラブ OBJ/スラブ キャッシュ サイズ名
1449510 666502 45% 1.06K 48317 30 1546144K xfs_inode
1229592 967866 78% 0.10K 31528 39 126112K バッファーヘッド
1018560 375285 36% 0.06K 15915 64 63660K kmalloc-64
643216 322167 50% 0.57K 11486 56 367552K radix_tree_node
350826 147688 42% 0.38K 8353 42 133648K blkdev_requests
310421 131953 42% 0.15K 5857 53 46856K xfs_ili
273420 95765 35% 0.19K 6510 42 52080K デントリ
174592 36069 20% 0.25K 2728 64 43648K kmaloc-256
155680 155680 100% 0.07K 2780 56 11120K Acpi-ParseExt
88704 34318 38% 0.50K 1386 64 44352K kmalloc-512
85176 52022 61% 0.19K 2028 42 16224K kmalloc-192
59580 59580 100% 0.11K 1655 36 6620K sysfs_dir_cache
43031 42594 98% 0.21K 1163 37 9304K vm_area_struct
35392 30850 87% 0.12K 553 64 4424K kmalloc-128
35070 20418 58% 0.09K 835 42 3340K kmaloc-96
34304 34304 100% 0.03K 268 128 1072K kmalloc-32
$ cat /proc/slabinfo
スラブ情報 - バージョン: 2.1
# name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : 調整パラメータ <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
kvm_async_pf 0 0 136 60 2 : 調整可能パラメータ 0 0 0 : スラブデータ 0 0 0
kvm_vcpu 0 0 16256 2 8 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
kvm_mmu_page_header 0 0 168 48 2 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
xfs_dqtrx 0 0 528 62 8 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
xfs_dquot 0 0 472 69 8 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
xfs_icr 0 0 144 56 2 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
xfs_ili 131960 310421 152 53 2 : 調整パラメータ 0 0 0 : スラブデータ 5857 5857 0
xfs_inode 666461 1449510 1088 30 8 : 調整パラメータ 0 0 0 : スラブデータ 48317 48317 0
xfs_efd_item 8120 8280 400 40 4 : 調整パラメータ 0 0 0 : スラブデータ 207 207 0
xfs_da_state 2176 2176 480 68 8 : 調整パラメータ 0 0 0 : スラブデータ 32 32 0
xfs_btree_cur 1248 1248 208 39 2 : 調整パラメータ 0 0 0 : スラブデータ 32 32 0
xfs_log_ticket 12981 13200 184 44 2 : 調整パラメータ 0 0 0 : スラブデータ 300 300 0
nfsd4_openowners 0 0 440 37 4 : 調整可能パラメータ 0 0 0 : スラブデータ 0 0 0
rpc_inode_cache 51 51 640 51 8 : 調整パラメータ 0 0 0 : スラブデータ 1 1 0
ext4_groupinfo_4k 4440 4440 136 60 2 : 調整パラメータ 0 0 0 : スラブデータ 74 74 0
ext4_inode_cache 4074 5921 1048 31 8 : 調整パラメータ 0 0 0 : スラブデータ 191 191 0
ext4_xattr 276 276 88 46 1 : 調整パラメータ 0 0 0 : スラブデータ 6 6 0
ext4_free_data 3264 3264 64 64 1 : 調整パラメータ 0 0 0 : スラブデータ 51 51 0
ext4_allocation_context 2048 2048 128 64 2 : 調整パラメータ 0 0 0 : スラブデータ 32 32 0
ext4_io_end 1785 1785 80 51 1 : 調整パラメータ 0 0 0 : スラブデータ 35 35 0
ext4_extent_status 20706 20706 40 102 1 : 調整パラメータ 0 0 0 : スラブデータ 203 203 0
jbd2_journal_handle 2720 2720 48 85 1 : チューナブル 0 0 0 : スラブデータ 32 32 0
jbd2_journal_head 4680 4680 112 36 1 : チューナブル 0 0 0 : スラブデータ 130 130 0
jbd2_revoke_table_s 256 256 16 256 1 : チューナブル 0 0 0 : スラブデータ 1 1 0
jbd2_revoke_record_s 4096 4096 32 128 1 : チューナブル 0 0 0 : スラブデータ 32 32 0
scsi_cmd_cache 7056 7272 448 36 4 : 調整パラメータ 0 0 0 : スラブデータ 202 202 0
UDPLITEv6 0 0 1152 28 8 : 調整可能パラメータ 0 0 0 : スラブデータ 0 0 0
UDPv6 728 728 1152 28 8 : 調整パラメータ 0 0 0 : スラブデータ 26 26 0
tw_sock_TCPv6 0 0 256 64 4 : 調整可能パラメータ 0 0 0 : スラブデータ 0 0 0
TCPv6 405 405 2112 15 8 : 調整パラメータ 0 0 0 : スラブデータ 27 27 0
uhci_urb_priv 0 0 56 73 1 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
cfq_queue 27790 27930 232 70 4 : チューナブル 0 0 0 : スラブデータ 399 399 0
bsg_cmd 0 0 312 52 4 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
mqueue_inode_cache 36 36 896 36 8 : 調整パラメータ 0 0 0 : スラブデータ 1 1 0
hugetlbfs_inode_cache 106 106 608 53 8 : 調整パラメータ 0 0 0 : スラブデータ 2 2 0
configfs_dir_cache 0 0 88 46 1 : 調整可能パラメータ 0 0 0 : スラブデータ 0 0 0
dquot 2048 2048 256 64 4 : 調整パラメータ 0 0 0 : スラブデータ 32 32 0
kioctx 0 0 576 56 8 : 調整可能パラメータ 0 0 0 : スラブデータ 0 0 0
userfaultfd_ctx_cache 0 0 128 64 2 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
pid_namespace 0 0 2176 15 8 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
user_namespace 0 0 280 58 4 : 調整可能パラメータ 0 0 0 : スラブデータ 0 0 0
posix_timers_cache 0 0 248 66 4 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
UDP-Lite 0 0 1024 32 8 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
RAW 1530 1530 960 34 8 : 調整パラメータ 0 0 0 : スラブデータ 45 45 0
UDP 1024 1024 1024 32 8 : 調整パラメータ 0 0 0 : スラブデータ 32 32 0
tw_sock_TCP 10944 11328 256 64 4 : 調整パラメータ 0 0 0 : スラブデータ 177 177 0
TCP 2886 3842 1920 17 8 : 調整パラメータ 0 0 0 : スラブデータ 226 226 0
blkdev_queue 118 225 2088 15 8 : チューナブル 0 0 0 : スラブデータ 15 15 0
blkdev_requests 147485 350826 384 42 4 : 調整パラメータ 0 0 0 : スラブデータ 8353 8353 0
blkdev_ioc 2262 2262 104 39 1 : 調整パラメータ 0 0 0 : スラブデータ 58 58 0
fsnotify_event_holder 5440 5440 24 170 1 : 調整パラメータ 0 0 0 : スラブデータ 32 32 0
fsnotify_event 15912 16252 120 68 2 : 調整パラメータ 0 0 0 : スラブデータ 239 239 0
sock_inode_cache 12478 13260 640 51 8 : 調整パラメータ 0 0 0 : スラブデータ 260 260 0
net_namespace 0 0 4608 7 8 : 調整可能パラメータ 0 0 0 : スラブデータ 0 0 0
shmem_inode_cache 3264 3264 680 48 8 : 調整パラメータ 0 0 0 : スラブデータ 68 68 0
Acpi-ParseExt 155680 155680 72 56 1 : 調整パラメータ 0 0 0 : スラブデータ 2780 2780 0
Acpi 名前空間 16422 16422 40 102 1 : 調整パラメータ 0 0 0 : スラブデータ 161 161 0
taskstats 1568 1568 328 49 4 : 調整パラメータ 0 0 0 : スラブデータ 32 32 0
proc_inode_cache 12352 12544 656 49 8 : 調整パラメータ 0 0 0 : スラブデータ 256 256 0
sigqueue 1632 1632 160 51 2 : 調整パラメータ 0 0 0 : スラブデータ 32 32 0
bdev_cache 858 858 832 39 8 : 調整パラメータ 0 0 0 : スラブデータ 22 22 0
sysfs_dir_cache 59580 59580 112 36 1 : 調整パラメータ 0 0 0 : スラブデータ 1655 1655 0
inode_cache 15002 17050 592 55 8 : 調整パラメータ 0 0 0 : スラブデータ 310 310 0
dentry 96235 273420 192 42 2 : チューナブル 0 0 0 : スラブデータ 6510 6510 0
iint_cache 0 0 80 51 1 : 調整可能 0 0 0 : スラブデータ 0 0 0
selinux_inode_security 22681 23205 80 51 1 : 調整パラメータ 0 0 0 : スラブデータ 455 455 0
バッファーヘッド 968560 1229592 104 39 1 : 調整可能値 0 0 0 : スラブデータ 31528 31528 0
vm_area_struct 43185 43216 216 37 2 : 調整パラメータ 0 0 0 : スラブデータ 1168 1168 0
mm_struct 860 860 1600 20 8 : 調整パラメータ 0 0 0 : スラブデータ 43 43 0
files_cache 1887 1887 640 51 8 : チューナブル 0 0 0 : スラブデータ 37 37 0
signal_cache 3595 3724 1152 28 8 : 調整パラメータ 0 0 0 : スラブデータ 133 133 0
sighand_cache 2373 2445 2112 15 8 : 調整パラメータ 0 0 0 : スラブデータ 163 163 0
task_xstate 4920 5226 832 39 8 : 調整パラメータ 0 0 0 : スラブデータ 134 134 0
task_struct 2303 2420 2944 11 8 : 調整パラメータ 0 0 0 : スラブデータ 220 220 0
anon_vma 27367 27392 64 64 1 : 調整パラメータ 0 0 0 : スラブデータ 428 428 0
共有ポリシーノード 5525 5525 48 85 1 : 調整可能パラメータ 0 0 0 : スラブデータ 65 65 0
uma_policy 248 248 264 62 4 : 調整パラメータ 0 0 0 : スラブデータ 4 4 0
radix_tree_node 321897 643216 584 56 8 : 調整パラメータ 0 0 0 : スラブデータ 11486 11486 0
idr_layer_cache 953 975 2112 15 8 : 調整パラメータ 0 0 0 : スラブデータ 65 65 0
dma-kmalloc-8192 0 0 8192 4 8 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
dma-kmalloc-4096 0 0 4096 8 8 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
dma-kmalloc-2048 0 0 2048 16 8 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
dma-kmalloc-1024 0 0 1024 32 8 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
dma-kmalloc-512 0 0 512 64 8 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
dma-kmalloc-256 0 0 256 64 4 : 調整可能パラメータ 0 0 0 : スラブデータ 0 0 0
dma-kmalloc-128 0 0 128 64 2 : 調整可能パラメータ 0 0 0 : スラブデータ 0 0 0
dma-kmalloc-64 0 0 64 64 1 : チューナブル 0 0 0 : スラブデータ 0 0 0
dma-kmalloc-32 0 0 32 128 1 : 調整可能パラメータ 0 0 0 : スラブデータ 0 0 0
dma-kmalloc-16 0 0 16 256 1 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
dma-kmalloc-8 0 0 8 512 1 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
dma-kmalloc-192 0 0 192 42 2 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
dma-kmalloc-96 0 0 96 42 1 : 調整パラメータ 0 0 0 : スラブデータ 0 0 0
kmalloc-8192 314 340 8192 4 8 : 調整パラメータ 0 0 0 : スラブデータ 85 85 0
kmalloc-4096 983 1024 4096 8 8 : 調整パラメータ 0 0 0 : スラブデータ 128 128 0
kmalloc-2048 4865 4928 2048 16 8 : 調整パラメータ 0 0 0 : スラブデータ 308 308 0
kmalloc-1024 10084 10464 1024 32 8 : 調整パラメータ 0 0 0 : スラブデータ 327 327 0
kmalloc-512 34318 88704 512 64 8 : 調整パラメータ 0 0 0 : スラブデータ 1386 1386 0
kmalloc-256 35482 174592 256 64 4 : 調整パラメータ 0 0 0 : スラブデータ 2728 2728 0
kmalloc-192 52022 85176 192 42 2 : 調整可能値 0 0 0 : スラブデータ 2028 2028 0
kmalloc-128 30732 35392 128 64 2 : 調整可能値 0 0 0 : スラブデータ 553 553 0
kmalloc-96 20418 35070 96 42 1 : 調整パラメータ 0 0 0 : スラブデータ 835 835 0
kmalloc-64 375761 1018560 64 64 1 : 調整パラメータ 0 0 0 : スラブデータ 15915 15915 0
kmalloc-32 34304 34304 32 128 1 : 調整パラメータ 0 0 0 : スラブデータ 268 268 0
kmalloc-16 18432 18432 16 256 1 : 調整パラメータ 0 0 0 : スラブデータ 72 72 0
kmalloc-8 25088 25088 8 512 1 : 調整パラメータ 0 0 0 : スラブデータ 49 49 0
kmem_cache_node 683 704 64 64 1 : 調整パラメータ 0 0 0 : スラブデータ 11 11 0
kmem_cache 576 576 256 64 4 : 調整パラメータ 0 0 0 : スラブデータ 9 9 0
上記のコマンドにより、どのスラブキャッシュが最も多くのメモリを占有しているかを判断できます。
オンサイトのデータ分析から、Buddy は100 G 以上のメモリを割り当て、Slab は数 G のメモリしか使用していないことが判明しました。これは、リークしたメモリがSlabとkmallocからリークしたのではなく、 Buddyからリークしたことを示しています。
Buddyによって割り当てられたメモリは、ページ単位でメモリを適用する必要があるSlab 、巨大ページ メモリ、ページ キャッシュ、ブロック キャッシュ、ドライバ、アプリケーション プログラムのページ フォールト マッピングまたはmmapなどによって使用される場合があります。これらの部品の中で、最も可能性の高い問題はやはりドライバーです。