您的位置:首页 > 运维架构 > Linux

Linux内核PROC文件系统的初始化和主要函数分析

2017-04-01 22:57 555 查看
                                Linux内核PROC文件系统的初始化和主要函数分析

                                             written by aweii

一、概述(内核版本2.4.0)

系统启动初始化阶段,调用init_proc_fs进行proc文件系统的注册。内核初始化完成后,可由初始化用户进程(如init进程之类)调用mount -t proc /dev/null /proc 进行挂接。

二、结构

(1)虚拟目录结构

struct proc_dir_entry {

    unsigned short low_ino;

    unsigned short namelen;

    const char *name;

    mode_t mode;

    nlink_t nlink;

    uid_t uid;

    gid_t gid;

    unsigned long size;

    struct inode_operations * proc_iops;

    struct file_operations * proc_fops;

    get_info_t *get_info;

    struct module *owner;

    struct proc_dir_entry *next, *parent, *subdir;

    void *data;

    read_proc_t *read_proc;

    write_proc_t *write_proc;

    atomic_t count;        /* use count */

    int deleted;        /* delete flag */

    kdev_t    rdev;

};

inode->generic_ip指向proc_dir_entry,proc_lookup()通过proc_dir_entry的subdir链表搜寻其子节点的proc_dir_entry。

(2)

三、主要代码分析

(1)文件系统初始化

-------------init_proc_fs

static DECLARE_FSTYPE(proc_fs_type, "proc", proc_read_super, FS_SINGLE);

static int __init init_proc_fs(void)

{

    int err = register_filesystem(&proc_fs_type); //注册文件系统proc_fs_type,成员函数read_super指向proc_read_super

    if (!err) {

        proc_mnt = kern_mount(&proc_fs_type);//创建vfs_mount结构,挂接在proc_fs_type下,同时生成的元素包括超级块super_block和根节点root_inode

        err = PTR_ERR(proc_mnt);

        if (IS_ERR(proc_mnt))

            unregister_filesystem(&proc_fs_type);

        else

            err = 0;

    }

    return err;

}

(2)/proc/下的主要子节点初始化

--------------proc_misc_init

void __init proc_misc_init(void)

{

    struct proc_dir_entry *entry;

    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},

        {"cpuinfo",    cpuinfo_read_proc},

#ifdef CONFIG_PROC_HARDWARE

        {"hardware",    hardware_read_proc},

#endif

#ifdef CONFIG_STRAM_PROC

        {"stram",    stram_read_proc},

#endif

#ifdef CONFIG_DEBUG_MALLOC

        {"malloc",    malloc_read_proc},

#endif

#ifdef CONFIG_MODULES

        {"modules",    modules_read_proc},

        {"ksyms",    ksyms_read_proc},

#endif

        {"stat",    kstat_read_proc},

        {"devices",    devices_read_proc},

        {"partitions",    partitions_read_proc},

#if !defined(CONFIG_ARCH_S390)

        {"interrupts",    interrupts_read_proc},

#endif

        {"filesystems",    filesystems_read_proc},

        {"dma",        dma_read_proc},

        {"ioports",    ioports_read_proc},

        {"cmdline",    cmdline_read_proc},

#ifdef CONFIG_SGI_DS1286

        {"rtc",        ds1286_read_proc},

#endif

        {"locks",    locks_read_proc},

        {"mounts",    mounts_read_proc},

        {"swaps",    swaps_read_proc},

        {"iomem",    memory_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);

    /* And now for trickier ones */

    entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);

    if (entry)

        entry->proc_fops = &proc_kmsg_operations;

    proc_root_kcore = create_proc_entry("kcore", S_IRUSR, NULL);

    if (proc_root_kcore) {

        proc_root_kcore->proc_fops = &proc_kcore_operations;

        proc_root_kcore->size =

                (size_t)high_memory - PAGE_OFFSET + PAGE_SIZE;

    }

    if (prof_shift) {

        entry = create_proc_entry("profile", S_IWUSR | S_IRUGO, NULL);

        if (entry) {

            entry->proc_fops = &proc_profile_operations;

            entry->size = (1+prof_len) * sizeof(unsigned int);

        }

    }

#ifdef __powerpc__

    {

        extern struct file_operations ppc_htab_operations;

        entry = create_proc_entry("ppc_htab", S_IRUGO|S_IWUSR, NULL);

        if (entry)

            entry->proc_fops = &ppc_htab_operations;

    }

#endif

    entry = create_proc_read_entry("slabinfo", S_IWUSR | S_IRUGO, NULL,

                       slabinfo_read_proc, NULL);

    if (entry)

        entry->write_proc = slabinfo_write_proc;

}

(3)创建只读子节点的函数,能同时设置read_proc

-------------------------create_proc_read_entry

extern 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); //创建名为name的proc_dir_entry,其父节点为base

    if (res) {

        res->read_proc=read_proc; //设置read_proc方法

        res->data=data;

    }

    return res;

}

(4)创建子节点的函数

-------------------------create_proc_entry

struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,

                     struct proc_dir_entry *parent)

{

    struct proc_dir_entry *ent = NULL;

    const char *fn = name;

    int len;

    if (!parent && xlate_proc_name(name, &parent, &fn) != 0)

        goto out;

    len = strlen(fn);

    ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL); //分配proc_dir_entry,其后紧跟name+"\0",所以空间大小为sizeof(struct proc_dir_entry) + strlen(name) + 1

    if (!ent)

        goto out;

    memset(ent, 0, sizeof(struct proc_dir_entry));

    memcpy(((char *) ent) + sizeof(*ent), fn, len + 1);//拷贝name到proc_dir_entry后

    ent->name = ((char *) ent) + sizeof(*ent); //name指向它

    ent->namelen = len;

        

    //目录节点设置方法

    if (S_ISDIR(mode)) {

        if ((mode & S_IALLUGO) == 0)

        mode |= S_IRUGO | S_IXUGO;

        ent->proc_fops = &proc_dir_operations;

        ent->proc_iops = &proc_dir_inode_operations;

        ent->nlink = 2;

    } else {

        if ((mode & S_IFMT) == 0)

            mode |= S_IFREG;

        if ((mode & S_IALLUGO) == 0)

            mode |= S_IRUGO;

        ent->nlink = 1;

    }

    ent->mode = mode;

    proc_register(parent, ent); //级联到父节点

    

out:

    return ent;

}

(5)目录搜索函数

根据级联关系进行循环搜索,和目录中搜索文件一样,都要比对名称

-------------------------proc_lookup

struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry)

{

    struct inode *inode;

    struct proc_dir_entry * de;

    int error;

    error = -ENOENT;

    inode = NULL;

    de = (struct proc_dir_entry *) dir->u.generic_ip;

    if (de) {

        for (de = de->subdir; de ; de = de->next) {

            if (!de || !de->low_ino)

                continue;

            if (de->namelen != dentry->d_name.len)

                continue;

            if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {

                int ino = de->low_ino;

                error = -EINVAL;

                inode = proc_get_inode(dir->i_sb, ino, de);

                break;

            }

        }

    }

    if (inode) {

        dentry->d_op = &proc_dentry_operations;

        d_add(dentry, inode);

        return NULL;

    }

    return ERR_PTR(error);

}

(6)文件通用读函数,它调用proc_dir_entry的get_info或read_proc成员函数

注意proc_file_read和具体的read_proc的搭配使用方法,ppos和其他普通文件读函数相同,

但要注意ppos、start值的交互变化。

proc_file_read循环调用read_proc,read_proc每次最多只读PROC_BLOCK_SIZE(4K页的3/4,3KB),

保证每次读入量都能容纳进page页面中。根据ppos内容在page中的偏移,调节start指针

-----------------------------proc_file_read

static struct file_operations proc_file_operations = {

    llseek:        proc_file_lseek,

    read:        proc_file_read,

    write:        proc_file_write,

};

#define PROC_BLOCK_SIZE    (PAGE_SIZE - 1024)

static ssize_t

proc_file_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)

{

    struct inode * inode = file->f_dentry->d_inode;

    char     *page;

    ssize_t    retval=0;

    int    eof=0;

    ssize_t    n, count;

    char    *start;

    struct proc_dir_entry * dp;

    dp = (struct proc_dir_entry *) inode->u.generic_ip;

    if (!(page = (char*) __get_free_page(GFP_KERNEL)))

        return -ENOMEM;

    while ((nbytes > 0) && !eof)

    {

        count = MIN(PROC_BLOCK_SIZE, nbytes);

        start = NULL;

        if (dp->get_info) {

            /*

             * Handle backwards compatibility with the old net

             * routines.

             */

            n = dp->get_info(page, &start, *ppos, count);

            if (n < count)

                eof = 1;

        } else if (dp->read_proc) {

            n = dp->read_proc(page, &start, *ppos,

                      count, &eof, dp->data);

        } else

            break;

        if (!start) {

            /*

             * For proc files that are less than 4k

             */

            start = page + *ppos;

            n -= *ppos;

            if (n <= 0)

                break;

            if (n > count)

                n = count;

        }

        if (n == 0)

            break;    /* End of file */

        if (n < 0) {

            if (retval == 0)

                retval = n;

            break;

        }

        

        /* This is a hack to allow mangling of file pos independent

          * of actual bytes read.  Simply place the data at page,

          * return the bytes, and set `start' to the desired offset

          * as an unsigned int. - Paul.Russell@rustcorp.com.au

         */

         n -= copy_to_user(buf, start < page ? page : start, n);

        if (n == 0) {

            if (retval == 0)

                retval = -EFAULT;

            break;

        }

        *ppos += start < page ? (long)start : n; /* Move down the file */

        nbytes -= n;

        buf += n;

        retval += n;

    }

    free_page((unsigned long) page);

    return retval;

}

(7)read_proc成员函数实例,读内核符号表

-------------------------read_proc举例

//见proc_misc_init()中的{"ksyms",    ksyms_read_proc},

static int ksyms_read_proc(char *page, char **start, off_t off,

                 int count, int *eof, void *data)

{

    int len = get_ksyms_list(page, start, off, count);

    if (len < count) *eof = 1;

    return len;

}

/*

begin始终指向buf处的内容在文件中的偏移,而

if (pos < offset) {

                len = 0;

                begin = pos;

            }

不断调节begin的位置使offset最终落到buf缓冲页(也就是上级函数传递过来的page)中

因此buf + (offset - begin)就得到offset对应的缓存页中的位置。

因为start可能>buf,为预防offset - begin + length 超出缓冲页,所以buf中只用了3/4空间限额,后边作预留。

由(6)知,每次调用read_proc时length最大值设置为PROC_BLOCK_SIZE(即3KB),此时,若当offset - begin>0,那么本段读入数据的末尾是超出3KB的,

但是不会超出4KB(begin的不断调节作用)。

*/

/*

 * Called by the /proc file system to return a current list of ksyms.

 */

int

get_ksyms_list(char *buf, char **start, off_t offset, int length)

{

    struct module *mod;

    char *p = buf;

    int len     = 0;    /* code from  net/ipv4/proc.c */

    off_t pos   = 0;

    off_t begin = 0;

    for (mod = module_list; mod; mod = mod->next) {

        unsigned i;

        struct module_symbol *sym;

        if (!MOD_CAN_QUERY(mod))

            continue;

        for (i = mod->nsyms, sym = mod->syms; i > 0; --i, ++sym) {

            p = buf + len;

            if (*mod->name) {

                len += sprintf(p, "%0*lx %s\t[%s]\n",

                           (int)(2*sizeof(void*)),

                           sym->value, sym->name,

                           mod->name);

            } else {

                len += sprintf(p, "%0*lx %s\n",

                           (int)(2*sizeof(void*)),

                           sym->value, sym->name);

            }

            pos = begin + len;

            if (pos < offset) {

                len = 0;

                begin = pos;

            }

            pos = begin + len;

            if (pos > offset+length)

                goto leave_the_loop;

        }

    }

leave_the_loop:

    *start = buf + (offset - begin);

    len -= (offset - begin);

    if (len > length)

        len = length;

    return len;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux proc linux内核