mount系统调用初探
转载
文章目录
- 1.mount系统调用
- 2.mount的flags和data
- 3.Linux系统调用-- mount/umount函数详解
1.mount系统调用
- 但是注册一个文件系统后不代表这个文件系统就被马上使用了,就像你注册了一个账号但是不代表你登录了一样,对于文件系统来说这个登录就相当于“挂载(mount)”。
- 一个文件系统的file_system_type里有两个主要成员,一个是文件系统的名字,一个是mount这个文件系统的方法(其它参数也很重要,但重点就是这两个)。
(1)名字就是一个id,唯一标记一个文件系统,并方便内核在需要时根据它找到这个文件系统的file_system_type。
(2)mount方法在挂载这个文件系统时使用的,在需要挂载一个文件系统时通过name找到这个文件系统的file_system_type实例,然后使用这个实例中挂载的方法(mount函数),进行挂载。 - (1)mount系统调用:从一个命令的角度来看mount操作
一般我们类似这样挂载一个文件系统:
# mount -t xfs /dev/sdb1 /mnt -o ...
-t指定/dev/sdb1上的文件系统类型,如果不使用-t则mount命令也可以尝试探测device上的文件系统类型。
-o用来指定一些额外的(非默认的)挂载选项。
上面这个命令翻译成人话就是:请把/dev/sdb1上的XFS文件系统挂载到/mnt上,并在挂载时使能-o里的特性。
- (2)mount系统调用:mount系统调用
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
- mount系统调用具体过程
用mount系统调用的前三个参数,不带任何挂载选项就挂载了文件系统
(1)首先,我们在一个存储设备上创建一个文件系统
# mkfs.xfs -f /dev/sdb1
(2)然后我们准备一个挂载点
# mkdir /mnt/scratch
(3)最后我们使用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
2.mount的flags和data
- 实际上flags + data对应mount命令的所有-o选项。那怎么区分哪些属于flags哪些属于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来传递。
(1)我们选一个是flag的mount option,如nodev。
(2)再选一个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
结论:
(1)可见如我们所料,nodev传递给了第4个参数mountflags,noquota传递给了第5个参数data
(2)注意data参数是void *指针类型的参数,这表示它不一定接受的是字符串类型的变量,有些文件系统会将data定义为结构体格式,
或其它什么格式。
然后在挂载时解开这个格式分析出里面的选项。所以第5个参数传什么还要看你第3个参数是什么文件系统。
3.Linux系统调用-- mount/umount函数详解
功能描述:
mount挂上文件系统,umount执行相反的操作。
用法:
#include <sys/mount.h>
int mount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags, const void *data);
int umount(const char *target);
int umount2(const char *target, int flags);
source:将要挂上的文件系统,通常是一个设备名。
target:文件系统所要挂在的目标目录。
filesystemtype:文件系统的类型,可以是"ext2","msdos","proc","nfs","iso9660" 。。。
mountflags:指定文件系统的读写访问标志,可能值有以下
MS_BIND:执行bind挂载,使文件或者子目录树在文件系统内的另一个点上可视。
MS_DIRSYNC:同步目录的更新。
MS_MANDLOCK:允许在文件上执行强制锁。
MS_MOVE:移动子目录树。
MS_NOATIME:不要更新文件上的访问时间。
MS_NODEV:不允许访问设备文件。
MS_NODIRATIME:不允许更新目录上的访问时间。
MS_NOEXEC:不允许在挂上的文件系统上执行程序。
MS_NOSUID:执行程序时,不遵照set-user-ID 和 set-group-ID位。
MS_RDONLY:指定文件系统为只读。
MS_REMOUNT:重新加载文件系统。这允许你改变现存文件系统的mountflag和数据,而无需使用先卸载,再挂上文件系统的方式。
MS_SYNCHRONOUS:同步文件的更新。
MNT_FORCE:强制卸载,即使文件系统处于忙状态。
MNT_EXPIRE:将挂载点标志为过时。
成功执行时,返回0。失败返回-1,errno被设为以下的某个值
EACCES:权能不足,可能原因是,路径的一部分不可搜索,或者挂载只读的文件系统时,没有指定 MS_RDONLY 标志。
EAGAIN:成功地将不处于忙状态的文件系统标志为过时。
EBUSY:一. 源文件系统已被挂上。或者不可以以只读的方式重新挂载,因为它还拥有以写方式打开的文件。二. 目标处于忙状态。
EFAULT: 内存空间访问出错。
EINVAL:操作无效,可能是源文件系统超级块无效。
ELOOP :路径解析的过程中存在太多的符号连接。
EMFILE:无需块设备要求的情况下,无用设备表已满。
ENAMETOOLONG:路径名超出可允许的长度。
ENODEV:内核不支持某中文件系统。
ENOENT:路径名部分内容表示的目录不存在。
ENOMEM: 核心内存不足。
ENOTBLK:source不是块设备。
ENOTDIR:路径名的部分内容不是目录。
EPERM : 调用者权能不足。
ENXIO:块主设备号超出所允许的范围。