以上对于 copy_reg 的代码我做了极大的简化,把关键流程梳理了出来 。
小结:
- copy_reg 函数才是真正 cp 一个普通文件的逻辑所在,源文件的打开,目标文件的创建和数据的写入都在这里;
- 拷贝之前,会先用 is_probably_sparse 函数来判断源文件是否属于稀疏文件;
- 如果是 sparse always 模式,那么无论源文件是否是稀疏文件,那么都会尝试生成稀疏的目标文件(这种模式下,源文件如果是非稀疏文件,会判断是否是全 0 数据,如果是的话,还是会在目标文件中打洞);
- 如果是 sparse auto 模式,源文件是稀疏文件,那么生成的目标文件也会是稀疏文件;
- 源文件为稀疏文件的时候,会尝试使用效率更高的 extent_copy 函数来拷贝数据;
- 如果是 never 模式,那么是调用 sparse_copy 函数来拷贝数据,并且里面不会尝试 punch hole,拷贝过程会非常慢,会生成一个实打实的目标文件,物理空间占用完全和文件size一致;
问题一:is_probably_sparse 函数是怎么来判断源文件的?
看了源码你会发现,非常简单,其实就是 stat 一下源文件,拿到文件大小 size,还有物理块的占用个数(假设物理块 512 字节),比一下就知道了 。
static boolis_probably_sparse (struct stat const *sb){ return (HAVE_STRUCT_STAT_ST_BLOCKS && S_ISREG (sb->st_mode) && ST_NBLOCKS (*sb) < sb->st_size / ST_NBLOCKSIZE);}举个例子,文件大小 size 为 100G,物理占用块 8 个,那么 100G/512字节 > 8,所以就是稀疏文件 。文件大小 size 为 4K,物理占用块 8 个,那么 4K/512字节 == 8,所以就不是稀疏文件 。
问题二:extent_copy 为什么更有效率?
关键在于里面的一个子函数 extent_scan_read 的实现,extent_scan_read 位于 extent-scan.c 文件中 。extent_scan_read 位于 extent_copy 开头,用来获取到源文件的空洞位置信息 。这个就是 extent_copy 高效率的根本原因 。extent_scan_read 通过这个函数能够拿到文件的空洞的详细位置,那么拷贝数据的时候,就能针对性的跳过这些空洞,只拷贝有效的位置即可 。
那么,不禁又要问, extent_scan_read 又是怎么实现的呢?
答案是:ioctl 系统调用,搭配 FS_IOC_FIEMAP 参数,也就是 fiemap 的调用 。
/* Call ioctl(2) with FS_IOC_FIEMAP (available in linux 2.6.27) to obtain a map of file extents excluding holes. */fiemap 这个是一个非常关键的特性,ioctl 搭配 FS_IOC_FIEMAP 这个函数能够拿到文件的物理空间分配关系,能够让用户知道长达 100G 的文件中,哪些位置才是真正有物理块存储数据的,哪些位置是空洞 。
这个特性则由文件系统提供,也就是说,只有文件系统提供了这个对外接口,我们才能拿得到,比如 ext4,就支持这个接口,ext2 就没有 。
问题二:sparse_copy 为什么慢,里面哟是做了啥?
这个函数是标准的 copy 函数,对比 extent_copy 来说,没有 fiemap 的加持,那么这个函数就自己判断是否是空洞,怎么判断?
sparse_copy 认为,只要大块连续的全 0 数据,那么就认为是空洞,目标文件就不用写入,直接打洞即可 。
判断是否全 0 的函数是is_nul,位于 system.h 头文件中,实现非常简单,就是看整个内存块是否全部为 0。
举个例子,现在 sparse_copy 从源文件里读出 4k 的数据,发现全都是 0,那么目标文件对应的位置就不会写入,而是直接 punch hole 打洞,节省空间 。
但是注意了,这种行为只有在激进的 sparse always 策略才是这样的 。如果是其他策略,sparse_copy 不会做这样做,而是老老实实的拷贝数据,哪怕是全 0 的数据,也要如实的写入到目标文件 。
所以,always 模式下,目标文件所占物理空间比源文件小的根本原因就在于 sparse_copy 这个函数的实现 。
cp 快速的原因
梳理到这里,cp 的秘密已经彻底揭开了,cp 一个 100G 的文件为什么那么快?
因为源文件是稀疏文件啊,文件看似 100G,实际只占用了 2M 的物理空间 。文件系统将文件大小和物理空间占用这两个概念解耦,使得有更灵活的使用姿势,更有效的使用物理空间 。
推荐阅读
- Linux查看硬件信息超强命令sar,以及可视化工具ksar
- 微信正在用的深度学习框架开源!支持稀疏张量,基于C++开发
- PC电脑|5分钟开机上千台 无影云电脑免费体验1周:Win、Linux通吃
- linux内核SMP负载均衡浅析
- 浅谈在Linux中如何将脚本做成系统服务开机自启动
- 寒湿型肥胖喝什么茶,喝什么茶排寒湿深度好文
- Linux服务器磁盘满了怎么办
- 色相饱和度深度解析,photoshop明度观察层是什么?原理讲解
- linux安装php步骤详解
- 「linux专栏」top命令用法详解,再也不怕看不懂top了
