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

来源:互联网 发布:欧洲圣母 知乎 编辑:程序博客网 时间:2024/06/06 12:00
Linux中卸载文件系统由umount系统调用实现,入口函数为sys_umount()。较于文件系统的安装较为简单,下面是具体的实现。
   1. /*sys_umont系统调用*/     2. SYSCALL_DEFINE2(umount, char __user *, name, int, flags)     3. {     4.     struct path path;     5.     int retval;     6.     /*找到装载点的vfsmount实例和dentry实例,二者包装    7.     在一个nameidata结构中*/     8.     retval = user_path(name, &path);     9.     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. }  

卸载实际工作

   1. static int do_umount(struct vfsmount *mnt, int flags)     2. {     3.     /*从vfsmount对象的mnt_sb字段检索超级块对象sb的地址*/     4.     struct super_block *sb = mnt->mnt_sb;     5.     int retval;     6.     /*初始化umount_list,该链表在后面的释放中会做临时链表    7.     用*/     8.     LIST_HEAD(umount_list);     9.     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. }  

从内核链表中脱离

   1. /*完成实际的底层的卸载文件系统的任务。首先他将mnt的所有子移动至kill链表中,    2. 也就是传递进去的umount_list,然后将kill链表中的所有的vfsmount对象的一些字段设为无效状态。    3. */     4. void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill)     5. {     6.     struct vfsmount *p;     7.      8.     for (p = mnt; p; p = next_mnt(p, mnt))     9.         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. }  

释放引用计数

   1. void release_mounts(struct list_head *head)     2. {     3.     struct vfsmount *mnt;     4.     while (!list_empty(head)) {     5.         mnt = list_first_entry(head, struct vfsmount, mnt_hash);     6.         list_del_init(&mnt->mnt_hash);     7.         if (mnt->mnt_parent != mnt) {     8.             struct dentry *dentry;     9.             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. }