写在之前:
先来明确以下几点:
1、subprocess.Popen:这个类常用来执行系统命令的,就类似你打开一个终端窗口,然后输入命令执行的过程(具体的用法,请百度查询)
2、进程组、父进程、子进程(linux):
{
进程组pid = 父进程pid,即 父进程: 组长进程
组长进程
组长进程标识: 其进程组ID==其父进程ID
组长进程可以创建一个进程组,创建该进程组中的进程,然后终止
只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关
进程组生存期: 进程组创建到最后一个进程离开(终止或转移到另一个进程组)
}
3、脚本的执行,是放在linux服务器上
4、我用python语言,篇幅较长,带上耐心看
一、为什么写这往篇文章
正巧遇到这样一个问题:
在linux有一个服务,在需要用的时候,再开启;
但开启后自己又不会自动结束,还需要手动去停止;
一切的执行操作均需要通过代码去完成;
开启服务很简单,直接用Popen就可以,但结束的时候,发现用Popen.kill()方法,仅仅杀死了终端进程,并不能全完杀死由 终端 所开启的所有子进程;
然后百度了一番找到了一个可行方案,记录下来方便查看和后人学习。
二、以开启结束STF服务为例
开启STF服务脚本如下(代码仅为举例,以简单为主):
import os
import time
import subprocess
from subprocess import PIPE
def start_stf():
cmd = "stf local --public-ip 192.168.2.233 --allow-remote".split()
stf_p = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE, preexec_fn=os.setsid)
# os.setsid 用于为当前进程创建进程组
time.sleep(5) # 等上几秒,开服务需求时间
print("状态:", stf_p.poll())
print("开启进程的pid", stf_p.pid)
print("所属进程组的pid", os.getpgid(stf_p.pid))
time.sleep(60) # 再等60秒,然后通过以下方式关闭服务试下
stf_p.kill()
if __name__ == "__main__":
start_stf()
测试内容:
通过stf_p.kill()是否可以杀死所有开启的子进程
在执行脚本之前查看关于STF的进程、端口情况:
端口:
[root@localhost ~]# netstat -tulnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1070/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1282/master
tcp6 0 0 :::22 :::* LISTEN 1070/sshd
tcp6 0 0 ::1:25 :::* LISTEN 1282/master
udp 0 0 127.0.0.1:323 0.0.0.0:* 728/chronyd
udp6 0 0 ::1:323 :::* 728/chronyd
进程:
[root@localhost ~]# ps -aux | grep stf
root 1282 0.0 0.0 89544 2080 ? Ss 12:28 0:00 /usr/libexec/postfix/master -w
postfix 1283 0.0 0.0 89648 3992 ? S 12:28 0:00 pickup -l -t unix -u
postfix 1284 0.0 0.0 89716 4016 ? S 12:28 0:00 qmgr -l -t unix -u
root 1374 0.0 0.0 112676 980 pts/0 S+ 12:34 0:00 grep --color=auto stf
[root@localhost ~]#
可以看出,没有执行脚本之前,没有关于STF任务进程和端口,下面是执行脚本之后的进程情况
脚本的打印结果如下:
命令执行开启STF服务成功,开启的进程和进程组pid均为1507
# -----------------------------------------------
(venv) [root@localhost test_one]# python test_kill_popen.py
状态: None
开启进程的pid 1507
所属进程组的pid 1507
(venv) [root@localhost test_one]#
# -----------------------------------------------
在60秒结束之前,1507进程及STF相关进程和端口情况如下:
# -----------------
1507进程:
可以看出1507进程作用是执行那条命令,相当于一个终端口窗口占用的进程
[root@localhost ~]# ps -aux | grep 1507
root 1507 1.8 0.1 883816 26976 ? Ssl 12:50 0:00 node /usr/local/bin/stf local --public-ip 192.168.2.233 --allow-remote
root 1630 0.0 0.0 112680 976 pts/1 S+ 12: