python脚本如何监听终止进程行为,如何通过脚本名获取pid

本文探讨了Python脚本在终止时如何监听并利用内部变量,介绍使用signal和atexit模块监听脚本终止事件,以及通过脚本名获取PID的方法。
部署运行你感兴趣的模型镜像

一、前言

需求是: 一个正在运行的脚本,当结束脚本的时候,需要获取里面的变量,如果变量值存在则执行插入数据操作。如果变量不存在则正常关闭脚本。

      这个需求可以理解成是在要杀死脚本的时候,让脚本监听到这个终止事件,从而做一些事情,比如持久化数据之类的。在实现需求的时候碰到很多有意思的知识点,下面咱们就来讲一下这些知识点。

二、最初的想法:直接获取脚本中的变量值

      刚开始是想着定义一个test()方法,当要结束a脚本的时候,先执行另一个b脚本,调用a脚本的test()方法获取变量,然后对比变量的值,符合条件就执行数据库操作等。

1、获取运行中py脚本的变量值

刚开始是想着获取脚本中的变量,例如变量名是test,通过类似于:

import a 
print a.test

      但是这种方式只能获取变量的初始值,比如在a脚本刚开始的时候,设置 test =1,后续代码中累加test的值,我们通过这种方式,获取的test的值一直都是1,并不能获取脚本中变量的实时值

2、换一种方式

参考:https://blog.csdn.net/shiyf/article/details/47093899

      完全按照这个参考博客的代码,本地测试的结果还是一样的,并不能获取脚本中的变量。仔细想想也是,人家脚本运行的时候,肯定是在一套封闭的环境中运行,想从中拿到变量不太科学。虽然是这么想,但是并不知道其中的原理

3、为什么不能获取脚本中的变量

main.py 文件:

import other

isRunning = True
def shutdown():
    global isRunning
    isRunning = False

def main():
    while isRunning:
        # some logic here
        other.check()

if __name__ == '__main__':
    main()

other.py 文件:

import main

def check():
    someCondition = #some logic to check the condition
    if someCondition:
        main.shutdown()

      这个例子非常相似,这位朋友的main.py脚本是根据isRunning 变量一直循环运行的脚本。他想通过other脚本改变isRunning 的值,从而中止main.py脚本的运行,和咱们的需求很相似了,但是他也一直实现不了自己的需求,后来有大佬是这么解释的:

      该问题与正在加载两次的main.py文件有关.当您将其作为脚本运行时,它首先作为__main__加载.它也被other.py导入为常规名称main。这两个副本彼此完全分开,并且具有单独的全局变量!当other.check调用main.shutdown来修改isRunning全局变量时,它只更改main.isRunning版本,而不是__main __.isRunning.主要功能是检查__main __.isRunning,它永远不会被改变。

      这就能解释为什么上面咱们通过引入的方式,每次都只能获取变量的初始值,而不能获取运行中脚本的变量。那么是否真的毫无办法呢?

4、下下策,使用使用python的gdb调试工具

参考:https://blog.csdn.net/xuzhina/article/details/76733977

      这块大家可以自己百度下,知识点还是挺多的,只是使用gdb调试工具,相当于要自己手动执行一些查询操作,而且过程比较复杂,并不建议使用这种方式。

三、python脚本监听终止进程行为

      既然不能获取变量,那么能不能自己监听中止事件呢,当检测到脚本要停止的时候,从而执行一系列的操作。而且这种方式的话,相当于是在a.py脚本内完成的操作,那么直接使用脚本中的变量即可。

1、python的signal模块以及atexit模块

通过不断的百度,最终锁定了pythonsignal模块以及atexit模块。

signal模块:

通过模块的特定方法,可以监听到脚本终止事件,从而执行相关操作,主要的方法包括:

 signal.SIGHUP  # 连接挂断	(适用于nohub守护进程的关闭)
 signal.SIGILL  # 非法指令
 signal.SIGINT  # 连接中断	(ctrl + c 和 kill -2)
 signal.SIGKILL # 终止进程(此信号不能被捕获或忽略)
 signal.SIGQUIT # 终端退出
 signal.SIGTERM # 终止	(kill pid)
 signal.SIGALRM  # 超时警告
 signal.SIGCONT  # 继续执行暂停进程

atexit模块:

      pythonatexit模块定义了一个register函数,用于在python解释器中注册一个退出函数,这个函数在解释器正常终止时自动执行,一般用来做一些资源清理的操作。

      但是这个函数,如果脚本是非正常crash,或者通过os._exit()退出,都不会调用退出函数。而且kill -2ctrl +c这种退出也不能调用该函数,因此一般要与signal模块配合使用。

注意: 这两个模块都不能监听kill -9这种方式的终止脚本,因为执行kill -9之后,内核根本不听你多哔哔,直接就把进程强制删除了。

参考:
https://www.jianshu.com/p/2fef52b7a231
https://www.return520.com/posts/9865/

2、使用os.kill()退出程序

      因为要模拟退出程序,首选是kill的方式退出程序,这里选择os.kill()的方式退出,该函数是模拟传统的UNIX函数发信号给进程,其中包含两个参数:

一个是进程名,即所要接收信号的进程;一个是所要进行的操作。
操作(第二个参数)的常用取值为:
SIGINT      终止进程     中断进程
SIGTERM   终止进程     软件终止信号
SIGKILL    终止进程      杀死进程
SIGALRM   闹钟信号

      结合上面咱们对于signal模块的分析,选用signal.SIGTERM的方式是比较合适的,主要监听的是kill pid的方式。因此中止代码如下:

os.kill(PID, signal.SIGTERM)

3、脚本监听中止信号代码实例

a脚本:

import signal
import os
import time
def onsignal_term(a, b):
    print 'SIGTERM'
signal.signal(signal.SIGTERM, onsignal_term)
while 1:
    print 'id:', os.getpid()
    time.sleep(2)

b 脚本:

import getopt
import sys
PID = 1000
os.kill(PID, signal.SIGTERM)  # 注意此处的PID需要是int类型的
b脚本中的pid为a脚本的pid,先执行a脚本,然后执行b脚本,此时触发os.kill()函数,
a脚本中也会成功监听到signal.SIGTERM时间,从而执行onsignal_term()函数。

注意: onsignal_term()函数需要有两个参数,不过这两个参数咱们也用不着,写上就可以。

四、python通过脚本名获取pid

      这一步更多的是完善上面的想法。通过pid杀死进程,a脚本成功监听终止事件。只是这个pid不能每次都手动查询把,这样太费劲了,那么能不能用过什么方式来获取到脚本的pid呢。

一种方案是通过传参的方式,把pid传进去,然后b脚本根据pid杀死进程;
一种方案是根据脚本名称获取pid,也是用传参的方式,传入脚本名称;

      两种方案都是通过传参的方式,只是第一种方案的pid还需要手动查证或者脚本内打印pid,操作相对来说复杂点。第二种方案就简单多了,只要知道脚本名称即可,难点在于怎么通过脚本名称获取pid。这里选择第二种方案。

1、通过脚本名称获取pid

File = '脚本名称'
#直接写死文件名
PID = os.popen("ps -ef | grep  a.py" ).readlines()[0].split()[1]  
#带上变量的文件名 		
PID = os.popen("ps -ef | grep  %s" % File).readlines()[0].split()[1]	

      这里采用脚本内调用linux命令行的方法来获取,通过os.popen()获取符合条件的进程,再调用read()或者readlines()等对shell的输出结果进行读取。

具体的可以参考:https://blog.51cto.com/2681882/2317053

2、脚本内获取pid

顺带记录一下吧,在脚本内获取脚本pid的方法:

os.getpid()

3、单个脚本监听命令行kill行为

      以上都是双脚本的监听,那么a脚本自己是否可以监听命令行的操作呢,理论上肯定是可以的,那么咱们也来试试

脚本a.py 如上所示:

执行a.py如下:

ljf@ljf:/var/www/python/Python2$ python2 a.py
id: 6340
id: 6340

按照脚本内容,会一直打印pid,直到捕获到kill命令。

执行kill 6340,则脚本停止并打印SIGALRM:

id: 6340
id: 6340
SIGALRM

      其实单脚本和多脚本本质上是差不多的,我们在做多脚本的时候,顺带就也把单脚本的给做了,主要是看各自的业务吧。我这边为了给DBA方便,还是选择多脚本,毕竟项目也多,直接输入脚本名辨识度会高一些,而且也会更加安全。

如果要监听ctrl+c或者kill -2 pid这种模式的中断进程,那么可以选用:signal.signal(signal.SIGINT, onsignal_term) 经过测试,这种方式也完全没问题。当然,也可以选择两种方式都给写上,这样就能监听:kill pid ,ctrl + c , kill -2 pid ,建议是能同时监听多种情况,更方便一些。

身为一个python小白,以上很多知识点都是值得好好总结的,奈何技术有限,希望以后有机会讲一讲“why”,而不是单纯的“how”。

end

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

<think>我们有两个任务: 1. 获取进程(可能是通过用户输入或者其他方式获得) 2. 根据进程终止对应的应用程序 在Python中,我们可以使用`psutil`库来方便地获取系统进程信息并终止进程。但是,请注意,`psutil`是第三方库,需要安装。 步骤: 1. 安装psutil(如果尚未安装):`pip install psutil` 2. 编写脚本,通过进程获取进程列表,然后终止这些进程。 注意:终止进程需要管理员权限(在Windows上)或root权限(在Linux上)?实际上,终止自己启动的进程通常不需要,但终止其他用户或系统关键进程需要。所以,如果脚本无法终止某些进程,请检查权限。 代码思路: - 遍历所有正在运行的进程。 - 检查进程是否与目标称匹配(注意:进程可能不区分大小写?在Windows上,通常不区分大小写,但在Linux上区分。为了安全,我们可以统一转换为小写比较,但要注意进程的实际大小写可能是有意义的,比如在Linux上)。 - 终止匹配的进程。 但是,用户可能希望精确匹配还是部分匹配?这里我们假设用户输入完整的进程(包括扩展,如.exe)。但也可以设计为部分匹配。 下面提供两个版本: 版本1:精确匹配(包括大小写) 版本2:忽略大小写匹配(适用于Windows,因为Windows进程不区分大小写,但在Linux上,我们也可以忽略大小写,但要注意可能存在的多个不同大小写的进程) 另外,注意:同一个进程可能有多个实例(比如多个Chrome窗口),所以我们需要循环终止所有匹配的进程终止进程的方法:`process.terminate()`(发送终止信号,允许进程清理)或`process.kill()`(强制立即终止)。 通常,先尝试`terminate()`,如果进程不响应,再使用`kill()`。但为了简单,我们可以直接使用`terminate()`,然后等待一段时间,如果进程还在,再`kill()`。但这里为了简单,我们直接使用`terminate()`,然后检查是否终止,如果没有,再使用`kill()`。 但是,我们也可以让用户选择强制终止。 下面,我们写一个函数,用于根据进程终止进程。 注意:在Windows上,进程通常以.exe结尾,但在任务管理器中可能不显示。例如,记事本的进程是"notepad.exe"。 代码示例:</think># Python 脚本获取进程终止应用程序 下面我将介绍如何使用 Python 脚本获取进程终止对应的应用程序。主要使用 `psutil` 库,它提供了跨平台的进程管理功能。 ## 解决方案 ### 1. 安装所需库 ```bash pip install psutil ``` ### 2. Python 脚本实现 ```python import psutil import argparse def find_and_kill_process(process_name): """查找并终止指定称的进程""" killed = False # 遍历所有进程 for proc in psutil.process_iter(['pid', 'name']): try: # 检查进程是否匹配(不区分大小写) if process_name.lower() in proc.info['name'].lower(): print(f"找到进程: PID={proc.pid}, 称={proc.info['name']}") proc.kill() # 终止进程 print(f"✅ 已终止进程: {proc.info['name']} (PID: {proc.pid})") killed = True except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): # 忽略无法访问或已终止进程 continue if not killed: print(f"⚠️ 未找到称包含 '{process_name}' 的进程") if __name__ == "__main__": # 设置命令行参数解析 parser = argparse.ArgumentParser(description='终止指定称的进程') parser.add_argument('process_name', type=str, help='要终止进程称') args = parser.parse_args() find_and_kill_process(args.process_name) ``` ### 3. 使用方法 在命令行中执行脚本: ```bash python terminate_process.py "进程" ``` 示例: ```bash # 终止所有 Chrome 进程 python terminate_process.py "chrome" # 终止记事本进程 python terminate_process.py "notepad" ``` ## 关键功能说明 1. **进程查找**: - 使用 `psutil.process_iter()` 遍历所有运行中的进程 - 通过 `proc.info['name']` 获取进程称 - 支持不区分大小写的匹配(如 "chrome" 匹配 "chrome.exe") 2. **进程终止**: - 使用 `proc.kill()` 强制终止进程 - 处理异常情况(无权限、进程终止等) 3. **命令行接口**: - 使用 `argparse` 模块提供友好的命令行参数处理 - 可直接指定要终止进程称 ## 注意事项 1. **权限要求**: - 在 Windows 上需要管理员权限才能终止系统进程 - 在 Linux/macOS 上需要 root 权限终止其他用户的进程 2. **进程匹配**: - Windows 进程通常以 `.exe` 结尾(如 `chrome.exe`) - 脚本会自动匹配部分称(如 "chrome" 匹配 "chrome.exe") 3. **跨平台兼容性**: - 脚本在 Windows、Linux 和 macOS 上均可运行 - `psutil` 库自动处理不同操作系统的差异 4. **安全考虑**: - 避免终止关键系统进程(如 `svchost.exe`, `system` 等) - 建议先测试查找功能而不终止进程 ## 替代方案(不使用 psutil) 如果需要纯标准库解决方案,可以使用以下方法(仅限 Windows): ```python import os import subprocess def kill_process_by_name_win(process_name): """Windows 系统下终止指定称的进程""" try: # 使用 taskkill 命令终止进程 result = subprocess.run( ['taskkill', '/f', '/im', f'*{process_name}*'], capture_output=True, text=True ) if "SUCCESS" in result.stdout: print(f"✅ 已终止所有包含 '{process_name}' 的进程") else: print(f"⚠️ 未找到或无法终止进程: {result.stderr.strip()}") except Exception as e: print(f"❌ 错误: {str(e)}") # 使用示例 kill_process_by_name_win("notepad") ``` ## 相关问题 1. 如何在不终止进程的情况下列出所有运行中的进程? 2. 如何获取特定进程的 CPU 和内存使用情况? 3. 如何在 Linux 系统上使用纯 Python 标准库管理进程? 4. 如何安全地终止进程树(包括所有子进程)? > 注意:终止进程可能导致数据丢失,请谨慎操作关键应用程序[^1]。多线程应用中终止进程需注意全局解释器锁(GIL)的影响[^3]。
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铁柱同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值