Chinaunix首页 | 论坛 | 博客
  • 博客访问: 96821
  • 博文数量: 29
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2014-09-01 19:43
文章分类
文章存档

2017年(4)

2015年(24)

2014年(1)

我的朋友

分类: LINUX

2017-02-27 11:20:50

浅析/proc/sys/目录下sysctl文件显示和read读操作全过程

《浅析linux内核__sysctl_head_next()函数内部实现》

1.安装/proc/sys目录对应的节点和文件操作方法集
proc_root_init
==>proc_sys_init();
int proc_sys_init(void)
{
    proc_sys_root = proc_mkdir("sys", NULL); // 创建/proc/sys/目录,将de挂到/proc->subdir下面,以便proc_root_lookup使用,这里同时添加了 dp->low_ino唯一id号.
    proc_sys_root->proc_iops = &proc_sys_inode_operations;// 替换为自己的iops,这样"sys"目录项dentry对应的inode的i_fop就等于proc_sys_inode_operations
    proc_sys_root->proc_fops = &proc_sys_file_operations;
    proc_sys_root->nlink = 0;
    return 0;
}
static const struct inode_operations proc_sys_inode_operations = {
    .lookup = proc_sys_lookup, // 用来检查sys/目录下名为dentry->d_name.name的文件或目录是否存在,sys/目录下所有有文件都是来自 sysctl_head_next链表上的header->table元素,如果存在,调用proc_sys_make_inode()为其创建节点.
    .permission = proc_sys_permission,
    .setattr = proc_sys_setattr,
};

2.执行cat /proc/sys/kernel/printk命令
proc_sys_inode_operations.proc_sys_lookup()将执行,
首先创建kernel目录项和inode节点,
然后创建printk目录项和inode节点[luther.gliethttp].
static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
                    struct nameidata *nd)
{
    struct ctl_table_header *head;
    struct inode *inode;
    struct dentry *err;
    struct ctl_table *table;

    err = ERR_PTR(-ENOENT);
    // 比如查找/proc/sys/kernel目录,那么返回的table就是后面root_table[]中的
    // static struct ctl_table root_table[] = {
    //     {
    //         .ctl_name    = CTL_KERN,
    //         .procname    = "kernel",
    //         .mode        = 0555,
    //         .child        = kern_table,
    //     },
    //     {
    //         .ctl_name    = CTL_VM,
    //         .procname    = "vm",
    //         .mode        = 0555,
    //         .child        = vm_table,
    //     },
    //     {
    //         .ctl_name    = CTL_FS,
    //         .procname    = "fs",
    //         .mode        = 0555,
    //         .child        = fs_table,
    //     },
    //     {
    //         .ctl_name    = CTL_DEBUG,
    //         .procname    = "debug",
    //         .mode        = 0555,
    //         .child        = debug_table,
    //     },
    //     {
    //         .ctl_name    = CTL_DEV,
    //         .procname    = "dev",
    //         .mode        = 0555,
    //         .child        = dev_table,
    //     },
    // /*
    // * NOTE: do not add new entries to this table unless you have read
    // * Documentation/sysctl/ctl_unnumbered.txt
    // */
    //     { .ctl_name = 0 }
    // };
    //{
    //    .ctl_name    = CTL_KERN,
    //    .procname    = "kernel",
    //    .mode        = 0555,
    //    .child        = kern_table,
    //},
    //kern_table[] = {
    // ......
    // #if defined CONFIG_PRINTK
    //    {
    //        .ctl_name    = KERN_PRINTK,
    //        .procname    = "printk",
    //        .data        = &console_loglevel,
    //        .maxlen        = 4*sizeof(int),
    //        .mode        = 0644,
    //        .proc_handler    = &proc_dointvec, // 操作.data = &console_loglevel,数据的方法[luther.gliethttp]
    //    },
    // .......
    //}
    // 返回kernel所在table的child对应的printk对应table.[luther.gliethttp]
     table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); // 源码见后

    if (!table)
        goto out;
    // 好了,现在这个就是printk所在的table了
    err = ERR_PTR(-ENOMEM);
    // 为该printk文件建立inode,源码见后
    inode = proc_sys_make_inode(dir, table);
    if (!inode)
        goto out;

    err = NULL;
    dentry->d_op = &proc_sys_dentry_operations;
    d_add(dentry, inode);

out:
    sysctl_head_finish(head);
    return err;
}

比如查找/proc/sys/kernel,那么这里parent就指向sys
static struct ctl_table *do_proc_sys_lookup(struct dentry *parent,
                        struct qstr *name,
                        struct ctl_table_header **ptr)
{
    struct ctl_table_header *head;
    struct ctl_table *table = NULL;
    
    // 遍历root_table_header挂接的所有header
    for (head = sysctl_head_next(NULL); head;
            head = sysctl_head_next(head)) {
        // 第一次见到的就是root_table,它的代码见后面[luther.gliethttp]
        table = proc_sys_lookup_entry(parent, name, head->ctl_table); // 传入的是head->ctl_table顶层table
        if (table)
            break;
    }
    *ptr = head;
    return table;
}

static struct ctl_table *proc_sys_lookup_entry(struct dentry *dparent,
                        struct qstr *name,
                        struct ctl_table *table)
{
    // 传入的是head->ctl_table顶层table,所以这里的table为header_list所代表header的第一个顶层table
    table = proc_sys_lookup_table(dparent, table);
    if (table)
        table = proc_sys_lookup_table_one(table, name); // 检查该table是否含有名为name的一个项.
    return table;
}

static struct ctl_table *proc_sys_lookup_table(struct dentry *dentry,
                        struct ctl_table *table)
{
    struct dentry *ancestor;
    struct proc_inode *ei;
    int depth, i;
// 首先取出dentry所代表的proc_inode对应的fd,该fd对应该dentry在目录体系中
// 自己所处目录深度[luther.gliethttp],
// /proc/sys/ 目录深度fd为0
// /proc/sys/kernel 目录深度fd为1
// /proc/sys/kernel/printk 目录深度fd为2
    ei = PROC_I(dentry->d_inode);
    depth = ei->fd;

    if (depth == 0) // 因为这里查询的是"kernel"目录,所以dentry为"sys",所以它的depth等于0
        return table;
// 如果这里查询的是/proc/sys/kernel/printk
// 那么dentry就指向了"kernel",所以这里depth就等于1
// 传入的是head->ctl_table顶层table,所以这里的table为header_list所代表header的第一个顶层table
// 所以下面将从1层开始,从顶层head_table依次遍历child,这样来检出所需目录或文件[luther.gliethttp]
    for (i = 1; table && (i <= depth); i++) { // 保证i从1到depth之间,同时一旦当前table找不到ancestor->d_name文件,那么for将退出
        ancestor = proc_sys_ancestor(dentry, i); // 从第1层开始,依次检查相应table中是否真实存在ancestor->d_name文件或目录[luther.gliethttp]
        table = proc_sys_lookup_table_one(table, &ancestor->d_name); // 检查该table是否包含名为ancestor->d_name的文件或目录
        if (table)
            table = table->child; // 如果table包含名为ancestor->d_name的文件或目录,那么child就是管理i+1层目录深度的table
// 就这样一层层的搜索到depth目录深度对应的table,这是一个很有耐心很讲究章法的分层过程[luther.gliethttp]
    }
    return table;

}

// 从dentry所在目录深度出发,向回追溯到目录深度为depth的父dentry
static struct dentry *proc_sys_ancestor(struct dentry *dentry, int depth)
{
    for (;;) {
        struct proc_inode *ei;

        ei = PROC_I(dentry->d_inode);
        if (ei->fd == depth)
            break; /* found */ // 该目录深度fd等于depth,ok我们找到了指定深度的目录项[luther.gliethttp]

        dentry = dentry->d_parent;
    }
    return dentry;
}

static struct inode *proc_sys_make_inode(struct inode *dir, struct ctl_table *table)
{
    struct inode *inode;
    struct proc_inode *dir_ei, *ei;
    int depth;

    inode = new_inode(dir->i_sb);
    if (!inode)
        goto out;

    /* A directory is always one deeper than it's parent */
    dir_ei = PROC_I(dir);
    depth = dir_ei->fd + 1; // 新创建的目录永远比当前它的parent的深度多1,所以这里加1,/proc/sys的深度为0

    ei = PROC_I(inode);
    ei->fd = depth; // 记住自己的深度,这样在我下面创建的目录,将以我的该fd为基数,作上面的加1运算[luther.gliethttp].
    inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
    inode->i_op = &proc_sys_inode_operations;
    inode->i_fop = &proc_sys_file_operations;
    inode->i_flags |= S_PRIVATE; /* tell selinux to ignore this inode */
    proc_sys_refresh_inode(inode, table);
// 如果该table有proc_handler处理函数,那么他是一个文件,否则该table是一个目录,所以该inode也就是一个相应目录[luther.gliethttp]
out:
    return inode;
}

static void proc_sys_refresh_inode(struct inode *inode, struct ctl_table *table)
{
    /* Refresh the cached information bits in the inode */
    if (table) {
        inode->i_uid = 0;
        inode->i_gid = 0;
        inode->i_mode = table->mode;
        if (table->proc_handler) {
            inode->i_mode |= S_IFREG; // 如果该管理该inode的table含有proc_handler,那么说明该inode是一个文件[luther.gliethttp].
            inode->i_nlink = 1;
        } else {
            inode->i_mode |= S_IFDIR;
            inode->i_nlink = 0;    /* It is too hard to figure out */
        }
    }
}


因为在/proc/sys/目录下的所有文件,不论是目录文件还是普通用文件,他们都有唯一的文件方法集f_ops,:proc_sys_file_operations
static const struct file_operations proc_sys_file_operations = {
    .read        = proc_sys_read,
    .write        = proc_sys_write,
    .readdir    = proc_sys_readdir,
};
来看看sys_read()==>proc_sys_read()是怎么区分目录文件和普通文件的.
比如我们执行如下shell命令:
luther@gliethttp:~$ cat /proc/sys/kernel/printk
4    4    1    7
static ssize_t proc_sys_read(struct file *filp, char __user *buf,
                size_t count, loff_t *ppos)
{
    struct dentry *dentry = filp->f_dentry;
    struct ctl_table_header *head;
    struct ctl_table *table;
    ssize_t error;
    size_t res;
   //kern_table[] = {
    // ......
    // #if defined CONFIG_PRINTK
    //    {
    //        .ctl_name    = KERN_PRINTK,
    //        .procname    = "printk",
    //        .data        = &console_loglevel,
    //        .maxlen        = 4*sizeof(int),
    //        .mode        = 0644,
    //        .proc_handler    = &proc_dointvec, // 操作.data = &console_loglevel,数据的方法[luther.gliethttp]
    //    },
    // .......
    //}
    // 返回kernel所在table的child对应的printk对应table.[luther.gliethttp]
    table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
    /* Has the sysctl entry disappeared on us? */
    error = -ENOENT;
    if (!table)
        goto out;

    /* Has the sysctl entry been replaced by a directory? */
    error = -EISDIR;
    if (!table->proc_handler) // 该table没有proc_handler处理函数,说明它是一个目录,不能对一个目录执行read操作,只能执行proc_sys_readdir
        goto out;

    /*
     * At this point we know that the sysctl was not unregistered
     * and won't be until we finish.
     */

    error = -EPERM;
    if (sysctl_perm(table, MAY_READ))
        goto out;

    /* careful: calling conventions are nasty here */
    res = count;
    error = table->proc_handler(table, 0/* 0表示读操作,1表示写操作*/, filp, buf, &res, ppos); // 执行proc_dointvec
    if (!error)
        error = res;
out:
    sysctl_head_finish(head);

    return error;
}

阅读(2748) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~