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);
}
}
《解決方案》
謝謝分享
《解決方案》
樓主轉個帖子 得註明出處啊 呵呵