南宁网站开发外包性价比,安卓软件开发培训机构,平台公司转型方案,珠海做企业网站多少钱来源 | 程序猿石头责编 | Carol封图 | CSDN下载自视觉中国图来源于 SkyPixel知道为什么会有上面的结果吗#xff1f;什么又是稀疏文件#xff1f;这篇文章将为你揭秘。问题背景确切地说#xff0c;不是收到的自动告警短信或者邮件告诉我某机器上的磁盘满了#xff0c;而是某… 来源 | 程序猿石头责编 | Carol封图 | CSDN下载自视觉中国图来源于 SkyPixel知道为什么会有上面的结果吗什么又是稀疏文件这篇文章将为你揭秘。问题背景确切地说不是收到的自动告警短信或者邮件告诉我某机器上的磁盘满了而是某同学人肉发现该机器写不了新文件才发现该问题的. 说明我司告警服务还不太稳定 :)第一次出现该问题时我的处理方式是先删了 /tmp/ 目录, 空闲出部分空间然后检查下几个常用的用户目录最终发现某服务A的日志文件(contentutil.log)占用了好几个大G询问相关开发人员后确定该日志文件不需要压缩备份所以可直接删除, 于是 rm contentutil.log 之后就天真地认为万事大吉了…(不懂为啥当初没 df 再看看)然而大约xx天后发现该机器磁盘又满了惊呼奇怪咋这么快又满了。最终发现是上次 rm 后占用好几个大G的 contentutil.log 一直被服务A的进程打开了空间并没有释放。rm 其实是删除该文件名到文件真正保存到磁盘位置的链接此时该文件句柄还被服务A打开因此对应的数据并没有被回收其实可以理解为 GC 里面的引用计数rm 只是减少了引用计数并没有真正的进行释放内存当引用计数为0的时候OS 内核才会释放空间供其他进程使用。所以当A进程停止(文件句柄的引用计数会变为0)或者重启后占用的存储空间才被释放(从某种程度上讲说明该服务一直很稳定可以连续跑很久不出故障~ 微笑脸)。(tip如果不知道具体进程或文件名的话lsof | grep deleted这样会查找所有被删除的但是文件句柄没有释放的文件和相应的进程然后再kill掉进程或者重启进程即可)。其实可以简单用修改文件内容的方式例如echo contentutil.log在不用重启进程的情况下释放空间。du vs ls前两天该问题又出现了该服务A的日志文件(contentutil.log)占用了约7.6G(请原谅我们没有对该服务的日志做logrotate)。这一次学聪明了直接用echo hello contentutil.log然后 df 确认磁盘空间确实已经释放心想着这次可以 Happy 了突然手贱执行了下 ls 和 du有了以下结果[rootxxx shangtongdai-content-util]# ls -lah contentutil.log
-rw-r--r--. 1 root root 7.6G Nov 7 19:36 contentutil.log
[rootxxx shangtongdai-content-util]# du -h contentutil.log
2.3M contentutil.log
反正我看到这样的结果是百思不得其解。可以明确的是这里的 ls 和 du 结果肯定代表不同的含义具体原因不详在查阅相关资料和咨询强大的票圈后了解到这大概与文件空洞和稀疏文件(holes in ‘sparse’ files)相关。ls 的结果是 apparent sizes我的理解是文件长度就类似文件系统中 file 这个数据结构中的定义文件长度的这个字段du 的结果 disk usage即真正占用存储空间的大小且默认度量单位是 block。(apparent sizes 和 disk usage 说法摘自 man du 中的 --apparent-size 部分)给出一个具体的示例// Mac OS 10.11.6 (15G1004)
➜ _drafts git:(source) ✗ echo -n a 1B.log
➜ _drafts git:(source) ✗ ls -las 1B.log
8 -rw-r--r-- 1 tanglei staff 1 11 9 00:06 1B.log
➜ _drafts git:(source) ✗ du 1B.log
8 1B.log
➜ _drafts git:(source) ✗ du -h 1B.log
4.0K 1B.log
上面示例中文件 1B.log 内容仅仅包含一个字母”a”文件长度为1个字节前面的 8 为占用的存储空间 8 个 block(ls -s 的结果跟 du 的结果等价都是实际占用磁盘的空间)为什么1个字节的文件需要占用8个 block 呢可以这样理解 block 为磁盘存储的基本的单位方便磁盘寻址等(这里说的基本单位应该是磁盘物理结构单位例如一个扇区/柱面等, 对应一个物理单位)而此处的block可以理解为一个逻辑单位且一个文件除了包括数据外还需要存储描述此文件的其他信息因此包含1个字节的文件实际在磁盘中占用的存储空间不止1个字节。默认情况下Mac中1个逻辑block中是 512 字节因此 du -h 结果是 8 * 512 4096 4.0K。If the environment variable BLOCKSIZE is set, and the -k option is not specified, the block counts will be displayed in units of that size block. If BLOCKSIZE is not set, and the -k option is not specified, the block counts will be displayed in 512-byte blocks. (man du)因此通常情况下 ls 的结果应该比 du的结果更小(都指用默认的参数执行调整参数可使其表达含义相同)然而上面跑服务 A 的机器上 contentutil.log 的对比结果是 7.6G vs. 2.3M, 仍然无法理解了。沿着 man du 可以看到:although the apparent size is usually smaller, it may be larger due to holes in (‘sparse’) files, internal fragmentation, indirect blocks, and the like即因contentutil.log是一个稀疏文件虽然其文件长度很大到7.6G了然而其中包含大量的holes并不占用实际的存储空间。talk is cheap下面用一个具体的例子来复现以上遇到的问题。注意以下例子为 Linux version 2.6.32 Red Hat 4.4.7)中运行结果且在 Mac 中并不能复现(后文有指出为什么我的Mac不能复现。// 从标准输入中读取 count0 个block, 输出到 sparse-file 中,
// 一个 block 的大小为1k(bs1k), 输出时先将写指针移动到 seek 位置的地方
[rootlocalhost ~]# dd ofsparse-file bs1k seek5120 count0
00 records in
00 records out
0 bytes (0 B) copied, 1.6329e-05 s, 0.0 kB/s
// 所以此时的文件长度为: 5M 5120*1k(1024) 5242880
[rootlocalhost ~]# ls -l sparse-file
-rw-r--r--. 1 root root 5242880 Nov 8 11:32 sparse-file
[rootlocalhost ~]# ls -ls sparse-file
0 -rw-r--r--. 1 root root 5242880 Nov 8 11:32 sparse-file
// 而 sparse-file 占用的存储空间为 0 个 block
[rootlocalhost ~]# du sparse-file
0 sparse-file
[rootlocalhost ~]# du -h sparse-file
0 sparse-file
此时若用 vim 打开该文件用二进制形式查看 (tip :%!xxd 可以更改当前文件显示为2进制形式)能看到里面的内容全是0。或者直接用od命令查看2进制。// vim 二进制查看
0000000: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000010: 0000 0000 0000 0000 0000 0000 0000 0000 ................
....
//od -b sparse-file
0000000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
*
24000000
实际上Sparse 文件是并不占用磁盘存储空间的那为什么能看到文件里面包含很多0? 因为当在读取稀疏文件的时候文件系统根据文件的 metadata(就是前面所指描述文件的这个数据结构)自动用0填充[ref Wiki]; Wiki上还说现代的不少文件系统都支持 Sparse 文件包括 Unix 及其变种和 NTFS然而Apple File System(APFS)不支持因此我在我的 Mac 上用 du 查看占用空间与 ls 的结果一致。传闻指出 Apple 在今年6月的 WWWC 上宣称支持 Sparse 文件。(貌似目前我的系统版本还不支持)// In Mac
➜ ~ dd ofsparse-file bs1k seek5120 count0
00 records in
00 records out
0 bytes transferred in 0.000024 secs (0 bytes/sec)
➜ ~ ls -ls sparse-file
10240 -rw-r--r-- 1 tanglei staff 5242880 11 9 09:44 sparse-file
➜ ~ du sparse-file
10240 sparse-file
以上是用 dd 等命令创建稀疏文件也有同学用 c 代码实现了相同的功能。其实就是写文件的时候改变下当前文件写指针。前面遇到的问题就应该类似。#include stdio.h
#include fcntl.h
#include string.hint main() {int fd, result;char wbuf[] hello;if ((fd open(./filetest.log, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR))) {perror(open);return -1;}if ((result write(fd, wbuf, strlen(wbuf)1)) 0) {perror(write);return -1;}if ((result lseek(fd, 1024*1024*10, SEEK_END)) 0) {perror(lseek);return -1;}if ((result write(fd, wbuf, strlen(wbuf)1)) 0) {perror(write);return -1;}close(fd);return 0;
}
以上先将”hello”写入filetest.log然后改变文件指针到1024*1024*10(相当于文件长度这个字段变大了)gcc 编译后运行结果文件详情如下[rootlocalhost ~]# ls -ls filetest.log
8 -rw-------. 1 root root 10485772 Nov 9 17:45 filetest.log
[rootlocalhost ~]# du filetest.log
8 filetest.log
[rootlocalhost ~]# du -h filetest.log
8.0K filetest.log
[rootlocalhost ~]# ls -lh filetest.log
-rw-------. 1 root root 11M Nov 9 17:45 filetest.log
[rootlocalhost ~]# od -c filetest.log
0000000 h e l l o \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
0000020 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
*
50000000 \0 \0 \0 \0 \0 \0 h e l l o \0
50000014
解释下结果 文件长度应该是 “hello” 加上 “\n” 共6个字节*2 12再加上1024*1024*10个字节即为ls产生的结果10485772个字节约11M而du的结果为8个block也为8k(这台机器上的block大小与前面的Mac不一样这里是1024)。Display values are in units of the first available SIZE from –block-size, and the DU_BLOCK_SIZE, BLOCK_SIZE and BLOCKSIZE environment variables. Otherwise, units default to 1024 bytes (or 512 if POSIXLY_CORRECT is set. (du --help)总结总结一下出现以上问题说明自己对一些基础掌握得尚不牢固比如rm 某文件后, 文件占用的磁盘空间并不是立即释放, 而是其句柄没有被任意一个进程引用时才回收ls/du 命令结果的具体含义稀疏文件。然而这些知识点都在《UNIX环境高级编程》这本书中有讲 (之前走马观花看过不少咋对稀疏文件等一点印象都木有) 以上内容若有不清楚或不正确的地方还望大家指出感谢。6月3日20:00CSDN 创始人董事长、极客帮创投创始合伙人蒋涛携手全球顶级开源基金会主席、董事聚焦中国开源现状直面开发者在开源技术、商业上的难题你绝不可错过的开源巅峰对谈立即免费围观推荐阅读因为一个跨域请求我差点丢了饭碗没错你离分布式搜索只差一个Elasticsearch入门Python开发之Django基于Docker实现Mysql数据库读写分离、集群、主从同步详解 | 原力计划全球Python调查报告Python 2正在消亡PyCharm比VS Code更受欢迎无代码来了还要程序员吗再见Eclipse | 原力计划区块链共识算法总结 | 原力计划真香朕在看了