Python 进程 pid,使用 kill -15 pid,无法执行析构函数,求解释为什么

2018-06-19 11:27:04 +08:00
 ruoge3s

我想用 process 类记录进程的 id,写入文件 然后用使用 process 类的 close 方法关闭进程

问题是使用 kill pid 的时候,process 类的析构方法不能正常执行析构方法删除掉对应的 pid 文件

加上 signal.signal(signal.SIGTERM, self.signal_handler),即使 handler 中不写任何东西,被 kill 的进程也能调用析构方法

如果不写 signal.signal,析构方法就不会触发,求指教

python 版本为 2.7.6

Process.py

# coding=utf-8

# from tool import Tool
import os
import signal
import re


class Process:
    _pid = None
    _pid_dir = None

    prefix = 'pid.'

    def __init__(self):
        t = Tool()
        self._pid_dir = t.runtime_dir()  # 创建临时目录而已,可手动写死
        signal.signal(signal.SIGTERM, self.signal_handler)

    def signal_handler(self, signum, frame):
        # print('Received signal: ', signum)
        pass

    def pid_file_name(self, pid):
        return self.prefix + str(pid)

    def pid_file(self, pid):
        """
        获取当前进程的 pid 缓存文件
        :return:
        """
        return self._pid_dir + '/' + self.pid_file_name(pid)

    def save(self, pid=None):
        """
        保存当前进程 pid 文件
        :return:
        """
        self._pid = pid if pid else os.getpid()
        os.system('touch %s' % self.pid_file(self._pid))

    def close(self):
        """
        关闭全部的进程
        :return:
        """
        for pf in os.listdir(self._pid_dir):
            rer = re.match(self.prefix + '(\d+)', pf)
            if rer:
                pid = int(rer.group(1))
                try:
                    os.kill(pid, 15)
                except OSError:
                    # 抑制异常,不抛出进程不存在的问题,并删除不存在的进程号
                    self.del_pid_file(pid)

    def del_pid_file(self, pid):
        """
        删除进程缓存的文件
        :param pid:
        :return:
        """
        pid_file = self.pid_file(pid)
        os.path.exists(pid_file) and os.remove(pid_file)

    def __del__(self):
        """
        如果存在当前进程的 pid 文件,删除 pid 文件
        :return:
        """
        self._pid and self.del_pid_file(self._pid)

使用 python tester.py xx 和 python tester.py 命令进行对应的调试

tester.py

import sys
import time
from core.Process import Process

import datetime


p = Process()

m = True if len(sys.argv) == 2 else False
# m = False

if m:
    p.save()
    time.sleep(70)
else:
    p.close()
4185 次点击
所在节点    Python
22 条回复
msg7086
2018-06-19 12:24:41 +08:00
kill 的问题先不说。
m = True if len(sys.argv) == 2 else False
这是什么鬼……
直接写 m = len(sys.argv) == 2 不行么……

至于 kill term,我猜的是如果你不指定 handler,那么就用系统默认的 term handler,也就是直接杀掉进程。
wwqgtxx
2018-06-19 16:07:19 +08:00
从 python 规范中只是说__del__会在被垃圾回收的时候调用,而且还说这种用法容易造成死锁、内存泄漏等问题,貌似从来没说过可以当做析构函数使用
lolizeppelin
2018-06-19 16:11:30 +08:00
kill 是信号发送
signal_handler 接受信号


你接受信号以后什么都不干 当然就没反应

本来你的对应退出代码就该写在 signal_handler 里

为什么要写 del 里?
ruoge3s
2018-06-19 18:26:27 +08:00
@msg7086 m = xx 的问题只是一个写法的问题,每个人理解不一样而已.你理解的就是把 len 的长度与 2 的比较结果赋值给 m,我理解的是如果为 2 就赋值 true 否则赋值 false.

不指定 handler 的话,我在使用 os.kill(pid, 15)的时候,也应该通知该进程的信号是 15 啊,并没有直接杀死进程啊.
而运行的脚本这边在收到了-15 信号的时候,没有对应的进行回收,销毁变量,也就没有触发 process 这个对象的销毁,所以也么有进一步的调用到析构方法.

所有,我很迷惑.

那么你的意思是系统的默认的 term handler 无论接收到任何信号,都当成-9 来处理
ruoge3s
2018-06-19 18:27:37 +08:00
@wwqgtxx 这个解释看起来合理一点
ballshapesdsd
2018-06-19 18:27:58 +08:00
啥 python 还有析构函数。。
ruoge3s
2018-06-19 18:28:05 +08:00
@lolizeppelin 朋友,你没有看明白问题.
ruoge3s
2018-06-19 18:30:07 +08:00
__del__就是啊
ruoge3s
2018-06-19 18:30:25 +08:00
@ballshapesdsd __del__就是啊
tkmiles
2018-06-19 19:02:19 +08:00
@ruoge3s python 中的析构还是少用比较好, 可以看一下 gc
ruoge3s
2018-06-19 19:35:55 +08:00
@tkmiles 好的。
msg7086
2018-06-19 23:47:47 +08:00
操作系统默认的 term handler 接收到 term 的时候,除了当成 kill 以外好像也没有别的选择啊。
系统也不可能进入到你程序的内部来退出吧。
ryd994
2018-06-20 00:50:33 +08:00
@msg7086 我记得 term 是由 glibc 的默认 handler 处理
kill 是内核直接删进程
区别在于:如果进程 IO block,term 是没有用的,要 kill
msg7086
2018-06-20 00:53:04 +08:00
@ryd994 嗯应该没错。
IO block 的话,你说的是 D 状态?那个连 kill 都没用的。
ryd994
2018-06-20 01:09:06 +08:00
@msg7086 kill 是内核直接抽内存栈了啊
term 实际上是进程自己处理的,所以自己指定一个 handler 的话是可以不死的
但是 kill 表面上是个信号,但是根本不会被送到进程内,实际上是内核直接处理的
msg7086
2018-06-20 01:13:02 +08:00
@ryd994 是啊我同意你的观点。kill 是没有 handler 的,term 的默认 handler 类似 kill self。
我说的是 IO block 的情况,是内核自己炸了,所以 kill 也没用。
ryd994
2018-06-20 01:19:29 +08:00
@msg7086 呃,举个例子,读写 NFS,结果网断了
我是说这种情况
msg7086
2018-06-20 01:24:10 +08:00
@ryd994 是啊,NFS 断线是我遇到的最多的 kill 不掉的场景了。
没记错的话需要用 hup 去捅挂载进程,然后才能 kill 掉程序。
你直接 kill 的话,D 状态的程序是 kill 不掉的,否则内核状态会出问题。
sNullp
2018-06-20 01:26:04 +08:00
@ryd994 在这种情况下 kill 明确是没有用的,因为 stuck 在内核里
geelaw
2018-06-20 01:33:25 +08:00
析构函数本来就不一定运行啊。

你 C++ 写一个析构函数,然后发送一个导致杀死进程的信号,outstanding 对象的析构函数也不会运行。

另外,__del__ 应该类比于 C# 的 finalizer,而不是 C++ destructor,Python 文档也是这么说的。

终结化器不一定非要运行,包括程序正常退出的情况。终结化器不一定按照你想像的顺序运行,比如,当终结化器运行的时候,或许你的一个字段引用的对象已经被终结化了( C# 也有该现象)。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/464039

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX