记psutil.Process类使用注意事项

2020-11-18 16:51:39 Python 37

简介

如果你的日常工作是运维的话,我相信psutil这个模块大家肯定不陌生。psutil是一个开源切跨平台的库,其提供了便利的函数用来获取才做系统的信息,比如CPU,内存,磁盘,网络等。此外,psutil还可以用来进行进程管理,包括判断进程是否存在、获取进程列表、获取进程详细信息等。

安装psutil模块

进入你想要安装的Python环境,输入如下命令进行模块安装

pip install psutil

如果顺利,那么接下来你就可以使用psutil模块了。

问题review

最近在写一个CI的平台项目,项目的大体要求就是把一些平时需要在终端使用命令行方式的操作,提供一个可视化的web界面,在界面上填写相关信息,点击按钮就可以完成本地命令行的操作。

我采用的策略是通过SSH的方式去连接客户机,然后通过SSH方式去控制客户机的命令行操作。当然其中还涉及到很多技术以及细节,在这里不累述了。

在项目中我使用到了websocket来使客户端与服务端进行长连接,实现服务端主动推送消息到客户端上。当客户端离开当前页面,服务端的socketio就会自动触发disconnect事件,在disconnect事件中我做了如下处理。

存在bug的代码:

@socketio.on('disconnect', namespace='/manager')
def pty_disconnect():
    try:
        child_process = psutil.Process(session.get(current_user.username).get('child_pid'))
    except psutil.NoSuchProcess as err:
        disconnect()
        session[current_user.username] = {}
        return

    if child_process.status() in ('running', 'sleeping'):
        child_process.terminate()
        session[current_user.username] = {}
        print('client disconnect, session clear')
        socketio.emit('pty-output', {"output": 'Web Socket连接已断开,请刷新页面重新连接!'}, namespace="/manager")

上述代码描述的就是当客户端断开连接之后,服务端会自动销毁该客户端的子进程,并且清楚保存在服务端的session信息。

一开始的时候,代码运作都很正常,当有一次,我点击了进入了这个页面,然后没做任何操作,转到了另外一个页面中去了,然后我的程序自动终止了。

进入该页面时候,客户端就已经连接到了服务端,因此在客户端退出的时候服务端就会自动触发disconnect事件,然后执行上述代码逻辑。我盘查了一下上述代码的逻辑,我我没有发现哪里存在着不妥当的地方。那为什么我进入页面什么都不做,退出页面的时候,我的整个进程就自动被终止了呢??

问题排查

于是乎,我祭出bug排除神器 print()闪亮登场~~~~~ 大家赶紧撒花!!!!

加入了print()后的代码如下

@socketio.on('disconnect', namespace='/manager')
def pty_disconnect():
    try:
        child_process = psutil.Process(session.get(current_user.username).get('child_pid'))
        print('child process is ', child_process)
        print('session child process is ', session.get(current_user.username).get('child_pid'))       
    except psutil.NoSuchProcess as err:
        disconnect()
        session[current_user.username] = {}
        return

    if child_process.status() in ('running', 'sleeping'):
        child_process.terminate()
        session[current_user.username] = {}
        print('client disconnect, session clear')
        socketio.emit('pty-output', {"output": 'Web Socket连接已断开,请刷新页面重新连接!'}, namespace="/manager")

 代码结果输出如下

child process is psutil.Process(pid=15405, name='python', status='running', started='16:08:12')

session child process is None

session存在的为None,这是正确的,但是通过psutil.Process() 获取到的居然是我当前程序运行的pid,那就知道了为什么当前程序会自动终止了,因为terminate()函数终止了我们的主进程。

测试

于是乎,我做了如下的测试。

打开一个终端输入如下代码

import psutil
import os
os.getpid()
# 24980
psutil.Process(None)
# psutil.Process(pid=24980, name='python', status='running', started='16:44:47')

发现当我们在Process()函数中传入None时,就会获取到我们当前主进程的相关信息,这尼玛也太坑了ba~~~ 我不知道这要设计的用意?

process1

然后我们在终端中调用terminate()方法,结果如下。

process finish

我们可以清楚的看到 Process finished with exit code 143.

问题解决

修改了一下代码逻辑,然后这个bug就消失殆尽啦!

@socketio.on('disconnect', namespace='/manager')
def pty_disconnect():
    try:
        child_id = session.get(current_user.username).get('child_pid')
        if child_id is not None:
            child_process = psutil.Process(session.get(current_user.username).get('child_pid'))
    except psutil.NoSuchProcess as err:
        disconnect()
        session[current_user.username] = {}
        return

    if child_process.status() in ('running', 'sleeping'):
        child_process.terminate()
        session[current_user.username] = {}
        print('client disconnect, session clear')
        socketio.emit('pty-output', {"output": 'Web Socket连接已断开,请刷新页面重新连接!'}, namespace="/manager")

Note:

我要去看看psutil的文档中有没有说明这个问题,如果没有那么我切不是可以给psutil做个contribution~~~hhhh

 

 

您尚未登录, 登录注册 后评论

当前共有1条评论


清水 博主

看了文档发现Process类默认pid为None

psutil.Process(pid=None)