Linux内核源代码阅读---filesystem.c

来源:互联网 发布:jquery.min.js引用 编辑:程序博客网 时间:2024/05/21 23:00

 

该文件位于源码中的fs/中:

      在Linux中是使用mount和umount指令来安装和卸载(或者说是挂载和卸载)文件系统的,一旦改变文件系统,那么在文件系统中的一些接口和处理函数也会发生相应的改变。在使用诸如open、read等系统调用的时候,内核是先调用VFS的通用系统调用,然后再找到当前文件系统的种类,然后具体的调用该文件系统的对应的函数来实现具体的功能,而下面的这个文件就是完成安装和卸载文件系统的功能。

      在最新内核版本(2.6.36.1)中filesystems.c文件位于内核源代码下的 fs/ 里,代码共286行,虽然比较短小,但是承担的任务还是比较重要的,其中的register_filesystem函数就是完成mount的功能,unregister_filesystem函数是完成umount的功能。

      源代码分析:

 

/*

 *  linux/fs/filesystems.c

 *

 *  Copyright (C) 1991, 1992  Linus Torvalds

 *

 *  table of configured filesystems

 */

#include <linux/syscalls.h>

#include <linux/fs.h>

#include <linux/proc_fs.h>

#include <linux/seq_file.h>

#include <linux/kmod.h>

#include <linux/init.h>

#include <linux/module.h>

#include <linux/slab.h>

#include <asm/uaccess.h>

static struct file_system_type *file_systems;

/*

该结构体定义在include/linux/fs.h中:

struct file_system_type {

        const char *name;//文件系统的名称

        int fs_flags;//标志位

        int (*get_sb) (struct file_system_type *, int,

                       const char *, void *, struct vfsmount *);

得到super block的函数,在security/inode.c中实现:

static int get_sb(struct file_system_type *fs_type,

                  int flags, const char *dev_name,

                  void *data, struct vfsmount *mnt)

{

        return get_sb_single(fs_type, flags, data, fill_super, mnt);

该函数在fs/super.c中实现,得到系统的super block。

}

        void (*kill_sb) (struct super_block *);//删除super block

        struct module *owner;//模块所有者

        struct file_system_type * next;//类似链表,下一个文件类型

        struct list_head fs_supers;//文件系统super的一个链

        struct lock_class_key s_lock_key;

        struct lock_class_key s_umount_key;

        struct lock_class_key s_vfs_rename_key;

        struct lock_class_key i_lock_key;

        struct lock_class_key i_mutex_key;

        struct lock_class_key i_mutex_dir_key;

        struct lock_class_key i_alloc_sem_key;

/*

lock_class_key在/include/linux/lockdep.h中定义:

struct lock_class_key {

      struct lockdep_subclass_key     subkeys[MAX_LOCKDEP_SUBCLASSES];

*/

};

应该是一个类似于锁的一种对文件系统更换操作时进行加锁保护的一些变量。

*/

static DEFINE_RWLOCK(file_systems_lock);

/*

该宏在include/linux/rwlock_types.h中定义:

#define DEFINE_RWLOCK(x)        rwlock_t x = __RW_LOCK_UNLOCKED(x)

大概就是为文件系统进行加锁的操作

*/

void get_filesystem(struct file_system_type *fs)

{

__module_get(fs->owner);

}

/*

该函数是用来加载文件系统模块的,其中的函数在include/linux/module.h中定义:

static inline void __module_get(struct module *module)

{

        if (module) {

                preempt_disable();

                __this_cpu_inc(module->refptr->incs);

                trace_module_get(module, _THIS_IP_);

                preempt_enable();

        }

}

具体实现不用搞懂,只要知道它是把module模块加载到内核中。

*/

void put_filesystem(struct file_system_type *fs)

{

module_put(fs->owner);

}

/*

该函数是用来卸载文件系统模块的,其中函数定义在kernel/module.c中:

void module_put(struct module *module)

{

        if (module) {

                preempt_disable();

                smp_wmb();

                __this_cpu_inc(module->refptr->decs);

                trace_module_put(module, _RET_IP_);

                if (unlikely(!module_is_live(module)))

                        wake_up_process(module->waiter);

                preempt_enable();

        }

}

和上面的那个函数很像,它的作用就是把module这个模块从内核中卸载。

*/

static struct file_system_type **find_filesystem(const char *name, unsigned len)

{

struct file_system_type **p;

for (p=&file_systems; *p; p=&(*p)->next)

if (strlen((*p)->name) == len &&

    strncmp((*p)->name, name, len) == 0)

break;

return p;

}

/*

看名字就知道了,这个函数的作用就是找到文件系统。再看里面的实现过程,就是在file_systems这个链表中,查找名字为name,长度为len的文件系统,把这个文件系统的节点提取出来。当然,这个节点是file_system_type类型的,这个上面已经提到过了。如果找到的话,就直接返回p所在位置;如果没有找到,返回值是最后一个节点的next域。

*/

int register_filesystem(struct file_system_type * fs)

{

int res = 0;

struct file_system_type ** p;

BUG_ON(strchr(fs->name, '.'));

/*源码位于arch/mips/include/asm/bug.h中,这个函数应该是用来检测BUG的*/

if (fs->next)

return -EBUSY;

/*如果fs指向的是已被加载的文件系统,那么返回错误信息*/

INIT_LIST_HEAD(&fs->fs_supers);

/*初始化链表,以供使用*/

write_lock(&file_systems_lock);

/*要操作文件系统链表时,需要先进行加锁工作*/

p = find_filesystem(fs->name, strlen(fs->name));

/*找到需挂载的文件系统*/

if (*p)

res = -EBUSY;

/*如果p不为空,那么说明该文件系统已经存在,那么直接返回*/

else

*p = fs;

/*

如果为空,说明find_filesystem函数已经搜索到链表的最后,那么就要在此添加一个新的节点,内容就是要挂载的fs的信息

*/

write_unlock(&file_systems_lock);

/*对文件系统链表操作结束后,进行解锁操作*/

return res;

}

/*

重点函数之一,该函数的作用就是用来挂载文件系统的。如果挂载失败,则返回-EBUSY,如果成功那么就把新的文件系统加载到文件系统链表中,然后返回res(就是0)。

*/

EXPORT_SYMBOL(register_filesystem);

 

int unregister_filesystem(struct file_system_type * fs)

{

struct file_system_type ** tmp;

write_lock(&file_systems_lock);

/*要操作文件系统链表时,需要先进行加锁工作*/

tmp = &file_systems;

/*获得文件系统链表的头节点,以便后面进行遍历*/

while (*tmp) {

if (fs == *tmp) {

/*

如果找到要卸载的文件系统,那么就把它从文件系统链表中删除,采用的方法是链表最简单的删除方式

*/

*tmp = fs->next;

fs->next = NULL;

write_unlock(&file_systems_lock);

/*

如果在这里就找到了要删除的文件系统,那么就对文件系统进行解锁,然后返回0表示成功

*/

return 0;

}

tmp = &(*tmp)->next;

/*保证链表能向后移动遍历的语句*/

}

write_unlock(&file_systems_lock);

/*

如果通过遍历没有返回,说明没有找到要删除的文件系统,那么还是要对文件系统链表进行解锁操作,返回错误信息

*/

return -EINVAL;

}

/*

重点函数之一,该函数的作用就是用来卸载文件系统的。如果找到要卸载除的文件系统,那么把它从文件系统链表中删除,然后返回0;如果没有找到,返回错误信息(参数错误)。

*/

EXPORT_SYMBOL(unregister_filesystem);

static int fs_index(const char __user * __name)

{

struct file_system_type * tmp;

char * name;

int err, index;

name = getname(__name);

/*

getname这个函数在fs/namei.c中定义:

char * getname(const char __user * filename)

{

        char *tmp, *result;

        result = ERR_PTR(-ENOMEM);

        tmp = __getname();

        if (tmp)  {

                int retval = do_getname(filename, tmp);

                result = tmp;

                if (retval < 0) {

                        __putname(tmp);

                        result = ERR_PTR(retval);

                }

        }

        audit_getname(result);

        return result;

}

作用就是把filename(用户态的字符串)拷贝到内核空间中

__name所指示得字符串拷入内核空间,并且如果内核定制了audit属性,增加该项进入查找access访问控制表 ---引用

*/

err = PTR_ERR(name);

if (IS_ERR(name))

return err;

/*一些错误处理*/

err = -EINVAL;

read_lock(&file_systems_lock);

/*现在相当于是读操作(遍历文件系统链表),所以要首先进行加锁操作*/

for (tmp=file_systems, index=0 ; tmp ; tmp=tmp->next, index++) {

if (strcmp(tmp->name,name) == 0) {

err = index;

break;

}

/*

遍历文件系统链表,并且让index自加,找到指定的文件系统所在的节点后,跳出循环,然后返回index值

*/

}

read_unlock(&file_systems_lock);

/*操作结束后,解锁*/

putname(name);

/*

putname的源代码在fs/namei.c中定义:

void putname(const char *name)

{

        if (unlikely(!audit_dummy_context()))

                audit_putname(name);

        else

                __putname(name);

}

函数的作用是把内核态的name字符串写回到用户态下。和之前的getname的作用刚好相反。

*/

return err;

/*如果在遍历的时候一直没有找到指定的文件系统,那么返回-EINVAL(参数错误)*/

}

/*

该函数是为了计算得到指定文件系统在文件系统链表中的index值。

*/

static int fs_name(unsigned int index, char __user * buf)

{

struct file_system_type * tmp;

int len, res;

read_lock(&file_systems_lock);

/*对文件系统链表进行操作首先还是要加锁*/

for (tmp = file_systems; tmp; tmp = tmp->next, index--)

/*通过index的自减操作找到位于第index的节点,保存到tmp里*/

if (index <= 0 && try_module_get(tmp->owner))

break;

read_unlock(&file_systems_lock);

/*操作结束后,要记得解锁*/

if (!tmp)

return -EINVAL;

/*如果没有找到该文件系统,那么返回错误信息*/

len = strlen(tmp->name) + 1;

res = copy_to_user(buf, tmp->name, len) ? -EFAULT : 0;

/*

把文件系统名拷贝到用户空间的buf字符串中返回,拷贝成功返回0,失败返回错误信息(-EFAULT)

*/

put_filesystem(tmp);

/*再把tmp写到用户空间中*/

return res;

}

/*

该函数的作用就是找到文件系统链表中第index个文件系统的名称,然后将值返回到用户空间中。

*/

static int fs_maxindex(void)

{

struct file_system_type * tmp;

int index;

read_lock(&file_systems_lock);

for (tmp = file_systems, index = 0 ; tmp ; tmp = tmp->next, index++)

;

read_unlock(&file_systems_lock);

return index;

}

/*

该函数的作用计算文件系统链表中节点的总数,同样是操作前加锁,操作后解锁,其中的遍历过程使用index自加来记录,最终的index值就是节点的总数。

*/

SYSCALL_DEFINE3(sysfs, int, option, unsigned long, arg1, unsigned long, arg2)

{

int retval = -EINVAL;

switch (option) {

case 1:

retval = fs_index((const char __user *) arg1);

break;

case 2:

retval = fs_name(arg1, (char __user *) arg2);

break;

case 3:

retval = fs_maxindex();

break;

}

return retval;

}

/*

这是一种系统调用的定义方式,通过option的不同,可以完成3种不同的功能:(1)查找指定文件系统所在文件系统链表的位置(2)查找指定文件系统的名称(3)查看当前文件系统链表的总长度。

*/

int __init get_filesystem_list(char *buf)

{

int len = 0;

struct file_system_type * tmp;

read_lock(&file_systems_lock);/*加锁*/

tmp = file_systems;

while (tmp && len < PAGE_SIZE - 80) {

/*遍历文件系统链表*/

len += sprintf(buf+len, "%s/t%s/n",

(tmp->fs_flags & FS_REQUIRES_DEV) ? "" : "nodev",

tmp->name);

/*

将当前存在于文件系统链表中的文件系统的名称保存到buf中,并且记录下长度len。而且区分需要dev和不需要dev(即nodev)的两种文件系统

*/

tmp = tmp->next;

}

read_unlock(&file_systems_lock);/*解锁*/

return len;

}

/*

得到当前存在于文件系统链表的文件系统(也就是当前支持的文件系统)的名称,保存到buf中。

*/

#ifdef CONFIG_PROC_FS

/*当处于配置虚拟的文件系统时,进行如下操作*/

static int filesystems_proc_show(struct seq_file *m, void *v)

/*

结构体seq_file在include/linux/seq_file.h中定义:

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;

};

它是针对于proc文件的不足而产生的一种文件结构。

*/

{

struct file_system_type * tmp;

read_lock(&file_systems_lock);/*加锁*/

tmp = file_systems;

while (tmp) {

seq_printf(m, "%s/t%s/n",/*在fs/seq_file.c中实现,针对proc文件进行的字符串拷贝函数,功能同sprintf一致。*/

(tmp->fs_flags & FS_REQUIRES_DEV) ? "" : "nodev",

tmp->name);/*遍历文件系统链表,将当前存在于文件系统链表中的文件系统的名称保存到buf中,并且记录下长度len。而且区分需要dev和不需要dev(即nodev)的两种文件系统*/

tmp = tmp->next;

}

read_unlock(&file_systems_lock);/*解锁*/

return 0;

}

/*

该函数的作用就是在proc模式时(虚拟文件系统),显示当前支持的文件系统的名称

*/

static int filesystems_proc_open(struct inode *inode, struct file *file)

{

return single_open(file, filesystems_proc_show, NULL);

/*

该函数在fs/seq_file.c中定义:

int single_open(struct file *file, int (*show)(struct seq_file *, void *),

                void *data)

{

        struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL);

/*

结构体seq_operations定义在include/linux/seq_file.h中:

struct seq_operations {

        void * (*start) (struct seq_file *m, loff_t *pos);

        void (*stop) (struct seq_file *m, void *v);

        void * (*next) (struct seq_file *m, void *v, loff_t *pos);

        int (*show) (struct seq_file *m, void *v);

};

就是要实现4个功能:start、stop、next、show。

*/

        int res = -ENOMEM;

        if (op) {

                op->start = single_start;

                op->next = single_next;

                op->stop = single_stop;

                op->show = show;

                res = seq_open(file, op);

                if (!res)

                        ((struct seq_file *)file->private_data)->private = data;

                else

                        kfree(op);

        }

        return res;

}

作用就是填充seq_file结构体中的对于文件的操作函数。*/

}

/*

该函数就是打开一个proc文件。

*/

static const struct file_operations filesystems_proc_fops = {

.open= filesystems_proc_open,

.read= seq_read,

.llseek= seq_lseek,

.release= single_release,

};

/*

这个就是填充file_operations结构体,以完成对于proc文件的系统调用的操作,包括open、read、llseek、release。

*/

static int __init proc_filesystems_init(void)

{

proc_create("filesystems", 0, NULL, &filesystems_proc_fops);

return 0;

}

/*该模块(对于proc虚拟文件系统的处理)的入口函数,进行初始化过程。*/

module_init(proc_filesystems_init);

/*插入该模块。*/

#endif

static struct file_system_type *__get_fs_type(const char *name, int len)

{

struct file_system_type *fs;

read_lock(&file_systems_lock);/*加锁*/

fs = *(find_filesystem(name, len));

/*根据指定的信息,在链表中查找文件系统,返回指向该节点的指针*/

if (fs && !try_module_get(fs->owner))

fs = NULL;

/*没有找到指定的文件系统,就返回一个NULL指针*/

read_unlock(&file_systems_lock);/*解锁*/

return fs;

}

/*

该函数的作用就是根据指定的文件系统的名称,找到该文件系统所在文件系统链表的节点位置,返回其指针指向。如果没有找到就返回一个NULL指针。当然,在对文件系统链表进行操作的时候要进行加锁保护。

*/

struct file_system_type *get_fs_type(const char *name)

{

struct file_system_type *fs;

const char *dot = strchr(name, '.');

int len = dot ? dot - name : strlen(name);

fs = __get_fs_type(name, len);

/*对指定的文件系统进行查找,找到就返回该节点的指针,如果没找到返回NULL*/

if (!fs && (request_module("%.*s", len, name) == 0))

fs = __get_fs_type(name, len);

/*如果没有找到,就再找一遍*/

if (dot && fs && !(fs->fs_flags & FS_HAS_SUBTYPE)) {

put_filesystem(fs);

fs = NULL;

}

/*如果还是没有找到该文件系统,那么就要把指定的文件系统的名称加入到内核中*/

return fs;

}

/*

对上面的函数的一种封装,作用还是一样的,就是根据指定的文件系统的名称,找到该文件系统所在文件系统链表的节点位置,返回其指针指向。如果没有找到就返回一个NULL指针。

*/

EXPORT_SYMBOL(get_fs_type);

 

分析结束,可能也不算是分析吧,只是把源码过了一遍,主要的体会还是在它对问题的解决的方法上,具有一种比较能够统筹化的处理。可能现在还是不能体会到其中的奥妙,不过仍在学习中,模仿就是一种进步。