一次 pidof 命令卡住导致业务中断引发的设计思考

马谦马谦马谦 程序员评论1,104字数 1373阅读 4 分 34 秒阅读模式

一、问题回顾

问题现象:线上业务,某个进程被卡住了,所有任务都不响应,导致业务中断。

问题原因:程序中调用了 system 命令,执行了一次 pidof 命令,然而作者万万没想到这个 pidof 命令会卡住了,导致整个进程都阻塞了。

排查过程

第一步,确定进程状态,看看进程在干什么:先通过 ps 命令得到进程的 pid,然后执行 strace -p ${pid}挂进去。

执行完 strace 后发现进程一直卡在 wait 调用上,但是因为程序卡住了,没有更多的信息可以输出了,也不知道程序执行到哪了,所以并不能知道为什么会执行 wait 卡住。

于是分析执行 wait 的场景:wait 只会出现在创建了子进程、父进程在等待子进程退出的情况下。会不会是某个子进程卡住了导致父进程阻塞呢?这个是很有可能的,但是反复检查代码,没有发现有创建子进程的地方,并且程序就是单进程设计的,没有主动调用 wait 的场景。所以这一点上被排除了。

那还会是什么原因导致程序卡在 wait 了?会不会是 strace 程序分析有问题导致的?当然这种可能性很小,基本可以排除。

然后又想,程序没有主动创建子进程,会不会是通过其他系统函数间接创建了子进程?很多系统函数都会创建子进程出来,典型的是 system 和 popen 函数,代码中是不是调用这两个函数?这很有可能!

于是,就在代码中搜索 system 和 popen,还真找到了一处调用 system 的地方,但是 system 调用的都是系统命令:

看到命令后想到的第一个问题就是:这个会卡住吗?没听说过 kill 会阻塞,也没听说过 pidof 会阻塞啊。虽然我不太相信真的是它卡住了,但是我还是不自觉的打开了 gdb 。。。使用 gdb -p ${pid}挂进去,直接输入 bt 打印出堆栈调用,映入眼帘的刚好就是这个代码所在的 system 调用。说明,真的就是这个 system 卡住了。

为了确定到底是 kill 卡住了还是 pidof 卡住了,使用 ps 过滤出来所有执行这个命令的进程:

一次pidof命令卡住导致业务中断引发的设计思考

可以看到,所有执行 pid 命令的 pid 是大于 kill 的,说明是 pidof 卡住了才导致 kill 卡住。

再仔细看,系统已经存在多个被卡住的 pidof 命令了,最早的可以追溯到一个月前。难以相信一个 pidof 命令会执行一个月。

本想着再查查为什么 pidof 命令会卡住,但线上业务着急恢复,执行 strace 和 gdb 没看出什么信息后就先恢复了业务。后来本地再也无法重现了,也没有找到 pidof 卡住的真正原因。

解决方案

  1. 去掉了 system 机制,kill 命令直接通过 signal 函数来完成。
  2. 进程启动的时候,保存一份 pid 变量,不再执行 pidof 命令来获取 pid 。

二、总结和思考

  1. 程序中少使用 system 或 popen 调用外部命令。这点是我一直以来都提倡的,排斥使用 system 是因为调用 system 要创建子进程,要屏蔽信号,会有各种不确定的因素在里面,不如系统调用简单、安全。我认为一个好的系统,要尽量减少调用外部命令 (但我们公司的传统就是喜欢各种脚本、命令调来调去,实在令人头疼) 。
  2. 尽可能减少重复的复杂的逻辑。这里的体现就是 pidof 命令,是否有必要每次都通过外部 pidof 命令来获取 pid?现在大部分的程序都是把 pid 保存到一个 pidfile 中,需要用的时候从文件中读取,这种方式来设计是不是会好一些?

  最后更新:2020-3-8
马谦马谦马谦
  • 本文由 马谦马谦马谦 发表于 2019 年 10 月 21 日 17:23:31
  • 转载请务必保留本文链接:https://www.dyxmq.cn/program/pidof-command-stuck-html.html
system和popen返回ENOMEM问题 程序员

system 和 popen 返回 ENOMEM 问题

一、我为什么不喜欢 system 和 popen 要说到我为什么不喜欢 system 和 popen 这两个函数,这个说来就话长了。最开始,我还是很喜欢用这两个函数的,直到后来发现了太多因为滥用导致的程序异常后,它...
匿名

发表评论

匿名网友
:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:
确定

拖动滑块以完成验证