Linux 下删点日志也能搞死人

2020-07-04 12:54:47 +08:00
 felix021

想了十天十夜不知道写些什么,那就写写面试题吧。

1

在面试应聘者的时候,我常常会问:

在 Linux 下,如何删除一个目录下的所有 log 文件?

不知道是不是我人畜无害的围笑给了应聘者我很好应付的错觉

以至于应聘者全都回答:rm *.log

追问:该目录下可能有很多子目录,如何把子目录里的 log 文件也删掉呢?

答:rm -r *.log

2

令我很意外的是,真的只有很少的应聘者能想到 find 命令。

而且想到的人也很少有记得具体用法的。

目前为止只有一个应聘者能够给出完整的命令:

find -name \*.log -exec rm -f {} \;

注:这里的两个斜杠都不是笔误。

我觉得 find 应该不算一个很罕见的命令?

我们有一台共享开发机,因为大家都懒得删 log,经常磁盘爆满,后来我们给它加了个 crontab:

0 4 * * * find /home/ -type f -name "*.log*" -size +100M -exec bash -c "echo -n > '{}'" \;

注:".log*" 后面的 * 是考虑了 log rotate 。

注意,这个命令没有用 rm,而是使用重定向来清空文件,原因后面会讲。

3

基于清理磁盘空间这个场景,我还会继续问:

你有遇到过删了 log 文件,但是磁盘空间却没有释放的情况吗?

有些候选人可能心里在想着:文件删了不就删了吗,还有什么磁盘空间没释放?

所以有时候我需要解释一下,是 df 命令看到的磁盘空间没有减少。

还有个候选人努力想了想,和我确认,是不是正好这个目录挂载的是其他磁盘,所以看起来当前磁盘空间没减少。(当然不是)

思路稍微开阔一点的候选人会想到:你个憨批莫不是删了个软链接吧?

当然候选人的语气会比较友好。

然后我会和候选人继续沟通:

你提到了软链接,那它和硬链接的区别是什么呢?

有时候我怀疑这几个连续的问题问到候选人开始怀疑人生,因为有的候选人有点犹豫,觉得自己想说的其实是硬链接。

不过还是有几个候选人知道,软链接是一种文件类型,其内容是目标文件的路径;硬链接是 inode 的别名,同一个 inode 可以有多个链接,在 inode 里记录了硬链接的数量(引用计数)。

比如这样:

创建一个空文件,看下 inode 和链接数:

$ touch a.txt #创建一个空文件
$ stat -c 'inode %i, links %h' a.txt
inode 12058942, links 1

创建一个软链接,再看看文件大小:

$ ln -s a.txt b.txt #软链接
$ stat -c 'inode %i, links %h' b.txt
inode 12058978, links 1

$ ls -l b.txt #大小 5 字节
lrwxrwxrwx ... 5 ... b.txt -> a.txt

$ readlink b.txt #文件内容
a.txt

创建一个硬链接,看下 inode 和链接数

$ ln a.txt c.txt #硬链接,inode 不变,链接数变成 2
$ stat -c 'inode %i, links %h' c.txt
inode 12058942, links 2

$ ls -l c.txt #大小 0 字节,和 a 一样
lrwxrwxrwx ... 0 ... c.txt

4

但实际生产上,遇到 “删了 log 文件、但空间不释放” 通常和软 /硬链接没有什么关系。

实战经验比较丰富的候选人会知道,这往往是因为 log 文件正被另一个进程打开。

比如在终端 1 打开 a.txt:

$ python
>>> f = open("a.txt")

然后在终端 2 可以看到该文件被 Python 打开:

$ lsof a.txt
COMMAND  PID ...     NODE NAME
python  2390 ... 12058942 a.txt

删掉 a.txt ,再查看 python 打开的文件列表:

$ rm a.txt
$ ls -l /proc/2390/fd
lrwx------ 1 user ... 00:04 0 -> /dev/pts/5
lrwx------ 1 user ... 00:04 1 -> /dev/pts/5
lrwx------ 1 user ... 00:04 2 -> /dev/pts/5
lr-x------ 1 user ... 00:04 3 -> /tmp/a.txt (deleted)

注:0 、1 、2 、3 是内核的 fd 编号。0=stdin, 1=stdout, 2=stder 。

可以看到,a.txt  被标记为已删除,但因为进程还开着它,可能会访问文件的内容,所以内核会等到进程关闭该文件(或进程退出后)才在磁盘上移除这个文件。

5

在面试中通常没有机会再问下去了,但实践中往往问题还没解决。

比如前述共享开发机,就曾遇到了磁盘空间共 800G,但用 du 命令查看,所有文件只占用了 500G 的情况。

那么:

1. 如何才能知道现在系统中有哪些文件已删除、但是仍被占用呢?

$ sudo lsof | grep deleted
COMMAND   PID  …  NAME
main   893246  …  /../nohup.out (deleted)
...

发现是有大量已经被删除、但仍被某些进程打开的 nohup.out 。

2. 坑是找到了,该怎么填呢?

由于这是开发机,很简单,把进程杀掉就好了,进程退出时,内核会负责关闭文件,然后清理占用的空间。

但如果是线上服务呢?

Linux 下有一个 package 叫 logrotate,像 nginx 这些服务就是使用它来做日志切割 /轮转的。

但 nginx 是在后台持续运行的,不能为了切个日志就停止服务,所以它们是这样约定的:

  1. logrotate 执行 rename 系统调用(相当于 mv 命令)重命名日志文件;
  2. 由于 inode 不变,这时 nginx 会继续写入重命名后的日志文件;
  3. logrotate 给 nginx 发送一个 SIGHUP 信号;
  4. nginx 在收到信号后会关闭当前文件,并重新打开日志文件(即创建了新的日志文件)。

注:为什么是用 SIGHUP 而不是其他信号,以后可能会另开一篇讲讲。

这样 logrotate 出来的日志,就可以放心删除了。

对于不支持类似逻辑的服务怎么办呢?

重启大法。

如果不怕背 P0 的话,还可以这么作死:

$ sudo gdb
(gdb) attach $PID
(gdb) call ftruncate(3, 0) #按需修改 fd
$1 = 0

注:看起来文件是清空了,但可能存在其他坑,后果自负。ftruncate 只是清空文件,如果想关闭文件,可以结合 dup 、dup2 、open 和 close 来搞事,不细说了。

6

看到这里你应该明白了为什么前面那个 find 命令不直接用 rm 了吧?

照例总结下:

  1. 可以用 find 查找文件
  2. 软链接存的是路径,硬链接共享 inode
  3. 删除被进程打开的文件,磁盘空间不会释放
  4. lsof 很好用(不只是看文件的占用)

还想知道其他有意思的面试题吗?

不如投个简历来亲身体验下:

~ 投递链接 ~

投放研发工程师(上海)

https://job.toutiao.com/s/J8DRDyG

高级广告研发工程师(北京)

https://job.toutiao.com/s/J8DNwJY

欢迎关注

   ▄▄▄▄▄▄▄   ▄      ▄▄▄▄ ▄▄▄▄▄▄▄  
   █ ▄▄▄ █ ▄▀ ▄ ▀██▄ ▀█▄ █ ▄▄▄ █  
   █ ███ █  █  █  █▀▀▀█▀ █ ███ █  
   █▄▄▄▄▄█ ▄ █▀█ █▀█ ▄▀█ █▄▄▄▄▄█  
   ▄▄▄ ▄▄▄▄█  ▀▄█▀▀▀█ ▄█▄▄   ▄    
   ▄█▄▄▄▄▄▀▄▀▄██   ▀ ▄  █▀▄▄▀▄▄█  
   █ █▀▄▀▄▄▀▀█▄▀█▄▀█████▀█▀▀█ █▄  
    ▀▀  █▄██▄█▀  █ ▀█▀ ▀█▀ ▄▀▀▄█  
   █▀ ▀ ▄▄▄▄▄▄▀▄██  █ ▄████▀▀ █▄  
   ▄▀▄▄▄ ▄ ▀▀▄████▀█▀  ▀ █▄▄▄▀▄█  
   ▄▀▀██▄▄  █▀▄▀█▀▀ █▀ ▄▄▄██▀ ▀   
   ▄▄▄▄▄▄▄ █ █▀ ▀▀   ▄██ ▄ █▄▀██  
   █ ▄▄▄ █ █▄ ▀▄▀ ▀██  █▄▄▄█▄  ▀  
   █ ███ █ ▄ ███▀▀▀█▄ █▀▄ ██▄ ▀█  
   █▄▄▄▄▄█ ██ ▄█▀█  █ ▀██▄▄▄  █▄  
15267 次点击
所在节点    推广
180 条回复
vk42
2020-07-04 13:00:25 +08:00
最后那个,废那么大劲上 gdb 干嘛,直接 echo > name.log 就行了……
felix021
2020-07-04 13:02:10 +08:00
@vk42 name.log 已经被删了呀,你试试看[doge]
imdong
2020-07-04 13:02:25 +08:00
曾经自己负责面试,发现自己有时会落入这样的思维:

5 * 6 = 30,提问:30 等于什么?

而往往面试者的回答让你无法满意。
Cooky
2020-07-04 13:02:38 +08:00
讲了半天废话,lsof 自己去看
webassembly
2020-07-04 13:03:17 +08:00
写的很棒。 但是不好意思,我题还没刷够,不敢投。
felix021
2020-07-04 13:04:22 +08:00
@imdong 嗯,知识的诅咒
felix021
2020-07-04 13:04:43 +08:00
@imdong 想起这个实验

1990 年,美国斯坦福大学研究生伊丽莎白·牛顿( Elizabeth Newton )因一项研究而获得心理学博士学位。他研究的是一个简单的游戏,受试者分别扮演 “击节者” 和 “听猜者” 两类角色。击节者拿到一份列有 25 首著名曲目的清单,包括《祝你生日快乐》和美国国歌《星条旗永不落》等耳熟能祥的歌曲。每位击节者选定一首,然后在桌上把曲子的节奏敲给听猜者听。听猜者的任务是根据敲击的节奏猜出正确的歌名。 这个游戏里听猜者的工作相当不轻松。在牛顿的实验过程中,击节者一共击打了 120 首歌,听猜者只猜对了 3 首,成功率 2.5%。
felix021
2020-07-04 13:11:21 +08:00
@Cooky 如果你能跳出喷子的思维方式的话,就会发现这篇文章还介绍针对面试官的面试技巧、一些冷门的知识点、以及在写作时如何调动读者的情绪。
vk42
2020-07-04 13:11:38 +08:00
@felix021 我的意思是对已知占用的文件一般都习惯用 echo 清空,如果已经不小心删了,可以从 procfs 里面直接 echo 对应 fd
felix021
2020-07-04 13:15:19 +08:00
@vk42 赞,试了下 echo > /proc/$PID/fd/$fd 确实可以;不过随着时间累积这个文件还是会继续增长,依然让人蛋疼。
darksword21
2020-07-04 13:15:26 +08:00
面试者:hashmap 在 1.8 与 1.7 不一样,是用红黑树实现的
面试官:哦,那你简单实现下红黑树吧🐕️
( ps:就是说以下,我不是搞 java 的)
felix021
2020-07-04 13:16:39 +08:00
@darksword21 哈哈哈,我们内部面试培训的时候专门喷过这个事情,不要让候选人手撕红黑树,我最多就问下这个数据结构的特性。
wangking
2020-07-04 13:18:32 +08:00
1 楼的方法可能更好一些, 之前遇到过 rm 日志服务报错的 ,感觉 lz 这个面试题,还是侧重一些有工作经验的人员。
felix021
2020-07-04 13:23:23 +08:00
@wangking 这是一套有区分度的题目,用来考察候选人掌握到什么程度,再往深也还可以继续问,文件系统缓存之类的。
matrix67
2020-07-04 13:24:51 +08:00
我们有一台共享开发机,因为大家都懒得删 log,经常磁盘爆满,后来我们给它加了个 crontab:

0 4 * * * find /home/ -type f -name "*.log*" -size +100M -exec bash -c "echo -n > '{}'" \;

开发机这么做也太暴力了,我感觉干掉.1 .2 的日志还行,你把当天的也干掉了,有问题也查不了了吧。
momocraft
2020-07-04 13:35:41 +08:00
find -delete
felix021
2020-07-04 13:37:34 +08:00
@matrix67 嗯,如果加上 mtime 会更好,不过是凌晨 4 点清日志,目前没人抱怨。另外我们其实有磁盘足够大的开发机,但很多人懒得迁,如果不爽的话正好能有点动力迁。
felix021
2020-07-04 13:45:20 +08:00
@momocraft 赞 我之前还没注意到 find 有这么个参数
CRVV
2020-07-04 13:51:25 +08:00
https://www.v2ex.com/t/655096

原来是同一个人发的,怪不得又是这种奇怪的面试题。我再来喷一次。

1. 在 Linux 下,如何删除一个目录下的所有 log 文件?

你需要给定什么叫做 log 文件,如果是文件名以 log 结尾的文件,那当然是 rm *log
删子目录是 rm **/*log

日志文件是日志文件,文件名随便是什么都可以;文件名以 log 结尾的文件是文件名以 log 结尾的文件;文件名以 .log 结尾的文件是文件名以 .log 结尾的文件,这都不一样的。真不知道你的 “log 文件” 到底是啥。

2. find 是一个不常用的命令,原因是这个命令的功能通常能被 shell 的 * 或者 ** 代替。
另外 find 功能很强,用法也很复杂,有几十个参数,参数之间有逻辑关系而且还可以套别的命令,如果不是有很特殊的需求,不会来用 find 。而且 GNU find 和 BSD find 的用法还不完全一样。
你给出的那一行 find 命令是错的,少了一个 .
find . -name \*.log -exec rm -f {} \;
这一行也可以写成
IFS=$'\n'; for f in **/*.log; do rm "$f"; done
这个可能是更常见的用法

3. 删了文件磁盘空间不释放这很正常,还有可能是 btrfs 或者 zfs 上给 dedup 了。
有这么多种可能性,我不知道为什么来面试的人就必须要想到是文件还在被占用。

4. 把日志文件删掉了,因为那个文件还在被使用,所以磁盘空间没释放。
我就想问一句,贵司真是这么删日志的么?
正在被打开的日志文件,意思是还有进程在往里面写日志,我想不到有正常的运维会删一个正在被写入的日志文件。
在我的认知里,删日志都是删若干时间之前,肯定不会再用到的老日志。这全新的还在写的日志文件你这么都随便地删掉了,那当初为啥要记这个日志呢?
能被这种神操作理直气壮地出到面试题里面,贵司确实蛮厉害的。
felix021
2020-07-04 13:59:46 +08:00
@CRVV
1. 面试的时候可以多沟通,这也是考查的一部分
2. 在 mac 下跑会错,但我问的是在 Linux 下,请实践一下再喷。
3. 我没有说必须要想到这一点,如果能给出其他答案我也会很愿意沟通。
4. 你开心就好,我没打算写出让所有人都喜欢的文章。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/687093

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX