王子勇
腾讯中级业务运维工程师,有10年研制与运维工作经验。崇尚开源,喜欢钻研系统技术。微讯号:jacuro
1.缘起
某个月朗淡远的夜晚,正在公司旁边的厦大操场慢跑,忽然接到朋友发来的消息,他发觉某机器上的文件句柄使用量有十一万多个(下边输出中的第一个数组)
其实很不科学,这儿听到的数据不到1万个,剩下10多万的文件句柄那里去了呢(系统完整性检测已排除黑客入侵可能性)
2.文件描述符和文件句柄的故事
先看一张知名的图吧
这儿我们先分辨好两个概念:文件描述符和文件句柄
简单来说,每位进程都有一个打开的文件表(fdtable)。表中的每一项是structfile类型,包含了打开文件的一些属性诸如偏斜量,读写访问模式等,这是真正意义上的文件句柄。
文件描述符是一个整数。代表fdtable中的索引位置(下标),指向具体的structfile(文件句柄)。
3.file-nr文件里的值是文件描述符还是文件句柄?
沿着内核代码找一下:
可以看出file-nr指标是由proc_nr_files函数处理,该函数最终虽然是读取了nr_files全局变量
找下哪些地方降低了这个值:
可以看见fs/file_table.c文件中第127行的get_empty_filp函数会降低这个值。这么这个函数是干哪些的呢?
内核上面有注释“Findanunusedfilestructureandreturnapointertoit.”,虽然就是拿来分配structfile的。
到此,相信你已然晓得答案了。file-nr文件上面的第一个数组代表的是内核分配的structfile的个数,也就是文件句柄个数,而不是文件描述符。
4.什么地方会分配文件句柄?
晓得文件句柄最终是通过get_empty_filp函数从filpcache中分配的以后linux操作系统安装,我们沿着函数调用链路简单梳理下,才能晓得有什么地方会分配文件句柄了:
实际上,lsof的指南页也有部份描述:
5.找出真凶
有了前面的知识,我们不仅看lsof的输出之外puppy linux,再通过ipcs命令看一下共享显存的情况:
竟然有部份共享显存段被attach了9多万次,数目级对得上,毫无疑惑就是它了!
可能有些同学会有疑惑,同一个进程竟然可以重复attach同一段共享显存这么次?答案是可以的,内核无限制。其实,这样做逻辑上是有问题的,对共享显存的正常操作,要么是attach后仍然用,要么是attach用完以后就detach。
6.排除了共享显存等的情况,我看见的file-nr值和lsof输出还是有很大差别?
里面的案例算是比较典型,但是,尽管在一个比较“正常”的系统上,我们可以看见file-nr和lsof的输出还是有不小的差别的:
这儿本质上是由于文件描述符和文件句柄是两个不同的东西:lsof在用户空间,主要还是从文件描述符的角度来看文件句柄。
我们来做一个实验:只打开一次文件,之后复制1000次文件描述符。测试代码如下:
我们启动dupfd进程打开了一次/dev/zero文件,复制了1000次文件描述符。file-nr中的文件句柄数只是个位数的变化,而lsof见到的结果涨了1000多。
假如我们把上面的代码换成open1000次linux句柄数,就可以看见file-nr和lsof的输出几乎都涨了1000。
lsof看见的是文件描述符不能代表文件句柄,还有一个有趣的反例。下边的mmap程序运行后。文件句柄降低了将近1000,而lsof听到的文件描述符才个位数:
我们来看一下测试的mmap程序代码:
代码中,我们循环1000次打开/dev/zero文件,然后mmap映射到进程地址空间,之后把这种打开的文件描述符都关闭。很其实,打开的描述符都被close掉了,不会有哪些变化。那为何文件句柄数还是降低了1000个左右呢?
原先,linux内核中好多对象都是有引用计数的。其实文件句柄是由open先打开的,但mmap以后,引用计数被加1,虽然我们接着把文件描述符close掉了,并且底层指向的structfile因为引用数小于0,不会被回收。
通过前面两个反例,你应当晓得lsof的输出和实际的文件句柄数有差别的缘由了。
7.怎么找出显存映射间接占用的文件句柄?
实际上,不管是mmap映射文件,还是通过shmat连共享显存,最终就会在进程地址空间中分配一片显存区。通过pmap命令可以看出一些疲态:
回到故事的开头。那种使用了11万文件句柄的机器,在内核slabcache中linux句柄数,不仅文件句柄(structfile对象)对应的filpcache对象多之外,对应的显存区对象vm_area_struct占用也是超多的.
下边是slabtop的部份输出:
8.还有其他lsof漏掉的情况吗?
其实有了,lsof是通过查看进程的显存映射和文件描述符表来枚举打开文件的,假如是一个多线程的服务。主线程先退出了,子线程还活着,这么进程的fd表看上去就是空的。
9.总结
Linux内核曝露下来的指标对系统监控很有意义,认识这种指标背后蕴涵的对象以及下降缘由,才能帮助我们在异常时找出问题所在。
9月的北京,运维人有个必去的地方,第10届GOPS,回到起点,回到初心!
AIOps风向标!GOPS全球运维会议2018·上海站惊艳袭来!
GOPS2018成都站亮点之一:腾讯运维双雄:聂鑫、大梁带亮相GOPS广州站带您嗨聊一整天!