Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15309793
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类: LINUX

2008-10-19 18:59:06


浅析proc文件系统的创建和create_proc_read_entry函数的具体实现
start_kernel
=>proc_root_init
和http://blog.chinaunix.net/u1/38994/showart_1328541.html
《浅析kern_mount加载sysfs的流程》细节部分一模一样,

   因为sysfs是一个内存文件系统,所以文件的物理存储关系就需要使用sd来维护,因此sysfs_dirent即sd就类似于硬盘中的磁道.
   sysfs文件系统是一个排它式的文件系统,不论被mount多少次都只产生一个sb超级块,
如果尝试再次mount,即尝试再次调用sysfs_get_sb获取另一个sb超级块,那么将执行atomic_inc(old->s_active);增加
已被mount的引用计数,然后如果s已经执行了alloc_super,那么调用destroy_super将其销毁,然后返回这个已被mount了的
super_block超级块old,这样就实现了sysfs文件系统不论被mount多少次都只产生一个sb超级块的效果,所以取名为get_sb_single[luther.gliethttp]

  类似的:proc也是一个内存文件系统,所以文件的物理存储关系是通过proc_dir_entry来维护,因此proc_dir_entry即dp就类似硬盘中的物理磁道.
  proc的sb超级块和sysfs一样,不论被mount多少次也都只产生一个sb超级块.
如果de指定了内容,那么,将inode格式化为de这个"物理介质上的文件"的所对应的信息
这和sysfs的实现方式一模一样[luther.gliethttp].

proc_mnt = kern_mount_data(&proc_fs_type, &init_pid_ns);执行完成之后,proc_root这个"物理介质上的文件"对应的内存inode节点就创建出来了,也就是proc_root这个"物理介质上的文件"信息成功的被加载到kernel内存,翻译成了kernel所需的存在形式,因此内核将知晓该proc_root"物理介质上的文件"的存在,创建查找文件之类的操作也就能够紧接着可以使用[luther.gliethttp]
  将dp这个'物理文件'添加到'物理磁盘'的文件视图链表中,以供以后查找访问
  这是一个从proc_root开始遍历所有子目录的过程函数,以下纯属'物理磁盘'性质的操作,所以和inode没有产生任何关系,
将在proc_lookup函数中来建立这种'物理磁盘文件'和inode之间一一对应的映射关联[luther.gliethttp]

//在'物理磁盘'上创建'物理文件',然后替换为我们这里指定的proc_fops方法
//可以用来在'物理磁盘'上创建文件型的'物理文件'或者目录型的'物理文件',完全由mode指定[luther.gliethttp]
void create_seq_entry(char *name, mode_t mode, const struct file_operations *f);
//固定方法,创建目录型的'物理文件'
struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent);
//可以用来在'物理磁盘'上创建文件型的'物理文件'或者目录型的'物理文件'
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent);

init进程会执行:
mkdir("/proc", 0755);
mount("proc", "/proc", "proc", 0, NULL);//将proc文件系统可视化的挂载到创建出来的/proc目录下

struct proc_dir_entry proc_root = {
    .low_ino = PROC_ROOT_INO,
    .namelen = 5,
    .name = "/proc",
    .mode = S_IFDIR | S_IRUGO | S_IXUGO,
    .nlink = 2,
    .count = ATOMIC_INIT(1),
    .proc_iops = &proc_root_inode_operations,
    .proc_fops = &proc_root_operations,
    .parent = &proc_root,
};
void __init proc_root_init(void)
{
    int err = proc_init_inodecache();
    if (err)
        return;
    err = register_filesystem(&proc_fs_type);//登记proc文件系统结构体
//参看http://blog.chinaunix.net/u1/38994/showart_1328559.html
//《浅析sysfs文件系统注册流程》
    if (err)
        return;
    proc_mnt = kern_mount_data(&proc_fs_type, &init_pid_ns);//生成"proc"文件系统的mount节点
//参看http://blog.chinaunix.net/u1/38994/showart_1328541.html
//《浅析kern_mount加载sysfs的流程》
    err = PTR_ERR(proc_mnt);
    if (IS_ERR(proc_mnt)) {
        unregister_filesystem(&proc_fs_type);
        return;
    }

    proc_misc_init();

    proc_net_init();

#ifdef CONFIG_SYSVIPC
    proc_mkdir("sysvipc", NULL);
#endif
    proc_root_fs = proc_mkdir("fs", NULL);
    proc_root_driver = proc_mkdir("driver", NULL);
    proc_mkdir("fs/nfsd", NULL); /* somewhere for the nfsd filesystem to be mounted */
#if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
    /* just give it a mountpoint */
    proc_mkdir("openprom", NULL);
#endif
    proc_tty_init();
#ifdef CONFIG_PROC_DEVICETREE
    proc_device_tree_init();
#endif
    proc_bus = proc_mkdir("bus", NULL);
    proc_sys_init();
}
static struct file_system_type proc_fs_type = {
    .name = "proc",
    .get_sb = proc_get_sb,
    .kill_sb = proc_kill_sb,
};
static int proc_get_sb(struct file_system_type *fs_type,
    int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
    int err;
    struct super_block *sb;
    struct pid_namespace *ns;
    struct proc_inode *ei;

    if (proc_mnt) {
//已经被执行过一次kern_mount_data(&proc_fs_type, &init_pid_ns);了
        /* Seed the root directory with a pid so it doesn't need
         * to be special in base.c. I would do this earlier but
         * the only task alive when /proc is mounted the first time
         * is the init_task and it doesn't have any pids.
         */

        ei = PROC_I(proc_mnt->mnt_sb->s_root->d_inode);
        if (!ei->pid)
            ei->pid = find_get_pid(1);
    }

    if (flags & MS_KERNMOUNT)
        ns = (struct pid_namespace *)data;
    else
        ns = current->nsproxy->pid_ns;

    sb = sget(fs_type, proc_test_super, proc_set_super, ns);
    if (IS_ERR(sb))
        return PTR_ERR(sb);

    if (!sb->s_root) {
        sb->s_flags = flags;
        err = proc_fill_super(sb);//只有第一次生成sb才会执行此句来填充超级块
        if (err) {
            up_write(&sb->s_umount);
            deactivate_super(sb);
            return err;
        }

        ei = PROC_I(sb->s_root->d_inode);//找到包含sb超级块对应的inode的proc_inode指针
        if (!ei->pid) {
            rcu_read_lock();
            ei->pid = get_pid(find_pid_ns(1, ns));
            rcu_read_unlock();
        }

        sb->s_flags |= MS_ACTIVE;
        ns->proc_mnt = mnt;//指定current内核管理结构体的proc_mnt信息
    }

    return simple_set_mnt(mnt, sb);//做一个简单mnt设置
}
int proc_fill_super(struct super_block *s)
{
    struct inode * root_inode;

    s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC;
    s->s_blocksize = 1024;
    s->s_blocksize_bits = 10;
    s->s_magic = PROC_SUPER_MAGIC;
    s->s_op = &proc_sops;//sb超级块操作函数集
    s->s_time_gran = 1;
    
    de_get(&proc_root);
    root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);
//和sysfs的实现一模一样,请参考《浅析kern_mount加载sysfs的流程》
    if (!root_inode)
        goto out_no_root;
    root_inode->i_uid = 0;
    root_inode->i_gid = 0;
    s->s_root = d_alloc_root(root_inode);
    if (!s->s_root)
        goto out_no_root;
    return 0;

out_no_root:
    printk("proc_read_super: get root inode failed\n");
    iput(root_inode);
    de_put(&proc_root);
    return -ENOMEM;
}
struct inode *proc_get_inode(struct super_block *sb, unsigned int ino,
                struct proc_dir_entry *de)
{
    struct inode * inode;

    if (de != NULL && !try_module_get(de->owner))
        goto out_mod;

    inode = iget_locked(sb, ino);//生成inode,和sysfs不同的是
//proc超级块的s_op = &proc_sops;操作函数集自定义了.alloc_inode = proc_alloc_inode,
//节点生成函数,而sysfs使用的是
//inode = (struct inode *) kmem_cache_alloc(inode_cachep, GFP_KERNEL);//所以从cache中获取一个空闲染色的slab对象
//
    if (!inode)
        goto out_ino;
    if (inode->i_state & I_NEW) {
        inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
        PROC_I(inode)->fd = 0;
        PROC_I(inode)->pde = de;//该inode对应"物理介质上的文件"--de
        if (de) {
//如果de指定了内容,那么,将inode格式化为de这个"物理介质上的文件"的所对应的信息
//这和sysfs的实现方式一模一样[luther.gliethttp].
            if (de->mode) {
                inode->i_mode = de->mode;
                inode->i_uid = de->uid;
                inode->i_gid = de->gid;
            }
            if (de->size)
                inode->i_size = de->size;
            if (de->nlink)
                inode->i_nlink = de->nlink;
            if (de->proc_iops)
                inode->i_op = de->proc_iops;
            if (de->proc_fops) {
                if (S_ISREG(inode->i_mode)) {
#ifdef CONFIG_COMPAT
                    if (!de->proc_fops->compat_ioctl)
                        inode->i_fop =
                            &proc_reg_file_ops_no_compat;
                    else
#endif
                        inode->i_fop = &proc_reg_file_ops;
                } else {
                    inode->i_fop = de->proc_fops;
                }
            }
        }
        unlock_new_inode(inode);
    }
    return inode;

out_ino:
    if (de != NULL)
        module_put(de->owner);
out_mod:
    return NULL;
}
//proc文件系统自定义特殊化的inode节点生成函数
static struct inode *proc_alloc_inode(struct super_block *sb)
{
    struct proc_inode *ei;
    struct inode *inode;

    ei = (struct proc_inode *)kmem_cache_alloc(proc_inode_cachep, GFP_KERNEL);
    if (!ei)
        return NULL;
    ei->pid = NULL;
    ei->fd = 0;
    ei->op.proc_get_link = NULL;
    ei->pde = NULL;
    inode = &ei->vfs_inode;//ei的vfs_inode为inode节点
    inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;//当前时间
    return inode;
}
====================================================
看看proc文件系统下如何创建文件
void __init proc_misc_init(void)
{
    static struct {
        char *name;
        int (*read_proc)(char*,char**,off_t,int,int*,void*);
    } *p, simple_ones[] = {
        {"loadavg", loadavg_read_proc},
        {"uptime", uptime_read_proc},
        {"meminfo", meminfo_read_proc},
        {"version", version_read_proc},
#ifdef CONFIG_PROC_HARDWARE
        {"hardware", hardware_read_proc},
#endif
#ifdef CONFIG_STRAM_PROC
        {"stram", stram_read_proc},
#endif
        {"filesystems", filesystems_read_proc},
        {"cmdline", cmdline_read_proc},
        {"execdomains", execdomains_read_proc},
        {NULL,}
    };
    for (p = simple_ones; p->name; p++)
        create_proc_read_entry(p->name, 0, NULL, p->read_proc, NULL);//我们就分析这个函数,其它函数大同小异[luther.gliethttp]
    ...
}
//在'物理磁盘'上创建'物理文件',然后替换为我们这里指定的读方法
static inline struct proc_dir_entry *create_proc_read_entry(const char *name,
    mode_t mode, struct proc_dir_entry *base,
    read_proc_t *read_proc, void * data)
{
    struct proc_dir_entry *res=create_proc_entry(name,mode,base);
    if (res) {
        res->read_proc=read_proc;//替换为我们期望的,指定的读方法
        res->data=data;
    }
    return res;
}
//在parent"物理存储空间"下创建一个新的"物理文件"[luther.gliethttp]
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
                     struct proc_dir_entry *parent)
{
    struct proc_dir_entry *ent;
    nlink_t nlink;

    if (S_ISDIR(mode)) {//目录
        if ((mode & S_IALLUGO) == 0)
            mode |= S_IRUGO | S_IXUGO;
        nlink = 2;
    } else {
        if ((mode & S_IFMT) == 0)
            mode |= S_IFREG;//普通文件
        if ((mode & S_IALLUGO) == 0)
            mode |= S_IRUGO;
        nlink = 1;
    }

    ent = __proc_create(&parent, name, mode, nlink);//检查,创建,同时获得parent的"物理存储空间"信息
    if (ent) {
        if (proc_register(parent, ent) < 0) {
            kfree(ent);
            ent = NULL;
        }
    }
    return ent;
}
static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
                     const char *name,
                     mode_t mode,
                     nlink_t nlink)
{
    struct proc_dir_entry *ent = NULL;
    const char *fn = name;
    int len;

    /* make sure name is valid */
    if (!name || !strlen(name)) goto out;

    if (!(*parent) && xlate_proc_name(name, parent, &fn) != 0)
        goto out;

    /* At this point there must not be any '/' characters beyond *fn */
    if (strchr(fn, '/'))//fn为最终需要创建的文件名,所以不会存在'/'字符
        goto out;

    len = strlen(fn);

    ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL);
//proc_dir_entry结构体的记下来内存用来存放这个'物理文件'的文件名[luther.gliethttp].
    if (!ent) goto out;

    memset(ent, 0, sizeof(struct proc_dir_entry));
    memcpy(((char *) ent) + sizeof(struct proc_dir_entry), fn, len + 1);//拷贝name文件名,到这个'物理磁盘文件存储空间'
    ent->name = ((char *) ent) + sizeof(*ent);//name紧邻proc_dir_entry结构体之后的剩余内存
//所以这样就导致了不能使用定大小的slab对象方式来以着色手段快速和基本不浪费内存的方式获取结构体,
//所以不论从速度还是内存利用率上proc生成文件都是低效的[luther.gliethttp]
    ent->namelen = len;
    ent->mode = mode;//填充dp->mode该'物理文件'的文件模式属性[luther.gliethttp]
    ent->nlink = nlink;
    atomic_set(&ent->count, 1);
    ent->pde_users = 0;
    spin_lock_init(&ent->pde_unload_lock);
    ent->pde_unload_completion = NULL;
 out:
    return ent;
}
//这是一个从proc_root开始遍历所有子目录的过程函数,以下纯属'物理磁盘'性质的操作,所以和inode没有产生任何关系,
//将在proc_lookup函数中来建立这种'物理磁盘文件'和inode之间一一对应的映射关联[luther.gliethttp]
static int xlate_proc_name(const char *name,
             struct proc_dir_entry **ret, const char **residual)
{
    const char *cp = name, *next;
    struct proc_dir_entry *de;
    int len;
    int rtn = 0;

    spin_lock(&proc_subdir_lock);
    de = &proc_root;//如果name本来就没有'/'路径,那么将直接将proc_root作为parent返回
    while (1) {
        next = strchr(cp, '/');
        if (!next)
            break;//查到最后一个或者根本就没有'/'绝对路径

        len = next - cp;
        for (de = de->subdir; de ; de = de->next) {
            if (proc_match(len, cp, de))//简单的比较文件名是否相同
                break;
        }
//执行到这里说明是以'/'开头的绝对路径,所以必须保证每一个父目录的存在.
        if (!de) {
            rtn = -ENOENT;//尝试在一个缺失父目录的路径下创建文件,错误返回[luther.gliethttp]
            goto out;
        }
        cp += len + 1;//继续查找进一层的parent父目录在这个"物理存储系统"上是否存在,我们期望它存在.
    }
    *residual = cp;//返回路径的最后文件名,比如:'luther/gliethttp',那么如果luther存在,那么cp等于'gliethttp'
    *ret = de;//比如:'luther/gliethttp',需要生成的gliethttp文件的parent为de,也就是'luther'这个'物理存储'文件.
out:
    spin_unlock(&proc_subdir_lock);
    return rtn;
}
static int proc_match(int len, const char *name, struct proc_dir_entry *de)
{
    if (de->namelen != len)
        return 0;
    return !memcmp(name, de->name, len);
}
//维护'物理磁盘文件'之间的关联图谱[luther.gliethttp]
static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
{
    unsigned int i;
    struct proc_dir_entry *tmp;
    
    i = get_inode_number();//从radix树上获得一个顺序生成的唯一整数,作为inode
    if (i == 0)
        return -EAGAIN;
    dp->low_ino = i;//inode节点号
//根据mode来填充操作该'物理磁盘文件'的操作函数集,前提是dp没有自定义自己的操作函数处理集[luther.gliethttp]
    if (S_ISDIR(dp->mode)) {
        if (dp->proc_iops == NULL) {//没指定
            dp->proc_fops = &proc_dir_operations;
            dp->proc_iops = &proc_dir_inode_operations;
        }
        dir->nlink++;
    } else if (S_ISLNK(dp->mode)) {
        if (dp->proc_iops == NULL)//没指定
            dp->proc_iops = &proc_link_inode_operations;
    } else if (S_ISREG(dp->mode)) {
        if (dp->proc_fops == NULL)//没指定
            dp->proc_fops = &proc_file_operations;
        if (dp->proc_iops == NULL)//没指定
            dp->proc_iops = &proc_file_inode_operations;
    }

    spin_lock(&proc_subdir_lock);

    for (tmp = dir->subdir; tmp; tmp = tmp->next)
//遍历dir目录,保证dp文件不存在'物理磁盘'上.
        if (strcmp(tmp->name, dp->name) == 0) {
            printk(KERN_WARNING "proc_dir_entry '%s' already "
                    "registered\n", dp->name);
            dump_stack();
            break;
        }
//将dp这个'物理文件'添加到'物理磁盘'的文件视图链表中,以供以后查找访问[luther.gliethttp]
    dp->next = dir->subdir;
    dp->parent = dir;
    dir->subdir = dp;
    spin_unlock(&proc_subdir_lock);

    return 0;
}
就这样我们成功的向proc这个内存方式的'物理磁盘',添加了我们需要的'物理文件',同时细化了该'物理文件'排它的个性化信息[luther.gliethttp]
====================================================
//在'物理磁盘'上创建'物理文件',然后替换为我们这里指定的proc_fops方法
//可以用来在'物理磁盘'上创建文件型的'物理文件'或者目录型的'物理文件',完全由mode指定[luther.gliethttp]
void create_seq_entry(char *name, mode_t mode, const struct file_operations *f)
{
    struct proc_dir_entry *entry;
    entry = create_proc_entry(name, mode, NULL);
    if (entry)
        entry->proc_fops = f;
}
====================================================
//固定方法,创建目录型的'物理文件'
struct proc_dir_entry *proc_mkdir(const char *name,
        struct proc_dir_entry *parent)
{
    return proc_mkdir_mode(name, S_IRUGO | S_IXUGO, parent);
}
truct proc_dir_entry *proc_mkdir_mode(const char *name, mode_t mode,
        struct proc_dir_entry *parent)
{
    struct proc_dir_entry *ent;

    ent = __proc_create(&parent, name, S_IFDIR | mode, 2);
    if (ent) {
        if (proc_register(parent, ent) < 0) {
            kfree(ent);
            ent = NULL;
        }
    }
    return ent;
}
====================================================
//可以用来在'物理磁盘'上创建文件型的'物理文件'或者目录型的'物理文件'
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
                     struct proc_dir_entry *parent)
{
    struct proc_dir_entry *ent;
    nlink_t nlink;

    if (S_ISDIR(mode)) {
        if ((mode & S_IALLUGO) == 0)
            mode |= S_IRUGO | S_IXUGO;
        nlink = 2;
    } else {
        if ((mode & S_IFMT) == 0)
            mode |= S_IFREG;
        if ((mode & S_IALLUGO) == 0)
            mode |= S_IRUGO;
        nlink = 1;
    }

    ent = __proc_create(&parent, name, mode, nlink);
    if (ent) {
        if (proc_register(parent, ent) < 0) {
            kfree(ent);
            ent = NULL;
        }
    }
    return ent;
}

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