内容简介:需求是:一个正在运行的脚本,当结束脚本的时候,需要获取里面的变量,如果变量值存在则执行插入数据操作。如果变量不存在则正常关闭脚本。这个需求可以理解成是在要杀死脚本的时候,让脚本监听到这个终止事件,从而做一些事情,比如持久化数据之类的。在实现需求的时候碰到很多有意思的知识点,下面咱们就来讲一下这些知识点。刚开始是想着定义一个
文章目录
- 二、最初的想法:直接获取脚本中的变量值
- 1、获取运行中py脚本的变量值
- 3、为什么不能获取脚本中的变量
- 4、下下策,使用使用 python 的gdb调试工具
- 三、python脚本监听终止进程行为
- 1、python的signal模块以及atexit模块
- 2、使用os.kill()退出程序
- 3、脚本监听中止信号代码实例
- 四、python通过脚本名获取pid
- 1、通过脚本名称获取pid
- 3、单个脚本监听命令行kill行为
一、前言
需求是:一个正在运行的脚本,当结束脚本的时候,需要获取里面的变量,如果变量值存在则执行插入数据操作。如果变量不存在则正常关闭脚本。
这个需求可以理解成是在要杀死脚本的时候,让脚本监听到这个终止事件,从而做一些事情,比如持久化数据之类的。在实现需求的时候碰到很多有意思的知识点,下面咱们就来讲一下这些知识点。
二、最初的想法:直接获取脚本中的变量值
刚开始是想着定义一个 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模块
通过不断的百度,最终锁定了 python
的 signal
模块以及 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模块:
python
的 atexit
模块定义了一个 register
函数,用于在 python
解释器中注册一个退出函数,这个函数在解释器正常终止时自动执行,一般用来做一些资源清理的操作。
但是这个函数,如果脚本是非正常 crash
,或者通过 os._exit()
退出,都不会调用退出函数。而且 kill -2
和 ctrl +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
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 用 Python 脚本,监听附近网络 Wi-Fi 设备,通过邮件和微信进行消息推送
- Laravel 给生产环境添加监听事件 - SQL日志监听
- Flutter事件监听
- 初始化监听端口
- Vue简便监听事件
- react源码-事件监听
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。