@HUST张友东 work@taobao zyd_com@126.com
分类: LINUX
2012-11-10 18:45:20
昨日线上一台机器上的nginx rt飙高,@明俨 调查发现这台机器上的metaserver内存占用很高,同时还有个奇怪的现象,df发现/home的空间占用在增长飞快,但metaserver和nginx的日志文件增长都很慢,通过du -sh /home统计home下文件的总大小,发现跟df命令/home占用的空间小10+g,到底谁占用了我的磁盘空间?
后来把nginx进程都停掉后(当时应该先通过lsof或/proc/pid/fd/看看nginx当时打开的文件情况),df显示/home的空间一下降下来了,跟du统计的结果能匹配上,说明这些磁盘空间都被nginx进程占用着。内核组的同事告诉我们,一个文件被进程打开后,在关闭前如果文件被删除,此时这个文件已经在它父目录的目录项中被删除掉(在父目录ls已经看不到这个文件),但该进程依然能够正常的读写文件,直到文件被该进程关闭后,这个文件的空间才会被回收,通过下面一个小例子解释下。
点击(此处)折叠或打开
- int main()
- {
- int fd = open("afile", O_CREAT | O_RDWR);
- assert(fd > 0);
- unlink("afile");
- while (1)
- {
- char buf[1024]; // 1K
- for (int i = 0; i < 102400; i++)
- {
- int len = write(fd, buf, 1024);
- assert(len == 1024);
- }
- lseek(fd, 0, SEEK_SET); // keep writing, but don't make it increase
- sleep(5);
- }
- return 0;
- }
# g++ filetest.cpp
# ls
a.out filetest.cpp 没有afile这个文件
# du -sh
16K .
# df -m | grep home
/dev/sda9 639406 521255 85148 86% /home
# ./a.out & 执行测试程序,持续写文件前100M的空间
# ls
a.out filetest.cpp 没有afile这个文件
# du -sh
16K .
# df -m | grep home
/dev/sda9 639406 521355 85048 86% /home
# killall a.out 杀掉测试程序
# df -m | grep home
/dev/sda9 639406 521255 85148 86% /home
可以看出,进程在打开afile之后立即unlink掉这个文件,通过ls看不到这个文件,因为父目录中这个文件的信息已经删除,在进程持续写文件的过程中(为了避免空间暴增,一直写前100M,仅供测试观察之用),通过du -sh发现该目录下文件总空间没有变化,因为du是通过遍历文件来统计的,而通过df我们发现home的used刚好增加了100M的空间,接下来我们把测试程序关闭掉,home的used又降了100M,说明afile的空间已经被回收了。
nginx有个特性,post请求的body如果超过2个page(可配置),就会把body的数据写到磁盘暂存,等请求处理完后,再删除临时的文件。nginx在open创建临时文件后,会先unlink掉这个文件(为什么要这么做,有什么好处?),然后在请求处理完后close掉文件。告警的服务器当时由于metaserver占用了大部分的内存,已经出现了swap的情况,而通过nginx写的文件平均大小在60-70K左右,基本都要写临时文件,导致nginx的服务越来越慢,积压大量的POST请求,这些请求对应的临时文件一直占用着磁盘空间,但因为文件已经删除,外面已经看不到这些文件的存在,从而导致du,df显示的结果相差很大。