现象
发部系统发布 mysql 任务,deploy.py 部署脚本一直没有返回,直到超时被杀掉。实际上 mysql 早己部署完成了。
部署脚本会调用 mysql.sh shell 脚本去启动 mysql, 由于要放到后台运行,所以在 mysqld_safe 最后加了个 &
cd /home/qboxserver/mysql_pxc_3358/_package/ ;
(./bin/mysqld_safe --defaults-file=/home/qboxserver/mysql_pxc_3358/_package/my.conf --wsrep-new-cluster &)
现场
通过 ps axjf
查看所有相关进程,也没有敏感信息直接截图了
可以看到组 gid 都是 16291,但是 mysqld_safe 的父进程己经是 1 了,被系统接管。但是 deploy.py 的 shell 子进程 20020 却是处于 Z 状态,也就是僵尸进程。kill 跟本杀不掉,只能通过杀父进程的方式来回收僵尸进程。
分析
分别通过 lsof -p 查看 deploy.py 进程和 mysql 进程,看看打开发哪些文件
可以看到 deploy.py 有个文件描术符 8,和 mysql 打开的 13 是同一个文件,inode 都是 50994157,到这问题就很明确了。mysqld 不该继承无用的 fd,这也就是 c 语言中 close on exec 的用处。
解决
知道原因了,解决方案却不好办。deploy.py 通过 os.system 去执行的 mysql.sh 脚本,查看文档 os.system 好像没有类似 close on exec 的参数。并且如果打开的 fd 全关掉,mysql.sh 的输出日志部署系统会看不到。暂时的方案其实很丑:
closefd () {
for fd in $(ls /proc/$$/fd); do
case "$fd" in
*)
eval "exec $fd>&-"
;;
esac
done
}
创建一个函数 closefd,作用就是关闭当前进程打开的所有文件
ls | closefd;cd /home/qboxserver/mysql_pxc_3358/_package/ &&
(./bin/mysqld_safe --defaults-file=/home/qboxserver/mysql_pxc_3358/_package/my.conf --wsrep-new-cluster 1>/tmp/aaaaaaaaaa 2>&1 &)
我们知道 shell 里 |
管理会创建新的进程,那索性 ls |
开个管道,在新的进程里调用 closefd 关闭所有 fd 就能达到目的。
问题到此解决了,却也丑巴拉即的..........
更新20181221
原来 Popen 有 close_fds 选项... 就说么,这么牛逼的坑肯定早有人踩过...