mount系统调用初探

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/sdb12)然后我们准备一个挂载点
# mkdir /mnt/scratch3)最后我们使用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系统调用】
功能描述:
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);
  • 参数mountflags:
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:将挂载点标志为过时。
  • data:文件系统特有的参数。
  • 返回说明:
成功执行时,返回0。失败返回-1,errno被设为以下的某个值   
EACCES:权能不足,可能原因是,路径的一部分不可搜索,或者挂载只读的文件系统时,没有指定 MS_RDONLY 标志。
EAGAIN:成功地将不处于忙状态的文件系统标志为过时。
EBUSY:一. 源文件系统已被挂上。或者不可以以只读的方式重新挂载,因为它还拥有以写方式打开的文件。二. 目标处于忙状态。
EFAULT: 内存空间访问出错。
EINVAL:操作无效,可能是源文件系统超级块无效。
ELOOP :路径解析的过程中存在太多的符号连接。
EMFILE:无需块设备要求的情况下,无用设备表已满。
ENAMETOOLONG:路径名超出可允许的长度。
ENODEV:内核不支持某中文件系统。
ENOENT:路径名部分内容表示的目录不存在。
ENOMEM: 核心内存不足。
ENOTBLK:source不是块设备。
ENOTDIR:路径名的部分内容不是目录。
EPERM : 调用者权能不足。
ENXIO:块主设备号超出所允许的范围。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章