Notes
main
main
  • Introduction
  • linuxKernel
    • tips
    • make_help
    • old linux
      • compile_linux0.11
      • TestEnvironment
      • load_setup
      • get_hard_data
    • list
    • plist
    • fifo
    • idr
    • xarray
    • rbtree
    • maple_tree
    • syscall
    • bitmap
    • page
    • page_flags
    • page_size
    • page mapcount
    • page refcount
    • folio
    • slub
      • proc_slabinfo
      • slub_theory
      • kmalloc_kfree
      • kmem_cache
      • slab_alloc
      • slab_free
      • proc_meminfo_SReclaimable_SReclaimable
    • vmalloc
    • brk
    • mmap
    • mremap
    • mprotect
    • madvise
    • read
    • write
    • shmem
    • huge_page
    • page_fault
    • rmap
    • lru
    • multi-gen-LRU
    • page_reclaim
    • page_cache
    • page_table
    • rcu
    • kvm
    • aarch64_boot
    • tracing_system
    • cache_coherence_and_memory_consistency
    • cpu_speculates
    • mmap_lock
    • per-vma_lock
    • cgroup
    • symbol
    • campact
    • page_ext
    • mempool
    • kernelstack
    • filesystem
    • io_stack
    • workingset
    • ioremap
    • sched_period
  • linuxDebug
    • openocd_openjtag
    • i2c_tools
    • objdump
    • addr2line
    • gdb_useage
    • debug_linux_kernel_via_gdb
    • debug_linux_module_via_gdb
    • early_boot
    • sequentially_execute
    • dynamic_debug
    • research_linuxKernel_by_patch
    • tracefs
    • ebpf
    • bpftrace
    • perf
    • flame_graph
    • crash
    • ASAN_HWASAN_MTE_check_mem_bug
    • page_owner
    • vmtouch
    • fio
    • benchmark
  • linuxSystem
    • common
      • system_version
      • procfs
      • proc_sys_vm
      • cmd_ps
      • makefile
      • file_descriptor
      • psi
      • ulimit
      • top
      • delay_accounting
    • ubuntu
      • custom_kernel
      • get_cmd_src
      • record_ssh_info
      • log
      • run_custom_script
      • repo
      • cockpit
      • nfs
      • tftp
      • misc
    • fedora
      • system_upgrade
      • custom_kernel
      • lvextend
      • yt-dlp
      • jellyfin
  • linuxDriver
    • i2c_peripherals_driver
    • spi_peripherals_driver
    • gpio_subsystem
    • IRQ_driver
    • blockIO_unblockIO_async
    • linux_own_driver
    • misc_device
    • input_device
    • timer
    • atomic_spinlock_semaphore_mutex
    • lcd
    • touch_screen
    • debugfs
    • v4l2
    • mmap
  • hardware
    • paging_mmu_pt
    • iommu
  • process_thread_scheduler
    • scheduler01
    • scheduler02
    • scheduler03
    • scheduler04
    • scheduler05
    • scheduler06
  • memory_management
    • mm1
    • mm2
    • mm3
    • mm4
    • mm5
  • input_output_filesystem
    • io_fs_01
    • io_fs_02
    • io_fs_03
    • io_fs_04
  • lock_and_lockup_detector
    • general_lock
    • hung_task
    • softLockup_hardLockup
    • crash_experiment
  • MIT_6.S081
    • 6.S081_Operating_System_Engineering
    • Schedule.md
    • Class
      • Overview
      • Administrivia
    • Labs
      • Tools
      • Guidance
      • startup
      • syscall
      • page_table
      • Calling_Convention
      • traps
    • xv6
      • xv6
    • References.md
  • qemu
    • qemu_buildroot
    • qemu_busybox.md
    • Serial.md
    • demo_mini2440
      • 0_compilation_error_summary
      • 1_compilation_steps
      • 2_operation_mode
      • 3_transplant_tools_libraries
      • 4_tools_use
      • reference_website
  • tools
    • getKernelSourceCodeList
    • nat
    • shell
    • translating
    • YouCompleteMe
    • cscope
    • global
    • vscode
    • vim
    • binary
    • markdown
    • draw
    • git
    • tig
    • tmux
    • mail_client
    • download_patchset_from_LKML
    • minicom
    • clash
  • other
    • interview
    • interview_c_base
    • know_dontknow
    • Stop-Ask-Questions-The-Stupid-Ways
    • How-To-Ask-Questions-The-Smart-Way
    • docker
    • buildroot
    • rv32_to_rv64
Powered by GitBook
On this page
  • 进程VMA
  • page fault的可能性
  • 进程内存消耗的4个概念:VSS、RSS、PSS和USS
  • 内存泄露的界定方法?
  • 应用空间内存泄露检查步骤:
  • 内核空间内存泄露检查步骤:

Was this helpful?

  1. memory_management

mm3

进程VMA

每一个进程都有0GB~3GB虚拟地址,内核虚拟地址只有一个3GB~4GB,如下:

      3GB~4GB              内核空间
     /     |    \
   /       |      \
---------------------------------------
0GB~3GB  0GB~3GB  0GB~3GB       用户空间

A进程    B进程     C进程

因此不同进程可以有相同虚拟地址,但是不同进程的页表不一样,所以相同虚拟地址映射到不同的物理地址中。

每一个进程的0GB~3GB虚拟地址,只是用某几段区域,不会全部使用完;一般有text 段、data 段、BSS 段、Heap 段、Stack 段等,每一段虚拟地址区域就是一个vma。

将每一个进程的所有段通过链表方式进行连接,如下:

struct task_struct {
    struct mm_struct *mm
};

struct mm_struct {
    struct vm_area_struct * mmap;        /* list of VMAs */
};

struct vm_area_struct {
    unsigned long vm_start;        /* Our start address within vm_mm. */
    unsigned long vm_end;        /* The first byte after our end address

    /* linked list of VM areas per task, sorted by address */
    struct vm_area_struct *vm_next;
};

查看一个进程vma的分布情况:

$ cat test.c 
#include <stdio.h>

void main(void)
{
    while(1);
}

$ pidof a.out 
1487

$ pmap 1487
## vma起始地址   vma大小 vma权限
0000000000400000      4K r-x--  /xxx/a.out  ## text 段,通过权限可知
0000000000600000      4K r----  /xxx/a.out  ## data 段
0000000000601000      4K rw---  /xxx/a.out  ## Heap 段
00007f27579f4000   1504K r-x--  /lib/libc-2.11.1.so
00007f2757b6c000   2048K -----  /lib/libc-2.11.1.so
00007f2757d6c000     16K r----  /lib/libc-2.11.1.so
00007f2757d70000      4K rw---  /lib/libc-2.11.1.so
00007f2757d71000     20K rw---    [ anon ]
00007f2757d76000    128K r-x--  /lib/ld-2.11.1.so
00007f2757f83000     12K rw---    [ anon ]
00007f2757f93000      8K rw---    [ anon ]
00007f2757f95000      4K r----  /lib/ld-2.11.1.so
00007f2757f96000      4K rw---  /lib/ld-2.11.1.so
00007f2757f97000      4K rw---    [ anon ]
00007fffffa74000     84K rw---    [ stack ]
00007fffffbff000      4K r-x--    [ anon ]
ffffffffff600000      4K r-x--    [ anon ]
 total             3856K

$ cat /proc/1487/maps 
## vma起始地址-结束地址 vma权限
00400000-00401000     r-xp 00000000 00:16 51646247  /xxx/a.out  ## text 段,通过权限可知
...

$ cat /proc/1487/smaps
## vma起始地址-结束地址 vma权限
00400000-00401000    r-xp 00000000 00:16 51646247     /xxx/a.out  ## text 段,通过权限可知
Size:                  4 kB  ## VSS
Rss:                   4 kB  ## RSS
Pss:                   4 kB  ## PSS
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         4 kB
Private_Dirty:         0 kB
Referenced:            4 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
...

page fault的可能性

  1. 访问没有VMA的非法区内存区域,应用程序出现segv

  2. heap区域vma(R+W权限),指向页表是R权限;因此第一次写操作时,发生page fault,申请一页内存,页表权限是R+W,此过程称为minor

  3. 代码段vma(R+X权限),执行W操作,权限不对,应用程序出现segv

  4. 代码段 vma(R+X权限),执行X操作,如果页表不存在,需要申请页,读出代码段,有会硬盘I/O操作,此过程称为major

进程内存消耗的4个概念:VSS、RSS、PSS和USS

VSS:虚拟内存占用空间,VMA大小

RSS:实际内存占用空间,私有数据+共享内存

PSS:实际内存占用空间,私有数据+共享内存/共享数量

USS:实际内存占用空间,私有数据

查看VSS/RSS/PSS/USS占用大小,如下:

$ smem
  PID User     Command                         Swap      USS      PSS      RSS 
 3454 vernon   ./a.out                            0       84       89      356
or
$ smem --pie=command
or
$ smem --bar=command

内存泄露的界定方法?

原理:连续多点采样法,随着运行时间越久,进程使用内存越多

  1. 先用free命令判断系统是否内存泄露?

  2. 分别检查应用空间与内核空间是否内存泄露?

应用空间内存泄露检查步骤:

  • 连续多点采样后,USS从560 -> 608 -> 644,判断应用空间内存泄露

$ cat leak-example.c 
void main(void)
{
    unsigned int *p1, *p2;
    while(1)
    {
        p1=malloc(4096*3);
        p1[0] = 0;
        p1[1024] = 1;
        p1[1024*2] = 2;

        p2=malloc(1024);
        p2[0] = 1;
        free(p2);
        sleep(1);
    }
}

$ gcc -g leak-example.c
$ ./a.out
$ smem -P a.out ## 主要观察USS大小
  PID User     Command                         Swap      USS      PSS      RSS 
 3896 vernon   ./a.out                            0      560      563      876 
 3900 vernon   /usr/bin/python /usr/bin/sm        0     3704     4071     6136 
$ smem -P a.out 
  PID User     Command                         Swap      USS      PSS      RSS 
 3896 vernon   ./a.out                            0      608      611      924 
 3902 vernon   /usr/bin/python /usr/bin/sm        0     3700     4067     6132 
$ smem -P a.out 
  PID User     Command                         Swap      USS      PSS      RSS 
 3896 vernon   ./a.out                            0      644      647      960 
 3904 vernon   /usr/bin/python /usr/bin/sm        0     3704     4071     6136
  • 通过valgrind进行检测,能够精确定位哪一行源码发生内存泄露,如下:

# valgrind
$ valgrind --tool=memcheck --leak-check=yes ./a.out
==4760== Memcheck, a memory error detector
==4760== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==4760== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==4760== Command: ./a.out
==4760== 
^C==4760== 
==4760== HEAP SUMMARY:
==4760==     in use at exit: 86,016 bytes in 7 blocks
==4760==   total heap usage: 14 allocs, 7 frees, 93,184 bytes allocated
==4760== 
==4760== 73,728 bytes in 6 blocks are definitely lost in loss record 2 of 2
==4760==    at 0x4C274A8: malloc (vg_replace_malloc.c:236)
==4760==    by 0x4005C5: main (leak-example.c:6)  ## 此行源码发生内存泄露
==4760== 
==4760== LEAK SUMMARY:
==4760==    definitely lost: 73,728 bytes in 6 blocks
==4760==    indirectly lost: 0 bytes in 0 blocks
==4760==      possibly lost: 0 bytes in 0 blocks
==4760==    still reachable: 12,288 bytes in 1 blocks
==4760==         suppressed: 0 bytes in 0 blocks
==4760== Reachable blocks (those to which a pointer was found) are not shown.
==4760== To see them, rerun with: --leak-check=full --show-reachable=yes
==4760== 
==4760== For counts of detected and suppressed errors, rerun with: -v
==4760== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)

或者通过asan进行检测,但是需要gcc版本大于等于8,能够精确定位哪一行源码发生内存泄露,如下:

# asan
$ cat lsan.c 
#include <unistd.h>
#include <sanitizer/lsan_interface.h>

void main(void)
{
    unsigned int *p1, *p2, i=0;
    while(1)
    {
        p1=malloc(4096*3);
        p1[0] = 0;
        p1[1024] = 1;
        p1[1024*2] = 2;

        p2=malloc(1024);
        p2[0] = 1;
        free(p2);
        usleep(10000);

        /* check memory leak by asan */
        if(i++==10)
            __lsan_do_leak_check(); ## 需要加上此行
    }
}

$ gcc -g -fsanitize=address ./lsan.c  ## 需要加上-fsanitize=address
$ ./a.out 
=================================================================
==15338==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 122880 byte(s) in 10 object(s) allocated from:
    #0 0x7f6cf112cbc8 in malloc (/lib/x86_64-linux-gnu/libasan.so.5+0x10dbc8)
    #1 0x55aece4f1225 in main lsan.c:9  ## 此行源码发生内存泄露
    #2 0x7f6cf0e540b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

SUMMARY: AddressSanitizer: 122880 byte(s) leaked in 10 allocation(s).

内核空间内存泄露检查步骤:

  • 查看内核空间的内存实时情况,判断内核空间内存泄露

$ cat /proc/meminfo
$ cat /proc/buddyinfo
$ cat /proc/zoneinfo
$ cat /proc/slabinfo
  • linux kernel启动中DEBUG_KMEMLEAK功能,进行定位哪里出现内存泄露

# linux 2.6.34
$ make menuconfig
Kernel hacking  --->
    [ ] Kernel memory leak detector
or
# linux3.16
$ make menuconfig
Kernel hacking  --->
    Memory Debugging  --->
        [*] Kernel memory leak detector
Previousmm2Nextmm4

Last updated 4 years ago

Was this helpful?