Linux中卸载文件系统由umount系统调用实现,入口函数为sys_umount()。较于文件系统的安装较为简单,下面是具体的实现。
[cpp]
- /*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]- 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; }