凝望长空 发表于 2012-02-13 14:22

Linux虚拟文件系统之文件系统卸载(sys_umount()) .

Linux虚拟文件系统之文件系统卸载(sys_umount()) .







Linux中卸载文件系统由umount系统调用实现,入口函数为sys_umount()。较于文件系统的安装较为简单,下面是具体的实现。

view plaincopyprint?01./*sys_umont系统调用*/
02.SYSCALL_DEFINE2(umount, char __user *, name, int, flags)
03.{
04.    struct path path;
05.    int retval;
06.    /*找到装载点的vfsmount实例和dentry实例,二者包装
07.    在一个nameidata结构中*/
08.    retval = user_path(name, &path);
09.    if (retval)
10.      goto out;
11.    retval = -EINVAL;
12.    /*如果查找的最终目录不是文件系统的挂载点*/
13.    if (path.dentry != path.mnt->mnt_root)
14.      goto dput_and_out;
15.    /*如果要卸载的文件系统还没有安装在命名空间中*/
16.    if (!check_mnt(path.mnt))
17.      goto dput_and_out;
18.
19.    retval = -EPERM;
20.    /*如果用户不具有卸载文件系统的特权*/
21.    if (!capable(CAP_SYS_ADMIN))
22.      goto dput_and_out;
23.    /*实际umount工作*/
24.    retval = do_umount(path.mnt, flags);
25.dput_and_out:
26.    /* we mustn't call path_put() as that would clear mnt_expiry_mark */
27.    dput(path.dentry);
28.    mntput_no_expire(path.mnt);
29.out:
30.    return retval;
31.}
/*sys_umont系统调用*/
SYSCALL_DEFINE2(umount, char __user *, name, int, flags)
{
        struct path path;
        int retval;
        /*找到装载点的vfsmount实例和dentry实例,二者包装
        在一个nameidata结构中*/
        retval = user_path(name, &path);
        if (retval)
                goto out;
        retval = -EINVAL;
        /*如果查找的最终目录不是文件系统的挂载点*/
        if (path.dentry != path.mnt->mnt_root)
                goto dput_and_out;
        /*如果要卸载的文件系统还没有安装在命名空间中*/
        if (!check_mnt(path.mnt))
                goto dput_and_out;

        retval = -EPERM;
        /*如果用户不具有卸载文件系统的特权*/
        if (!capable(CAP_SYS_ADMIN))
                goto dput_and_out;
        /*实际umount工作*/
        retval = do_umount(path.mnt, flags);
dput_and_out:
        /* we mustn't call path_put() as that would clear mnt_expiry_mark */
        dput(path.dentry);
        mntput_no_expire(path.mnt);
out:
        return retval;
}卸载实际工作

view plaincopyprint?01.static int do_umount(struct vfsmount *mnt, int flags)
02.{
03.    /*从vfsmount对象的mnt_sb字段检索超级块对象sb的地址*/
04.    struct super_block *sb = mnt->mnt_sb;
05.    int retval;
06.    /*初始化umount_list,该链表在后面的释放中会做临时链表
07.    用*/
08.    LIST_HEAD(umount_list);
09.
10.    retval = security_sb_umount(mnt, flags);
11.    if (retval)
12.      return retval;
13.
14.    /*
15.   * Allow userspace to request a mountpoint be expired rather than
16.   * unmounting unconditionally. Unmount only happens if:
17.   *(1) the mark is already set (the mark is cleared by mntput())
18.   *(2) the usage count == 1 + 1
19.   */
20.   /*如果设置了MNT_EXPIRE标志,即要标记挂载点“到期”*/
21.    if (flags & MNT_EXPIRE) {
22.      /*若要卸载的文件系统是根文件系统或者同时设置了
23.      MNT_FORCE或MNT_DETACH,则返回-EINVAL*/
24.      if (mnt == current->fs->root.mnt ||
25.            flags & (MNT_FORCE | MNT_DETACH))
26.            return -EINVAL;
27.      /*检查vfsmount的引用计数,若不为2,则返回-EBUSY,
28.      要卸载的文件系统在卸载的时候不能有引用者,
29.      这个2代表vfsmount的父vfsmount和sys_umount()对本对象的引用*/
30.      if (atomic_read(&mnt->mnt_count) != 2)
31.            return -EBUSY;
32.      /*设置vfsmount对象的mnt_expiry_mark字段为1。*/
33.      if (!xchg(&mnt->mnt_expiry_mark, 1))
34.            return -EAGAIN;
35.    }
36.
37.    /*
38.   * If we may have to abort operations to get out of this
39.   * mount, and they will themselves hold resources we must
40.   * allow the fs to do things. In the Unix tradition of
41.   * 'Gee thats tricky lets do it in userspace' the umount_begin
42.   * might fail to complete on the first run through as other tasks
43.   * must return, and the like. Thats for the mount program to worry
44.   * about for the moment.
45.   */
46.   /*如果用户要求强制卸载操作,则调用umount_begin
47.   超级块操作中断任何正在进行的安装操作*/
48.    /*当然如果特定的文件系统定义了下面函数则调用它*/
49.    if (flags & MNT_FORCE && sb->s_op->umount_begin) {
50.      sb->s_op->umount_begin(sb);
51.    }
52.
53.    /*
54.   * No sense to grab the lock for this test, but test itself looks
55.   * somewhat bogus. Suggestions for better replacement?
56.   * Ho-hum... In principle, we might treat that as umount + switch
57.   * to rootfs. GC would eventually take care of the old vfsmount.
58.   * Actually it makes sense, especially if rootfs would contain a
59.   * /reboot - static binary that would close all descriptors and
60.   * call reboot(9). Then init(8) could umount root and exec /reboot.
61.   */
62.   /*如果要卸载的文件系统是根文件系统,且用户
63.   并不要求真正地把它卸载下来(即设置了MNT_DETACH标志,
64.   这个标志仅仅标记挂载点为不能再访问,知道挂载不busy
65.   时才卸载),则调用do_remount_sb()重新安装根文件系统为只
66.   读并终止,并返回do_remount_sb()的返回值。*/
67.    if (mnt == current->fs->root.mnt && !(flags & MNT_DETACH)) {
68.      /*
69.         * Special case for "unmounting" root ...
70.         * we just try to remount it readonly.
71.         */
72.      down_write(&sb->s_umount);
73.      if (!(sb->s_flags & MS_RDONLY))
74.            retval = do_remount_sb(sb, MS_RDONLY, NULL, 0);
75.      up_write(&sb->s_umount);
76.      return retval;
77.    }
78.      
79.    down_write(&namespace_sem);
80.    /*为进行写操作而获取当前进程的namespace_sem读/写信号量和vfsmount_lock自旋锁*/
81.    spin_unlock(&vfsmount_lock);
82.    spin_lock(&vfsmount_lock);
83.    event++;
84.
85.    if (!(flags & MNT_DETACH))
86.      shrink_submounts(mnt, &umount_list);
87.
88.    retval = -EBUSY;
89.    /*如果已安装文件系统不包含任何子安装文件系统的安装点,或者用户要求强制
90.    卸载文件系统,则调用umount_tree()卸载文件系统(及其所有子文件系统)。*/
91.    if (flags & MNT_DETACH || !propagate_mount_busy(mnt, 2)) {
92.      if (!list_empty(&mnt->mnt_list))
93.            /*完成实际的底层的卸载文件系统的任务。首先他将mnt的所有孩子移动至kill链表中,
94.            也就是传递进去的umount_list,然后将kill链表中的所有的vfsmount对象的一些字段设为无效状态。
95.            */
96.            umount_tree(mnt, 1, &umount_list);
97.      retval = 0;
98.    }
99.      
100.    if (retval)
101.      security_sb_umount_busy(mnt);
102.    /*释放vfsmount_lock自旋锁和当前进程的namespace_sem读/写信号量*/
103.    up_write(&namespace_sem);
104.    /*减小相应文件系统根目录的目录项对象和已经安装文件系统
105.    描述符的引用计数器值,这些计数器值由path_lookup()增加*/
106.    release_mounts(&umount_list);
107.    return retval;
108.}
static int do_umount(struct vfsmount *mnt, int flags)
{
        /*从vfsmount对象的mnt_sb字段检索超级块对象sb的地址*/
        struct super_block *sb = mnt->mnt_sb;
        int retval;
        /*初始化umount_list,该链表在后面的释放中会做临时链表
        用*/
        LIST_HEAD(umount_list);

        retval = security_sb_umount(mnt, flags);
        if (retval)
                return retval;

        /*
       * Allow userspace to request a mountpoint be expired rather than
       * unmounting unconditionally. Unmount only happens if:
       *(1) the mark is already set (the mark is cleared by mntput())
       *(2) the usage count == 1 + 1
       */
       /*如果设置了MNT_EXPIRE标志,即要标记挂载点“到期”*/
        if (flags & MNT_EXPIRE) {
                /*若要卸载的文件系统是根文件系统或者同时设置了
                MNT_FORCE或MNT_DETACH,则返回-EINVAL*/
                if (mnt == current->fs->root.mnt ||
                  flags & (MNT_FORCE | MNT_DETACH))
                        return -EINVAL;
                /*检查vfsmount的引用计数,若不为2,则返回-EBUSY,
                要卸载的文件系统在卸载的时候不能有引用者,
                这个2代表vfsmount的父vfsmount和sys_umount()对本对象的引用*/
                if (atomic_read(&mnt->mnt_count) != 2)
                        return -EBUSY;
                /*设置vfsmount对象的mnt_expiry_mark字段为1。*/
                if (!xchg(&mnt->mnt_expiry_mark, 1))
                        return -EAGAIN;
        }

        /*
       * If we may have to abort operations to get out of this
       * mount, and they will themselves hold resources we must
       * allow the fs to do things. In the Unix tradition of
       * 'Gee thats tricky lets do it in userspace' the umount_begin
       * might fail to complete on the first run through as other tasks
       * must return, and the like. Thats for the mount program to worry
       * about for the moment.
       */
       /*如果用户要求强制卸载操作,则调用umount_begin
       超级块操作中断任何正在进行的安装操作*/
        /*当然如果特定的文件系统定义了下面函数则调用它*/
        if (flags & MNT_FORCE && sb->s_op->umount_begin) {
                sb->s_op->umount_begin(sb);
        }

        /*
       * No sense to grab the lock for this test, but test itself looks
       * somewhat bogus. Suggestions for better replacement?
       * Ho-hum... In principle, we might treat that as umount + switch
       * to rootfs. GC would eventually take care of the old vfsmount.
       * Actually it makes sense, especially if rootfs would contain a
       * /reboot - static binary that would close all descriptors and
       * call reboot(9). Then init(8) could umount root and exec /reboot.
       */
       /*如果要卸载的文件系统是根文件系统,且用户
       并不要求真正地把它卸载下来(即设置了MNT_DETACH标志,
       这个标志仅仅标记挂载点为不能再访问,知道挂载不busy
       时才卸载),则调用do_remount_sb()重新安装根文件系统为只
       读并终止,并返回do_remount_sb()的返回值。*/
        if (mnt == current->fs->root.mnt && !(flags & MNT_DETACH)) {
                /*
               * Special case for "unmounting" root ...
               * we just try to remount it readonly.
               */
                down_write(&sb->s_umount);
                if (!(sb->s_flags & MS_RDONLY))
                        retval = do_remount_sb(sb, MS_RDONLY, NULL, 0);
                up_write(&sb->s_umount);
                return retval;
        }
       
        down_write(&namespace_sem);
        /*为进行写操作而获取当前进程的namespace_sem读/写信号量和vfsmount_lock自旋锁*/
        spin_unlock(&vfsmount_lock);
        spin_lock(&vfsmount_lock);
        event++;

        if (!(flags & MNT_DETACH))
                shrink_submounts(mnt, &umount_list);

        retval = -EBUSY;
        /*如果已安装文件系统不包含任何子安装文件系统的安装点,或者用户要求强制
        卸载文件系统,则调用umount_tree()卸载文件系统(及其所有子文件系统)。*/
        if (flags & MNT_DETACH || !propagate_mount_busy(mnt, 2)) {
                if (!list_empty(&mnt->mnt_list))
                        /*完成实际的底层的卸载文件系统的任务。首先他将mnt的所有孩子移动至kill链表中,
                        也就是传递进去的umount_list,然后将kill链表中的所有的vfsmount对象的一些字段设为无效状态。
                        */
                        umount_tree(mnt, 1, &umount_list);
                retval = 0;
        }
       
        if (retval)
                security_sb_umount_busy(mnt);
        /*释放vfsmount_lock自旋锁和当前进程的namespace_sem读/写信号量*/
        up_write(&namespace_sem);
        /*减小相应文件系统根目录的目录项对象和已经安装文件系统
        描述符的引用计数器值,这些计数器值由path_lookup()增加*/
        release_mounts(&umount_list);
        return retval;
}从内核链表中脱离

view plaincopyprint?01./*完成实际的底层的卸载文件系统的任务。首先他将mnt的所有子移动至kill链表中,
02.也就是传递进去的umount_list,然后将kill链表中的所有的vfsmount对象的一些字段设为无效状态。
03.*/
04.void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill)
05.{
06.    struct vfsmount *p;
07.
08.    for (p = mnt; p; p = next_mnt(p, mnt))
09.      list_move(&p->mnt_hash, kill);
10.
11.    if (propagate)
12.      propagate_umount(kill);
13.
14.    list_for_each_entry(p, kill, mnt_hash) {
15.      list_del_init(&p->mnt_expire);
16.      list_del_init(&p->mnt_list);
17.      __touch_mnt_namespace(p->mnt_ns);
18.      p->mnt_ns = NULL;
19.      list_del_init(&p->mnt_child);
20.      if (p->mnt_parent != p) {
21.            p->mnt_parent->mnt_ghosts++;
22.            p->mnt_mountpoint->d_mounted--;
23.      }
24.      change_mnt_propagation(p, MS_PRIVATE);
25.    }
26.}
/*完成实际的底层的卸载文件系统的任务。首先他将mnt的所有子移动至kill链表中,
也就是传递进去的umount_list,然后将kill链表中的所有的vfsmount对象的一些字段设为无效状态。
*/
void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill)
{
        struct vfsmount *p;

        for (p = mnt; p; p = next_mnt(p, mnt))
                list_move(&p->mnt_hash, kill);

        if (propagate)
                propagate_umount(kill);

        list_for_each_entry(p, kill, mnt_hash) {
                list_del_init(&p->mnt_expire);
                list_del_init(&p->mnt_list);
                __touch_mnt_namespace(p->mnt_ns);
                p->mnt_ns = NULL;
                list_del_init(&p->mnt_child);
                if (p->mnt_parent != p) {
                        p->mnt_parent->mnt_ghosts++;
                        p->mnt_mountpoint->d_mounted--;
                }
                change_mnt_propagation(p, MS_PRIVATE);
        }
}释放引用计数

view plaincopyprint?01.void release_mounts(struct list_head *head)
02.{
03.    struct vfsmount *mnt;
04.    while (!list_empty(head)) {
05.      mnt = list_first_entry(head, struct vfsmount, mnt_hash);
06.      list_del_init(&mnt->mnt_hash);
07.      if (mnt->mnt_parent != mnt) {
08.            struct dentry *dentry;
09.            struct vfsmount *m;
10.            spin_lock(&vfsmount_lock);
11.            dentry = mnt->mnt_mountpoint;
12.            m = mnt->mnt_parent;
13.            mnt->mnt_mountpoint = mnt->mnt_root;
14.            mnt->mnt_parent = mnt;
15.            m->mnt_ghosts--;
16.            spin_unlock(&vfsmount_lock);
17.            /*下面两个函数为减小引用计数,减到0时释放*/
18.            dput(dentry);            
19.            mntput(m);
20.      }
21.      /*vfsmount对象所占的内存空间最终在mntput()函数中释放*/
22.      mntput(mnt);
23.    }
24.}
void release_mounts(struct list_head *head)
{
        struct vfsmount *mnt;
        while (!list_empty(head)) {
                mnt = list_first_entry(head, struct vfsmount, mnt_hash);
                list_del_init(&mnt->mnt_hash);
                if (mnt->mnt_parent != mnt) {
                        struct dentry *dentry;
                        struct vfsmount *m;
                        spin_lock(&vfsmount_lock);
                        dentry = mnt->mnt_mountpoint;
                        m = mnt->mnt_parent;
                        mnt->mnt_mountpoint = mnt->mnt_root;
                        mnt->mnt_parent = mnt;
                        m->mnt_ghosts--;
                        spin_unlock(&vfsmount_lock);
                        /*下面两个函数为减小引用计数,减到0时释放*/
                        dput(dentry);                       
                        mntput(m);
                }
                /*vfsmount对象所占的内存空间最终在mntput()函数中释放*/
                mntput(mnt);
        }
}

第一夫人 发表于 2012-02-13 14:22

谢谢分享

bullbat_v 发表于 2012-02-26 16:26

楼主转个帖子 得注明出处啊 呵呵
页: [1]
查看完整版本: Linux虚拟文件系统之文件系统卸载(sys_umount()) .