一、事件描述
因Linux系统根分区使用空间达到最高预警,特清理相关磁盘,发现在/etc目录有个大文件,但是奇怪的是清理完之后,根分区仅有的空间瞬间满了,清理的文件所占用空间未正常释放;
二、操作及分析
故障原因:
du 命令对统计文件逐个进行 fstat 系统调用,获取文件大小。它的数据是基于文件获取,可以跨多个分区操作。
df 命令使用 statfs 系统调用,直接读取分区的超级块信息获取分区使用情况。它的数据基于分区元数据,只能针对整个分区。
因此,如果文件未从meta-data中删除,df是不会察觉到文件数据被删除的,且因进程锁定继续想文件原指针位置写入数据,故空间并不会释放,后来的数据是无法重用该部分数据的。必须将文件指针从meta-data冲删除/释放,或写空,才能释放这部分空间。而du 命令是不会在文件系统目录中统计这些被标记未deleted的文件的,所以看到的不一样。
从文件存储特性来看,一个文件在文件系统中的存放分为两个部分:数据部分和指针部分,指针位于文件系统的meta-data中,数据被删除后,这个指针就从meta-data中清除了,而数据部分存储在磁盘中,数据对应的指针从meta-data中清除后,文件数据部分占用的空间就可以被覆盖并写入新的内容,但是如果被删除的文件,某进程对该文件锁定,还在一直向指针处写数据,即文件对应的指针部分由于进程锁定,并未从meta-data中清除,而由于指针并未被删除,那么系统内核就认为文件并未被删除,故通过df命令查询空间看到并未释放。
如果是某个文本占据空间太多需要删除,不要直接rm删除,用echo “” > filename的方式先对文本置空,才会释放空间。
当进程打开了某个文件时,只要该进程保持打开该文件,即使将其删除,它依然存在于磁盘中。这意味着,进程并不知道文件已经被删除,它仍然可以向打开该文件时提供给它的文件描述符进行读取和写入。除了该进程之外,这个文件是不可见的,因为已经删除了其相应的目录索引节点。大多数与 lsof 相关的信息都存储于在/proc目录以进程的 PID 命名的目录中,在/proc 目录下,其中包含了反映内核和进程树的各种文件。/proc目录挂载的是在内存中所映射的一块区域,所以这些文件和目录并不存在于磁盘中,因此当我们对这些文件进行读取和写入时,实际上是在从内存中获取相关信息。因此,当系统中的某个文件被删除,只要这个时候系统中还有进程正在访问该文件,那么我们就可以通过lsof从/proc目录下恢复该文件的内容。
执行:
lsof |grep deleted //如果命令不存在可yum安装下yum install -y lsof
或者执行:
lsof |grep delete | sort -nrk 7| more //将已经删除的文件进行大小排序
上图中的on文件,是因为nginx进程还在一直向这个文件写入内容,导致虽然删除了on文件,但文件对应的指针部分由于进程锁定,并未从meta-data中清除,那么系统内核就认为文件并未被删除。上图中on文件,显示还是11G,而根分区只有20G,在最后一列的“deleted”状态,说明这个日志文件已经被删除,但由于nginx进程还在一直向此文件写入数据,故空间并未释放。且文件描述符为4(4w),查看/proc目录:
然后执行:echo “” > /proc/11474/fd/4/on
验证: df -h
总结: 日常运维过程中,如果我们需要删除比较大的文件 可以使用echo “” > filename 来释放磁盘空间,使用 rm 如果有进程在访问文件,则有可能出现磁盘空间不释放的情况。
三、附录:
1、lsof 其他用例:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
输出列说明:
COMMAND:进程的名称
PID:进程标识符
PPID:父进程标识符(需要指定-R参数)
USER:进程所有者
FD:文件描述符,应用程序通过文件描述符识别该文件。
TYPE:文件类型
DEVICE:指定磁盘的名称
SIZE/OFF:文件的大小(Byte)
NODE:索引节点(文件在磁盘上的标识)
NAME:打开文件的确切名称
FD文件描述符列表如下:
cwd:表示current work dirctory,即:应用程序的当前工作目录,这是该应用程序启动的目录,除非它本身对这个目录进行更改
txt:该类型的文件是程序代码,如应用程序二进制文件本身或共享库,如上列表中显示的 /sbin/init 程序
lnn:library references (AIX);
er:FD information error (see NAME column);
jld:jail directory (FreeBSD);
ltx:shared library text (code and data);
mxx :hex memory-mapped type number xx.
m86:DOS Merge mapped file;
mem:memory-mapped file;
mmap:memory-mapped device;
pd:parent directory;
rtd:root directory;
tr:kernel trace file (OpenBSD);
v86 VP/ix mapped file;
0:表示标准输出
1:表示标准输入
2:表示标准错误
一般在标准输出、标准错误、标准输入后还跟着文件状态模式:
u:表示该文件被打开并处于读取/写入模式。
r:表示该文件被打开并处于只读模式。
w:表示该文件被打开并处于。
空格:表示该文件的状态模式为unknow,且没有锁定。
-:表示该文件的状态模式为unknow,且被锁定。
其中TYPE文件类型列表如下:
DIR:表示目录
CHR:表示字符类型
BLK:块设备类型
UNIX:UNIX域套接字
FIFO:先进先出(FIFO)队列
IPv4:网际协议(IP)套接字
其他示例:
lsof 列车所有打开的文件
lsof /filepath/file 查看谁正在使用某个文件
lsof -c abc 显示abc进程现在打开的文件
lsof +d /bin/ #显示/bin当前目录下所有打开的文件,如果是想递归的打开则使用选项 +D即可
lsof -p 1234 列出进程号为1234的进程所打开的文件
lsof -g gid 显示归属gid的进程情况
lsof +d /usr/local/ 显示目录下被进程开启的文件 lsof +D /usr/local/ 同上,但是会搜索目录下的目录,时间较长
lsof -d 4 显示使用fd为4的进程
lsof -i | head -20 | tail #使用 -i 选项显示网络连接
lsof -i 4 | head #仅获取IPV4的流量 ,如果想获取IPV6的流量的话,把4改成6即可
lsof -i[46] [protocol][@hostname|hostaddr][:service|port] 46 --> IPv4 or IPv6 protocol --> TCP or UDP hostname --> Internet host name hostaddr --> IPv4地址 service --> /etc/service中的 service name (可以不止一个) port --> 端口号 (可以不止一个)
lsof -itcp |head #显示所有tcp连接,如果想显示udp连接就把tcp改成udp即可
lsof -i@192.168.88.133 #使用-i@host显示该主机是否连接了指定主机,使用-i@host:port显示该主机是否通过某个指定端口连接指定主机
lsof -i -stcp:LISTEN |head #找出所有监听的文件,把LISTEN换成ESTABLISHED表示显示所有建立连接的文件
lsof -a -u root -d 2 | head //列出是root用户,并且DF为2的文件,-a只有两者满足都满足要求才列出
lsof -c mysql 列出某个程序所打开的文件信息,备注: -c(command) 选项将会列出所有以mysql开头的程序的文件
sof +|-r [t] 控制lsof不断重复执行,缺省是15s刷新;-r,lsof会永远不断的执行,直到收到中断信号;+r,lsof会一直执行,直到没有档案被显示
lsof -i tcp@192.168.1.111:ftp -r //不断查看目前ftp连接的情况
命令常用选项:
无选项 #默认列出所有打开的文件相关信息
file #列出打开文件存在的进程
-a #使用AND逻辑,合并选项输出内容
-c<进程名> #列出指定进程所打开的文件
-g #列出GID号进程详情
-d<文件号> #列出占用该文件描述符的进程
+d<目录> #列出目录下被打开的文件
+D<目录> #递归列出目录下被打开的文件
-n<目录> #列出使用NFS的文件
-i<条件> #列出符合条件的进程。(4、6、协议、:端口、 @ip )
-p<进程号> #列出指定进程号所打开的文件
-u #列出UID号进程详情
-U #获取 UNIX 套接口地址
-t #列出进程
-h #显示帮助信息
-v #显示版本信息
2、文件描述符
linux下最大文件描述符的限制有两个方面,一个是用户级的限制,另外一个则是系统级限制。以下是查看Linux文件描述符的三种方式:
1)sysctl -a | grep -i file-max --color //系统级限制:sysctl命令和proc文件系统中查看到的数值是一样的,这属于系统级限制,它是限制所有用户打开文件描述符的总和。
其中会内核动态分配文件句柄,但因此不会释放它们,而是分配给下一个。file-max中的值表示Linux内核将分配的能处理文件的最大数量;当遇到很多有关文件句柄的错误消息,可能就需要提高这个限制。file-nr中的三个值分别表示分配的文件句柄数量,未使用的文件句柄的数量和文件句柄数最大值。当分配的文件处理句柄数接近最大值时,但未使用的文件处理句柄数量这时又明显大于0,这表明遇到了句柄使用峰值,而无需调整最大值。
2)cat /proc/sys/fs/file-max // 修改系统限制执行:echo350000 > /proc/sys/fs/file-max //重启后失效,永久修改把fs.file-max=400000添加到/etc/sysctl.conf中,使用sysctl -p重新加载即可
3)ulimit -n //用户级限制:ulimit命令看到的是用户级的最大文件描述符限制,也就是说每一个用户登录后执行的程序占用文件描述符的总数不能超过这个限制; grep -vE’$|#’ /etc/security/limits.conf 默认配置文件中只有hard选项,soft 指的是当前系统生效的设置值,hard 表明系统中所能设定的最大值,如果配置soft,其中,soft值要小于hard。
3、另外借助lsof定位到进程打开的文件位置,即使这个文件被删除了,只要进程未退出,这个文件还会存在于内存的虚拟文件系统里,即proc的某个目录,通过lsof的文件描述符定位到进程下打开的文件位置,然后读取该文件重写到被删除的文件,达到恢复文件的目的。
示例如下:
cat /proc/6511/fd/6 > /var/log/messages //还原数据