Python 踩坑之旅进程篇其三pgid是个什么鬼 (子进程\子孙进程没法kill 退出的解法)

代码示例支持
平台: Centos 6.3
Python: 2.7.14
Github: https://github.com/baidu/CUP

1.1 踩坑案例

pid, ppid是你们比较常见的术语, 表明进程号,父进程号. 但pgid是个什么鬼?python

了解pgid以前, 咱们先复习下:mysql

  • 进程篇其一
    • 里面场景是: 一个进程经过os.system或者Popen家族启动子进程
    • 后经过杀死父进程的方式没法杀死它的连带子进程
    • 咱们经过其余方式进行了解决

这个场景还有个后续就是:linux

  • 若是这个子进程还有孙子怎么办?
  • 它还有孙子的孙子怎么办?

这个就是今天咱们遇到的坑, 怎么处理孙子进程. 你们注意, 不只是Python会遇到这个问题, 其余语言包括 Shell 都同样会遇到这种"孙子"进程怎么进程异常处理的问题.git

1.2 填坑解法

本期的坑位解法其实有两种, 第一种比较暴力, 简称穷尽搜索孙子法.github

a. 穷尽搜索孙子法, 代码示例sql

关键点:shell

  • 使用cup.res.linux中的Process类, 得到该进程全部的子孙进程
  • 使用kill方法所有杀死
from cup.res import linux
pstatus = linux.Process(pid)
for child in pstatus.children(recursive=True):
    os.kill(child, signal.SIGKILL)

b. 得到该进程的 PGID, 进行 kill 操做编程

b1. 先讲个 shell 操做的作法, 使用ps 获取进程的pgid, 注意不是pidbash

# 以mysqld为例, 注意 pgid 项
ps -e -o uid,pid,gid,pgid,cmd|grep mysql

结果:

  • 注意其中第三列, 该进程和子进程都使用了一样的pgid 9779

    9790 0 9779 /bin/sh /usr/bin/mysqld_safe --datadir=/home/maguannan/mysql/mysql/....

    10171 501 9779 /home/maguannan/bin/mysqld --basedir=/home/maguannan/mysql/....

  • 经过kill -9 -9779的方式能够杀死该pgid底下的全部子孙进程

b2. 在讲 Python 里的处理方式

import os
import signal
from cup.res import linux
pstatus = linux.Process(pid)
os.killpg(pstatus.getpgid(), signal.SIGKILL)

1.3 坑位分析

进程组特性

a. 在*unix 编程中, 进程组(man getpgid)概念是个很重要但容易被忽略的内容

  • 进程组ID (pgid) 标记了一系列相关的进程
  • 进程组有一个组长进程, 通常组长进程 ID 等于进程组 ID
  • 进程组只要任一进程存在, 进程组就存在. 进程组存在与否与组长死活无关
  • 能够经过setpgid方式设置一个进程 pgid
    • 一个进程只能为本身或者子进程设置进程组 id
    • 子进程一旦执行了exec函数, 它就不能改变子进程的进程组 id

b. 进程组内的全部成员会收到来自相同的信号

引用 wikipedia 原文:

a process group is used to control the distribution of a signal; when a signal is directed to a process group, the signal is delivered to each process that is a member of the group.

坑位解决

  • 因为进程组拥有以上的特性, 进程组内的进程能够被当作相同的处理单元
    • 默认子进程与父进程拥有一样的进程组
    • 组内每一个进程收到相同的信号)
  • 使用kill发送信号 SIGKILL 便可知足杀死全部子进程的目的

1.4.1 技术关键字

  • pgid 进程组
  • pid, ppid 进程ID, 父进程ID

下期坑位预告

  • 踩坑之旅进程篇其四: 一次性踩透uid, euid, gid, egid的坑坑洼洼
相关文章
相关标签/搜索