代碼示例支持 |
---|
平台: 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的坑坑窪窪