Linux内核源码分析-卸载文件系统-sys_umount 本文主要参考《深入理解Linux内核》,结合2.6.11.1版的内核代码,分析内核文件子系统中的卸载文件系统函数。 注意: 1、不描述内核同步、错误处理、参数合法性验证相关的内容 2、源码摘自Linux内核2.6.11.1版 3、阅读本文请结合《深入理解Linux内核》第三版相关章节 4、本文会不定时更新 1、sys_umount函数源码: - /*
- * Now umount can handle mount points as well as block devices.
- * This is important for filesystems which use unnamed block devices.
- *
- * We now support a flag for forced unmount like the other 'big iron'
- * unixes. Our API is identical to OSF/1 to avoid making a mess of AMD
- */
- asmlinkage long sys_umount(char __user * name, int flags)
- {
- struct nameidata nd;
- int retval;
- retval = __user_walk(name, LOOKUP_FOLLOW, &nd);
- if (retval)
- goto out;
- retval = -EINVAL;
- if (nd.dentry != nd.mnt->mnt_root)
- goto dput_and_out;
- if (!check_mnt(nd.mnt))
- goto dput_and_out;
- retval = -EPERM;
- if (!capable(CAP_SYS_ADMIN))
- goto dput_and_out;
- retval = do_umount(nd.mnt, flags);
- dput_and_out:
- path_release_on_umount(&nd);
- out:
- return retval;
- }
复制代码函数处理流程: 1、把mnt对象的mnt_sb存入类型为super_block*的局部变量sb中 2、调用安全验证函数security_sb_umount验证安全性 3、如果以参数MNT_EXPIRE卸载文件系统: (1) 如果是卸载当前进程的根文件系统、卸载参数中包MNT_FORCE或 MNT_DETACH标志,则返回EINVAL (2) 如果mnt->mnt_count 不等于2,即除了父mnt和该函数外,有其他引用,标志mnt非空闲,返回EBUSY (3) 调用函数xchg,如果mnt->mnt_expiry_mark为0,即非过期,设置为过期并返回EAGAIN;已过期则执行下一步 4、如果是强制卸载且sb->s_op->umount_begin非空,这调用该函数,用于网络文件系统 5、卸载根文件系统,且卸载标志不含MNT_DETACH(即非懒卸载),如果超级块不是以只读方式安装,以只读方式重新安装 6、如果超级块的引用计数器为1,处理acct相关的信息 7、如果文件系统已为空闲或是懒卸载,调用函数umount_tree卸载以该文件系统根的文件系统树 3、umount_tree
函数源码: - void umount_tree(struct vfsmount *mnt)
- {
- struct vfsmount *p;
- LIST_HEAD(kill);
- for (p = mnt; p; p = next_mnt(p, mnt)) {
- list_del(&p->mnt_list);
- list_add(&p->mnt_list, &kill);
- }
- while (!list_empty(&kill)) {
- mnt = list_entry(kill.next, struct vfsmount, mnt_list);
- list_del_init(&mnt->mnt_list);
- list_del_init(&mnt->mnt_fslink);
- if (mnt->mnt_parent == mnt) {
- spin_unlock(&vfsmount_lock);
- } else {
- struct nameidata old_nd;
- detach_mnt(mnt, &old_nd);
- spin_unlock(&vfsmount_lock);
- path_release(&old_nd);
- }
- mntput(mnt);
- spin_lock(&vfsmount_lock);
- }
- }
复制代码
函数处理流程: 1、循环调用函数next_mnt,对以mnt为根的文件系统对象树进行深度优先搜索,每次返回一个文件系统对象,把该对象从文件系统对象链表中删除,然后加入kill链表中、 2、对kill链表中的每个文件系统对象,把该对象从kill链表和文件系统到期链表中删除;如果文件系统不是命名空间的根文件系统,调用函数detach_mnt释放该文件对象相关的数据结构,返回文件系统的安装点信息,调用函数path_release释放安装点目录 4、next_mnt函数源码: - static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root)
- {
- struct list_head *next = p->mnt_mounts.next;
- if (next == &p->mnt_mounts) {
- while (1) {
- if (p == root)
- return NULL;
- next = p->mnt_child.next;
- if (next != &p->mnt_parent->mnt_mounts)
- break;
- p = p->mnt_parent;
- }
- }
- return list_entry(next, struct vfsmount, mnt_child);
- }
复制代码函数处理流程: 1、取出当前文件系统对象的子链表的第一个元素存入局部变量next中 2、如果当前文件系统的子文件系统链表为空,循环执行下列步骤 (1) 如果当前文件系统是根,返回NULL (2) 否则,把当前文件系统对象的兄弟链表中下一个元素存入next (3) 如果next不是当前文件系统对象的父文件系统对象的子链表的头,即父文件系统子链表未处理完成,退出循环 (4) 如果当前文件系统的父文件系统的子文件系统链表已处理完,把当前文件系统设置为当前文件系统的父文件系统 3、返回包含next的文件系统对象 5、detach_mnt
函数源码: - static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd)
- {
- old_nd->dentry = mnt->mnt_mountpoint; //安装点目录项对象
- old_nd->mnt = mnt->mnt_parent; //父文件对象
- mnt->mnt_parent = mnt; //把文件对象从文件对象树中移除
- mnt->mnt_mountpoint = mnt->mnt_root; //安装点目录项对象
- list_del_init(&mnt->mnt_child); //把文件对象从兄弟链表中删除
- list_del_init(&mnt->mnt_hash);//把文件对象从文件对象hash表中删除
- old_nd->dentry->d_mounted--; //减少原安装点的安装文件系统数
- }
复制代码
|