- 论坛徽章:
- 0
|
Linux虚拟文件系统之文件系统卸载(sys_umount()) .
Linux中卸载文件系统由umount系统调用实现,入口函数为sys_umount()。较于文件系统的安装较为简单,下面是具体的实现。
[cpp] 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;
- }
复制代码 卸载实际工作
[cpp] 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 [parent vfsmount] + 1 [sys_umount]
- 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 [parent vfsmount] + 1 [sys_umount]
- */
- /*如果设置了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;
- }
复制代码 从内核链表中脱离
[cpp] 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);
- }
- }
复制代码 释放引用计数
[cpp] 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);
- }
- }
复制代码 |
|