Linux虛擬文件系統之文件系統卸載(sys_umount()) .

←手機掃碼閱讀     火星人 @ 2014-03-03 , reply:0

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);
        }
}
《解決方案》

謝謝分享
《解決方案》

樓主轉個帖子 得註明出處啊 呵呵




[火星人 ] Linux虛擬文件系統之文件系統卸載(sys_umount()) .已經有626次圍觀

http://coctec.com/docs/service/show-post-945.html