Python 踩坑之旅進程篇其三pgid是個什麼鬼 (子進程\子孫進程無法kill 退出的解法)

目錄

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

1.1 踩坑案例

pid, ppid是大家比較常見的術語, 代表進程號,父進程號. 但pgid是個什麼鬼?

了解pgid之前, 我們先複習下:

  • 進程篇其一
    • 裏面場景是: 一個進程通過os.system或者Popen家族啟動子進程
    • 后通過殺死父進程的方式無法殺死它的連帶子進程
    • 我們通過其他方式進行了解決

這個場景還有個後續就是:

  • 如果這個子進程還有孫子怎麼辦?
  • 它還有孫子的孫子怎麼辦?

這個就是今天我們遇到的坑, 怎麼處理孫子進程. 大家注意, 不僅是Python會遇到這個問題, 其他語言包括 Shell 都一樣會遇到這種”孫子”進程怎麼進程異常處理的問題.

1.2 填坑解法

本期的坑位解法其實有兩種, 第一種比較暴力, 簡稱窮盡搜索孫子法.

a. 窮盡搜索孫子法, 代碼示例

關鍵點:

  • 使用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, 注意不是pid

# 以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的坑坑窪窪
点赞

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *