proc源码解析(三)--proc初始化

   在使用proc之前,我们必须首先初始化并挂载proc,并在内核内存中创建数据结构来描述文件系统。但是,不同的体系结构拥有不同的proc内容,所以,在初始化阶段并不完全创建子目录的内容,有些文件要等到系统运行时动态创建。Proc文件系统初始化的流程图如下:

proc源码解析(三)--proc初始化_第1张图片

  proc_root_init的定义定义如下:


void __init proc_root_init(void)
{
    int err = proc_init_inodecache();
    err = register_filesystem(&proc_fs_type);
    。。。 
   proc_mnt = kern_mount_data(&proc_fs_type, &init_pid_ns); 
   proc_misc_init();
    proc_net_init(); 
    。。。
    proc_root_fs = proc_mkdir("fs", NULL);
    proc_root_driver = proc_mkdir("driver", NULL);
    proc_mkdir("fs/nfsd", NULL);
   。。。
    proc_bus = proc_mkdir("bus", NULL);
    proc_sys_init();


int __init proc_init_inodecache(void):为proc_inode创建slab cache,这是proc文件系统的主要部分,通常需要快速创建或销毁。
int register_filesystem(struct file_system_type * fs):注册文件系统类型proc,这个过程是文件系统初始化的关键步骤,下边会专门解释这个步骤。


struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)
{
    return vfs_kern_mount(type, MS_KERNMOUNT, type->name, data);
}


vfs_kern_mount:创建与proc_fs_type相关的vfsmount结构,该函数在VFS中已有讲述。
proc_misc_init:创建/proc目录下的文件和子目录,每个文件的创建都由相应的函数来完成,例如:meminfo的创建由 create_proc_read_entry 创建,而meminfo_read_proc则用于初始化meminfo的read_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},
    。。。
    {"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);

    proc_symlink("mounts", NULL, "self/mounts");
    create_seq_entry("locks", 0, &proc_locks_operations);
    create_seq_entry("devices", 0, &proc_devinfo_operations);
    create_seq_entry("cpuinfo", 0, &proc_cpuinfo_operations);
    。。。
}


另外一些文件是用create_seq_entry来调用创建函数。
create_seq_entry("locks", 0, &proc_locks_operations);
    这两个函数 create_proc_read_entry与create_seq_entry的不同之处是前者创建的是只读文件,后者创建的是可读可写文件。
proc_net_init:创建大量与网络相关的文件,创建方法与前者相似,所以此处不在赘述。
    以上步骤完成之后,初始化需要创建的文件都已完成,下边要调用proc_mkdir来创建子目录。
   实际上,函数最后还调用proc_sys_init();来完成sys子目录的创建。

register_filesystem的实现 
    在proc_root_init函数中,调用该函数的格式是:


static struct file_system_type proc_fs_type = {

    .name = "proc",
    .get_sb = proc_get_sb,
    .kill_sb = proc_kill_sb,
};

void __init proc_root_init(void)
{
    。。。
    err = register_filesystem(&proc_fs_type);
    if (err)
    return;
    。。。
}


   也是就调用register_filesystem函数把描述proc文件类型的结构 proc_fs_type注册到系统中,其中register_filesystem函数函数实现很简单,这里主要关注 proc_get_sb函数。该函数的主要功能是获取proc的super_block。其调用流程图如下:

proc源码解析(三)--proc初始化_第2张图片

proc_get_sb中主要的执行函数是proc_fill_super;该函数的主要执行过程如下,为了方便理解,我删去了"多余部分",只留下程序骨干。


int proc_fill_super(struct super_block *s)
{
    struct inode * root_inode;
    /* 初始化super block的元素*/
    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;
    s->s_time_gran = 1; //初始化时间精度1ns

    /* 获得root_inode*/
    de_get(&proc_root);
    root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);
    。。。
    /* 二者初始化为0说明proc只能是root用户的*/
    root_inode->i_uid = 0;

    root_inode->i_gid = 0;

    s->s_root = d_alloc_root(root_inode);
    。。。
}


第一个阶段: 初始化super block的元素,唯一值得注意的是s->s_op = &proc_sops;
这是初始化super block的struct super_operations *s_op;其中函数集是:


static const struct super_operations proc_sops = {
    .alloc_inode    = proc_alloc_inode,
    .destroy_inode    = proc_destroy_inode,
    .read_inode    = proc_read_inode,
    .drop_inode    = generic_delete_inode,
    .delete_inode    = proc_delete_inode,
    .statfs        = simple_statfs,
    .remount_fs    = proc_remount,
};


 

第二个阶段 : proc_get_inode函数用来获得proc的inode,其主要的实现过程如下,为了便于理解我删去了部分代码:


struct inode *proc_get_inode(struct super_block *sb, unsigned int ino,
                struct proc_dir_entry *de)
{
    struct inode * inode;
    inode = iget(sb, ino);
    if (de) {
        /* 使用proc_root的mode初始化,此处的mode和虚拟文件系统的一样 */
        if (de->mode) {
            inode->i_mode = de->mode;
            inode->i_uid = de->uid;
            inode->i_gid = de->gid;
        }
        /* 使用proc_root的各个域初始化inode */
        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;//proc_root的inode操作函数
        if (de->proc_fops) {
            if (S_ISREG(inode->i_mode)) {
                    inode->i_fop = &proc_reg_file_ops;
            }
            else
                inode->i_fop = de->proc_fops; //proc_root的file操作函数
        }
    }
}   


 

红色部分说明如果是普通文件使用 proc_reg_file_ops 初始化,其定义为:


static const struct file_operations proc_reg_file_ops = {
    .llseek    = proc_reg_llseek,
    .read        = proc_reg_read,
    .write        = proc_reg_write,
    .poll        = proc_reg_poll,
    .unlocked_ioctl    = proc_reg_unlocked_ioctl,
    .mmap        = proc_reg_mmap,
    .open        = proc_reg_open,
    .release    = proc_reg_release,
};

 


proc_fill_super在调用该函数时(proc_get_inode(s, PROC_ROOT_INO, &proc_root))传入的参数为proc_root,这是一个全局变量,是proc的根目录的dentry,其定义为:


static const struct file_operations proc_root_operations = {
    .read         = generic_read_dir,
    .readdir     = proc_root_readdir,
};
static const struct inode_operations proc_root_inode_operations = {
    .lookup        = proc_root_lookup,
    .getattr    = proc_root_getattr,
};
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,

};


    在 proc_get_inode函数的初始化过程中,大部分的域都是用proc_root中相应的域来初始化,这也正好将proc的dentry和VFS层的dentry和inode对接起来。各个域的操作函数集合此处就不在深入,有需要的朋友可以查阅源码。

 

 

第三个阶段: 调用 d_alloc_root创建proc根结点的dentry,该函数比较短,此处引用它的定义:


/**
 * d_alloc_root - allocate root dentry
 * @root_inode: inode to allocate the root for
 * Allocate a root ("/") dentry for the inode given. The inode is
 * instantiated and returned. %NULL is returned if there is insufficient
 * memory or the inode passed is %NULL.
 */
struct dentry * d_alloc_root(struct inode * root_inode)
{
    struct dentry *res = NULL;

    if (root_inode) {
        static const struct qstr name = { .name = "/", .len = 1 };

        res = d_alloc(NULL, &name);//allocate a dcache entry
        if (res) {
            res->d_sb = root_inode->i_sb;
            res->d_parent = res;
            d_instantiate(res, root_inode);//初始化dentry res
        }
    }
    return res;
}


    到此为止proc的初始化过程已经完成,此时,/proc目录下除了要动态创建的文件和子目录外,初始化过程中应该创建的文件和子目录都已创建完毕,接下来就需要挂载proc文件系统到目录树上,这是下一节要讲的内容。


你可能感兴趣的:(linux驱动,struct,filesystems,null,file,symlink,system)