哈喽,大家好,我就是那个不喜欢在大厂搬砖,不喜欢在研究院做研究,只喜欢创业做计算机底层课程的coder,子牙老师
前两天写了篇关于mount底层运行原理的文章【mount藏着的Linux内核的秘密】,感兴趣的可以看看。如果你想看懂本篇文章,推荐你看看。如果你已然知道,那就不用看
mount,最常用的用法就是把新增的硬盘挂载到某个目录中,方便后续操作。umount,相反操作,卸载
mount操作进入Linux内核后,做了很多事情,本质是把这些struct串起来:task_struct、nsproxy、mnt_namespace、mount、vfsmount、mountpoint、super_block、dentry、inode、file_system_type
umount做的就是断开它们之间的联系。有了这层认知,找到相关代码就算通过umount了
开整~
不太一样
mount操作,是同步完成的。而umount操作,是异步完成的,涉及到延迟清理。这个我是怎么知道的?单步调试Linux内核误打误撞碰上的。接下来我把这个过程重现给大家
想研究umount,理所当然的在相关代码处下断点 运行起来,看在这里断下来的调用栈 是不是很奇怪,没看到ksys_umount,于是我就追了下代码,在函数task_work_run中找到了答案 调用自实现文件系统注册的卸载函数,是执行task->task_works执行到的。task->task_works中存储的是延迟执行的任务,是在内核态返回用户态之前执行。
你是不是想问,为什么要把umount的部分工作放到task->task_works中延迟执行?我也没想明白,于是问了下ChatGPT,贴下答案 你是不是还想问:什么时候把umount的部分工作放入task->task_works的?执行umount的过程中放入的,核心代码在函数mntput_no_expire中 所以umount的工作分两个阶段完成:阶段一,进入内核,执行ksys_mount,执行完将任务放入task->task_works;阶段二,内核态返回用户态的时候,把阶段一加入的任务拿出来执行,进行最后的清理工作
kill_litter_super看下代码,这个函数主要完成super_block、inode、dentry的回收 函数d_genocide会遍历到所有的dentry,flags加上DCACHE_GENOCIDE,引用计数-1 接下来调用函数kill_anon_super,这个函数的主要功能是回收虚拟设备号、关联的dentry、inode 看下generic_shutdown_super代码 前面不是没有做dentry的清理工作吗,在函数shrink_dcache_for_umount中做了 1620行,断开super_block与dentry的关系,调用do_one_tree清理dentry。回收dentry的核心函数是dentry_kill 函数dentry_kill中,会断开dentry与inode的关系,断开该dentry与其父dentry的关系,代码我就贴了,感兴趣的可以自己去看源码,很容易看懂
继续执行,函数evict_inodes是清理该super_block关联的所有inode,代码我就不贴了,核心函数是destroy_inode,执行到i_callback,完成内存回收 代码474行, 把这个超级块从全局 sb->s_type->fs_supers 链表里摘掉,彻底移除
至此,只剩孤零零的super_block没有被清理,其他都清理干净了
清理super_block
代码接着走,执行到put_super 在put_super中,回收super_block 现在super_block、dentry、inode都清理完了,mount、vfsmount、mountpoint可以清理了
cleanup_mnt看代码 1104行,delayed_free_vfsmnt回收mount的内存 那mount与task_struct、nsproxy、mnt_namespace之间的连接是何时断掉的呢?执行ksys_mount过程中
ksys_mount看核心代码 do_umount中执行到函数umount_tree 1465行,与父mount脱离关系
1483行,与mnt_namepsace脱离关系
此mount与父mount、mnt_namespace断掉关系后,调用函数mntput_no_expire,将mnt的清理工作包装成异步任务放入task->task_works,完工!
至此,umount在Linux内核中的所有工作流程都清晰了!
|