smap节点,位于/proc/{pid}/smaps。
通过这个节点可以看到一个进程映射的内存信息,不会包括设备使用的内存,比如gpumem。
smaps节点内核定义
kernel/msm-4.19/fs/proc/base.c
#ifdef CONFIG_PROC_PAGE_MONITOR
REG("clear_refs", S_IWUSR, proc_clear_refs_operations),
REG("smaps", S_IRUGO, proc_pid_smaps_operations),
REG("smaps_rollup", S_IRUGO, proc_pid_smaps_rollup_operations),
REG("pagemap", S_IRUSR, proc_pagemap_operations),
#endif
kernel/msm-4.19/fs/proc/task_mmu.c
const struct file_operations proc_pid_smaps_operations = {
.open = pid_smaps_open,
.read = seq_read,
.llseek = seq_lseek,
.release = proc_map_release,
};
open系统调用分析
对smaps节点的open系统调用,我觉得应该类似binder_open,打开这个节点做一些初始化工作
调用堆栈如下:
kernel/msm-4.19/fs/proc/task_mmu.c pid_smaps_open()
--->kernel/msm-4.19/fs/proc/task_mmu.c do_maps_open()
--->kernel/msm-4.19/fs/proc/task_mmu.c proc_maps_open()
重点看一下阿proc_maps_open函数调用了__seq_open_private方法,这一番操作实际上是把smaps文件与proc_pid_smaps_op关联上。
实际上就是在seq_file结构体的seq_operations指针上放了proc_pid_smaps_op,之后对smaps进行cat操作就会执行到show_smap()方法。
具体的细节可以按照
kernel/msm-4.19/fs/proc/task_mmu.c
static int proc_maps_open(struct inode *inode, struct file *file,
const struct seq_operations *ops, int psize)
{
//在这里初始化,关联了seq_file的proc_pid_smaps_op
struct proc_maps_private *priv = __seq_open_private(file, ops, psize);
priv->inode = inode;
priv->mm = proc_mem_open(inode, PTRACE_MODE_READ);
if (IS_ERR(priv->mm)) {
int err = PTR_ERR(priv->mm);
seq_release_private(inode, file);
return err;
}
return 0;
}
kernel/msm-4.19/include/linux/seq_file.h
struct seq_file {
char *buf;
size_t size;
size_t from;
size_t count;
size_t pad_until;
loff_t index;
loff_t read_pos;
u64 version;
struct mutex lock;
const struct seq_operations *op;
int poll_event;
const struct file *file;
void *private;
};
show方法分析
在执行cat /proc/{pid}/smaps之后,会执行下面的调用栈。
在调用栈(2)的smap_gather_stats()函数中,会初始化一个mm_walk结构体,并设置smaps_pte_range()函数作为回调,
所以在smap_gather_stats()函数最后执行walk_page_vma()后,遍历页表时会回调smaps_pte_range()函数。
然后在smaps_account方法中把VMA的所有子项与结构体mem_size_stats关联起来。
最后在show_smap方法里就可以通过打印mem_size_stats中的数据,将一个进程的一块VMA信息打印出来了。
分析到这里,可以知道smaps节点的信息都是从mm_struct中的vma中获得的。
调用栈:
1. kernel/msm-4.19/fs/proc/task_mmu.c show_smap()
2. --->kernel/msm-4.19/fs/proc/task_mmu.c smap_gather_stats()
3. --->kernel/msm-4.19/mm/pagewalk.c walk_page_vma()
4. --->kernel/msm-4.19/mm/pagewalk.c __walk_page_range()
5. --->kernel/msm-4.19/mm/pagewalk.c walk_pgd_range()
6. --->kernel/msm-4.19/mm/pagewalk.c walk_p4d_range()
7. --->kernel/msm-4.19/mm/pagewalk.c walk_pud_range()
8. --->kernel/msm-4.19/mm/pagewalk.c walk_pmd_range()
9. --->kernel/msm-4.19/fs/proc/task_mmu.c smaps_pte_range()
10. --->kernel/msm-4.19/fs/proc/task_mmu.c smaps_pte_entry()
11. --->kernel/msm-4.19/fs/proc/task_mmu.c smaps_account()
每执行一次,会打印smaps节点的一块VMA信息,如下:
kernel/msm-4.19/fs/proc/task_mmu.c
static const struct seq_operations proc_pid_smaps_op = {
.start = m_start,
.next = m_next,
.stop = m_stop,
.show = show_smap
};
static int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
struct mm_walk *walk)
{
struct vm_area_struct *vma = walk->vma;
pte_t *pte;
spinlock_t *ptl;
ptl = pmd_trans_huge_lock(pmd, vma);
...
/*
* The mmap_sem held all the way back in m_start() is what
* keeps khugepaged out of here and from collapsing things
* in here.
*/
pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
for (; addr != end; pte++, addr += PAGE_SIZE)
//在这里遍历此vma的每一个页表项
smaps_pte_entry(pte, addr, walk);
pte_unmap_unlock(pte - 1, ptl);
return 0;
}
\