文件系统篇——篇四:aufs文件系统简单版源码详解

说明
  本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
  QQ 群 号:513683159 【相互学习】
内容来源
  《Linux内核完全注释》、《深入理解理解Linux内核第三版中文》第十二章(详细的结构体与函数指针的中文注释)
  linux存储栈及编写最简单的aufs系统模块并加载进内核文件系统
  VS code找不到头文件驱动模块的 编译与加载

上一篇:文件系统篇——篇三:文件系统结构体与函数详解注释
下一篇:

一、源码

  文件系统的结构体或函数可参照前篇进行理解。

1、源文件

aufs_simple.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/pagemap.h>
#include <linux/init.h>
#include <linux/namei.h>
#include <linux/cred.h>
#include <linux/mount.h>

#define AUFS_MAGIC 0x64668735 //每个文件系统需要一个MAGIC number

static struct vfsmount *aufs_mount; // aufs_simple文件系统的挂载点
static int aufs_mount_count;

/**
 * @brief :创建inode节点
 *
 * @param sb  : 超级块
 * @param mode: 文件类型
 * @param dev : 设备号
 * @return struct inode* :返回inode节点指针
 *
 * @description:
 *    在aufs文件系统的super_block创建inode结构体
 */
static struct inode *aufs_creat_inode(struct super_block *sb, int mode, dev_t dev)
{
    struct inode *inode = new_inode(sb); //在超级块sb上申请一个新的inode

    /* inode创建成功后进行赋值 */
    if (inode)
    {
        inode->i_mode = mode;                             //文件类型和属性
        inode->i_uid = current_fsuid();                   //用户ID
        inode->i_gid = current_fsgid();                   //组ID
        inode->i_blocks = 0;                              //文件所占块数
        inode->i_atime = inode->i_mtime = inode->i_ctime; //最后访问时间
        switch (mode & S_IFMT)                            //文件类型掩码
        {
        case S_IFREG: //常规文件
            printk("create a file \\n");
            break;
        case S_IFDIR: //目录
            inode->i_op = &simple_dir_inode_operations;
            inode->i_fop = &simple_dir_operations;
            inode->__i_nlink++;
            printk("creat a dir file \\n");
            break;
        default:
            init_special_inode(inode, mode, dev); //实现把inode节点和设备号绑定
            break;
        }
    }

    return inode;
}

/**
 * @brief :把创建的inode和dentry结构体连接起来
 *
 * @param dir    :
 * @param dentry :目录项
 * @param mode   :模式(文件类型)
 * @param dev    :设备号
 * @return int
 *
 * @description:
 */
static int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
    struct inode *inode;
    int error = -EPERM;

    /* 若磁盘上inode已存在则直接退出 */
    if (dentry->d_inode)
        return -EEXIST;

    /*  */
    inode = aufs_creat_inode(dir->i_sb, mode, dev);
    if (inode)
    {
        d_instantiate(dentry, inode);
        dget(dentry);
        error = 0;
    }

    return error;
}

/**
 * @brief   创建文件夹
 *
 * @param dir   :
 * @param dentry:
 * @param mode  :模式(文件类型与属性)
 * @return int
 */
static int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
    int res;

    res = aufs_mknod(dir, dentry, mode | S_IFDIR, 0); //以文件夹模式创建inode
    if (!res)
    {
        dir->__i_nlink++; //链接数(多少文件目录项指向该inode)
    }

    return res;
}

/**
 * @brief:  aufs 创建函数
 *
 * @param dir   :
 * @param dentry:目录项
 * @param mode  :模式(文件类型和属性)
 * @return int
 *
 * @description:
 */
static int aufs_create(struct inode *dir, struct dentry *dentry, int mode)
{
    return aufs_mknod(dir, dentry, mode | S_IFREG, 0);
    //在目录项“dentry”下创建一个常规文件+mode属性
}

/**
 * @brief 创建子dentry
 *
 * @param name   :文件名
 * @param mode   :模式(文件类型)
 * @param parent :父目录项
 * @param dentry :目录项(传入地址用于在外面获取目录项)
 * @return int
 *
 * @description:
 *          根据父dentry、mode、name创建子dentry
 */
static int aufs_create_by_name(const char *name, mode_t mode,
                               struct dentry *parent, struct dentry **dentry)
{
    int error = 0;

    /* 若不存在父目录项,则将超级快的根目录项赋值给父目录项 */
    if (!parent)
    {
        if (aufs_mount && aufs_mount->mnt_sb) //挂载点存在且挂载点超级块也存在
        {
            parent = aufs_mount->mnt_sb->s_root; //将根目录项给父目录项
        }
    }

    /* 若还不存在父目录项目则报错 */
    if (!parent)
    {
        printk("Ah! can not find a parent!\\n");
        return -EFAULT;
    }

    /* 查找“name”的dentry对象,找到后根据类型创建目录或 */
    *dentry = NULL;                                       //对子dentry清空
    *dentry = lookup_one_len(name, parent, strlen(name)); //在父目录项下找“name”的子dentry返回该指针,不存在则创建。
    if (!IS_ERR(dentry))
    {
        if ((mode & S_IFMT) == S_IFDIR)
            error = aufs_mkdir(parent->d_inode, *dentry, mode);
        else
            error = aufs_create(parent->d_inode, *dentry, mode);
    }
    else
    {
        error = PTR_ERR(dentry);
    }

    return error;
}

/**
 * @brief :aufs中创建文件函数
 *
 * @param name  :文件名
 * @param mode  :文件类型
 * @param parent:文件的上级目录项
 * @param data  :数据
 * @param fops  :文件的操作函数指针结构体变量
 * @return struct dentry*
 */
struct dentry *aufs_create_file(const char *name, mode_t mode,
                                struct dentry *parent, void *data,
                                struct file_operations *fops)
{
    struct dentry *dentry = NULL;
    int error;

    printk("aufs: creating file \'%s\'", name);

    error = aufs_create_by_name(name, mode, parent, &dentry);
    if (error)
    {
        dentry = NULL;
        goto exit;
    }

    /*
        若为文件夹则此处两个都为空,
        若为文件则会对进行赋值。
    */
    if (dentry->d_inode)
    {
        if (data)
            dentry->d_inode->i_private = data; //文件系统或设备私有指针
        if (fops)
            dentry->d_inode->i_fop = fops; //文件的操作函数结构体的指针变量
    }
exit:
    return dentry;
}

/**
 * @brief :aufs中创建文件夹函数
 *
 * @param name   :文件夹名称
 * @param parent :文件夹上级目录项
 * @return struct dentry*
 *
 * @description:
 *      创建目录其实就是创建一个特殊的文件
 */
struct dentry *aufs_create_dir(const char *name, struct dentry *parent)
{
    return aufs_create_file(name, S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, parent, NULL, NULL);
    //在目录项目"parent"下创建文件夹名为:“name”,文件者可读/写/执行+其他用户和用户组可读/执行权限
}
#if 0
static int enabled = 1;
/**
 * @brief:应于打开的aufs文件的读取方法
 *
 * @param fle   :文件对象
 * @param buf   :
 * @param nbytes:
 * @param ppos  :文件位置
 * @return ssize_t
 */
static ssize_t aufs_file_read(struct file *fle, char __user *buf, size_t nbytes, loff_t *ppos)
{
    char *s = enabled ? "aufs read enabled\\n" : "aufs read disabled\\n";
    dump_stack();
    return simple_read_from_buffer(buf, nbytes, ppos, s, strlen(s));
}

/**
 * @brief :对应于打开的aufs文件的写入方法
 *
 * @param file  :
 * @param buffer:
 * @param count :
 * @param ppos  :
 * @return ssize_t
 */
static ssize_t aufs_file_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
    int res = *buffer - '0';

    if (res)
        enabled = 1;
    else
        enabled = 0;

    return count;
}

/**
 * @brief:aufs文件系统的文件操作方式
 */
static struct file_operations aufs_file_operations = {
    .read = aufs_file_read,   /* aufs文件系统读取文件函数 */
    .write = aufs_file_write, /* aufs文件系统写文件函数  */
};
#endif
/**
 * @brief:填充aufs的super_block
 *
 * @param sb    : 超级块对象
 * @param data  :
 * @param silent:
 * @return int
 */
static int aufs_fill_super(struct super_block *sb, void *data, int silent)
{
    static struct tree_descr debug_files[] = {{""}};

    return simple_fill_super(sb, AUFS_MAGIC, debug_files); //简单填充超级块
}

/**
 * @brief :创建aufs文件系统的对应的根目录的dentry
 *
 * @param fs_type :文件系统类型结构体,该文件系统对象的所有信息描述
 * @param flags   :
 * @param dev_name: 设备名
 * @param data    :
 * @return struct dentry*
 */
static struct dentry *aufs_mount_sb(struct file_system_type *fs_type,
                                    int flags, const char *dev_name, void *data)
{
    return mount_single(fs_type, flags, data, aufs_fill_super);
    /*
        mount_single()主要用于挂载单例的文件系统,在系统中仅有一个超级块,所有挂载
        该文件系统节点都共享这个超级块。也无需获取设备打开设备过程,直接生成超级块后,
        就判断超级块里的根节点是否已生成,若已生成则无需在调用aufs_fill_super
    */
}

/**
 * @brief: 定义file_system_type结构体变量并初始化
 *      struct file_system_type 结构体描述:该文件系统具体信息和操作方法
 */
static struct file_system_type aufs_object = {
    .owner = THIS_MODULE,
    .name = "aufs_simple",        /* 文件系统名称:aufs_simple*/
    .mount = aufs_mount_sb,       /* 挂载超级快函数指针 */
    .kill_sb = kill_litter_super, /* 释放超级块函数指针 */
};

/**
 * @brief:初始化aufs文件系统
 *
 * @return int
 *
 * @description:
 *      创建aufs文件系统,同时创建对应的文件夹和文件
 */
static int __init aufs_init(void)
{
    int ret;
    struct dentry *pslot;

    /* STEP 1:注册文件系统对象 */
    ret = register_filesystem(&aufs_object);
    if (ret)
    {
        printk(KERN_ERR "aufs_simple: cannot register file system\\n");
        return ret;
    }

    /* STEP 2:获取文件系统的super_block对象与根目录的inode与dentry对象,并将这些对象加入到系统链表*/
    if (!ret)
    {
        aufs_mount = kern_mount(&aufs_object); // kern_count函数主要用于那些没有实体介质的文件系统,该函数主要是获取文件系统的super_block对象与根目录的inode与dentry对象,并将这些对象加入到系统链表。
        if (IS_ERR(aufs_mount))
        {
            printk(KERN_ERR "aufs: cannot mount file system\\n");
            unregister_filesystem(&aufs_object); //取消注册文件系统
            return ret;
        }
    }
    /*
        STEP 3:创建文件夹与文件
            文件夹:woman_star -->3个文件:lbb,fbb,lj1
            文件夹:man_star   -->3个文件:ldh,lcw,jw
        若想对文件进行操作则可在aufs_create_file()最后参数中传入&aufs_file_operations
        并在上面条件编译处0改为1,也可以加入其他文件操作函数
    */
    pslot = aufs_create_dir("woman_star", NULL);                   //创建woman_star文件系统,返回所创建文件夹的dentry
    aufs_create_file("lbb", S_IFREG | S_IRUGO, pslot, NULL, NULL); //在对应的文件夹下,创建具体的文件
    aufs_create_file("fbb", S_IFREG | S_IRUGO, pslot, NULL, NULL);
    aufs_create_file("lj1", S_IFREG | S_IRUGO, pslot, NULL, NULL);

    pslot = aufs_create_dir("man_star", NULL);
    aufs_create_file("ldh", S_IFREG | S_IRUGO, pslot, NULL, NULL);
    aufs_create_file("lcw", S_IFREG | S_IRUGO, pslot, NULL, NULL);
    aufs_create_file("jw", S_IFREG | S_IRUGO, pslot, NULL, NULL);

    return 0;
}

/**
 * @brief :卸载aufs文件系统
 */
static void __exit aufs_exit(void)
{
    simple_release_fs(&aufs_mount, &aufs_mount_count);
    // kern_unmount(aufs_mount);
    unregister_filesystem(&aufs_object);
    // aufs_mount = NULL;
}

module_init(aufs_init);
module_exit(aufs_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("This is a simple module");
MODULE_VERSION("Ver 0.1");

Makefile

KERNEL_VERSION = $(shell uname -r)#内核版本
MOD_NAME = aufs_simple

###############################################################################
# Common
###############################################################################
obj-m := $(MOD_NAME).o

all:
	make  -C /lib/modules/$(KERNEL_VERSION)/build M=$(shell pwd) modules

clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order 

  1️⃣make modules:单独编译模块
  2️⃣MOD_NAME = aufs_simple此为文件系统名,与程序中aufs_object变量中的name一致。
  3️⃣obj-m := 文件指定要编译为模块的目标文件。
  4️⃣-C = 路径:跳转到该路径下,读取哪里的Makefile。
  5️⃣M= 路径:跳转到该路径下执行先前读取的Makefile,编译源文件为模块,生成.ko文件。
  总结:先进入内核树下执行Makefile文件在读取当前路径的Makefile文件将源文件编译生成模块文件(.ko)。

2、实验

xsndz@Linux:~$ make
...
LD [M]  /home/xsndz/aufs/aufs_simple.ko
xsndz@Linux:~/aufs$ sudo insmod aufs_simple.ko 
[sudo] password for xsndz: 
xsndz@Linux:~/aufs$ lsmod | grep aufs_simple
aufs_simple            16384  1
xsndz@Linux:~/aufs$ mkdir au
xsndz@Linux:~/aufs$ sudo mount -t aufs_simple none ./au
xsndz@Linux:~/aufs$ tree ./au
./au
├── man_star
│   ├── jw
│   ├── lcw
│   └── ldh
└── woman_star
    ├── fbb
    ├── lbb
    └── lj1

2 directories, 6 files

  1️⃣make:运行Makefile文件,编译生成模块文件:aufs_simple.ko
  2️⃣sudo insmod aufs_simple.ko :加载aufs_simple模块。
  3️⃣lsmod | grep aufs_simple:查看是否成功加载该模块。
  4️⃣mkdir au:创建au文件夹,用于测试该文件系统
  5️⃣sudo mount -t aufs_simple none ./au:在au文件夹下挂载文件系统
  6️⃣tree ./au:查看挂载文件系统后产生的效果。
  卸载文件系统和卸载模块请参照篇二。

3、VScode 头文件波浪线解决

场景
  VScode 找不到头文件(头文件下有波浪线),实际编译是成功的。
解决方法
  ①在软件界面按住Ctrl+Shift+P,在软件上方出现输入框中输入:C/C++:Edit Configurations。选择第一个配置JSON配置。
  ②修改该json文件,添加头文件路径"/usr/src/linux-headers-4.15.0-142/include/"
   路径可通过指令:find ./ -type f -name "module.h",大概该处使用的头文件路径为:xxxx/linux-x.x.x/src/include/linux

"includePath": [
                "${workspaceFolder}/**",
                "/usr/src/linux-headers-4.15.0-142/include/"
            ],

4、VFS的四大对象

  可查看《Linux内核完全注释》、《深入理解理解Linux内核第三版中文》第十二章(详细的结构体与函数的中文注释)。

1、超级块对象(super_block)

  超级块对象是对应文件系统自身的控制块结构。超级块保存了:文件系统设定的文件块大小,超级块操作函数等信息。
  超级块的内容需读取具体文件系统在硬盘上的超级块结构获得,每个文件系统都有一个超级块对象
  注意:文件系统内所有inode也都要链接到超级块的链表头。
超级块结构体简化定义更多具体注释点击此链接struct super_block
  该结构体给出文件系统的全局信息。

struct super_block {
	struct list_head	s_list;/* Keep this first *///超级块链表
	dev_t			s_dev;							//设备号
	unsigned char		s_blocksize_bits;			//指定文件系统块大小(位为单位)
	unsigned long		s_blocksize;				//指定文件系统块大小(字节为单位)
	loff_t			s_maxbytes;						//指定文件系统中最大文件的尺寸
	struct file_system_type	*s_type;				//指向file_system_type结构的指针
	unsigned long		s_magic;					//魔术数字,每个文件系统都有一个魔术数字
	struct dentry		*s_root;					//指向文件系统根dentry的指针
	struct block_device	*s_bdev;					//指向文件系统存在的块设备指针
	struct list_head	s_inodes;	                //指向文件系统内所有inode,通过它可遍历所有inode对象
	const struct super_operations	*s_op;			//该结构指针指向于对超级块操作
	...
}

超级块操作结构体简化定义更多具体注释点击此链接
  该结构体给出最重要的超级块操作。

struct super_operations {
   	struct inode *(*alloc_inode)(struct super_block *sb);				//给定的超级块下创建并初始化一个新的索引节点对象
	void (*destroy_inode)(struct inode *);								//释放跟定的索引节点
   	void (*dirty_inode) (struct inode *, int flags);					//VFS在索引节点脏时(被修改)会调用此函数,日志文件系统(ect3等)执行该函数进行日志更新
	int (*write_inode) (struct inode *, struct writeback_control *wbc);//将给定的索引节点写入磁盘,wait参数知名写操作是否需要同步
	...
}

2、索引节点对象(inode)

  1️⃣概念:索引节点对象代表一个文件,索引节点保存了:文件大小、创建时间、文件块大小等文件属性信息(元数据)。
  2️⃣inode种类:①VFS的inode:在内存中。②具体文件系统的inode:在磁盘中。
  3️⃣inode号:是唯一的,访问文件都是通过inode号进行。
  4️⃣inode与dentry的关系:一个真实的文件可有多个多个dentry,因为指向文件的路径可有多个(考虑文件的链接),而inode只有一个。
  5️⃣inode与文件的关系:一个inode号值对应一个文件,一个文件也只有一个inode号,inode最大数量值就是文件最大值。
PS:
  注意文件与文件对象的区别,文件是指磁盘上具体存在的,文件对象是指内存上进程创建的对象。
索引节点结构体简化定义struct inode
  该结构给出文件的相关信息。

struct inode {
	umode_t	 i_mode;									//inode的类型:S_IFBLK(块设备)、S_IFCHR(字符设备)、S_IFDIR(目录)、S_IFSOCK(socket)、S_IFIFO(FIFO)
	kuid_t	i_uid;										//inode拥有者的id
	kgid_t	i_gid;										//inode所属的群组id
	unsigned int i_flags;								//记录此inode的参数
	struct super_block *i_sb;							//inode所属档案系统的super block 
	struct list_head	i_sb_list;						//用于链接超级块中inode链表。
	const struct inode_operations	*i_op;				//该结构指针指向用于索引节点操作
	...
}

索引节点操作结构体简化定义inode_operations
  该结构给出对文件的所有操作。

struct inode_operations {
	struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);				//在特定目录中寻找索引节点,该索引节点要对应于dentry中给出的文件名
	int (*create) (struct inode *,struct dentry *, umode_t, bool);	//VFS通过系统调用create()和open()来调用该函数,从而为dentry对象创建一个新的索引节点。在创建时使用mode制定初始模式
	int (*link) (struct dentry *,struct inode *,struct dentry *);	//该函数被系统调用link(),用来创建硬连接。硬链接名称由dentry参数指定,连接对象是dir目录中ld_dentry目录想所代表的文件
	int (*unlink) (struct inode *,struct dentry *);					//该函数被系统调用unlink()调用,从目录dir中删除由目录项dentry制动的索引节点对象
	int (*symlink) (struct inode *,struct dentry *,const char *);	//该函数被系统调用symlik()调用,创建符号连接,该符号连接名称由symname指定,连接对象是dir目录中的dentry目录项
	...
}

3、目录项对象(dentry)

  目录项对象即目录,本身也是一个特殊文件,每个文件都有一个dentry(可能不止一个),该dentry链接到上级目录的dentry。根目录有一个dentry结构,子目录在链接上级目录,层层链接形成dentry树,反映了文件系统的树状关系(描述文件的逻辑属性)。
  为加快对dentry查找,内核使用hash表缓存dentry,称为dentry cache(所有的dentry都指向dentry_hashtable)。
目录项结构体简化定义struct dentry
  该结构给出目录项的相关信息。

struct dentry {
	unsigned int d_flags;				/* protected by d_lock */
	seqcount_t d_seq;					/* per dentry seqlock */
	struct hlist_bl_node d_hash;		//查找hash表(hash cache)。
	struct dentry *d_parent;			//指向父dentry结构
	struct qstr d_name;					//保存文件或目录名,打开文件时,根据该成员与用户输入名比较进行搜索。
	struct inode *d_inode;				//指向inode结构。该inode与dentry共同描述一个普通文件或目录文件。
	unsigned char d_iname[DNAME_INLINE_LEN];	/* small names */
	struct list_head d_child;			//dentry自身的链表头,(当移动文件时,需把一个dentry结构从旧的父dentry链表上脱离,然后链接到新父dentry的d_subdirs成员)
	struct list_head d_subdirs;			//子项(目录/文件)的链表头
	const struct dentry_operations *d_op;//该结构指针指向于对目录项操作
	...
}

目录项操作结构体简化定义dentry_operations
  该结构给出对目录项的相关操作。

struct dentry_operations {
	int (*d_revalidate)(struct dentry *, unsigned int);				//用于使一个dentry重新生效
	int (*d_hash)(const struct dentry *, struct qstr *);			//用于VFS向哈希表中加入一个dentry
	int (*d_compare)(const struct dentry *,
			unsigned int, const char *, const struct qstr *);		//dentry的最后一个inode被释放时(d_count等于零),此方法被调用,因为这意味这没有inode再使用此dentry;当然,此dentry仍然有效,并且仍然在dcache中。
	void (*d_release)(struct dentry *);								//用于清除一个dentry。
	...
}

4、文件对象(file)

  文件对象是描述进程和文件交互关系
  硬件上并不存在一个文件结构,而是当进程打开一个文件时,内核就动态创建一个文件对象,同一文件在不同进程中有不同的文件对象(一个文件可能存在多个文件对象)。
文件结构体简化定义struct file
  描述的是进程已经打开的文件的相关信息。

struct file {
	struct path		f_path;						//文件路径
	struct inode		*f_inode;	/* cached value */
	spinlock_t		f_lock;						//自旋锁
	atomic_long_t		f_count;				//引用计数
	unsigned int 		f_flags;				//打开文件时候指定的标识
	fmode_t			f_mode;						//文件的访问模式
	loff_t			f_pos;						//目前文件的相对开头的偏移
	struct fown_struct	f_owner;				//记录一个进程ID,以及当某些事发送的时候发送给该ID进程的信号
	const struct file_operations	*f_op;		//文件操作,当进程打开文件的时候,这个文件的关联inode中的i_fop文件操作会初始化这个f_op字段
	...
}

文件操作结构体简化定义file_operations
  把系统调用和驱动程序关联起来的关键数据结构。


struct file_operations {
	struct module *owner;															//不是一个操作;是一个指向拥有这个结构的模块的指针. 用来在它的操作还在被使用时阻止模块被卸载.
	loff_t (*llseek) (struct file *, loff_t, int);									//用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值. l
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);				//用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以 -EINVAL("Invalid argument") 失败
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);		//发送数据给设备. 如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数.
	int (*open) (struct inode *, struct file *);									//尽管这常常是对设备文件进行的第一个操作, 不要求驱动声明一个对应的方法. 如果这个项是 NULL, 设备打开一直成功, 但是你的驱动不会得到通知.
	int (*flush) (struct file *, fl_owner_t id);									//在进程关闭它的设备文件描述符的拷贝时调用; 它应当执行(并且等待)设备的任何未完成的操作. 这个必须不要和用户查询请求的 fsync 操作混淆了. 
	...
}

总结

  ① super_block:文件系统相关控制信息。
  ② inode:文件相关信息。
  ③ dentry:文件之间的逻辑关系,构成文件系统的树状关系。
  ④ file:进程和具体文件交互的关系,具体的操作对象。
  1️⃣超级块与超级块的关系:每个文件系统都有一个超级块,每个超级块都要链接到超级块链表
  2️⃣超级块与inode的关系:文件系统中每个文件打开时都需要分配inode结构,这些inode结构都要链接到超级块上。
  3️⃣inode与文件的关系:一个inode号值对应一个文件,一个文件也只有一个inode号,inode最大数量值就是文件最大值。
  4️⃣inode与dentry的关系:一个真实的文件可有多个dentry,因为指向文件的路径可有多个(考虑文件的链接),而inode只有一个。
  5️⃣file与文件的关系:一个文件可能对应多个file对象(一/多个进程打开同一个文件)

  inode 和dentry分别代表文件通用的两部分,只是对某些文件系统可能还需其他信息。
  file表示进程和具体文件交互的关系,每个进程都指向一个文件描述符表,每打开一个文件就会有申请一个文件对象生成文件号并保存到文件描述符表数组中。若想操作该文件对象则只需指定该文件对象的文件号即可。(两个进程允许有相同的文件描述符,但指向的不是同一个文件)

二、源码详解

1、构建文件系统对象

  构建文件系统对象,指定挂载超级块函数与释放超级块函数,当将文件系统进行挂载时会调用对应的函数。

/**
 * @brief: 定义file_system_type结构体变量并初始化
 *      struct file_system_type 结构体描述:该文件系统具体信息和操作方法
 */
static struct file_system_type aufs_object = {
    .owner = THIS_MODULE,			/* 表明所有者是该模块将保留其所有权,防止模块在运行时被卸载 */
    .name = "aufs_simple",          /* 文件系统名称:aufs_simple*/
    .mount = aufs_mount_sb,         /* 挂载超级快函数指针 */
    .kill_sb = kill_litter_super,   /* 释放超级块函数指针 */
};

(1)挂载超级块函数【.mount】

/**
 * @brief :创建aufs文件系统的对应的根目录的dentry
 *
 * @param fs_type :文件系统类型结构体,该文件系统对象的所有信息描述
 * @param flags   :
 * @param dev_name: 设备名
 * @param data    :
 * @return struct dentry*
 */
static struct dentry *aufs_mount_sb(struct file_system_type *fs_type,
                                    int flags, const char *dev_name, void *data)
{
    return mount_single(fs_type, flags, data, aufs_fill_super);
    /*
        mount_single()主要用于挂载单例的文件系统,在系统中仅有一个超级块,所有挂载
        该文件系统节点都共享这个超级块。也无需获取设备打开设备过程,直接生成超级块后,
        就判断超级块里的根节点是否已生成,若已生成则无需在调用aufs_fill_super
    */
}
①挂载单例文件系统(仅一个超级块)【mount_single】

  struct dentry *mount_single(struct file_system_type *fs_type,int flags, void *data,int (*fill_super)(struct super_block *, void *, int));
  若尚未有根dentry则调用aufs_fill_super为超级块填充根dentry和根inode.

②超级块的填充方式【aufs_fill_super】
/**
 * @brief:填充aufs的super_block
 *
 * @param sb    : 超级块对象
 * @param data  :
 * @param silent:
 * @return int
 */
static int aufs_fill_super(struct super_block *sb, void *data, int silent)
{
    static struct tree_descr debug_files[] = {{""}};
    return simple_fill_super(sb, AUFS_MAGIC, debug_files); //简单填充超级块
}

  AUFS_MAGIC:为文件的魔术字
  aufs_fill_super函数作用:为超级块对象申请必备成员。
  对aufs系统来说,定义struct tree_descr结构体,用于描述一些文件。
    若不为空,填充超级块的同时,需在根目录下创建一些文件。
    若为空,说明不需要创建任何文件。

(2)释放超级块函数【.kill_sb】

  kill_litter_super

2、初始化文件系统

static struct vfsmount *aufs_mount; // aufs_simple文件系统的挂载点
/**
 * @brief:初始化aufs文件系统
 *
 * @return int
 *
 * @description:
 *      创建aufs文件系统,同时创建对应的文件夹和文件
 */
static int __init aufs_init(void)
{
    int ret;
    struct dentry *pslot;

    /* STEP 1:注册文件系统对象 */
    ret = register_filesystem(&aufs_object);
    if (ret)
    {
        printk(KERN_ERR "aufs_simple: cannot register file system\\n");
        return ret;
    }

    /* STEP 2:获取文件系统的super_block对象与根目录的inode与dentry对象,并将这些对象加入到系统链表*/
    if (!ret)
    {
        aufs_mount = kern_mount(&aufs_object); // kern_count函数主要用于那些没有实体介质的文件系统,该函数主要是获取文件系统的super_block对象与根目录的inode与dentry对象,并将这些对象加入到系统链表。
        if (IS_ERR(aufs_mount))
        {
            printk(KERN_ERR "aufs: cannot mount file system\\n");
            unregister_filesystem(&aufs_object); //取消注册文件系统
            return ret;
        }
    }
    /*
        STEP 3:创建文件夹与文件
            文件夹:woman_star -->3个文件:lbb,fbb,lj1
            文件夹:man_star   -->3个文件:ldh,lcw,jw
        若想对文件进行操作则可在aufs_create_file()最后参数中传入&aufs_file_operations
        并在上面条件编译处0改为1,也可以加入其他文件操作函数
    */
    pslot = aufs_create_dir("woman_star", NULL);                   //创建woman_star文件系统,返回所创建文件夹的dentry
    aufs_create_file("lbb", S_IFREG | S_IRUGO, pslot, NULL, NULL); //在对应的文件夹下,创建具体的文件
    aufs_create_file("fbb", S_IFREG | S_IRUGO, pslot, NULL, NULL);
    aufs_create_file("lj1", S_IFREG | S_IRUGO, pslot, NULL, NULL);

    pslot = aufs_create_dir("man_star", NULL);
    aufs_create_file("ldh", S_IFREG | S_IRUGO, pslot, NULL, NULL);
    aufs_create_file("lcw", S_IFREG | S_IRUGO, pslot, NULL, NULL);
    aufs_create_file("jw", S_IFREG | S_IRUGO, pslot, NULL, NULL);

    return 0;
}

(1)将文件系统对象登记到系统【register_filesystem】

  int register_filesystem(struct file_system_type *);
  所有的文件系统都保存在一个单链表中,当新的文件系统注册到内核时,将逐元素扫描该链表,直至链表尾部或找到对应文件系统。
    ①直至链表尾部表示没找到则将新文件系统对象置于链表末尾。
    ②找到对应文件系统则返回适当错误信息,因为一个文件系统不能注册两次。

(2)为文件系统申请必备的数据结构【kern_mount】

  struct vfsmount *kern_mount (struct file_system_type *type)
  真正为文件系统分配超级块对象和vfsmount对象
  该函数主要实现三个方面的功能:
    ①根据新文件系统名创建一个vfsmount结构,用于为文件系统之间的挂载关系而设计的。
    ②调用文件系统对象中的mount函数创建一个超级块对象,同时还创建一个dentry结构作为文件根dentry和一个inode结构作为根inode.(后续创建的目录都以该根目录作为父目录)。
    ③设置vfsmount结构的父指针为自身,mnt_mountpoint为文件系统的根dentry。若文件系统mount到其他文件系统,那么这两个参数就要设置为源文件系统的参数。
PS:
  可知,该函数执行了文件系统登记的大部分工作,像文件系统对象中mount指向的函数也是在此时调用运行,是比较重要的部分。
  至此,创建了一个超级块对象,创建了一个根dentry和一个根inode,后续的创建的文件和目录都会连接在这个根dentry上。
  若是创建失败则会调用unregister_filesystem()取消文件系统的注册。

(3)创建两目录,每目录下创建三文件【aufs_create_dir、aufs_create_file】

①创建目录:aufs_create_dir
/**
 * @brief :aufs中创建文件夹函数
 *
 * @param name   :文件夹名称
 * @param parent :文件夹上级目录项
 * @return struct dentry*
 *
 * @description:
 *      创建目录其实就是创建一个特殊的文件
 */
struct dentry *aufs_create_dir(const char *name, struct dentry *parent)
{
    return aufs_create_file(name, S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, parent, NULL, NULL);
    //在目录项目"parent"下创建文件夹名为:“name”,文件者可读/写/执行+其他用户和用户组可读/执行权限
}

  可知aufs_create_dir()aufs_create_file()的封装函数,即:目录是一种特殊的文件,使用创建文件函数时记得使用标记:S_IFDIR

②创建文件:aufs_create_file
/**
 * @brief :aufs中创建文件函数
 *
 * @param name  :文件名
 * @param mode  :文件类型
 * @param parent:文件的上级目录项
 * @param data  :数据
 * @param fops  :文件的操作函数指针结构体变量
 * @return struct dentry*
 */
struct dentry *aufs_create_file(const char *name, mode_t mode,
                                struct dentry *parent, void *data,
                                struct file_operations *fops)
{
    struct dentry *dentry = NULL;
    int error;
    
    printk("aufs: creating file \'%s\'", name);
    error = aufs_create_by_name(name, mode, parent, &dentry);
    if (error)
    {
        dentry = NULL;
        goto exit;
    }

    /*  若为文件夹则此处两个都为空, 若为文件则会对进行赋值 */
    if (dentry->d_inode)
    {
        if (data)
            dentry->d_inode->i_private = data; //文件系统或设备私有指针
        if (fops)
            dentry->d_inode->i_fop = fops; //文件的操作函数结构体的指针变量
    }
exit:
    return dentry;
}

/**
 * @brief 创建文件的dentry和inode结构
 *
 * @param name   :文件名
 * @param mode   :模式(文件类型)
 * @param parent :父目录项
 * @param dentry :目录项(传入地址用于在外面获取目录项)
 * @return int
 *
 * @description:
 *          根据父dentry、mode、name创建子dentry
 */
static int aufs_create_by_name(const char *name, mode_t mode,
                               struct dentry *parent, struct dentry **dentry)
{
    int error = 0;

    /* 若不存在父目录项,则将超级快的根目录项赋值给父目录项 */
    if (!parent)
    {
        if (aufs_mount && aufs_mount->mnt_sb) //挂载点存在且挂载点超级块也存在
        {
            parent = aufs_mount->mnt_sb->s_root; //将根目录项给父目录项
        }
    }

    /* 若还不存在父目录项目则报错 */
    if (!parent)
    {
        printk("Ah! can not find a parent!\\n");
        return -EFAULT;
    }

    /* 查找“name”的dentry对象,找到后根据类型创建目录 */
    *dentry = NULL;                                       //对子dentry清空
    *dentry = lookup_one_len(name, parent, strlen(name)); //在父目录项下找“name”的子dentry返回该指针,不存在则创建。
    if (!IS_ERR(dentry))
    {
        if ((mode & S_IFMT) == S_IFDIR)
            error = aufs_mkdir(parent->d_inode, *dentry, mode);
        else
            error = aufs_create(parent->d_inode, *dentry, mode);
    }
    else
    {
        error = PTR_ERR(dentry);
    }

    return error;
}

/**
 * @brief:  aufs 创建函数
 *
 * @param dir   :父目录项下的inode节点
 * @param dentry:目录项
 * @param mode  :模式(文件类型和属性)
 * @return int
 */
static int aufs_create(struct inode *dir, struct dentry *dentry, int mode)
{
    return aufs_mknod(dir, dentry, mode | S_IFREG, 0);
    //在目录项“dentry”下创建一个常规文件+mode属性
}


/**
 * @brief   创建文件夹
 *
 * @param dir   :父目录项下的inode节点
 * @param dentry:目录项
 * @param mode  :模式(文件类型与属性)
 * @return int
 */
static int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
    int res;
    res = aufs_mknod(dir, dentry, mode | S_IFDIR, 0); //以文件夹模式创建inode
    if (!res)
    {
        dir->__i_nlink++; //链接数(多少文件目录项指向该inode)
    }
    return res;
}

/**
 * @brief :把创建的inode和dentry结构体连接起来
 *
 * @param dir    :父目录项下的inode节点
 * @param dentry :目录项
 * @param mode   :模式(文件类型)
 * @param dev    :设备号
 * @return int
 */
static int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
    struct inode *inode;
    int error = -EPERM;

    /* 若磁盘上inode已存在则直接退出 */
    if (dentry->d_inode)
        return -EEXIST;

    /*  */
    inode = aufs_creat_inode(dir->i_sb, mode, dev);
    if (inode)
    {
        d_instantiate(dentry, inode);
        dget(dentry);
        error = 0;
    }
    return error;
}

/**
 * @brief :创建inode节点
 *
 * @param sb  : 超级块
 * @param mode: 文件类型
 * @param dev : 设备号
 * @return struct inode* :返回inode节点指针
 *
 * @description:
 *    在aufs文件系统的super_block创建inode结构体
 */
static struct inode *aufs_creat_inode(struct super_block *sb, int mode, dev_t dev)
{
    struct inode *inode = new_inode(sb); //在超级块sb上申请一个新的inode

    /* inode创建成功后进行赋值 */
    if (inode)
    {
        inode->i_mode = mode;                             //文件类型和属性
        inode->i_uid = current_fsuid();                   //用户ID
        inode->i_gid = current_fsgid();                   //组ID
        inode->i_blocks = 0;                              //文件所占块数
        inode->i_atime = inode->i_mtime = inode->i_ctime; //最后访问时间
        switch (mode & S_IFMT)                            //文件类型掩码
        {
        case S_IFREG: //常规文件
            printk("create a file \\n");
            break;
        case S_IFDIR: //目录
            inode->i_op = &simple_dir_inode_operations;
            inode->i_fop = &simple_dir_operations;
            inode->__i_nlink++;
            printk("creat a dir file \\n");
            break;
        default:
            init_special_inode(inode, mode, dev); //实现把inode节点和设备号绑定
            break;
        }
    }

    return inode;
}

  1️⃣ aufs_create_file():创建文件。
    ①aufs_create_by_name()创建出dentry、inode。(文件是由dentry、inode表示)
    ②后对d_inode成员赋值。
  2️⃣ aufs_create_by_name():创建出dentry、inode
    ①判断存不存在父dentry,若不存在则将根dentry作为父dentry.【创建dentry】
    ②lookup_one_len():父dentry下找“name”的子dentry返回该指针,不存在则创建。
    ③aufs_mkdir()/aufs_create():根据掩码判断是创建文件夹还是创建普通文件。【创建inode并将其与dentry联系起来】
  3️⃣ aufs_mkdir()/aufs_create():创建inode并将其与dentry联系起来。
    这两个函数其实就是对aufs_mknod()的封装
  4️⃣ aufs_mknod():创建inode并将其与dentry联系起来
    ①调用aufs_creat_inode():创建目录文件的inode。
    ②调用d_instantiate():把dentry加入到inode的链表头。
  5️⃣ aufs_creat_inode():创建目录文件的inode。
    ①调用new_inode():在超级块sb上申请一个新的inode
    ② inode创建成功后进行赋值.
    ③根据不同的掩码得出inode的类型,设置不同的操作函数
      1.若是普通文件则仅输出一句话。
      2.若是目录文件则设置i_opi_fop
      3.若是特殊文件,如:块设备/字符设备文件则调用:init_special_inode()
  
  
  
  

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值