想了十天十夜不知道写些什么,那就写写面试题吧。
在面试应聘者的时候,我常常会问:
在 Linux 下,如何删除一个目录下的所有 log 文件?
不知道是不是我人畜无害的围笑给了应聘者我很好应付的错觉
以至于应聘者全都回答:rm *.log
追问:该目录下可能有很多子目录,如何把子目录里的 log 文件也删掉呢?
答:rm -r *.log
令我很意外的是,真的只有很少的应聘者能想到 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,而是使用重定向来清空文件,原因后面会讲。
基于清理磁盘空间这个场景,我还会继续问:
你有遇到过删了 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
但实际生产上,遇到 “删了 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 被标记为已删除,但因为进程还开着它,可能会访问文件的内容,所以内核会等到进程关闭该文件(或进程退出后)才在磁盘上移除这个文件。
在面试中通常没有机会再问下去了,但实践中往往问题还没解决。
比如前述共享开发机,就曾遇到了磁盘空间共 800G,但用 du 命令查看,所有文件只占用了 500G 的情况。
那么:
1. 如何才能知道现在系统中有哪些文件已删除、但是仍被占用呢?
$ sudo lsof | grep deleted
COMMAND PID … NAME
main 893246 … /../nohup.out (deleted)
...
发现是有大量已经被删除、但仍被某些进程打开的 nohup.out 。
2. 坑是找到了,该怎么填呢?
由于这是开发机,很简单,把进程杀掉就好了,进程退出时,内核会负责关闭文件,然后清理占用的空间。
但如果是线上服务呢?
Linux 下有一个 package 叫 logrotate,像 nginx 这些服务就是使用它来做日志切割 /轮转的。
但 nginx 是在后台持续运行的,不能为了切个日志就停止服务,所以它们是这样约定的:
注:为什么是用 SIGHUP 而不是其他信号,以后可能会另开一篇讲讲。
这样 logrotate 出来的日志,就可以放心删除了。
对于不支持类似逻辑的服务怎么办呢?
重启大法。
如果不怕背 P0 的话,还可以这么作死:
$ sudo gdb
(gdb) attach $PID
(gdb) call ftruncate(3, 0) #按需修改 fd
$1 = 0
注:看起来文件是清空了,但可能存在其他坑,后果自负。ftruncate 只是清空文件,如果想关闭文件,可以结合 dup 、dup2 、open 和 close 来搞事,不细说了。
看到这里你应该明白了为什么前面那个 find 命令不直接用 rm 了吧?
照例总结下:
还想知道其他有意思的面试题吗?
不如投个简历来亲身体验下:
~ 投递链接 ~
投放研发工程师(上海)
https://job.toutiao.com/s/J8DRDyG
高级广告研发工程师(北京)
https://job.toutiao.com/s/J8DNwJY
▄▄▄▄▄▄▄ ▄ ▄▄▄▄ ▄▄▄▄▄▄▄
█ ▄▄▄ █ ▄▀ ▄ ▀██▄ ▀█▄ █ ▄▄▄ █
█ ███ █ █ █ █▀▀▀█▀ █ ███ █
█▄▄▄▄▄█ ▄ █▀█ █▀█ ▄▀█ █▄▄▄▄▄█
▄▄▄ ▄▄▄▄█ ▀▄█▀▀▀█ ▄█▄▄ ▄
▄█▄▄▄▄▄▀▄▀▄██ ▀ ▄ █▀▄▄▀▄▄█
█ █▀▄▀▄▄▀▀█▄▀█▄▀█████▀█▀▀█ █▄
▀▀ █▄██▄█▀ █ ▀█▀ ▀█▀ ▄▀▀▄█
█▀ ▀ ▄▄▄▄▄▄▀▄██ █ ▄████▀▀ █▄
▄▀▄▄▄ ▄ ▀▀▄████▀█▀ ▀ █▄▄▄▀▄█
▄▀▀██▄▄ █▀▄▀█▀▀ █▀ ▄▄▄██▀ ▀
▄▄▄▄▄▄▄ █ █▀ ▀▀ ▄██ ▄ █▄▀██
█ ▄▄▄ █ █▄ ▀▄▀ ▀██ █▄▄▄█▄ ▀
█ ███ █ ▄ ███▀▀▀█▄ █▀▄ ██▄ ▀█
█▄▄▄▄▄█ ██ ▄█▀█ █ ▀██▄▄▄ █▄
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.