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的坑坑窪窪
相關文章
相關標籤/搜索