linux下文件删除的原理:
Linux 是通过 Link 的数量来控制文件删除的,只有当一个文件不存在任何 link 的时候,这个文件才会被删除。
一般来说,每个文件都有2个link计数器:
- i_count
- 进程的引用计数
- i_link
- 硬链接数量
i_count的意义是当前文件使用者(或被调用)的数量;
i_link 的意义是介质连接的数量。
当一个文件被某一个进程引用时,对应i_count数就会增加;
当创建文件的硬链接的时候,对应i_link数就会增加。
对于删除命令rm而言,实际就是减少磁盘引用计数i_link。
这里就会有一个问题,如果一个文件正在被某个进程调用,而用户却执行rm操作把文件删除了,那么会出现什么结果呢?
当用户执行rm操作删除文件后,再执行ls或者其他文件管理命令,无法再找到这个文件了,但是调用这个删除的文件的进程却在继续正常执行,依然能够从文件中正确的读取及写入内容。这又是为什么呢?
这是因为rm操作只是将文件的i_link减少了,如果没其它的链接i_link就为0了;但由于该文件依然被进程引用,因此,此时文件对应的i_count并不为0,所以即使执行rm操作,但系统并没有真正删除这个文件,当只有i_link及i_count都为0的时候,这个文件才会真正被删除。也就是说,还需要解除该进程的对该文件的调用才行。
以上讲的i_link及i_count是文件删除的真实条件,但是当文件没有被调用时,执行了rm操作删除文件后是否还可以找回被删的文件呢?
前面说了,rm操作只是将文件的i_link减少了,或者说置0了,实际就是将文件名到inode的链接删除了,此时,并没有删除文件的实体即(block数据块),此时,如果及时停止机器工作,数据是可以找回的,如果此时继续写入数据,那么当新数据就可能会被分配到被删除的数据的block数据块,此时,文件就会被真正的回收了,那时就是神仙也没有办法了。
文件删除生产场景案例解决实战
- Web服务器磁盘满故障深入解析 https://blog.51cto.com/oldboy/612351
- 磁盘满的故障(inode满了)
案例实战环境模拟:
1. 安装 httpd web 服务
# 安装 httpd yum install httpd -y # 开启服务 [root@oldboy test]# /etc/init.d/httpd start Starting httpd: httpd: apr_sockaddr_info_get() failed for oldboy httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName [ OK ] # 查看80端口,表示服务已经运行 [root@oldboy test]# lsof -i :80 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME httpd 4801 root 4u IPv6 39743 0t0 TCP *:http (LISTEN) httpd 4803 apache 4u IPv6 39743 0t0 TCP *:http (LISTEN) httpd 4804 apache 4u IPv6 39743 0t0 TCP *:http (LISTEN) httpd 4805 apache 4u IPv6 39743 0t0 TCP *:http (LISTEN) httpd 4806 apache 4u IPv6 39743 0t0 TCP *:http (LISTEN) httpd 4807 apache 4u IPv6 39743 0t0 TCP *:http (LISTEN) httpd 4808 apache 4u IPv6 39743 0t0 TCP *:http (LISTEN) httpd 4809 apache 4u IPv6 39743 0t0 TCP *:http (LISTEN) httpd 4810 apache 4u IPv6 39743 0t0 TCP *:http (LISTEN) # 编辑配置文件,让日志记录到/app/log 下面 cd /etc/hpptd/conf vi /etc/httpd/conf # 查看CustomLog logs/access_log common 的参数在哪一行,退出来用sed命令替换。 # 先grep过滤确认替换的文本值是否唯一 [root@oldboy conf]# grep "CustomLog logs/access_log common" httpd.conf #CustomLog logs/access_log common # 将日志路径从logs/access_log替换成/app/logs/access_log # 注意,因为要将‘#’除掉,所以sed命令替换的分隔符换成了 ‘@’ [root@oldboy conf]# sed -i 's@#CustomLog logs/access_log common@CustomLog /app/logs/access_log common@g' httpd.conf [root@oldboy conf]# grep "CustomLog logs/access_log common" httpd.conf # 确认替换结果 [root@oldboy conf]# grep "CustomLog /app/logs/access_log common" httpd.conf CustomLog /app/logs/access_log common
关闭防火墙:
[root@oldboy test]# /etc/init.d/iptables stop iptables: Setting chains to policy ACCEPT: filter [ OK ] iptables: Flushing firewall rules: [ OK ] iptables: Unloading modules: [ OK ]
2. 创建一个小的文件系统,用于存放上述日志
# bs是块大小,count是块数量 [root@oldboy conf]# dd if=/dev/zero of=/dev/sdc bs=8K count=10 10+0 records in 10+0 records out 81920 bytes (82 kB) copied, 0.000283861 s, 289 MB/s [root@oldboy conf]# ls -l /dev/sdc -rw-r--r-- 1 root root 81920 Sep 5 23:58 /dev/sdc # 格式化创建文件系统 [root@oldboy conf]# mkfs -t ext3 /dev/sdc mke2fs 1.41.12 (17-May-2010) /dev/sdc is not a block special device. Proceed anyway? (y,n) y Filesystem label= OS type: Linux Block size=1024 (log=0) Fragment size=1024 (log=0) Stride=0 blocks, Stripe width=0 blocks 16 inodes, 80 blocks 4 blocks (5.00%) reserved for the super user First data block=1 1 block group 8192 blocks per group, 8192 fragments per group 16 inodes per group Writing inode tables: done Filesystem too small for a journal Writing superblocks and filesystem accounting information: done This filesystem will be automatically checked every 22 mounts or 180 days, whichever comes first. Use tune2fs -c or -i to override. # 取消180天的检查(可做可不做) [root@oldboy conf]# tune2fs -c -1 /dev/sdc tune2fs 1.41.12 (17-May-2010) Setting maximal mount count to -1 # 设置挂载点和存放日志的目录 [root@oldboy conf]# mkdir /app/logs -p [root@oldboy conf]# mount -o loop /dev/sdc /app/logs # 查看是否挂载成功 [root@oldboy conf]# df -h Filesystem Size Used Avail Use% Mounted on /dev/sda3 6.9G 1.5G 5.1G 23% / tmpfs 499M 0 499M 0% /dev/shm /dev/sda1 190M 36M 145M 20% /boot /dev/sdc 73K 14K 55K 21% /app/logs
成功模拟了一个73K的小磁盘。
3. 重启httpd服务,确保日志记录到了上述文件系统挂载的/app/log下面
[root@oldboy conf]# /etc/init.d/httpd restart Stopping httpd: [ OK ] Starting httpd: httpd: apr_sockaddr_info_get() failed for oldboy httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName [ OK ]
访问localhost或127.0.0.1或Linux的ip地址,可以访问网站,然后/app/logs/access_log中会生成日志文件。
另起一个会话查看访问的动态:tail -f /app/logs/access_log
[root@oldboy conf]# echo oldboy >/var/www/html/index.html [root@oldboy conf]# for n in `seq 100` > do > curl 127.0.0.1 > done
查看硬盘使用情况,发现日志盘已经满了:
[root@oldboy conf]# df -h Filesystem Size Used Avail Use% Mounted on /dev/sda3 6.9G 1.5G 5.1G 23% / tmpfs 499M 0 499M 0% /dev/shm /dev/sda1 190M 36M 145M 20% /boot /dev/sdc 73K 73K 0 100% /app/logs
删除日志文件,查看硬盘空间,发现硬盘还是满的。
[root@oldboy conf]# rm /app/logs/access_log rm: remove regular file `/app/logs/access_log'? y [root@oldboy conf]# df -h Filesystem Size Used Avail Use% Mounted on /dev/sda3 6.9G 1.5G 5.1G 23% / tmpfs 499M 0 499M 0% /dev/shm /dev/sda1 190M 36M 145M 20% /boot /dev/sdc 73K 73K 0 100% /app/logs
查看被删除的但仍由进程占用的文件名:
[root@oldboy conf]# lsof |grep del rdisc 3212 root 0u CHR 136,0 0t0 3 /dev/pts/0 (deleted) httpd 4957 root 7w REG 7,0 59356 12 /app/logs/access_log (deleted) httpd 4959 apache 7w REG 7,0 59356 12 /app/logs/access_log (deleted) httpd 4960 apache 7w REG 7,0 59356 12 /app/logs/access_log (deleted) httpd 4961 apache 7w REG 7,0 59356 12 /app/logs/access_log (deleted) httpd 4962 apache 7w REG 7,0 59356 12 /app/logs/access_log (deleted) httpd 4963 apache 7w REG 7,0 59356 12 /app/logs/access_log (deleted) httpd 4964 apache 7w REG 7,0 59356 12 /app/logs/access_log (deleted) httpd 4965 apache 7w REG 7,0 59356 12 /app/logs/access_log (deleted) httpd 4966 apache 7w REG 7,0 59356 12 /app/logs/access_log (deleted) tail 8895 root 3r REG 7,0 59356 12 /app/logs/access_log (deleted) [root@oldboy conf]#
4. 重启httpd 服务
/etc/init.d/httpd restart
注意确保没有进程占用文件,我删除并重启服务后,由于tail -f /app/logs/access_log也在占用该文件,导致一直没有释放,结束命令后即可。
5. 较好的解决方案
清空日志而不删除日志。
/app/logs/access_log