subprocess调度实验

202 阅读2分钟

主进程功能

  1. 启动 3 个子进程
  2. 采集子进程所有输出
  3. 检测 子进程状态 ,某个子进程状态变化时,打印 <子程pid> stat:
  4. 子进程全部退出时,退出主进程

子进程功能

  1. 存活期间定时输出 ,10s 输出一次 sleep module: eventlet.greenthread
  2. 当被非强制 终止时,嘲讽一下,"i do not want be kill ! you can not kill me ! hahaha !\n"
  3. 被强制终止时,退出

使用方法

在另外一个终端中操控子进程

# 查看 所有 存活 子进程信息
ps -aux|grep capture_process |grep -v grep
root      97475  0.1  0.1 190000  9940 pts/0    S+   00:29   0:00 python ./capture_process.py 2>&1
root      97476  0.1  0.1 190000  9940 pts/0    S+   00:29   0:00 python ./capture_process.py 2>&1

# 发送 非强制性 终止信号
ps -aux|grep capture_process | grep -v grep|awk '{print $2}'|xargs kill


# 发送 强制性 终止信号
ps -aux|grep capture_process | grep -v grep|awk '{print $2}'|xargs kill -9

# 根据查询到的 信息,的pid 控制其中一个
## 非强制
kill 97475
## 强制
kill -9 [pid]

简单测试

cd 到项目目录, 
pip 安装好 eventlet

# 启动主进程
# 主进程可以被正常 crtl c
python proc_manager.py 

打开另外一个终端 输入
ps -aux|grep capture_ | grep -v grep|awk '{print $2}'|xargs kill

发现主进程终端会显示

i do not want be kill ! you can not kill me ! hahaha !
i do not want be kill ! you can not kill me ! hahaha !
i do not want be kill ! you can not kill me ! hahaha !


输入
ps -aux|grep capture_ | grep -v grep|awk '{print $2}'|xargs kill -9
发现主进程终端 
退出



 
[root@localhost ~]# ps -aux|grep capture_ | grep -v grep|awk '{print $2}'|xargs kill -9
[root@localhost ~]#

实验观察

根据 操控子进程提示,退出一个 子进程观察 主进程输出 再退出 1 个, 再退出 1 个

capture_process.py

#!/usr/bin/python
# -*- encoding: utf-8 -*-
'''
@File    :   capture_process.py
@Time    :   2021/01/29 09:31:18
@Author  :   lmk
@Version :   1.0
@Contact :   lmk@zettakit.com
@Purpose    :   mock one process can be terminate 
'''

# import signal
 
# signal.SIGABORT
# signal.SIGHUP  # 连接挂断
# signal.SIGILL  # 非法指令
# signal.SIGINT  # 连接中断
# signal.SIGKILL # 终止进程(此信号不能被捕获或忽略)
# signal.SIGQUIT # 终端退出
# signal.SIGTERM # 终止
# signal.SIGALRM  # 超时警告
# signal.SIGCONT  # 继续执行暂停进程

import signal,time
import sys
import eventlet
eventlet.monkey_patch()
from eventlet import sleep

def term_sig_handler(signum,frame):
    std_print("i do not want be kill ! you can not kill me ! hahaha !\n")

def std_print(*args):
    s = " ".join(args)+"\n"
    sys.stdout.write(s)
    sys.stdout.flush()


#  ps -aux|grep capture_process | grep -v grep|awk '{print $2}'|xargs kill -9
# ps -aux|grep capture_process | grep -v grep
if __name__ == "__main__":
    signal.signal(signal.SIGTERM,term_sig_handler)
    signal.signal(signal.SIGINT,term_sig_handler)
    std_print("start")
    while True:
        std_print("sleep module:",sleep.__module__ )
        sleep(10)
    std_print("end")

proc_manager.py

#!/usr/bin/python
# -*- encoding: utf-8 -*-
'''
@File    :   process.py
@Time    :   2021/01/28 17:03:10
@Author  :   lmk
@Version :   1.0
@Contact :   lmk@zettakit.com
@Purpose    :   
'''

import sys
import subprocess as sp
import eventlet
eventlet.monkey_patch()
from eventlet import spawn,spawn_after,sleep
import eventlet.debug
# eventlet.debug.hub_prevent_multiple_readers(False)

# RuntimeError: Second simultaneous read on fileno 6 detected.  Unless you really know what you're doing, make sure that only one greenthread can read any particular socket.  Consider using a pools.Pool. If you do know what you're doing and want to disable this error, call eventlet.debug.hub_prevent_multiple_readers(False) - MY THREAD=<built-in method switch of GreenThread object at 0x7fbd883d1050>; THAT THREAD=FdListener('read', 6, <built-in method switch of GreenThread object at 0x7fbd883b60f0>, <built-in method throw of GreenThread object at 0x7fbd883b60f0>)
m_ver = sys.version.split(".")[0]
_out = sys.stdout.write

class VolumeProcessManager(object):
    def __init__(self):
        self._proc_dict = {}
        self.run_subprocs()
        self.get_all_subp_out()
        self.real_kill_all()
        self.wait_all()
        # self.x = input()

    def get_backends_conf(self):
        return range(3)

    def run_subprocs(self):
        list(map(self.run_subproc, self.get_backends_conf()))

    def run_subproc(self, cmd):
        
        params ={
             "stdout":sp.PIPE ,
             "stderr":sp.STDOUT,
             "universal_newlines":True
        }
        # if m_ver == '3':
        #     params.update({ "text":True})
        p = sp.Popen(["python","./capture_process.py","2>&1"], **params)
        self._proc_dict[p.pid] = p
        print("pid: ",p.pid)
        # p.terminate()

    def wait_all(self):
        _p_statues = {}
        while True:
            # None 代表未 全部 退出 ,1 代表 所有sub p退出
            all_code = 1
            for p in self._proc_dict.values():
                ret_code = p.poll()
                if _p_statues.get(p.pid,"null") != ret_code:
                    _p_statues[p.pid] = ret_code
                    _out("{} stat: {}\n".format(p.pid,ret_code))
                if ret_code ==None:
                    # 代表 子进程还在运行
                    all_code = None
                    # 其实此时就可以终止判断, 但是为了 更好地体验交互,就不提前退出了
                    # break
            if all_code != None:
                break
            sleep(1)
        # [p.wait() for p in self._proc_dict.values()]
    
    def get_all_subp_out(self):
        def f():
            for p in self._proc_dict.values():
                self.get_subp_out(p)
        spawn(f)
    
    def real_kill_all(self):
        # sleep(1)
        for p in self._proc_dict.values():
            spawn(self.real_kill,p)
            # self.real_kill(p)

    @staticmethod
    def get_subp_out(p):
        def read_line(p):
            while True:
                out = p.stdout.readline()
                _out(out)
                sleep(1)
        spawn(read_line,p)
    
    @staticmethod
    def real_kill(p):
        def force_kill(p):
            if p.poll() == None:
                p.kill()
        p.terminate()
        spawn_after(3,force_kill,p)


if __name__ == "__main__":
    VolumeProcessManager()