SLAB内存泄露分析实践
背景:
測試在穩定性測試時發現設備內存耗盡。
分析:
階段1:判斷是用戶態泄露還是內核態泄露
使用cat /proc/meminfo查看內存泄露狀況
[root@vnf ~]# cat /proc/meminfo MemTotal: 1868688 kB MemFree: 1581588 kB MemAvailable: 1583504 kB Buffers: 948 kB Cached: 106224 kB SwapCached: 0 kB Active: 94420 kB Inactive: 80504 kB Active(anon): 68176 kB Inactive(anon): 8388 kB Active(file): 26244 kB Inactive(file): 72116 kB Unevictable: 0 kB Mlocked: 0 kB SwapTotal: 4194300 kB SwapFree: 4194300 kB Dirty: 0 kB Writeback: 0 kB AnonPages: 67792 kB Mapped: 17984 kB Shmem: 8812 kB Slab: 42604 kB SReclaimable: 18096 kB SUnreclaim: 24508 kB KernelStack: 8192 kB PageTables: 2620 kB NFS_Unstable: 0 kB Bounce: 0 kB WritebackTmp: 0 kB CommitLimit: 5128644 kB Committed_AS: 221652 kB VmallocTotal: 34359738367 kB VmallocUsed: 193376 kB VmallocChunk: 34359533052 kB HardwareCorrupted: 0 kB AnonHugePages: 6144 kB HugePages_Total: 0 HugePages_Free: 0 HugePages_Rsvd: 0 HugePages_Surp: 0 Hugepagesize: 2048 kB DirectMap4k: 61312 kB DirectMap2M: 2035712 kB DirectMap1G: 0 kB對比剛啟動和正常運行若干時間后的數據,發現是slab的持續增加導致free的值在不斷減少
?
?
這種slab的泄露基本都是內核態申請資源泄露的。
為啥要區分內核態和用戶態內存泄露呢,因為定位方法有些區別。內核態定位更加麻煩些。
?階段2:分析slab確定是slab的哪個地方在泄露
使用cat /proc/slabinfo查看slab使用的情況
cat /proc/slabinfo slabinfo - version: 2.1 # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail> nf_conntrack_ffffffff81a25e00 102 102 320 51 4 : tunables 0 0 0 : slabdata 2 2 0 xfs_dqtrx 0 0 528 62 8 : tunables 0 0 0 : slabdata 0 0 0 xfs_dquot 0 0 472 69 8 : tunables 0 0 0 : slabdata 0 0 0 xfs_icr 0 0 144 56 2 : tunables 0 0 0 : slabdata 0 0 0 xfs_ili 1590 1590 152 53 2 : tunables 0 0 0 : slabdata 30 30 0 xfs_inode 2550 2550 1088 30 8 : tunables 0 0 0 : slabdata 85 85 0 xfs_efd_item 80 80 400 40 4 : tunables 0 0 0 : slabdata 2 2 0 xfs_da_state 136 136 480 68 8 : tunables 0 0 0 : slabdata 2 2 0 xfs_btree_cur 78 78 208 39 2 : tunables 0 0 0 : slabdata 2 2 0 xfs_log_ticket 88 88 184 44 2 : tunables 0 0 0 : slabdata 2 2 0 scsi_cmd_cache 72 72 448 36 4 : tunables 0 0 0 : slabdata 2 2 0 kcopyd_job 0 0 3312 9 8 : tunables 0 0 0 : slabdata 0 0 0 dm_uevent 0 0 2608 12 8 : tunables 0 0 0 : slabdata 0 0 0 dm_rq_target_io 0 0 136 60 2 : tunables 0 0 0 : slabdata 0 0 0 UDPLITEv6 0 0 1152 28 8 : tunables 0 0 0 : slabdata 0 0 0 UDPv6 56 56 1152 28 8 : tunables 0 0 0 : slabdata 2 2 0 tw_sock_TCPv6 0 0 256 64 4 : tunables 0 0 0 : slabdata 0 0 0 TCPv6 30 30 2112 15 8 : tunables 0 0 0 : slabdata 2 2 0 uhci_urb_priv 438 438 56 73 1 : tunables 0 0 0 : slabdata 6 6 0 cfq_queue 140 140 232 70 4 : tunables 0 0 0 : slabdata 2 2 0 bsg_cmd 0 0 312 52 4 : tunables 0 0 0 : slabdata 0 0 0 mqueue_inode_cache 36 36 896 36 8 : tunables 0 0 0 : slabdata 1 1 0 hugetlbfs_inode_cache 106 106 608 53 8 : tunables 0 0 0 : slabdata 2 2 0 configfs_dir_cache 0 0 88 46 1 : tunables 0 0 0 : slabdata 0 0 0 dquot 0 0 256 64 4 : tunables 0 0 0 : slabdata 0 0 0 userfaultfd_ctx_cache 0 0 128 64 2 : tunables 0 0 0 : slabdata 0 0 0 pid_namespace 0 0 2176 15 8 : tunables 0 0 0 : slabdata 0 0 0 user_namespace 0 0 280 58 4 : tunables 0 0 0 : slabdata 0 0 0 posix_timers_cache 0 0 248 66 4 : tunables 0 0 0 : slabdata 0 0 0 UDP-Lite 0 0 1024 32 8 : tunables 0 0 0 : slabdata 0 0 0 RAW 340 340 960 34 8 : tunables 0 0 0 : slabdata 10 10 0 UDP 64 64 1024 32 8 : tunables 0 0 0 : slabdata 2 2 0 tw_sock_TCP 0 0 256 64 4 : tunables 0 0 0 : slabdata 0 0 0 TCP 34 34 1920 17 8 : tunables 0 0 0 : slabdata 2 2 0 blkdev_queue 45 45 2088 15 8 : tunables 0 0 0 : slabdata 3 3 0 blkdev_requests 882 882 384 42 4 : tunables 0 0 0 : slabdata 21 21 0 blkdev_ioc 78 78 104 39 1 : tunables 0 0 0 : slabdata 2 2 0 fsnotify_event_holder 340 340 24 170 1 : tunables 0 0 0 : slabdata 2 2 0 fsnotify_event 136 136 120 68 2 : tunables 0 0 0 : slabdata 2 2 0 sock_inode_cache 561 561 640 51 8 : tunables 0 0 0 : slabdata 11 11 0 net_namespace 0 0 4608 7 8 : tunables 0 0 0 : slabdata 0 0 0 shmem_inode_cache 1008 1008 680 48 8 : tunables 0 0 0 : slabdata 21 21 0 Acpi-ParseExt 6608 6608 72 56 1 : tunables 0 0 0 : slabdata 118 118 0 Acpi-Namespace 4590 4590 40 102 1 : tunables 0 0 0 : slabdata 45 45 0 taskstats 98 98 328 49 4 : tunables 0 0 0 : slabdata 2 2 0 proc_inode_cache 2254 2254 656 49 8 : tunables 0 0 0 : slabdata 46 46 0 sigqueue 102 102 160 51 2 : tunables 0 0 0 : slabdata 2 2 0 bdev_cache 78 78 832 39 8 : tunables 0 0 0 : slabdata 2 2 0 sysfs_dir_cache 29412 29412 112 36 1 : tunables 0 0 0 : slabdata 817 817 0 inode_cache 12925 12925 592 55 8 : tunables 0 0 0 : slabdata 235 235 0 dentry 26292 26292 192 42 2 : tunables 0 0 0 : slabdata 626 626 0 iint_cache 0 0 80 51 1 : tunables 0 0 0 : slabdata 0 0 0 selinux_inode_security 14455 14841 80 51 1 : tunables 0 0 0 : slabdata 291 291 0 buffer_head 156 156 104 39 1 : tunables 0 0 0 : slabdata 4 4 0 vm_area_struct 2611 2738 216 37 2 : tunables 0 0 0 : slabdata 74 74 0 mm_struct 60 60 1600 20 8 : tunables 0 0 0 : slabdata 3 3 0 files_cache 204 204 640 51 8 : tunables 0 0 0 : slabdata 4 4 0 signal_cache 812 812 1152 28 8 : tunables 0 0 0 : slabdata 29 29 0 sighand_cache 540 540 2112 15 8 : tunables 0 0 0 : slabdata 36 36 0 task_xstate 672 672 576 56 8 : tunables 0 0 0 : slabdata 12 12 0 task_struct 552 572 2944 11 8 : tunables 0 0 0 : slabdata 52 52 0 anon_vma 1747 2048 64 64 1 : tunables 0 0 0 : slabdata 32 32 0 shared_policy_node 2380 2380 48 85 1 : tunables 0 0 0 : slabdata 28 28 0 numa_policy 186 186 264 62 4 : tunables 0 0 0 : slabdata 3 3 0 radix_tree_node 1568 1568 584 56 8 : tunables 0 0 0 : slabdata 28 28 0 idr_layer_cache 165 165 2112 15 8 : tunables 0 0 0 : slabdata 11 11 0 dma-kmalloc-8192 0 0 8192 4 8 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-4096 0 0 4096 8 8 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-2048 0 0 2048 16 8 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-1024 0 0 1024 32 8 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-512 64 64 512 64 8 : tunables 0 0 0 : slabdata 1 1 0 dma-kmalloc-256 0 0 256 64 4 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-128 0 0 128 64 2 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-64 0 0 64 64 1 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-32 0 0 32 128 1 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-16 0 0 16 256 1 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-8 0 0 8 512 1 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-192 0 0 192 42 2 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-96 0 0 96 42 1 : tunables 0 0 0 : slabdata 0 0 0 kmalloc-8192 36 48 8192 4 8 : tunables 0 0 0 : slabdata 12 12 0 kmalloc-4096 224 240 4096 8 8 : tunables 0 0 0 : slabdata 30 30 0 kmalloc-2048 1014 1104 2048 16 8 : tunables 0 0 0 : slabdata 69 69 0 kmalloc-1024 2144 2144 1024 32 8 : tunables 0 0 0 : slabdata 67 67 0 kmalloc-512 3454 3456 512 64 8 : tunables 0 0 0 : slabdata 54 54 0 kmalloc-256 2526 4288 256 64 4 : tunables 0 0 0 : slabdata 67 67 0 kmalloc-192 6132 6132 192 42 2 : tunables 0 0 0 : slabdata 146 146 0 kmalloc-128 3520 3520 128 64 2 : tunables 0 0 0 : slabdata 55 55 0 kmalloc-96 1806 1806 96 42 1 : tunables 0 0 0 : slabdata 43 43 0 kmalloc-64 12631 12864 64 64 1 : tunables 0 0 0 : slabdata 201 201 0 kmalloc-32 4480 4480 32 128 1 : tunables 0 0 0 : slabdata 35 35 0 kmalloc-16 7168 7168 16 256 1 : tunables 0 0 0 : slabdata 28 28 0 kmalloc-8 9216 9216 8 512 1 : tunables 0 0 0 : slabdata 18 18 0 kmem_cache_node 192 192 64 64 1 : tunables 0 0 0 : slabdata 3 3 0 kmem_cache 192 192 256 64 4 : tunables 0 0 0 : slabdata 3 3 0 [root@vnf ~]#運行一段時間進行對比發現 <active_objs> 列的 kmalloc-8192一直在增長
階段3:打開內核調試選項
這個階段需要打開Linux內核的調試編譯選項,重新編譯內核
1,內核編譯選項:
下載源碼,或者打開自己定制的linux內核,使用make menuconfig打開配置。(具體的內核命令得看自己使用的編譯方式)
2,重新編譯的內核就可以查看slab詳細信息。因為我這是kmalloc-8192在漲所以我就看kmalloc-8192。這個需要具體情況判斷。
cat /sys/kernel/slab/kmalloc-8192/ /sys/kernel/slab/kmalloc-8192/aliases /sys/kernel/slab/kmalloc-8192/align /sys/kernel/slab/kmalloc-8192/alloc_calls /sys/kernel/slab/kmalloc-8192/cpu_partial /sys/kernel/slab/kmalloc-8192/cpu_slabs /sys/kernel/slab/kmalloc-8192/ctor /sys/kernel/slab/kmalloc-8192/destroy_by_rcu /sys/kernel/slab/kmalloc-8192/free_calls /sys/kernel/slab/kmalloc-8192/hwcache_align /sys/kernel/slab/kmalloc-8192/min_partial /sys/kernel/slab/kmalloc-8192/object_size /sys/kernel/slab/kmalloc-8192/objects /sys/kernel/slab/kmalloc-8192/objects_partial /sys/kernel/slab/kmalloc-8192/objs_per_slab /sys/kernel/slab/kmalloc-8192/order /sys/kernel/slab/kmalloc-8192/partial /sys/kernel/slab/kmalloc-8192/poison /sys/kernel/slab/kmalloc-8192/reclaim_account /sys/kernel/slab/kmalloc-8192/red_zone /sys/kernel/slab/kmalloc-8192/reserved /sys/kernel/slab/kmalloc-8192/sanity_checks /sys/kernel/slab/kmalloc-8192/shrink /sys/kernel/slab/kmalloc-8192/slab_size /sys/kernel/slab/kmalloc-8192/slabs /sys/kernel/slab/kmalloc-8192/slabs_cpu_partial /sys/kernel/slab/kmalloc-8192/store_user /sys/kernel/slab/kmalloc-8192/total_objects /sys/kernel/slab/kmalloc-8192/trace /sys/kernel/slab/kmalloc-8192/validate # cat /sys/kernel/slab/kmalloc-8192/?3,查看alloc和free的情況
# cat /sys/kernel/slab/kmalloc-8192/alloc_calls 3 timer_cpu_notify+0x18c/0x1f4 age=411895/411909/411920 pid=1 cpus=01 qos_policy_perhost_table_init+0x64/0x100 age=411552 pid=1 cpus=315 os_alloc_mem+0x1c/0x34 age=386584/404626/411488 pid=1-1093 cpus=1-33103 __alloc_skb+0x98/0x238 age=430/366961/410069 pid=0-1467 cpus=1,329 pskb_expand_head+0xa0/0x2b0 age=22161/203241/396156 pid=0-1467 cpus=11 ipv4_sysctl_init_net+0xb0/0x11c age=411447 pid=1 cpus=21 mac_filter_init+0x38/0x280 age=411551 pid=1 cpus=3 # cat /sys/kernel/slab/kmalloc-8192/free_calls 2546 <not-available> age=382073 pid=0 cpus=02 ubifs_iget+0x3a4/0x620 age=400698/405547/410397 pid=672-784 cpus=12 RoutingTabDestory+0xcc/0xe4 age=396474/396474/396475 pid=694 cpus=3603 skb_free_head+0x74/0x94 age=1348/194381/410033 pid=0-1474 cpus=0-1,3 #?4,發現__alloc_skb申請較多,經過長時間運行后發現它一直在增長
?階段4:增加內核打印詳細信息,找具體調用棧
找調用棧。
理論上有兩種方法:1,在內核代碼添加__alloc_skb()中添加dump_stack()函數,對內核重新編譯后可以打印出調用棧;2,使用echo 1>?/sys/kernel/slab/kmalloc-8192/trace 打開slab跟蹤,也可以打出slab相關調用棧。
調用棧如下:因為打印較多必須得細心查看,到這里基本可以查看自己的代碼邏輯了,看是否申請的資源沒有被釋放。
CPU: 3 PID: 0 Comm: swapper/3 Tainted: P O 3.18.21 #9 Stack : 00000000 00000004 00000006 81383900 00000000 00000000 00000000 0000000080ffb812 00000041 00000000 00000000 00000000 00000000 80a72e04 80ff5b5c8fc4ddc8 80badce7 00000000 00000000 00002240 81250000 00000000 80945f4889204078 8003130c 00000000 00000000 80a77994 8fcabbcc 8fcabbcc 80a72e0481250000 00000000 00000000 00000000 00000000 00000000 00000000 00000000... Call Trace: [<80015ad4>] show_stack+0x88/0xa4 [<80948ee8>] dump_stack+0x6c/0x8c [<8079f940>] __alloc_skb+0x1e8/0x238 [<807d195c>] skbmgr_alloc_skb4k+0xc8/0x124 [<806baf60>] RTMP_AllocateRxPacketBuffer+0x40/0x1b0 [<805ef068>] pci_get_pkt_dynamic_page_ddone+0x120/0x3bc [<805ef3d0>] pci_rx_dma_done_handle+0xcc/0x194 [<805ebf50>] pci_rx_data_done_func+0x12c/0x26c [<80033f20>] tasklet_hi_action+0x100/0x1cc [<8003464c>] __do_softirq+0x1e8/0x308 [<80034a1c>] irq_exit+0x78/0x84 [<800101ec>] ret_from_irq+0x0/0x4 [<80011e54>] r4k_wait_irqoff+0x18/0x20 [<8006791c>] cpu_startup_entry+0x13c/0x1b0CPU: 3 PID: 0 Comm: swapper/3 Tainted: P O 3.18.21 #9 Stack : 00000000 00000004 00000006 81382500 00000000 00000000 00000000 0000000080ffb812 00000041 00000000 00000000 00000000 00000000 80a72e04 80ff5b5c8fc4ddc8 80badce7 00000000 00000000 00002240 81250000 00000000 80945f4889204078 8003130c 00000000 00000000 80a77994 8fcabbcc 8fcabbcc 80a72e0481250000 00000000 00000000 00000000 00000000 00000000 00000000 00000000... Call Trace: [<80015ad4>] show_stack+0x88/0xa4 [<80948ee8>] dump_stack+0x6c/0x8c [<8079f940>] __alloc_skb+0x1e8/0x238 [<807d195c>] skbmgr_alloc_skb4k+0xc8/0x124 [<806baf60>] RTMP_AllocateRxPacketBuffer+0x40/0x1b0 [<805ef068>] pci_get_pkt_dynamic_page_ddone+0x120/0x3bc [<805ef3d0>] pci_rx_dma_done_handle+0xcc/0x194 [<805ebf50>] pci_rx_data_done_func+0x12c/0x26c [<80033f20>] tasklet_hi_action+0x100/0x1cc [<8003464c>] __do_softirq+0x1e8/0x308 [<80034a1c>] irq_exit+0x78/0x84 [<800101ec>] ret_from_irq+0x0/0x4 [<80011e54>] r4k_wait_irqoff+0x18/0x20 [<8006791c>] cpu_startup_entry+0x13c/0x1b0CPU: 3 PID: 19 Comm: ksoftirqd/3 Tainted: P O 3.18.21 #9 Stack : 00000000 00000004 00000006 81383d00 00000000 00000000 00000000 0000000080ffb812 00000044 00000000 00000000 00000000 00000000 80a72e04 80ff5b5c8fc4c158 80badce7 00000000 00000000 00002240 81250000 00000000 80945f4800100007 8003130c 00000000 00000000 80a77994 8fcebc2c 8fcebc2c 80a72e0481250000 00000000 00000000 00000000 00000000 00000000 00000000 00000000... Call Trace: [<80015ad4>] show_stack+0x88/0xa4 [<80948ee8>] dump_stack+0x6c/0x8c [<8079f940>] __alloc_skb+0x1e8/0x238 [<807d195c>] skbmgr_alloc_skb4k+0xc8/0x124 [<806baf60>] RTMP_AllocateRxPacketBuffer+0x40/0x1b0 [<805ef068>] pci_get_pkt_dynamic_page_ddone+0x120/0x3bc [<805ef3d0>] pci_rx_dma_done_handle+0xcc/0x194 [<805ebf50>] pci_rx_data_done_func+0x12c/0x26c [<80033f20>] tasklet_hi_action+0x100/0x1cc [<8003464c>] __do_softirq+0x1e8/0x308 [<800347b8>] run_ksoftirqd+0x4c/0x6c [<80050a90>] smpboot_thread_fn+0x1c8/0x1d0 [<8004bebc>] kthread+0xd8/0xf0 [<80010230>] ret_from_kernel_thread+0x10/0x18?階段5:具體函數具體分析
由于階段4,我們的目標函數比較底層,所以棧打印較多。需要逐個分析調用棧,找到懷疑點后,還需要在懷疑點多基礎上分析棧上的函數。用時可多可少。。。
總結:
1,寫這個看起來簡單,實際上用了7個工作日才找到內存泄漏點
2,其實在第3個工作日已經找到方法了,但是打印的棧信息太多分析時漏掉了幾個棧的代碼,導致一直在錯誤的路上(棧里)走了好幾天,分析代碼。。。。
3,如果已經打印出棧信息,就不能放過任何一個,盡量都分析下。我走歪路的原因就是認為那幾個我分析的棧是不可能有問題的。。。最后確實是那幾個我忽略的棧出的問題。
4,不能太自信,往往錯過的才是真的,而貌似正確的確帶著你走向深淵
?
總結
以上是生活随笔為你收集整理的SLAB内存泄露分析实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信小程序——账号及开发工具
- 下一篇: c语言判断整数_C语言技能|(草稿,不断