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

欢迎关注

   ▄▄▄▄▄▄▄   ▄      ▄▄▄▄ ▄▄▄▄▄▄▄  
   █ ▄▄▄ █ ▄▀ ▄ ▀██▄ ▀█▄ █ ▄▄▄ █  
   █ ███ █  █  █  █▀▀▀█▀ █ ███ █  
   █▄▄▄▄▄█ ▄ █▀█ █▀█ ▄▀█ █▄▄▄▄▄█  
   ▄▄▄ ▄▄▄▄█  ▀▄█▀▀▀█ ▄█▄▄   ▄    
   ▄█▄▄▄▄▄▀▄▀▄██   ▀ ▄  █▀▄▄▀▄▄█  
   █ █▀▄▀▄▄▀▀█▄▀█▄▀█████▀█▀▀█ █▄  
    ▀▀  █▄██▄█▀  █ ▀█▀ ▀█▀ ▄▀▀▄█  
   █▀ ▀ ▄▄▄▄▄▄▀▄██  █ ▄████▀▀ █▄  
   ▄▀▄▄▄ ▄ ▀▀▄████▀█▀  ▀ █▄▄▄▀▄█  
   ▄▀▀██▄▄  █▀▄▀█▀▀ █▀ ▄▄▄██▀ ▀   
   ▄▄▄▄▄▄▄ █ █▀ ▀▀   ▄██ ▄ █▄▀██  
   █ ▄▄▄ █ █▄ ▀▄▀ ▀██  █▄▄▄█▄  ▀  
   █ ███ █ ▄ ███▀▀▀█▄ █▀▄ ██▄ ▀█  
   █▄▄▄▄▄█ ██ ▄█▀█  █ ▀██▄▄▄  █▄  
16897 次点击
所在节点    推广
180 条回复
ChanKc
2020-07-05 20:08:06 +08:00
@newtype0092

我不介意和这样的人做同事,当然还要看具体的情况

> 这个意义不大,实际场景用不到

如果知道为什么“这个意义不大”,是很有用的。比如 JavaScript 的变量提升。如果不是最近准备面试我根本不会想到这个东西。我所有的变量都是手动提升的。这样对代码阅读来说也会是更加清晰。有些面试题真的很蠢,甚至是在变相鼓励一些程序员在实际工作中去写一些看似酷炫实际上很难维护的代码。有些“知识”确实是“没有用”的。你生而为人不需要知道屎是什么味道就应该避免去吃屎。

> 这个不用自己写,调一下库就行

工作中我最怕同事在明明有库可以调的时候,偏要自己写。熟悉库并且合理运用库是很重要的技能。而且这个世界本来就是分工合作的,有的人会写库有的人写 test 。有时候一些面试题就好比“你吃过肯德基?讲讲原味鸡是怎么做的?”。如果我原味鸡一直吃得好好的为什么我要知道怎么做?

> 这个我没用过,用的时候百度一下就知道了

我很讨厌百度出来的答案。头几条出来的全是 CSDN,一些不知道什么样的人把同一个问题反复 copy paste 出来的所谓博客。如果对于没有用过的东西,能马上找到准确的答案,比如相关的 spec 或者 doc,我觉得也是不错的能力。

被人这么三板斧的时候为什么不反思一下你的问题,是不是“屎是什么味道”,还是“原味鸡怎么做”?
yanqiyu
2020-07-05 22:08:15 +08:00
要我 NAS 直接暴力 rm log 文件大概就不会再有日志了( SELinux 上下文问题)

journalctl 它不香吗
yanqiyu
2020-07-05 22:27:26 +08:00
其实我要是遇到删除文件之后空间没释放第一时间会怀疑是我的 btrfs 在搞事情(因为我遇到过)

然后删除正在使用的文件并且不释放空间的事情...我没遇到过这种事情,所以自然想不到(因为我不是运维,且从来没遇到过删除正在被使用的文件的事情)
no1xsyzy
2020-07-05 23:17:45 +08:00
既然没有 log rotate 为什么要考虑 log rotate 而 -name '*.log*'?
*.log* 还是比较危险的,我扫了一圈 arch 的全盘,发现 '*.log.*' 会更好

@mingl0280 #101 不加 -L 是不会的。
felix021
2020-07-05 23:30:33 +08:00
@no1xsyzy
1. 很多服务的日志库会自动 logrotate,但是开发机上自己启动的服务,之前没有机制来清理这些 log
2. 是偷懒了,因为这么写的话得分成 "*.log" 和 "*.log*" 写 2 条
eallion
2020-07-06 00:47:21 +08:00
我 2 岁的儿子可以拿着点读笔不停地问我一天关于点读笔的问题。
uuspider
2020-07-06 07:20:51 +08:00
为啥要删.log ?日志是为了追溯,压缩起来备份不是更好吗?这东西能占多少空间?
newtype0092
2020-07-06 10:37:01 +08:00
@ChanKc 面试的时候这么回答的人,面对大多数问题基本都这套路。

还有,你去面试人家让你写快排你就写个 qsort()?
ahbcd112
2020-07-06 10:51:31 +08:00
说了两件事
1. 批量删除文件
2. 删除的文件如果被其他进程打开,文件只会进程解除锁定时,才实际删除文件
3. LINUX 下面的几个命令组合。
貌似都很基础~~~是给初中级人学的么?
ChanKc
2020-07-06 11:05:05 +08:00
@newtype0092 这不就是“原味鸡怎么做”吗
ChanKc
2020-07-06 11:17:39 +08:00
@newtype0092 我想说的是,工作更关心的应该是解决工作上的问题。问快排更像是一个数据结构与算法的本科生考试。工作上更关心的就应该是合理地选择技术方案去解决问题。说到排序,你可以问一个类似于 k 最值的问题(比如说我要分页显示一些数据,每一页相对前一页内容是有序的,但是页面内可以无序),这个就涉及到很多方面的内容了,而且也是有可能工作中遇到的。这个不是比“写个快排”好很多?
newtype0092
2020-07-06 11:27:51 +08:00
@ChanKc
你这个比喻一点也不合适。

好吧,我是觉得这种知其然不知其所以然的做法干不长久,工作经历长了以后总有“标准”做法不合适的情况,对原理一无所知的人也就止步于此了吧。
ChanKc
2020-07-06 11:42:53 +08:00
@newtype0092 一个合格的工程师应该是第一时间想到性价比最高的做法。调库的人可能知道原理也可能不知道原理。库的存在就是为了不知道原理的人在暂时不知道的情况下也出色地完成任务。

我这是觉得有时候一些面试问题问得很死板,包括但不限于“写个排序”。如果我调库,是不是显得我不懂?如果我直接写出底层的算法,我是不是工程师素养不够?会不会怀疑我是背的?
ChanKc
2020-07-06 12:14:58 +08:00
@newtype0092 说回快速排序。大部分的面试人应该都没办法写出比调库更快的排序算法。大部分人应该都不会写到 double pivot 。但是如果你让他面试通过了,你就是潜在地鼓励他在工作中去使用自己写的排序方法,然后导致你的工程效率下降。我看过很多这样的问题了。v2ex 上面时不时就有各种根本比不上 guava 和 Apache commons 的 Java“工具类包”,本质上就是这样的面试导向的结果。

不懂原理在大部分时候都不是问题。就排序这个问题而言,现在 JDK 用的是 TimSort,93 年才发表的论文上的。你可能每个项目都在用 JDK 的 sort 但你知道 TimSort 吗
6ufq0VLZn0DDkL80
2020-07-06 12:32:42 +08:00
广告工程师面试为啥要问这些东西??
6ufq0VLZn0DDkL80
2020-07-06 12:33:27 +08:00
感觉是来反串黑我条的。。。
newtype0092
2020-07-06 14:32:40 +08:00
@ChanKc 为什么我面试的时候让人写一个算法,就是鼓励他在工作中自己去实现啊?你这个逻辑完全没因果关系啊。
对于简单的算法最快的考察方法不就是让他手写一个么?如果是大型的数据结构、复杂的算法,肯定只要讲下核心部分或者写下核心伪代码啊。

我上面说回答面试问题的时候不应该用这些敷衍的方式,你搬出各种理论来证明:
敷衍是你题出的不行
敷衍的人工作的时候不一定会敷衍
面对死板的问题,不用敷衍的态度反而说明能力可能有问题

我实在很难认同。

不专业的面试官太常见了,尤其初面,很多人都是赶鸭子上架,或者低级面试高级,你不能指望每个人提的每个问题都那么完美。

只要是技术方面的问题,哪怕他问的问题不是那么合适,我也会根据我的知识给出尽量切题的解答,因为我知道所谓的面试问题是给我展示自身的技术实力而出的一个方向,而不是积分有奖竞答,我知道调库、百度这种回答并不能把我和刚毕业的实习生区别开来。

在我看来这种回答有两种原因,一种是真的只懂皮毛,一种是觉得问题太简单懒得废话,无论哪种,我还是不愿意和这种人同事。
lfzyx
2020-07-06 22:53:17 +08:00
LZ 说 “因为各种历史问题好几十个人使用同一个开发机,以及一个测试服务随便就可以打几十 GB debug log 的场景”,

-- 那这明显是业务开发的程序设计不合理啊,动不动就写 GB 级别的日志,没意义的日志就注释掉好吗,好吧有的业务开发不会用开发工具的断点功能,但能不能直接输出到控制台呢,非要写文件?
hahaayaoyaoyao
2020-07-07 11:20:54 +08:00
@ChanKc 现在面试都喜欢搞事情,有些资料我查了很久发现只有二手中文才会那么说
xyjincan
2020-07-07 11:38:24 +08:00
一个打开的无缓冲的高速写入日志文件,无法 用 echo 重定向进行清空,没反应

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

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

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

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

© 2021 V2EX