文件系统操作

首先,我们从标准库用来与内核通信的系统调用来研究。尽管文件操作对所有应用程序来说都属于标准功能,但对文件系统的操作只限于几个系统程序,即用于装载和卸载文件系统的mountumount程序。

注册文件系统

在文件系统注册到内核时,文件系统是编译为模块,或者持久编译到内核中。fs/super.c中的register_filesystem用来向内核注册文件系统。一个文件系统不能注册两次。
用于描述文件系统的结构源码如下

struct file_system_type
{const char *name;//保存文件系统的名称int fs_flags; //使用的标志,例如标明只读装载、禁止/setudi/setgid操作或进行其他的微调。//文件系统必须在物理设备上面#define FS_REQUIRES_DEV        1//此文件系统需要使用二进制数据结构mount data*.nfs使用这种mount data #define FS_BINARY_MOUNTDATA    2//系统含有子类型,最常见的就是FUSE,FUSE不是真正的文件系统,所以要通过文件系统类型来区别,通过FUSE接口实现不同的文件系统。#define FS_HAS_SUBTYPE     4//每次挂载后都是不同的user namespace,比如deypts#define FS_USERNS_MOUNT      8   /* Can be mounted by userns root */#define FS_USERNS_DEV_MOUNT  16 /* A userns mount does not imply MNT_NODEV */#define FS_USERNS_VISIBLE   32  /* FS must already be visible */#define FS_RENAME_DOES_D_MOVE   32768   /* FS will handle d_move() during rename() internally. */struct dentry *(*mount) (struct file_system_type *, int,const char *, void *);//用户调用sys_mount挂载某一个文件系统时,最终会调到该回调函数struct dentry *(*mount2) (struct vfsmount *, struct file_system_type *, int,const char *, void *);void *(*alloc_mnt_data) (void);void (*kill_sb) (struct super_block *);//删除内存中的superblock,在卸载文件系统时使用。struct module *owner; //指向实现这个文件系统的模块,通常为THIS_MODULES宏struct file_system_type * next; //指向文件系统链表的下一个文件系统模型struct hlist_head fs_supers; //此文件系统类型的文件系统超级块结构串都在这个表头中。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 s_writers_key[SB_FREEZE_LEVELS];struct lock_class_key i_lock_key;struct lock_class_key i_mutex_key;struct lock_class_key i_mutex_dir_key;
};

register_filesystem函数

/*** register_filesystem - register a new filesystem* @fs: the file system structure*  * Adds the file system passed to the list of file systems the kernel* is aware of for mount and other syscalls. Returns 0 on success,* or a negative errno code on an error.*  * The &struct file_system_type that is passed is linked into the kernel * structures and must not be freed until the file system has been* unregistered.*/int register_filesystem(struct file_system_type * fs)
{int res = 0;struct file_system_type ** p;BUG_ON(strchr(fs->name, '.'));if (fs->next)return -EBUSY;write_lock(&file_systems_lock);p = find_filesystem(fs->name, strlen(fs->name));if (*p)res = -EBUSY;else*p = fs;write_unlock(&file_systems_lock);return res;
}

装载和卸载

目录树的装载和卸载比仅仅注册文件系统复杂的多,因为后者只需要向一个链表中添加对象,而前者需要对内核的内部数据结构执行很多操作,所以要复杂的多,文件系统的装载由mount系统调用发起,我们需要阐明在现存目录树中装载新的文件系统必须执行的任务。还需要用于描述装载点的数据结构。

  • vfsmount结构:采用一种单一的文件系统层次结构,新的文件系统可以集成到其中,使用mount可查询目录树中各文件系统的装载情况如下:
    vfsmount结构(文件系统层次结构,包含各种文件系统类型)

    将文件系统装载到一个目录时,装载点的内容被替换为即将装载的文件系统的内容,但并没有丢失或损坏,只是无法访问到,只要卸载掉装载的文件系统就可以重新访问,这就相当于挂载一个U盘或者sd卡一样。
    vfsmount结构描述一个独立文件系统的挂载信息,每个不同挂载点对应一个独立的vfsmount结构,属于同一文件系统的所有目录和文件隶属于同一个vfsmount,该vfsmount结构对应于该文件系统的顶层目录,即挂载目录。
    mount源码分析如下:
struct mount {struct hlist_node mnt_hash;struct mount *mnt_parent;               //装载点所在的父文件系统struct dentry *mnt_mountpoint;         //装载点在父文件系统中的dentry(目录项)struct vfsmount mnt;union {struct rcu_head mnt_rcu;struct llist_node mnt_llist;};
#ifdef CONFIG_SMPstruct mnt_pcp __percpu *mnt_pcp;
#elseint mnt_count;int mnt_writers;
#endif//子文件系统链表struct list_head mnt_mounts; /* list of children, anchored here *///链表元素,用于父文件系统中的mnt_mount链表struct list_head mnt_child;  /* and going through their mnt_child */struct list_head mnt_instance;   /* mount instance on sb->s_mounts *///设备名称,例如/dev/dsk/hda1const char *mnt_devname;    /* Name of device e.g. /dev/dsk/hda1 */struct list_head mnt_list;//链表元素,用于特定于文件系统的到期链表中struct list_head mnt_expire;  /* link in fs-specific expiry list *///链表元素,用于共享装载的循环链表struct list_head mnt_share;   /* circular list of shared mounts *///从属装载的链表struct list_head mnt_slave_list;/* list of slave mounts *///链表元素,用于从属装载的链表struct list_head mnt_slave;   /* slave list entry *///指向主装载,从属装载位于master->mnt_slave_list链表上面struct mount *mnt_master;   /* slave is on master->mnt_slave_list *///所属的命名空间struct mnt_namespace *mnt_ns;   /* containing namespace */struct mountpoint *mnt_mp;    /* where is it mounted */struct hlist_node mnt_mp_list; /* list mounts with the same mountpoint */struct list_head mnt_umounting; /* list entry for umount propagation */
#ifdef CONFIG_FSNOTIFYstruct hlist_head mnt_fsnotify_marks;__u32 mnt_fsnotify_mask;
#endifint mnt_id;           /* mount identifier */int mnt_group_id;     /* peer group identifier */int mnt_expiry_mark;     /* true if marked for expiry */struct hlist_head mnt_pins;struct fs_pin mnt_umount;struct dentry *mnt_ex_mountpoint;
};

文件系统之间的父子关系有上述我们所讲述的两个成员实现的链表表示,mnt_mounts表头是子文件系统链表的起点,而mnt_child字段则用作该链表元素。
系统当中的每个vfsmount实例,通过两种途径标识:
1.一个命名空间的所有装载的文件系统都保存在namespace->list链表中;
2.使用vfsmountmnt_list成员作为链表元素。

超级块管理

在装载新的文件系统时,vfsmont并不是唯一需要在内存中创建的结构,装载操作开始于超级块的读取

struct super_block {//将该成员设置于起始位置struct list_head    s_list;     /* Keep this first *///搜索索引,不是kdev_tdev_t            s_dev;      /* search index; _not_ kdev_t */unsigned char       s_blocksize_bits;unsigned long      s_blocksize;loff_t          s_maxbytes; /* Max file size 最大文件长度*/ struct file_system_type   *s_type;const struct super_operations   *s_op; //指向一个包含函数指针的结构,提供接口用于处理超级块的相关操作,操作的实现必须由底层文件系统的代码实现。const struct dquot_operations *dq_op;const struct quotactl_ops    *s_qcop;const struct export_operations *s_export_op;unsigned long       s_flags;unsigned long       s_iflags;   /* internal SB_I_* flags */unsigned long        s_magic;struct dentry       *s_root;struct rw_semaphore s_umount;int            s_count;atomic_t        s_active;
#ifdef CONFIG_SECURITYvoid                    *s_security;
#endifconst struct xattr_handler **s_xattr;const struct fscrypt_operations  *s_cop;struct hlist_bl_head s_anon;     /* anonymous dentries for (nfs) exporting */struct list_head    s_mounts;   /* list of mounts; _not_ for fs use */struct block_device   *s_bdev;struct backing_dev_info *s_bdi;struct mtd_info      *s_mtd;struct hlist_node    s_instances;unsigned int        s_quota_types;  /* Bitmask of supported quota types */struct quota_info s_dquot;    /* Diskquota specific options */struct sb_writers   s_writers;char s_id[32];                /* Informational name */u8 s_uuid[16];              /* UUID */void          *s_fs_info; /* Filesystem private info 文件系统私有信息*/unsigned int       s_max_links;fmode_t         s_mode;/* Granularity of c/m/atime in ns.Cannot be worse than a second */u32           s_time_gran;file_system_type/** The next field is for VFS *only*. No filesystems have any business* even looking at it. You had been warned.*/struct mutex s_vfs_rename_mutex;   /* Kludge *//** Filesystem subtype.  If non-empty the filesystem type field* in /proc/mounts will be "type.subtype"*/char *s_subtype;/** Saved mount options for lazy filesystems using* generic_show_options()*/char __rcu *s_options;const struct dentry_operations *s_d_op; /* default d_op for dentries *//** Saved pool identifier for cleancache (-1 means none)*/int cleancache_poolid;struct shrinker s_shrink;   /* per-sb shrinker handle *//* Number of inodes with nlink == 0 but still referenced */atomic_long_t s_remove_count;/* Being remounted read-only */int s_readonly_remount;/* AIO completions deferred from interrupt context */struct workqueue_struct *s_dio_done_wq;struct hlist_head s_pins;/** Keep the lru lists last in the structure so they always sit on their* own individual cachelines.*/struct list_lru      s_dentry_lru ____cacheline_aligned_in_smp;struct list_lru       s_inode_lru ____cacheline_aligned_in_smp;struct rcu_head        rcu;struct work_struct  destroy_work;struct mutex       s_sync_lock;    /* sync serialisation lock *//** Indicates how deep in a filesystem stack this SB is*/int s_stack_depth;/* s_inode_list_lock protects s_inodes */spinlock_t     s_inode_list_lock ____cacheline_aligned_in_smp;struct list_head s_inodes;   /* all inodes 所有的inode的链表*/
};

*const struct super_operations s_op; //指向一个包含函数指针的结构,提供接口用于处理超级块的相关操作,操作的实现必须由底层文件系统的代码实现。
包含读写inode,删除inode等

struct super_operations {struct inode *(*alloc_inode)(struct super_block *sb);//将inode从内存和底层的存储介质删除void (*destroy_inode)(struct inode *);//将传递的inode结构标记为“脏的”,意思就是修改过void (*dirty_inode) (struct inode *, int flags);int (*write_inode) (struct inode *, struct writeback_control *wbc);int (*drop_inode) (struct inode *);void (*evict_inode) (struct inode *);void (*put_super) (struct super_block *);int (*sync_fs)(struct super_block *sb, int wait);int (*freeze_super) (struct super_block *);int (*freeze_fs) (struct super_block *);int (*thaw_super) (struct super_block *);int (*unfreeze_fs) (struct super_block *);int (*statfs) (struct dentry *, struct kstatfs *);int (*remount_fs) (struct super_block *, int *, char *);int (*remount_fs2) (struct vfsmount *, struct super_block *, int *, char *);void *(*clone_mnt_data) (void *);void (*copy_mnt_data) (void *, void *);void (*umount_begin) (struct super_block *);int (*show_options)(struct seq_file *, struct dentry *);int (*show_options2)(struct vfsmount *,struct seq_file *, struct dentry *);int (*show_devname)(struct seq_file *, struct dentry *);int (*show_path)(struct seq_file *, struct dentry *);int (*show_stats)(struct seq_file *, struct dentry *);
#ifdef CONFIG_QUOTAssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);struct dquot **(*get_dquots)(struct inode *);
#endifint (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t);long (*nr_cached_objects)(struct super_block *,struct shrink_control *);long (*free_cached_objects)(struct super_block *,struct shrink_control *);
};

mount系统调用

mount系统调用的入口点是sys_mount函数,

/** Flags is a 32-bit value that allows up to 31 non-fs dependent flags to* be given to the mount() call (ie: read-only, no-dev, no-suid etc).*  * data is a (void *) that can point to any structure up to* PAGE_SIZE-1 bytes, which can contain arbitrary fs-dependent* information (or be NULL).*  * Pre-0.97 versions of mount() didn't have a flags word.* When the flags word was introduced its top half was required* to have the magic value 0xC0ED, and this remained so until 2.4.0-test9.* Therefore, if this magic number is present, it carries no information* and must be discarded.*/
long do_mount(const char *dev_name, const char __user *dir_name,const char *type_page, unsigned long flags, void *data_page)
{struct path path;int retval = 0;int mnt_flags = 0;/* Discard magic */if ((flags & MS_MGC_MSK) == MS_MGC_VAL)flags &= ~MS_MGC_MSK;/* Basic sanity checks */if (data_page)((char *)data_page)[PAGE_SIZE - 1] = 0;/* ... and get the mountpoint */retval = user_path(dir_name, &path);if (retval)return retval;retval = security_sb_mount(dev_name, &path,type_page, flags, data_page);if (!retval && !may_mount())retval = -EPERM;if (retval)goto dput_out;/* Default to relatime unless overriden */if (!(flags & MS_NOATIME))mnt_flags |= MNT_RELATIME;/* Separate the per-mountpoint flags */if (flags & MS_NOSUID)mnt_flags |= MNT_NOSUID;if (flags & MS_NODEV)mnt_flags |= MNT_NODEV;if (flags & MS_NOEXEC)mnt_flags |= MNT_NOEXEC;if (flags & MS_NOATIME)mnt_flags |= MNT_NOATIME;if (flags & MS_NODIRATIME)mnt_flags |= MNT_NODIRATIME;if (flags & MS_STRICTATIME)mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME);if (flags & MS_RDONLY)mnt_flags |= MNT_READONLY;/* The default atime for remount is preservation */if ((flags & MS_REMOUNT) &&((flags & (MS_NOATIME | MS_NODIRATIME | MS_RELATIME |MS_STRICTATIME)) == 0)) {mnt_flags &= ~MNT_ATIME_MASK;mnt_flags |= path.mnt->mnt_flags & MNT_ATIME_MASK;}flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN |MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |MS_STRICTATIME);if (flags & MS_REMOUNT) //修改已经装载的文件系统的选项retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags,data_page);else if (flags & MS_BIND) //用于通过环回接口装载一个文件系统retval = do_loopback(&path, dev_name, flags & MS_REC);else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))retval = do_change_type(&path, flags);//负责处理共享、从属和不可绑定装载,他可以改变装载标志或者在涉及的各个vfsmount实例之间建立所需要的数据结构的关联。else if (flags & MS_MOVE)retval = do_move_mount(&path, dev_name); //用来移动一个已经装载的文件系统else//处理普通装载操作retval = do_new_mount(&path, type_page, flags, mnt_flags,dev_name, data_page);
dput_out:path_put(&path);return retval;
}

共享子树(shared subtrees)

共享子树最核心的特征是允许挂载和卸载事件以一种自动的,可控的方式在不同的namespace间传递(propagation)。这就意味着,在一个命名空间中挂载光盘的同时也会触发对于其他namespace对同一张光盘的挂载
在共享子树中,每个挂载点都存在一个名为传递类型(propagation type)的标记,该标记决定了一个namespase中创建或者删除的挂载点是否会传递到其他的namespaces
共享子树有四种传递类型:

  • MS_SHARED:该挂载点和它的共享挂载和卸载事件。
  • MS_PRIVATE:和共享挂载相反,标记为private的事件不会传递到任何的对等组,挂载操作默认使用次标志。
  • MS_SLAVE:这个传递类型介于shared和slave之间,一个slave mount拥有一个master(一个共享的对等组),slave mount不能将事件传递给master mount
  • MS_UNBINDABLE:该挂载点是不可缩写的。

处理VFS对象及标准函数---VFS对象相关推荐

  1. 处理VFS对象及标准函数--标准函数

    一.标准函数 VFS层提供的有用资源是用于读写数据的标准函数.这些操作对所有文件系统来说,在一定程度上都是相同的.如果数据所在的块是已知的,则首先查询页缓存,如果数据并未保存在其中,则向对应的块设备发 ...

  2. 对象存储3:对象存储的原理、构造和详解

    前两篇介绍了对象存储的基础,包括存储类型,常用存储分类和分类方法. SCSI,TCP/IP,FC等存储介质以及DAS\NAS\SAN等存储网络,请参考:对象存储1:传统存储类型和分类. 文件存储,块存 ...

  3. java查看对象锁级别_对象级别锁 vs 类级别锁(Java)

    前言 对于多线程(并发)和Spring Boot这两块在同步进行学习中,在看到使用synchronized关键字使操作同步时,看到和C#中不一样的东西,所以这里呢,就深入学习了下,若有错误之处,还望指 ...

  4. php一个数组赋值给对象,php数组与对象相互转换方法

    php教程数组与对象相互转换方法 function arrayToObject($e){ if( gettype($e)!='array' ) return; foreach($e as $k=> ...

  5. JavaScript如何声明对象、函数以及对象中的函数(即方法)

    目录 声明对象的2种最常见方法 声明函数的2种最常见方法 在对象中声明函数 声明对象的2种最常见方法 1) var Zhihuijun = {name:'彭志辉',age:28,upName:'稚晖君 ...

  6. python的类和对象_Python类与对象实验

    一.任务描述 本实验任务主要对Python类与对象进行一些基本操作,通过完成本实验任务,要求学生熟练掌握Python类与对象的关系,并对Python类与对象的基本操作进行整理并填写工作任务报告. 二. ...

  7. java对象的访问定位_2、JVM-Java对象的创建、对象结构、对象访问定位-Go语言中文社区...

    目录 记录下来方便个人学习复习 注:根据new的参数在常量池中定位一个类的符号引用 --这句话的意思是:常量池相当于c语言中的指针地址集合,所以就是在常量池中保存new对象的地址,通过地址定位对象在堆 ...

  8. jquery对象PHP转换,jquery对象和DOM对象如何相互转换?

    本篇文章给大家介绍一下jquery对象和DOM对象的转换,有需要的朋友可以参考一下,希望对你有所帮助. 我们在上一篇文章jquery对象和DOM对象的区别有哪些?中也简单介绍了关于jquery对象和D ...

  9. R语言将ggplot2对象转化为plotly对象并通过shiny将可视化结果在应用程序或者网页中显示出来

    R语言将ggplot2对象转化为plotly对象并通过shiny将可视化结果在应用程序或者网页中显示出来 目录

最新文章

  1. 【VBA】查看窗口当前状态
  2. 深入理解linux系统下proc文件系统内容
  3. Epic:把虚幻引擎推向所有游戏平台
  4. html中searchbutton点击没有反应,点击按钮加载完整的HTML后,使用Selenium加载其他元素...
  5. 数据源绑定控件的Row/ItemDataBound事件
  6. python网页内容获取记录pkg
  7. 人教版三年级下册计算机课教案,人教版三年级下册19课教案
  8. session的基本原理
  9. 【bzoj 2541】 [Ctsc2000]冰原探险(BFS)
  10. jquery多字段筛选,极简方法
  11. 输入一个整数(1~7),显示对应星期英文的缩写
  12. 【AI视野·今日CV 计算机视觉论文速览 第220期】Wed, 16 Jun 2021
  13. android蓝牙开启的通知,Android蓝牙LE通知的问题
  14. 实战分析SpringBoot整合JSON,面试题附答案
  15. Unity3D Gamma,Linear和sRGB
  16. 求助:Appium 如何实现登录手机淘宝时拖动苹果到购物车的验证
  17. Linq之IQueryable与IEnumerable
  18. 移动端小场景优化方案
  19. 设置Mac的输入法切换快捷键
  20. socket学习二、accept、read、write函数详解

热门文章

  1. '用户 'sa' 登录失败。该用户与可信 SQL Server 连接无关联(收藏)
  2. engineecms——工程师知识管理系统,带文档协作和状态和流程
  3. python显示前几行数据_python读取文件的前几行
  4. 屏保 cmatrix
  5. 广数980tc3尾座锁定代码_广州数控GSK980TC3系列 PLC及安装连接手册.pdf
  6. 程序中遇到乱码怎么办?这里有篇乱码恢复指北
  7. 怎么把视频里的音乐提取成音频?一分钟帮你搞定
  8. 服务器间文件同步工具Syncthing配置注意点汇总
  9. 51单片机DS18B20温度传感器及数码管显示温度
  10. Telink TL825X 安信可TB-02在水塔水位控制器中的应用