一般来说,用户通过fd的传入,调用open系统调用,来获取fd,然后read的时候,通过这个fd来查找对应的file*

SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
{
long ret; if (force_o_largefile())
flags |= O_LARGEFILE; ret = do_sys_open(AT_FDCWD, filename, flags, mode);
/* avoid REGPARM breakage on x86: */
asmlinkage_protect(, ret, filename, flags, mode);
return ret;
} SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
struct file *file;
ssize_t ret = -EBADF;
int fput_needed; file = fget_light(fd, &fput_needed);
if (file) {
loff_t pos = file_pos_read(file);
ret = vfs_read(file, buf, count, &pos);
file_pos_write(file, pos);
fput_light(file, fput_needed);
} return ret;
}

所以file*是fget_light的返回值,下面通过一个crash来分析对应的file指针

file指针是fget_light的返回值,默认在rax中,调用完之后,rax赋值给了rbx,且rbx在调用vfs_read之前未更改,所以rbx里面存放的file指针,调用vfs_read之后,rvx压栈在rbp-0x18的位置

crash> dis -l vfs_read
/usr/src/debug/kernel-2.6.-.el6/linux-2.6.-.el6.x86_64/fs/read_write.c:
0xffffffff8117e9c0 <vfs_read>: push %rbp
0xffffffff8117e9c1 <vfs_read+>: mov %rsp,%rbp
0xffffffff8117e9c4 <vfs_read+>: sub $0x30,%rsp
0xffffffff8117e9c8 <vfs_read+>: mov %rbx,-0x18(%rbp)
crash> px 0xffff8817fc9e1f28-0x18
$ = 0xffff8817fc9e1f10
crash>
crash> struct file ffff880c9ddbba80
struct file {
f_u = {
fu_list = {
next = 0xffff880c860d8500,
prev = 0xffff880c1188cce8
},
fu_rcuhead = {
next = 0xffff880c860d8500,
func = 0xffff880c1188cce8
}
},
f_path = {
mnt = 0xffff88180ec5eec0,
dentry = 0xffff8818014b1540
},
f_op = 0xffffffff8161f980 <proc_sops+>,---------------------这个f_op是我们要继续分析的:
f_lock = {
raw_lock = {
slock =
}
},
f_count = {
counter =
},
f_flags = ,
f_mode = ,
f_pos = ,
f_owner = {
lock = {
raw_lock = {
lock =
}
},
pid = 0x0,
pid_type = PIDTYPE_PID,
uid = ,
euid = ,
signum =
},
f_cred = 0xffff880c0ecd8b00,
f_ra = {
start = ,
size = ,
async_size = ,
ra_pages = ,
mmap_miss = ,
prev_pos = -
},
f_version = ,
f_security = 0x0,
private_data = 0xffff8817f3eee740,
f_ep_links = {
next = 0xffff880c9ddbbb28,
prev = 0xffff880c9ddbbb28
},
f_mapping = 0xffff88100a811918
}

我们继续取对应的f_op成员分析,这个是不同的文件系统有不同的函数,proc文件系统就是

crash> struct file_operations 0xffffffff8161f980
struct file_operations {
owner = 0x0,
llseek = 0xffffffff811e3600 <proc_reg_llseek>,
read = 0xffffffff811e3540 <proc_reg_read>,-----------------这个read要继续跟进
write = 0xffffffff811e3480 <proc_reg_write>,
aio_read = 0x0,
aio_write = 0x0,
readdir = 0x0,
poll = 0xffffffff811e33d0 <proc_reg_poll>,
ioctl = 0x0,
unlocked_ioctl = 0xffffffff811e36b0 <proc_reg_unlocked_ioctl>,
compat_ioctl = 0x0,
mmap = 0xffffffff811e3320 <proc_reg_mmap>,
open = 0xffffffff811e3a50 <proc_reg_open>,
flush = 0x0,
release = 0xffffffff811e31f0 <proc_reg_release>,
fsync = 0x0,
aio_fsync = 0x0,
fasync = 0x0,
lock = 0x0,
sendpage = 0x0,
get_unmapped_area = 0x0,
check_flags = 0x0,
flock = 0x0,
splice_write = 0x0,
splice_read = 0x0,
setlease = 0x0
}
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
ssize_t ret; if (!(file->f_mode & FMODE_READ))
return -EBADF;
if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))
return -EINVAL;
if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
return -EFAULT; ret = rw_verify_area(READ, file, pos, count);
if (ret >= ) {
count = ret;
if (file->f_op->read)
ret = file->f_op->read(file, buf, count, pos);-------proc文件系统,这个为proc_ref_read
else
static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode);-----------根据inode来获取pde

一个proc文件系统的entry结构是通过PDE宏来获取的。

PDE的实现如下:

static inline struct proc_inode *PROC_I(const struct inode *inode)
{
return container_of(inode, struct proc_inode, vfs_inode);
} static inline struct proc_dir_entry *PDE(const struct inode *inode)
{
return PROC_I(inode)->pde;
}

所以vfs层的inode结构其实就是嵌入到了proc_inode中;

crash> struct -xo proc_inode
struct proc_inode {
[0x0] struct pid *pid;
[0x8] int fd;
[0x10] union proc_op op;
[0x18] struct proc_dir_entry *pde;
[0x20] struct ctl_table_header *sysctl;
[0x28] struct ctl_table *sysctl_entry;
[0x30] struct inode vfs_inode;-------------------vfs层的inode
}

通过vfs_inode获取到proc_inode之后,就会获取proc_dir_entry

crash> struct -xo proc_dir_entry
struct proc_dir_entry {
[0x0] unsigned int low_ino;
[0x4] unsigned short namelen;
[0x8] const char *name;
[0x10] mode_t mode;
[0x18] nlink_t nlink;
[0x20] uid_t uid;
[0x24] gid_t gid;
[0x28] loff_t size;
[0x30] const struct inode_operations *proc_iops;
[0x38] const struct file_operations *proc_fops;
[0x40] struct proc_dir_entry *next;
[0x48] struct proc_dir_entry *parent;
[0x50] struct proc_dir_entry *subdir;
[0x58] void *data;
[0x60] read_proc_t *read_proc;
[0x68] write_proc_t *write_proc;
[0x70] atomic_t count;
[0x74] int pde_users;
[0x78] spinlock_t pde_unload_lock;
[0x80] struct completion *pde_unload_completion;
[0x88] struct list_head pde_openers;
}
SIZE: 0x98

所以我们找出inode,就可以找出pde。

crash> struct file.f_path ffff880c9ddbba80
f_path = {
mnt = 0xffff88180ec5eec0,
dentry = 0xffff8818014b1540
} crash> struct file.f_path.dentry ffff880c9ddbba80
f_path.dentry = 0xffff8818014b1540
crash> struct dentry.d_inode 0xffff8818014b1540
d_inode = 0xffff88100a8117f8 crash> px 0xffff88100a8117f8-0x30
$ = 0xffff88100a8117c8
crash> struct proc_inode.pde 0xffff88100a8117c8
pde = 0xffff880c0ef13b00
crash> struct proc_dir_entry.proc_fops 0xffff880c0ef13b00
proc_fops = 0xffffffff8161cdc0
crash> struct file_operations 0xffffffff8161cdc0
struct file_operations {
owner = 0x0,
llseek = 0xffffffff811a0490 <seq_lseek>,
read = 0xffffffff811a0950 <seq_read>,
write = 0xffffffff811687c0 <slabinfo_write>,
aio_read = 0x0,
aio_write = 0x0,
readdir = 0x0,
poll = 0x0,
ioctl = 0x0,
unlocked_ioctl = 0x0,
compat_ioctl = 0x0,
mmap = 0x0,
open = 0xffffffff81165640 <slabinfo_open>,----------------最终调用的open
flush = 0x0,
release = 0xffffffff8119fde0 <seq_release>,
fsync = 0x0,
aio_fsync = 0x0,
fasync = 0x0,
lock = 0x0,
sendpage = 0x0,
get_unmapped_area = 0x0,
check_flags = 0x0,
flock = 0x0,
splice_write = 0x0,
splice_read = 0x0,
setlease = 0x0
}
static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode);
ssize_t rv = -EIO;
ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); spin_lock(&pde->pde_unload_lock);
if (!pde->proc_fops) {
spin_unlock(&pde->pde_unload_lock);
return rv;
}
pde->pde_users++;
read = pde->proc_fops->read;--------------------调用的read是seq_read
spin_unlock(&pde->pde_unload_lock); if (read)
rv = read(file, buf, count, ppos); pde_users_dec(pde);
return rv;
}

所以本案例中,proc文件系统最终调用的read是seq_read,open是:slabinfo_open,当然,这个跟案例相关,因为proc的封装是到proc_reg_read 为止,函数proc_reg_read是vfs read进入proc的入口。看linux源码的时候,不能只见树木,不见森林,要有清晰的层次概念。后面的实现与不同的proc文件相关,不同的层次完成不同的功能是linux的设计哲学,由于seq_read是seq类文件的封装,实现在seq_file.c中,主要功能如下:

ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
struct seq_file *m = (struct seq_file *)file->private_data;
。。。。。
p = m->op->start(m, &pos);
while () {
err = PTR_ERR(p);
if (!p || IS_ERR(p))
break;
err = m->op->show(m, p);
if (err < )
break;
if (unlikely(err))
m->count = ;
if (unlikely(!m->count)) {
p = m->op->next(m, p, &pos);
m->index = pos;
continue;
}
if (m->count < m->size)
goto Fill;
m->op->stop(m, p);
kfree(m->buf);
m->buf = kmalloc(m->size <<= , GFP_KERNEL);
if (!m->buf)
goto Enomem;
m->count = ;
m->version = ;
pos = m->index;
p = m->op->start(m, &pos);
}
m->op->stop(m, p);
m->count = ;
goto Done;
Fill:
ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
struct seq_file *m = (struct seq_file *)file->private_data;
。。。。。
p = m->op->start(m, &pos);------------标准的start
while () {
err = PTR_ERR(p);
if (!p || IS_ERR(p))
break;
err = m->op->show(m, p);
if (err < )
break;
if (unlikely(err))
m->count = ;
if (unlikely(!m->count)) {
p = m->op->next(m, p, &pos);----next动作
m->index = pos;
continue;
}
if (m->count < m->size)
goto Fill;
m->op->stop(m, p);----stop动作
kfree(m->buf);
m->buf = kmalloc(m->size <<= , GFP_KERNEL);
if (!m->buf)
goto Enomem;
m->count = ;
m->version = ;
pos = m->index;
p = m->op->start(m, &pos);-----循环start
}
m->op->stop(m, p);
m->count = ;
goto Done;
Fill:
crash> struct file.private_data ffff880c9ddbba80
private_data = 0xffff8817f3eee740
crash> struct seq_file 0xffff8817f3eee740
struct seq_file {
buf = 0xffff8817f4448000 "size-65536(DMA) 0 0 65536 1 16 : tunables 8 4 0 : slabdata 0 0 0\nsize-65536 19 19 65536 1 16 : tunables 8 4 0 : slabdata 19 19 0\nsize-32768(DMA) 0 0 32768 "...,
size = ,
from = ,
count = ,
index = ,
read_pos = ,
version = ,
lock = {
count = {
counter =
},
wait_lock = {
raw_lock = {
slock =
}
},
wait_list = {
next = 0xffff8817f3eee780,
prev = 0xffff8817f3eee780
},
owner = 0xffff8817fc9e0000
},
op = 0xffffffff8161cea0 <proc_slabinfo_operations+>,
private = 0x0
}
crash> struct seq_file
struct seq_file {
char *buf;
size_t size;
size_t from;
size_t count;
loff_t index;
loff_t read_pos;
u64 version;
struct mutex lock;
const struct seq_operations *op;
void *private;
}
SIZE:
crash> struct seq_operations 0xffffffff8161cea0
struct seq_operations {
start = 0xffffffff81165680 <s_start>,
stop = 0xffffffff811652f0 <s_stop>,
next = 0xffffffff81165660 <s_next>,
show = 0xffffffff81166420 <s_show>
}

有seq_file,一般就会设计seq_operations,上面就是分析对应seq_operations中的函数的情况。由于后面已经不是proc文件系统层的范畴,本文结束。

用crash来分析一下proc的文件访问的更多相关文章

  1. 使用 Crash 工具分析 Linux dump 文件

    转自:http://blog.csdn.net/commsea/article/details/11804897 简介: Linux 内核由于其复杂性,使得对内核出现的各种异常的追踪变得异常困难.本文 ...

  2. /proc/cpuinfo 文件分析(查看CPU信息)

    /proc/cpuinfo文件分析 根据以下内容,我们则可以很方便的知道当前系统关于CPU.CPU的核数.CPU是否启用超线程等信息. <1>查询系统具有多少个逻辑核:cat /proc/ ...

  3. [转]linux /proc/cpuinfo 文件分析

    在Linux系统中,提供了proc文件系统显示系统的软硬件信息.如果想了解系统中CPU的提供商和相关配置信息,则可以通过/proc/cpuinfo文件得到.本文章针对该文件进行简单的总结. 基于不同指 ...

  4. iOS crash日志分析

    iOS crash日志分析 一. 寻找crash文件:手机崩溃后的ips或者crash文件(ips文件可以直接修改成crash文件,直接改后缀名就可以了),这里说下如何拿到crash文件 1. 把运行 ...

  5. Windbg分析蓝屏Dump文件

    一.WinDbg是什么?它能做什么? WinDbg是在windows平台下,强大的用户态和内核态调试工具.它能够通过dmp文件轻松的定位到问题根源,可用于分析蓝屏.程序崩溃(IE崩溃)原因,是我们日常 ...

  6. iOS symbolicatecrash工具crash日志分析

    若一个App没有加入Crashlytics或者Buggly这些崩溃日志监控,那么我们在App崩溃的时候如何获取崩溃信息呢? 此时我们可以通过symbolicatecrash工具对手机日志来进行分析定位 ...

  7. 14、/proc/cpuinfo 文件(查看CPU信息)

    转载http://www.cnblogs.com/itcomputer/p/4888438.html /proc/cpuinfo文件分析 根据以下内容,我们则可以很方便的知道当前系统关于CPU.CPU ...

  8. Crash日志分析

    从Crash文件出发解决bug的一般步骤,分三步: a, 获取设备上的崩溃日志. b, 分析崩溃日志,找到报错位置(定位到函数和代码行数). c, 打开代码,改bug. 1, 获取设备日志 1. 在可 ...

  9. iOS App Crash原理分析

    预备知识:OS X系统分析 1.内核XNU是Darwin的核心,也是整个OS X的核心.XNU本身由以下几个组件构成: Mach微核心 BSD层 libKern I/O Kit 此外,内核是模块化的, ...

随机推荐

  1. is与==

     is和==的区别 1. id() 通过id()我们可以查看到⼀一个变量表示的值在内存中的地址. a1 = 100 b1 = 100 print(id(a1),id(b1)) #14071247240 ...

  2. CVE-2017-8570漏洞利用

    CVE-2017-8570漏洞是一个逻辑漏洞,利用方法简单,影响范围广.由于该漏洞和三年前的SandWorm(沙虫)漏洞非常类似,因此我们称之为“沙虫”二代漏洞. 编号 CVE-2017-8570 影 ...

  3. bootstrap样式

    图片: <img src="w.jpg" alt="" class="img-rounded"><img src=&quo ...

  4. 02-zip文件打包

    package com.day1; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStre ...

  5. kafka的安装 (单机)

    https://blog.csdn.net/q282176713/article/details/81112988

  6. CS229 6.1 Neurons Networks Representation

    面对复杂的非线性可分的样本是,使用浅层分类器如Logistic等需要对样本进行复杂的映射,使得样本在映射后的空间是线性可分的,但在原始空间,分类边界可能是复杂的曲线.比如下图的样本只是在2维情形下的示 ...

  7. Hadoop2.6.5集群搭建

    一. Hadoop的分布式模型 Hadoop通常有三种运行模式:本地(独立)模式.伪分布式(Pseudo-distributed)模式和完全分布式(Fully distributed)模式.安装完成后 ...

  8. LeetCode 5. Longest Palindromic Substring & 回文字符串

    Longest Palindromic Substring 回文这种简单的问题,在C里面印象很深啊.希望能一次过. 写的时候才想到有两种情况: 454(奇数位) 4554(偶数位) 第1次提交 cla ...

  9. mongodb的管理员和安全认证

    超级管理员 为了更安全的访问mongodb,需要访问者提供用户名和密码,于是需要在mongodb中创建用户 采用了角色-用户-数据库的安全管理方式 常用系统角色如下: root:只在admin数据库中 ...

  10. 【学习】Python解决汉诺塔问题

    参考文章:http://www.cnblogs.com/dmego/p/5965835.html   一句话:学程序不是目的,理解就好:写代码也不是必然,省事最好:拿也好,查也好,解决问题就好!   ...