cat /proc/$pid/smaps浅析(一)

引子

    当我们执行cat /proc/pid/smaps读取某个进程对应的虚拟内存区间到信息显示给我们,整个过程究竟发生了什么呢?
        >>用户态open("/proc/pid/smaps")-->  内核proc_pid_smaps_operations.open()
        >>用户态        read(fd)                  -->  内核proc_pid_smaps_operations.read()
    其中open()函数最终会返回一个文件描述符fd供后续read(fd)函数使用。

 

内核实现

    我们先看一下open()read()进入在内核中是如何实现的,它们都定义在proc_pid_smaps_operations结构中:

const struct file_operations proc_pid_smaps_operations = {
        .open           = pid_smaps_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
        .release        = proc_map_release,
};

    可以看到proc_pid_smaps_operations()函数实现了内核中open、read的操作函数。

    两个函数的具体实现如下:
    (1)read

ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)

    (2)open

static int pid_smaps_open(struct inode *inode, struct file *file)

    此函数通过参数inode找到进程相关的结构并放到file的私有数据结构中;

   然而只有进程相关的数据结构还不够, 因为后面的read陷入内核后调用的seq_read()函数是内核的一个通用架构的函数,它并未实现特定的proc文件对应的特定动作。因而,特定的proc文件还需要提供自己特有的文件操作方法供通用的seq_read()调用。
    因此,pid_smaps_open()函数里面还加入了一个file_operations参数&proc_pid_smaps_op,专门为读取进程虚拟内存区(vma)信息提供了自己的实现方法。

static int pid_smaps_open(struct inode *inode, struct file *file)
{
        return do_maps_open(inode, file, &proc_pid_smaps_op);
}

    我们也看看proc_pid_smaps_op这个file_operations结构具体是如何实现的吧:

static const struct seq_operations proc_pid_smaps_op = {
        .start  = m_start,		//返回进程的第一VMA
        .next   = m_next,		//返回进程的下一个VMA
        .stop   = m_stop,
        .show   = show_pid_smap	//打印某一个VMA的详细信息
};

private_data建立起桥梁

    如果你是内核的设计者,在有了这些数据结构之后你会如何设计seq_read()来读取一个进程所有的vma信息呢?
我们首先来看看seq_read()函数的参数:文件对应的内核数据结构file,用户态buf用于存放读取到的信息,sizeppos分别是大小和偏移。通用的seq_read()函数要将进程的vma信息读取给用户的buf,似乎也只有靠参数struct file *file来和具体的进程产生关联了。
    没错,它有一个struct seq_file指针类型的成员file->private_data,看名字我们也能够猜得到:这个private_data成员专门存放私人数据,即特定的proc文件相关的内容。对应到我们的场景就是进程相关的结构和smaps文件操作函数集proc_pid_smaps_op结构。咱们的pid_smaps_open()函数主要工作就是这件事:将进程相关的结构proc_maps_private和smaps文件操作函数结构放到struct seq_file结构指针类型的file->private_data中:

struct seq_file {
	......
        const struct seq_operations *op;	//存放smaps文件的函数句柄proc_pid_smaps_op
	......
        void *private;	//存放特定文件的私有数据proc_maps_private
};

即:

file->private_data->op = proc_pid_smaps_op
file->private_data->private = proc_maps_private  //(应该说是一个struct proc_maps_private *的一个实例)

  结构proc_pid_smaps_op前面我们已经了解过了;proc_maps_private又是什么呢?

struct proc_maps_private {
        struct inode *inode;
        struct task_struct *task;
        struct mm_struct *mm;
#ifdef CONFIG_MMU
        struct vm_area_struct *tail_vma;
#endif
#ifdef CONFIG_NUMA
        struct mempolicy *task_mempolicy;
#endif
};

    看到了吧,这个proc_maps_private结构存放了文件的inode,进程描述符、进程的内存描述符等等进程相关的结构。
这样在丰满了file->private_data结构后,后续的seq_read()就可以通过参数file里面的内容来按部就班做smaps的读取操作了。


总结一下:

cat /proc/pid/smaps主要有先后两个步骤:open()read()。
open()陷入内核后会通过/proc/pid/smaps文件相关的inode来找到进程相关信息并搜集到proc_maps_private结构中,最后将proc_maps_private结构和smaps文件具体的操作函数结构放到file->private_data中,最终open()函数返回smaps文件对应的文件描述符fd;
read()函数通过入参fd找到前面open()函数准备好的file结构,进行读取操作,详细情况将在下一集讲述。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值