kill掉Popen的子子孙孙进程

昨天工作中遇到一个问题,折腾了一晚上,睡了一觉,早上来公司解决了。

简单描述一下:项目使用python的subprocess模块,调用Popen创建子进程,使用完毕之后调用terminate和wait方法结束进程,本来一切都挺好,但是ps检查服务的时候,发现调用的子进程并没有被真正干掉,因为Popen这里是充当了监工的角色,新起了一个子进程来替他干活,每调用一次都会出现两个新的子进程,还杀不掉,占用资源,很是烦躁。

kill掉Popen的子子孙孙进程

在网上找了找相关问题,发现Popen有个参数,start_new_session,这个属性置为True的话,Popen就会自动新建一个进程组ID,且与Popen创建的进程ID一致,即Popen创建的进程为相应进程组长,后续生成的所有子进程都是统一的父进程ID、进程组ID,也就是ppid和pgid都是Popen创建的进程的pid,这样一来,只要获取process.pid,就可以发送相应kill信号来杀掉整个进程组了。

这中间还有个插曲,因为没设置start_new_session参数,直接杀的pgid,导致属于同一pgid的后台服务进程uwsgi也被干掉了,系统不断重启Orz幸亏晚上搞的,没啥人用。

kill掉Popen的子子孙孙进程

 

下面是具体代码

try:

    # 给Popen方法增加start_new_session属性,新建进程组id

    pdmdb=subprocess.Popen(conndmdb,shell=True,start_new_session=True,stdout=subprocess.DEVNULL,stdin=subprocess.PIPE,stderr=subprocess.DEVNULL)

    ###此处省略掉相关功能代码

    #使用完毕terminate,不过好像这里终不终止都没啥用,没做较细致的研究

    pdmdb.terminate()

    # 干掉进程组id,即pdmdb的pid

    try:

        os.killpg(pdmdb.pid, signal.SIGKILL) #这里向进程组发送SIGTERM信号,即kill -15也是杀不掉,改成SIGKILL

    except OSError as err:

        log.error(err)

        traceback.print_exc()

        return str(err)

except Exception as err:

    log.error(err)

    traceback.print_exc()

    return str(err)

这样处理之后,后台清爽了,也不影响原有功能逻辑的执行。

哦,补充一句,后台服务跑在docker容器里,使用的是ash,估计和这个也有一定的关系,因为在bash环境里跑没发现这种情况出现,terminate之后就结束了。