A-A+

坑:ps 检测进程是否退出

2014年03月08日 Programming 评论 1 条

在bash脚本中经常会遇到这么一个需求:轮询检测某个(些)后台作业的进程是否退出。一旦退出了就继续往下执行后续操作。
这不是什么困难的问题,我脑子里一闪而过的有这么几个方法:
将需要放到后台运行的一系列操作写在一个新脚本中,如this_is_a_test.sh。轮询的时候只需:
  [bash]
  ps -aux | grep this_is_a_test.sh >&/dev/null
  while [ 1 ]; do
  if [ $? -ne 0 ]; then
  break
  else
  sleep sometime
  fi
  done
  blablabla ...
  [/bash]
方法1的缺点很明显,就是需要新建脚本,多一个文件就多一个维护,何况Linux程序员都是有洁癖的,秉承了KISS原则的我们能不能把问题再简单化一些?
  [bash]
  nohup blablabla && touch blablabla_finish.flag &
  While [ 1 ]; do
  if [ -f blablabla_finish.flag ]; then
  break
  else
  sleep sometime
  fi
  done
  [/bash]
Ok,方法2终于不用维护额外的脚本了,轮询一个(批)标志文件是否生成,且也不算太麻烦。虽算不上优雅,但是脚本嘛,就是干苦逼活的,搞定问题就行。可是,本人就在实际工作中,画蛇添足了一把,给自己的代码留下了隐患。请看如下代码:
  [bash]
  # recovery from archives of mysqldump’s output.
  zcat harry_db.harry_table.gz | mysql -uroot -Sxxx.sock harry_db
  
  while [ 1 ]; then
  # test whether the recovery is accomplished.
  ps aux | grep mysql >&/dev/null
  if [ $? -ne 0 ]; then
  break
  fi
  sleep sometime
  done
  [/bash]
  这个判断的问题在于,ps aux会将所有用户和终端的进程都列出来,于是便无法区分mysql、mysqld、mysqld_safe。如果机器上正好存在一个mysqld守护进程,轮询则永远不能退出。
  改进一下:
  [bash]
  while [ 1 ]; then
  # test whether the recovery is accomplished.
  ps aux | grep mysql$ >&/dev/null
  if [ $? -ne 0 ]; then
  break
  fi
  sleep sometime
  done
  [/bash]
  Ok,这样总算把mysqld/mysqld_safe这样的进程给排除了,但问题依然存在。试想一下,如果运行脚本的机器上到了半夜正好其他同学开着mysql客户端程序对数据库进行查询,结果那个mysql客户端还就没关闭一直连着,那这个轮询依然会失效,至少不能立刻生效。
  那再粗暴一些,我把整个命令都作为grep的对象总行了吧:
  [bash]ps aux | grep “mysql -uroot -Sxxx.sock harry_db”[/bash]
  看看会输出什么?是不是总是可以看到grep这个进程本身的信息,而不论是否真实存在进程”mysql -uroot -Sxxx.sock harry_db”。
  [bash]
  ....
  grep mysql -uroot -Sxxx.sock
  [/bash]
  这样一来,$?得到的值始终是0。
  
  再改进一下,
  [bash]
  ps aux | grep "mysql -uroot -Sxxx.sock harry_db" | grep -v "grep"
  [/bash]
  这样修改后总算是可以正常运作了,是不是很累赘?没事,还是那句话,脚本嘛,能work就行。
  
  不知道有没有小伙伴尝试用过不加任何选项的ps命令?即
  [bash]ps | grep COMMAND[/bash]
  ps默认是不会把COMMAND的参数输出的,比如进程启动时是:mysql -uroot -Sxxx.sock harry_db,在ps命令中显示的仅仅是mysql这个进程名。
  再回到ps aux | grep mysql$ 这段代码,通常我们在终端回车一个nohup ... & 命令后,ps一下,可以看到隶属这个终端的进程,比如有sh,ps本身,刚刚启动不久运行在后台的其他进程等。那么是不是意味着ps | grep mysql就可以以非常优雅的方式完美解决这个问题呢?因为当前这个终端只有mysql,mysqld和当前终端没有任何关联,属于daemon类型的东西,不带选项的ps命令是不会输出它的。
  答案是否定的。原因很简单,当前的终端一旦退出(失效)后,正在运行的进程也即不再和任何终端有关联。ps一下,居然可以看到很多在TTY列是”?”的进程,这其中就包含了mysqld。关于为什么终端退出(失效),原因有很多,比如程序员自己关闭了,或者是电脑重启了,或者是terminal程序自动断线等(比如我们的开发机长时间不动就断了)。

  以上所述,是我在实现一个恢复mysql备份中遇到的坑。用文件轮询的方式看似很不优雅,但却相对保险,能保证脚本流程如预期运行;ps|grep xxx的方式,表面看似简单直接,其实没有充分的运行测试,是留有隐患的。

原创文章,转载请注明: 转载自腾讯游戏DBA团队

本文链接地址: 坑:ps 检测进程是否退出

文章的脚注信息由WordPress的wp-posturl插件自动生成

标签:
  1. 这么麻烦? 记录子进程好,然后 周期性的kill -0 进程号 就好了. $cmd &pid= $! while kill -s 0 $pid >/dev/nullblablabla.donewait $pid 丢, 我竟然打了一个人例子出来.

Copyright © 腾讯游戏DBA团队 保留所有权利.  

用户登录

分享到: