Linux内核源码分析-卸载文件系统-sys_umount
来源:互联网 发布:乐视下载软件 编辑:程序博客网 时间:2024/06/06 20:10
本文主要参考《深入理解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、调用__user_walk函数(path_lookup封装函数)查找路径名信息nameidata对象,并存入局部变量nd中
2、判断查找到的目录是否是安装点目录(nd.dentry 和nd.mnt->mnt_root是否相等),不是则退出
3、调用函数check_mnt验证要卸载的文件系统是否在当前进程的命名空间中,没有则退出
4、调用函数capable验证是否有卸载文件系统的权限
5、调用do_umount卸载文件系统,具体分析见下文
2、do_umount
函数源码:static int do_umount(struct vfsmount *mnt, int flags)
{
struct super_block * sb = mnt->mnt_sb;
int retval;
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]
*/
if (flags & MNT_EXPIRE) {
if (mnt == current->fs->rootmnt ||
flags & (MNT_FORCE | MNT_DETACH))
return -EINVAL;
if (atomic_read(&mnt->mnt_count) != 2)
return -EBUSY;
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.
*/
lock_kernel();
if( (flags&MNT_FORCE) && sb->s_op->umount_begin)
sb->s_op->umount_begin(sb);
unlock_kernel();
/*
* 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.
*/
if (mnt == current->fs->rootmnt && !(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)) {
lock_kernel();
DQUOT_OFF(sb);
retval = do_remount_sb(sb, MS_RDONLY, NULL, 0);
unlock_kernel();
}
up_write(&sb->s_umount);
return retval;
}
down_write(¤t->namespace->sem);
spin_lock(&vfsmount_lock);
if (atomic_read(&sb->s_active) == 1) {
/* last instance - try to be smart */
spin_unlock(&vfsmount_lock);
lock_kernel();
DQUOT_OFF(sb);
acct_auto_close(sb);
unlock_kernel();
security_sb_umount_close(mnt);
spin_lock(&vfsmount_lock);
}
retval = -EBUSY;
if (atomic_read(&mnt->mnt_count) == 2 || flags & MNT_DETACH) {
if (!list_empty(&mnt->mnt_list))
umount_tree(mnt);
retval = 0;
}
spin_unlock(&vfsmount_lock);
if (retval)
security_sb_umount_busy(mnt);
up_write(¤t->namespace->sem);
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--; //减少原安装点的安装文件系统数
}
- Linux内核源码分析-卸载文件系统-sys_umount
- Linux虚拟文件系统之文件系统卸载(sys_umount())
- Linux虚拟文件系统之文件系统卸载(sys_umount())
- Linux虚拟文件系统之文件系统卸载(sys_umount())
- Linux内核源码分析--文件系统(二、高速缓存区)
- Linux内核源码分析--文件系统(三、buffer.c)
- Linux内核源码分析--文件系统(四、Bitmap.c)
- Linux内核源码分析--文件系统(五、Inode.c)
- Linux内核源码分析--文件系统(六、Super.c)
- Linux内核源码分析--文件系统(七、Namei.c)
- Linux内核源码分析--文件系统(八、Block_dev.c)
- Linux内核源码分析--文件系统(九、File_dev.c)
- Linux内核源码分析之文件系统(1) -- 三思而后行
- Linux内核源码分析-安装普通文件系统-mount系统调用
- Linux内核源码分析-安装根文件系统-init_rootfs- init_mount_tree
- Linux内核源码分析-安装实际根文件系统- prepare_namespace
- linux内核分析之文件系统
- Linux内核与文件系统分析
- [Developer Android] Loading Large Bitmaps Efficiently
- 2016区域赛前训练总结
- Fat Jar打包插件方法
- Activity详解(二)——Activity的四种启动模式
- LeetCode(120) Triangle
- Linux内核源码分析-卸载文件系统-sys_umount
- CentOS+VMware12.0 NAT静态IP上网设置 + Xshell
- 读书笔记:C++ primer 5th edition--chapter19.特殊工具与技术
- 华为OJ,字符串按8位的长度截取
- 安装ubuntu
- Memcache学习笔记一:Memcache在Linux下的安装和使用
- 剑指offer-二维数组中的查找
- 汽车加油站问题
- RxJava组合操作符实例