日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

mount 网络_mount系统调用(ksys_mount-gt;do_mount-gt;do_new_mount)

發布時間:2024/1/23 windows 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mount 网络_mount系统调用(ksys_mount-gt;do_mount-gt;do_new_mount) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

回顧

前文我們講了mount系統調用的用法,以及每個參數的含義。mount系統調用有5個參數,分別是:

  • source: 文件系統所在設備名稱(或網絡文件系統的remote掛載點等)
  • target: 要掛載到的位置,一般是目錄名。
  • filesystemtype: 文件系統名稱。
  • mountflags: 文件系統通用掛載選項。
  • data: 文件系統特用掛載選項。

下面我們就來看一下這五個參數在經過mount系統調用進入內核之后是如何被傳遞的。(本文會涉及大量代碼,解釋內容可能部分以代碼注釋的形式出現)


SYSCALL_mount

這是內核執行mount系統調用的第一個地方,代碼如下(來自Linux 4.17-rc2 fs/namespace.c):

SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, char __user *, type, unsigned long, flags, void __user *, data) { return ksys_mount(dev_name, dir_name, type, flags, data); }

沒有什么特別的(本文不講解系統調用定義過程),mount的五個參數中的四個指針參數所對應的內容還處在用戶層(__user),并別對應了dev_name, dirname, type, flags 和data,內容沒有做任何更改,從字面上也很容易理解。接著直接將參數傳遞給ksys_mount函數繼續處理(老kernel是沒有封裝到ksys_mount函數的)(來自Linux 4.17-rc2 fs/namespace.c):

int ksys_mount(char __user *dev_name, char __user *dir_name, char __user *type, unsigned long flags, void __user *data) { int ret; char *kernel_type; char *kernel_dev; void *options; kernel_type = copy_mount_string(type); ret = PTR_ERR(kernel_type); if (IS_ERR(kernel_type)) goto out_type; kernel_dev = copy_mount_string(dev_name); ret = PTR_ERR(kernel_dev); if (IS_ERR(kernel_dev)) goto out_dev; options = copy_mount_options(data); ret = PTR_ERR(options); if (IS_ERR(options)) goto out_data; ret = do_mount(kernel_dev, dir_name, kernel_type, flags, options); kfree(options); out_data: kfree(kernel_dev); out_dev: kfree(kernel_type); out_type: return ret; }

ksys_mount函數很簡單,copy_mount_string將用戶層的內容拷貝到內核層(至于dir_name為什么沒有被這樣處理我們下面再說),然后就是調用do_mount做下面的工作。

ksys_mount->do_mount

(來自Linux 4.17-rc2 fs/namespace.c):

/** 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;// 注意這里, 后面flags將被分為mnt_flags和sb_flags,// 這個在老的內核里是沒有分開的unsigned int mnt_flags = 0, sb_flags;int retval = 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;if (flags & MS_NOUSER)return -EINVAL;// 上面為什么沒有對dir_name做copy_mount_string操作, 答案在這。// 這里用user_path直接將dir_name轉撐struct path的格式放入內核層。// path是內核做路徑名操作的常用結構體。/* ... and get the mountpoint */retval = user_path(dir_name, &path);if (retval)return retval;// 這里和LSM有關,是另外話題retval = security_sb_mount(dev_name, &path,type_page, flags, data_page);if (!retval && !may_mount())retval = -EPERM;// 如果flags使能SB_MANDLOCK,檢查當前kernel是否支持MANDATORY_FILE_LOCKINGif (!retval && (flags & SB_MANDLOCK) && !may_mandlock())retval = -EPERM;if (retval)goto dput_out;// 下面英語說的很清楚.../* Default to relatime unless overriden */if (!(flags & MS_NOATIME))mnt_flags |= MNT_RELATIME;// 還在處理flags,對于這些flags的含義請從參考上一篇文章。/* 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;// 和remount相關的處理/* 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中提出sb_flags相關的部分sb_flags = flags & (SB_RDONLY |SB_SYNCHRONOUS |SB_MANDLOCK |SB_DIRSYNC |SB_SILENT |SB_POSIXACL |SB_LAZYTIME |SB_I_VERSION);// 下面根據flags的標記,決定是做下面哪種mount操作。// do_new_mount是最普通,我們最常見的mount。bind,move,remount以及和// shared-sub-tree有關操作的含義參見相關man page以及Documentation/filesystems/sharedsubtree.txt// 相關文檔。if (flags & MS_REMOUNT)retval = do_remount(&path, flags, sb_flags, 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);else if (flags & MS_MOVE)retval = do_move_mount(&path, dev_name);elseretval = do_new_mount(&path, type_page, sb_flags, mnt_flags,dev_name, data_page); dput_out:path_put(&path);return retval; }

從do_mount的代碼可以它主要就是:

  • 將dir_name解析為path格式到內核
  • 一路解析flags位表,將flags拆分位mnt_flags和sb_flags
  • 根據flags中的標記,決定下面做哪一個mount操作。
  • 為了保持文章的連貫性,這里就追蹤最普通的mount操作向下看,即do_new_mount。其它mount操作如果有時間再另外講解。

    ksys_mount->do_mount->do_new_mount

    注意到了do_new_mount這里原來的五個參數變成了六個參數,flags被分成了兩部分,dir_name也變成了path結構,其他還保持原格式被存入內核page中。

    (來自Linux 4.17-rc2 fs/namespace.c)

    /** create a new mount for userspace and request it to be added into the* namespace's tree*/ static int do_new_mount(struct path *path, const char *fstype, int sb_flags,int mnt_flags, const char *name, void *data) {struct file_system_type *type;struct vfsmount *mnt;int err;if (!fstype)return -EINVAL;// get_fs_type我們在第一篇講file_system_type的時候就提到過,它是根據文件系統// 名稱找到對應的已注冊的file_system_type實例。這個實例里有一個很重要的東西就是// mount回調函數。type = get_fs_type(fstype);if (!type)return -ENODEV;// 這個地方是很重要的一步,也是我們第一次接觸vfsmount這個結構的地方。// 先簡單來說vfs_kern_mount會調用特定文件系統類型(type里)的mount回調函數,// 構建好一個vfsmount結構。具體怎么做的我們等下面講vfsmount的時候再說。mnt = vfs_kern_mount(type, sb_flags, name, data);if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&!mnt->mnt_sb->s_subtype)mnt = fs_set_subtype(mnt, fstype);put_filesystem(type);if (IS_ERR(mnt))return PTR_ERR(mnt);if (mount_too_revealing(mnt, &mnt_flags)) {mntput(mnt);return -EPERM;}// 將得到的vfsmount結構加入全局目錄樹。err = do_add_mount(real_mount(mnt), path, mnt_flags);if (err)mntput(mnt);return err; }

    到此有兩個重要的地方vfs_kern_mount和do_add_mount。按照邏輯順序我們下面應該說到do_add_mount了,但是在此之前我們得先解釋一下vfs_kern_mount和vfsmount結構。

    struct mount和struct vfsmount

    struct mount代表著一個mount實例(一次真正掛載對應一個mount實例),其中struct vfsmount定義的mnt成員是它最核心的部分。過去沒有stuct mount,mount和vfsmount的成員都在vfsmount里,現在linux將vfsmount改作mount結構體,并將mount中mnt_root, mnt_sb, mnt_flags成員移到vfsmount結構體中了。這樣使得vfsmount的內容更加精簡,在很多情況下只需要傳遞vfsmount而已。

    來看一下struct vfsmount的定義(來自Linux 4.17-rc2 include/linux/mount.h)

    struct vfsmount {struct dentry *mnt_root; /* 指向這個文件系統的根的dentry */struct super_block *mnt_sb; /* 指向這個文件系統的超級塊對象 */int mnt_flags; /* 此文件系統的掛載標志 */ } __randomize_layout;

    以及struct mount的定義(來自Linux 4.17-rc2 fs/mount.h)

    struct mount {struct hlist_node mnt_hash; /* 用于鏈接到全局已掛載文件系統的鏈表 */struct mount *mnt_parent; /* 指向此文件系統的掛載點所屬的文件系統,即父文件系統 */ struct dentry *mnt_mountpoint; /* 指向此文件系統的掛載點的dentry */struct vfsmount mnt; /* 指向此文件系統的vfsmount實例 */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; #endifstruct list_head mnt_mounts; /* 掛載在此文件系統下的所有子文件系統的鏈表的表頭,下面的節點都是mnt_child */struct list_head mnt_child; /* 鏈接到被此文件系統所掛的父文件系統的mnt_mounts上 */struct list_head mnt_instance; /* 鏈接到sb->s_mounts上的一個mount實例 */const char *mnt_devname; /* 設備名,如/dev/sdb1 */struct list_head mnt_list; /* 鏈接到進程namespace中已掛載文件系統中,表頭為mnt_namespace的list域 */ struct list_head mnt_expire; /* 鏈接到一些文件系統專有的過期鏈表,如NFS, CIFS等 */struct list_head mnt_share; /* 鏈接到共享掛載的循環鏈表中 */struct list_head mnt_slave_list;/* 此文件系統的slave mount鏈表的表頭 */struct list_head mnt_slave; /* 連接到master文件系統的mnt_slave_list */struct mount *mnt_master; /* 指向此文件系統的master文件系統,slave is on master->mnt_slave_list */struct mnt_namespace *mnt_ns; /* 指向包含這個文件系統的進程的name space */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 fsnotify_mark_connector __rcu *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; } __randomize_layout;

    如果你被struct mount的結構嚇到了,那你是可以被理解的。能理解里面所有成員真正含義和用法及所有細節的人基本上就是Al Viro(VFS的maintainer)了。還好你在面對一個最普通的mount操作時不需要理解struct mount里所有的內容,后面我們會單獨講mount實例在掛載操作中的用途(當然也僅限普通掛載)。

    struct vfsmount里面的東西倒是比較少,只有三個。這3個參數也就是通過vfs_kern_mount來得到的。我們將在下一篇文章講述怎么通過vfs_kern_mount得到一個vfsmount(以及一個半初始化struct mount)。在這里先知道,在全局文件系統(目錄結構)樹上一個文件的位置不是由dentry唯一確定,因為有了掛載關系,一切都變的復雜了,比如一個文件系統可以掛裝載到不同的掛載點。所以文件系統樹的一個位置要由<mount, dentry>二元組(或者說<vfsmount, dentry>)來確定(路徑名查找是另一個大坑,那就不是下幾篇文章講的事情了,那是另一系列文章的問題了...)。


    更多內容請參閱:

    醉臥沙場:README - 專欄文章總索引

    總結

    以上是生活随笔為你收集整理的mount 网络_mount系统调用(ksys_mount-gt;do_mount-gt;do_new_mount)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。