免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1938 | 回复: 2
打印 上一主题 下一主题

Linux虚拟文件系统之文件系统卸载(sys_umount()) . [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-02-13 14:22 |只看该作者 |倒序浏览
Linux虚拟文件系统之文件系统卸载(sys_umount()) .








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

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

  49.         retval = -EPERM;
  50.         /*如果用户不具有卸载文件系统的特权*/
  51.         if (!capable(CAP_SYS_ADMIN))
  52.                 goto dput_and_out;
  53.         /*实际umount工作*/
  54.         retval = do_umount(path.mnt, flags);
  55. dput_and_out:
  56.         /* we mustn't call path_put() as that would clear mnt_expiry_mark */
  57.         dput(path.dentry);
  58.         mntput_no_expire(path.mnt);
  59. out:
  60.         return retval;
  61. }
复制代码
卸载实际工作

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

  117.         retval = security_sb_umount(mnt, flags);
  118.         if (retval)
  119.                 return retval;

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

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

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

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

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

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

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

  35.         if (propagate)
  36.                 propagate_umount(kill);

  37.         list_for_each_entry(p, kill, mnt_hash) {
  38.                 list_del_init(&p->mnt_expire);
  39.                 list_del_init(&p->mnt_list);
  40.                 __touch_mnt_namespace(p->mnt_ns);
  41.                 p->mnt_ns = NULL;
  42.                 list_del_init(&p->mnt_child);
  43.                 if (p->mnt_parent != p) {
  44.                         p->mnt_parent->mnt_ghosts++;
  45.                         p->mnt_mountpoint->d_mounted--;
  46.                 }
  47.                 change_mnt_propagation(p, MS_PRIVATE);
  48.         }
  49. }
复制代码
释放引用计数

[cpp] view plaincopyprint?
  1. 01.void release_mounts(struct list_head *head)  
  2. 02.{  
  3. 03.    struct vfsmount *mnt;  
  4. 04.    while (!list_empty(head)) {  
  5. 05.        mnt = list_first_entry(head, struct vfsmount, mnt_hash);  
  6. 06.        list_del_init(&mnt->mnt_hash);  
  7. 07.        if (mnt->mnt_parent != mnt) {  
  8. 08.            struct dentry *dentry;  
  9. 09.            struct vfsmount *m;  
  10. 10.            spin_lock(&vfsmount_lock);  
  11. 11.            dentry = mnt->mnt_mountpoint;  
  12. 12.            m = mnt->mnt_parent;  
  13. 13.            mnt->mnt_mountpoint = mnt->mnt_root;  
  14. 14.            mnt->mnt_parent = mnt;  
  15. 15.            m->mnt_ghosts--;  
  16. 16.            spin_unlock(&vfsmount_lock);  
  17. 17.            /*下面两个函数为减小引用计数,减到0时释放*/  
  18. 18.            dput(dentry);            
  19. 19.            mntput(m);  
  20. 20.        }  
  21. 21.        /*vfsmount对象所占的内存空间最终在mntput()函数中释放*/  
  22. 22.        mntput(mnt);  
  23. 23.    }  
  24. 24.}  
  25. void release_mounts(struct list_head *head)
  26. {
  27.         struct vfsmount *mnt;
  28.         while (!list_empty(head)) {
  29.                 mnt = list_first_entry(head, struct vfsmount, mnt_hash);
  30.                 list_del_init(&mnt->mnt_hash);
  31.                 if (mnt->mnt_parent != mnt) {
  32.                         struct dentry *dentry;
  33.                         struct vfsmount *m;
  34.                         spin_lock(&vfsmount_lock);
  35.                         dentry = mnt->mnt_mountpoint;
  36.                         m = mnt->mnt_parent;
  37.                         mnt->mnt_mountpoint = mnt->mnt_root;
  38.                         mnt->mnt_parent = mnt;
  39.                         m->mnt_ghosts--;
  40.                         spin_unlock(&vfsmount_lock);
  41.                         /*下面两个函数为减小引用计数,减到0时释放*/
  42.                         dput(dentry);                       
  43.                         mntput(m);
  44.                 }
  45.                 /*vfsmount对象所占的内存空间最终在mntput()函数中释放*/
  46.                 mntput(mnt);
  47.         }
  48. }
复制代码

论坛徽章:
0
2 [报告]
发表于 2012-02-13 14:22 |只看该作者
谢谢分享

论坛徽章:
0
3 [报告]
发表于 2012-02-26 16:26 |只看该作者
楼主转个帖子 得注明出处啊 呵呵
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP