1、介绍
简单来说就是查看进程的虚拟地址空间是如何使用的。总共包括六列,每列及其含义如下。
address | perms | offset | dev | inode | pathname
(1)地址:本段在虚拟内存中的地址范围;对应vm_area_struct中的vm_start和vm_end。 (2)权限:本段的权限; r-读,w-写,x-执行, p-私有;对应vm_flags。 (3)偏移地址:即本段映射地址在文件中的偏移;对于有名映射指本段映射地址在文件中的偏移,对应vm_pgoff;对于匿名映射为vm_area_struct->vm_start。 (4)主设备号与次设备号:所映射的文件所属设备的设备号,对应vm_file->f_dentry->d_inode->i_sb->s_dev。匿名映射为0。其中fd为主设备号,00为次设备号。 (5)文件索引节点号:对应vm_file->f_dentry->d_inode->i_ino,与ls –i显示的内容相符。匿名映射为0。; (6)映射的文件名:对有名映射而言,是映射的文件名,对匿名映射来说,是此段内存在进程中的作用。[stack]表示本段内存作为栈来使用,[heap]作为堆来使用,其他情况则为无。
关于更基础的地址空间划分、地址空间与mm_struct的管理 可以参照 这里? (很全面)!!!
其中涉及的task_struct, mm_struct , vm_area_struct等结构可以额外搜索文章详细了解,这里只做简要介绍。例如 这里 ?
①task_struct:每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,Linux内核的进程控制块是task_struct结构体。
代码位置:?位置:<include\linux\sched.h> - 593行
部分代码如下:
struct task_struct
{
/*...*/
struct mm_struct *mm;
struct mm_struct *active_mm;
/* Per-thread vma caching: */
struct vmacache vmacache;
/*...*/
}
②mm_struct:每一个进程都会有唯一的mm_struct结构体。
位置:<include\linux\mm_types.h> - 340行 部分代码如下:
struct mm_struct
{
/*...*/
struct vm_area_struct *mmap; /* list of VMAs */
struct rb_root mm_rb;
u64 vmacache_seqnum; /* per-thread vmacache */
unsigned long mmap_base; /*映射基地址*/
unsigned long mmap_legacy_base; /*不是很明白这里*/
unsigned long task_size; /*该进程能够vma使用空间大小*/
unsigned long highest_vm_end; /*该进程能够使用的vma结束地址*/
pgd_t * pgd;
atomic_t mm_users;
atomic_t mm_count;
int map_count; /* vma的总个数 */
unsigned long total_vm; /* 映射的总页面数*/
/*...*/
unsigned long start_code, end_code, start_data, end_data;
unsigned long start_brk, brk, start_stack;
unsigned long arg_start, arg_end, env_start, env_end;
/*...*/
③vm_area_struct:内核每次为用户空间中分配一个空间使用时,都会生成一个vm_are_struct结构用于记录跟踪分配情况,一个vm_are_struct就代表一段虚拟内存空间。
位置:<include\linux\mm_types.h> - 264行 全部代码如下:
struct vm_area_struct {
unsigned long vm_start; //虚存区起始
unsigned long vm_end; //虚存区结束
struct vm_area_struct *vm_next, *vm_prev; //前后指针
struct rb_node vm_rb; //红黑树中的位置
unsigned long rb_subtree_gap;
struct mm_struct *vm_mm; //所属的 mm_struct
pgprot_t vm_page_prot;
unsigned long vm_flags; //标志位
struct {
struct rb_node rb;
unsigned long rb_subtree_last;
} shared;
struct list_head anon_vma_chain;
struct anon_vma *anon_vma;
const struct vm_operations_struct *vm_ops; //vma对应的实际操作
unsigned long vm_pgoff; //文件映射偏移量
struct file * vm_file; //映射的文件
void * vm_private_data; //私有数据
atomic_long_t swap_readahead_info;
#ifndef CONFIG_MMU
struct vm_region *vm_region; /* NOMMU mapping region */
#endif
#ifdef CONFIG_NUMA
struct mempolicy *vm_policy; /* NUMA policy for the VMA */
#endif
struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
} __randomize_layout;
2、自测体验
(1)创建一个test.c文件,内容如下:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *str;
str = (char *) malloc(15);
while(1)
{
;
}
return 0;
}
(2)编译得到可执行文件
gcc test.c -o test
(3)明确进程号输出maps如下。
cat /proc/15204/maps
00400000-00401000 r-xp 00000000 fd:11 1573718 /data/home/shuozhuo/test
00600000-00601000 r--p 00000000 fd:11 1573718 /data/home/shuozhuo/test
00601000-00602000 rw-p 00001000 fd:11 1573718 /data/home/shuozhuo/test
00a97000-00ab8000 rw-p 00000000 00:00 0 [heap]
7f312de71000-7f312de73000 r-xp 00000000 fd:01 794931 /usr/lib64/libdl-2.17.so
7f312de73000-7f312e073000 ---p 00002000 fd:01 794931 /usr/lib64/libdl-2.17.so
7f312e073000-7f312e074000 r--p 00002000 fd:01 794931 /usr/lib64/libdl-2.17.so
7f312e074000-7f312e075000 rw-p 00003000 fd:01 794931 /usr/lib64/libdl-2.17.so
7f312e075000-7f312e22d000 r-xp 00000000 fd:01 794815 /usr/lib64/libc-2.17.so
7f312e22d000-7f312e42c000 ---p 001b8000 fd:01 794815 /usr/lib64/libc-2.17.so
7f312e42c000-7f312e430000 r--p 001b7000 fd:01 794815 /usr/lib64/libc-2.17.so
7f312e430000-7f312e432000 rw-p 001bb000 fd:01 794815 /usr/lib64/libc-2.17.so
7f312e432000-7f312e437000 rw-p 00000000 00:00 0
7f312e437000-7f312e457000 r-xp 00000000 fd:01 815248 /usr/lib64/ld-2.17.so
7f312e53a000-7f312e53c000 rw-p 00000000 00:00 0
7f312e54e000-7f312e551000 r-xp 00000000 fd:01 795444 /usr/lib64/libonion_security.so.1.0.19
7f312e551000-7f312e651000 ---p 00003000 fd:01 795444 /usr/lib64/libonion_security.so.1.0.19
7f312e651000-7f312e652000 rw-p 00003000 fd:01 795444 /usr/lib64/libonion_security.so.1.0.19
7f312e652000-7f312e656000 rw-p 00000000 00:00 0
7f312e656000-7f312e657000 r--p 0001f000 fd:01 815248 /usr/lib64/ld-2.17.so
7f312e657000-7f312e658000 rw-p 00020000 fd:01 815248 /usr/lib64/ld-2.17.so
7f312e658000-7f312e659000 rw-p 00000000 00:00 0
7ffef4c96000-7ffef4cb7000 rw-p 00000000 00:00 0 [stack]
7ffef4df7000-7ffef4df9000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
第一行的权限是只读,并且可执行,说明第一行是应用程序的代码段;
第三行的权限是可读可写,但是没有执行权限,说明该段是数据段。
第四行就是堆了。主要取决于程序中没有malloc之类的认为分配内存的语句。
3、用于协助定位内存泄漏
定位内存泄漏基本上是从宏观到微观,进而定位到代码位置。
从/proc/meminfo可以看到整个系统内存消耗情况,使用top可以看到每个进程的VIRT(虚拟内存)和RES(实际占用内存),基本上就可以将泄漏内存定位到进程范围。之前也大概了解过/proc/{pid}/maps,基于里面信息能大概判断泄露的内存的属性,是哪个区域在泄漏、对应哪个文件。辅助工具procmem输出更可读的maps信息。
其实对于微服务的场景,定位pid其实更简单;因为一般出问题的服务都是清楚的,其对应的进程也都是明确的,接下来就是查看这些pid的maps信息尝试找到线索就好了。
例如对于spp框架的C++微服务,在怀疑某服务可能内存泄漏的情况下ps即可找到对应pid。
查看其maps信息如下(不完全复制):
cat /proc/2558/maps
00400000-004a0000 r-xp 00000000 fc:01 912928 /usr/local/services/spp_xxxx-2.3/bin64/spp_worker
005a0000-005a3000 rw-p 000a0000 fc:01 912928 /usr/local/services/spp_xxxx-2.3/bin64/spp_worker
005a3000-007d6000 rw-p 00000000 00:00 0
01eb8000-01ed9000 rw-p 00000000 00:00 0 [heap]
01ed9000-11c89000 rw-p 00000000 00:00 0 [heap]
7f90e673e000-7f90eb39f000 rw-s 00000000 00:04 2326599 /SYSV00a6528e (deleted)
7f90eb39f000-7f90f0000000 rw-s 00000000 00:04 2424906 /SYSV00a6528d (deleted)
7f90f0000000-7f90f0021000 rw-p 00000000 00:00 0
7f90f0021000-7f90f4000000 ---p 00000000 00:00 0
7f90f7d4f000-7f90f7d68000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7d88000-7f90f7da1000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7da1000-7f90f7dba000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7dba000-7f90f7dd3000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7dd3000-7f90f7dec000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7dec000-7f90f7e05000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7e05000-7f90f7e1e000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7e1e000-7f90f7e37000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7e37000-7f90f7e50000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7e50000-7f90f7e69000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7e69000-7f90f7e82000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7e82000-7f90f7e9b000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7ea2000-7f90f7ebb000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7ebb000-7f90f7ed4000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7ed4000-7f90f7eed000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7eed000-7f90f7f06000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7f06000-7f90f7f1f000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7f1f000-7f90f7f38000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7f38000-7f90f7f51000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7f51000-7f90f7f6a000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7f6a000-7f90f7f83000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7f83000-7f90f7f9c000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7f9c000-7f90f7fb5000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7fb5000-7f90f7fce000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7fce000-7f90f7fe7000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f7fe7000-7f90f8000000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f90f8000000-7f90f8045000 rw-p 00000000 00:00 0
7f90f8045000-7f90fc000000 ---p 00000000 00:00 0
7f90fc000000-7f90fc021000 rw-p 00000000 00:00 0
7f90fc021000-7f9100000000 ---p 00000000 00:00 0
7f9100000000-7f9100021000 rw-p 00000000 00:00 0
7f9100021000-7f9104000000 ---p 00000000 00:00 0
7f9104000000-7f91041a1000 rw-p 00000000 00:00 0
7f91041a1000-7f9108000000 ---p 00000000 00:00 0
7f9108000000-7f9108e02000 rw-p 00000000 00:00 0
7f9108e02000-7f910c000000 ---p 00000000 00:00 0
7f910c009000-7f910c022000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f910c022000-7f910c03b000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f910c03b000-7f910c054000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f910c054000-7f910c06d000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f910c06d000-7f910c086000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f910c086000-7f910c09f000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f910c09f000-7f910c0b8000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
7f910c0b8000-7f910c0d1000 r--s 00000000 00:04 2457675 /SYSV09998877 (deleted)
很显然其中的deleted看起来很奇怪。然后在代码中再搜一下?09998877 发现是shm的key。接下来的指向就很明确了,沿着这个方向继续查就好了。
|