万爷遇到的一个小问题

现象

发部系统发布 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 查看所有相关进程,也没有敏感信息直接截图了

ps axjf

可以看到组 gid 都是 16291,但是 mysqld_safe 的父进程己经是 1 了,被系统接管。但是 deploy.py 的 shell 子进程 20020 却是处于 Z 状态,也就是僵尸进程。kill 跟本杀不掉,只能通过杀父进程的方式来回收僵尸进程。

分析

分别通过 lsof -p 查看 deploy.py 进程和 mysql 进程,看看打开发哪些文件


deploy.py 进程 lsof
mysql 进程 lsof

可以看到 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 选项... 就说么,这么牛逼的坑肯定早有人踩过...


Popen

你可能感兴趣的:(万爷遇到的一个小问题)