![86f3da4208853807ea41a4eef050457a.png](https://img-blog.csdnimg.cn/img_convert/86f3da4208853807ea41a4eef050457a.png)
回顾
我们在上一篇文章里介绍了文件系统的file_system_type,以及如何注册一个文件系统。但是注册一个文件系统后不代表这个文件系统就被马上使用了,就像你注册了一个账号但是不代表你登录了一样,对于文件系统来说这个登录就相当于“挂载(mount)”。
回忆一下,一个文件系统的file_system_type里有两个主要成员,一个是文件系统的名字,一个是mount这个文件系统的方法(其它参数也很重要,但重点就是这两个)。名字就是一个id,唯一标记一个文件系统,并方便内核在需要时根据它找到这个文件系统的file_system_type。mount方法在挂载这个文件系统时使用的,在需要挂载一个文件系统时通过name找到这个文件系统的file_system_type实例,然后使用这个实例中挂载的方法(mount函数),进行挂载。
当一个文件系统被挂载之后,一个文件系统实例就诞生并可以使用了。
mount系统调用
一般我们类似这样挂载一个文件系统:
# mount -t xfs /dev/sdb1 /mnt -o ...
-t指定/dev/sdb1上的文件系统类型,如果不使用-t则mount命令也可以尝试探测device上的文件系统类型。
-o用来指定一些额外的(非默认的)挂载选项。
上面这个命令翻译成人话就是:请把/dev/sdb1上的XFS文件系统挂载到/mnt上,并在挂载时使能-o里的特性。
这是从一个命令的角度来看mount操作,那么一个命令的最终执行还得是陷入内核后执行的系统调用,来看一下mount系统调用的定义(man 2 mount):
NAME
mount - mount filesystem
SYNOPSIS
#include <sys/mount.h>
int mount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags,
const void *data);
和上面的命令行对应一下,source对应/dev/sdb1,target对应/mnt,filesystemtype对应-t xfs,但是还剩下两个参数mountflags和data,可是我们只剩下一个-o选项了,这是怎么回事呢?
别急,我们先抛开mountflags和data看一下mount系统调用怎么用。
- 首先,我们在一个存储设备上创建一个文件系统
# mkfs.xfs -f /dev/sdb1
- 然后我们准备一个挂载点
# mkdir /mnt/scratch
- 最后我们使用mount系统调用来挂载上面的文件系统和挂载点
# cat mymount.c
#include <sys/mount.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
if (mount("/dev/sdb1", "/mnt/scratch", "xfs", 0, NULL)) {
perror("mount failed");
}
return 0;
}
# gcc -Wall -o mymount mymount.c
# ./mymount
# cat /proc/mounts |grep sdb1
/dev/sdb1 /mnt/scratch xfs rw,seclabel,relatime,attr2,inode64,noquota 0 0
用mount系统调用的前三个参数,不带任何挂载选项就挂载了文件系统。好像蛮简单的,别着急,后面还两个可选参数呢。
mount的flags和data
实际上flags + data对应mount命令的所有-o选项。那怎么区分哪些属于flags哪些属于data呢?在深入内核代码一探究竟之前(本篇不深入了)我们可以通俗的认为flags就是所有文件系统通用的挂载选项,由VFS层解析。data是每个文件系统特有的挂载选项,由文件系统自己解析。
由于每个文件系统特有的选项都各有不同,具体有哪些可以参考(man 8 mount),以及每个文件系统各自的man page。在此我们先说一下通用的flags,在内核中有对flags的宏定义,如下(来自Linux 4.17-rc2 include/uapi/linux/fs.h,我用注释大致解释了一下每一个flag对应哪个mount命令里的参数):
/*
* These are the fs-independent mount-flags: up to 32 flags are supported
*/
#define MS_RDONLY 1 /* 对应-o ro/rw */
#define MS_NOSUID 2 /* 对应-o suid/nosuid */
#define MS_NODEV 4 /* 对应-o dev/nodev */
#define MS_NOEXEC 8 /* 对应-o exec/noexec */
#define MS_SYNCHRONOUS 16 /* 对应-o sync/async */
#define MS_REMOUNT 32 /* 对应-o remount,告诉mount这是一次remount操作 */
#define MS_MANDLOCK 64 /* 对应-o mand/nomand */
#define MS_DIRSYNC 128 /* 对应-o dirsync */
#define MS_NOATIME 1024 /* 对应-o atime/noatime */
#define MS_NODIRATIME 2048 /* 对应-o diratime/nodiratime */
#define MS_BIND 4096 /* 对应-B/--bind选项,告诉mount这是一次bind操作 */
#define MS_MOVE 8192 /* 对应-M/--move,告诉mount这是一次move操作 */
#define MS_REC 16384 /* rec是recursive的意思,这个flag一般不单独出现,都是伴随这其它flag,表示递归的进行操作 */
#define MS_VERBOSE 32768 /* 对应-v/--verbose */
#define MS_SILENT 32768 /* 对应-o silent/loud */
#define MS_POSIXACL (1<<16) /* 让VFS不应用umask,如NFS */
#define MS_UNBINDABLE (1<<17) /* 对应--make-unbindable */
#define MS_PRIVATE (1<<18) /* 对应--make-private */
#define MS_SLAVE (1<<19) /* 对应--make-slave */
#define MS_SHARED (1<<20) /* 对应--make-shared */
#define MS_RELATIME (1<<21) /* 对应-o relatime/norelatime */
#define MS_KERNMOUNT (1<<22) /* 这个一般不在应用层使用,一般内核挂载的文件系统如sysfs使用,表示使用kern_mount()进行挂载 */
#define MS_I_VERSION (1<<23) /* 对应-o iversion/noiversion */
#define MS_STRICTATIME (1<<24) /* 对应-o strictatime/nostrictatime */
#define MS_LAZYTIME (1<<25) /* 对应 -o lazytime/nolazytime*/
/* 下面这几个flags都是内核内部使用的,不由mount系统调用传递 */
#define MS_SUBMOUNT (1<<26)
#define MS_NOREMOTELOCK (1<<27)
#define MS_NOSEC (1<<28)
#define MS_BORN (1<<29)
#define MS_ACTIVE (1<<30)
#define MS_NOUSER (1<<31)
/*
* Superblock flags that can be altered by MS_REMOUNT
*/
#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|
MS_LAZYTIME) // 可以在remount时改变的flags
/*
* Old magic mount flag and mask
*/
#define MS_MGC_VAL 0xC0ED0000 /* magic number */
#define MS_MGC_MSK 0xffff0000 /* flags mask */
除了上面这些flags对应的mount选项,剩下的基本就是data来传递。我们选一个是flag的mount option,如nodev。再选一个XFS支持的非通用mount option,如noquota。通过strace来看一下nodev和noquota对应mount系统调用的哪个参数:
strace mount /dev/sdb1 /mnt/scratch -o nodev,noquota
可以看到下面这一行:
mount("/dev/sdb1", "/mnt/scratch", "xfs", MS_MGC_VAL|MS_NODEV, "noquota") = 0
可见如我们所料,nodev传递给了第4个参数mountflags,noquota传递给了第5个参数data。
注意data参数是void *指针类型的参数,这表示它不一定接受的是字符串类型的变量,有些文件系统会将data定义为结构体格式,或其它什么格式。然后在挂载时解开这个格式分析出里面的选项。所以第5个参数传什么还要看你第3个参数是什么文件系统。
至此我们解释了mount系统调用的角度解释了mount需要哪些参数,以及这些参数的意义。后面该从mount系统调用陷入内核开始进入内核代码一探mount的过程了(希望我有时间写,孩子发高烧了,唉,这注定是一个不眠的夜晚)。
更多内容请参阅:
醉卧沙场:README - 专栏文章总索引