V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
ruoge3s
V2EX  ›  Python

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

  •  
  •   ruoge3s · Jun 19, 2018 · 4854 views
    This topic created in 2874 days ago, the information mentioned may be changed or developed.

    我想用 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()
    
    22 replies    2018-06-20 09:56:49 +08:00
    msg7086
        1
    msg7086  
       Jun 19, 2018
    kill 的问题先不说。
    m = True if len(sys.argv) == 2 else False
    这是什么鬼……
    直接写 m = len(sys.argv) == 2 不行么……

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


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

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

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

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

    所有,我很迷惑.

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

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

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

    终结化器不一定非要运行,包括程序正常退出的情况。终结化器不一定按照你想像的顺序运行,比如,当终结化器运行的时候,或许你的一个字段引用的对象已经被终结化了( C# 也有该现象)。
    ryd994
        21
    ryd994  
       Jun 20, 2018 via Android
    @msg7086
    @sNullp
    看来是我记错了,抱歉
    ruoge3s
        22
    ruoge3s  
    OP
       Jun 20, 2018
    @geelaw 受教了,我去多读读 python 的关于这点的文档
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2344 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 90ms · UTC 11:24 · PVG 19:24 · LAX 04:24 · JFK 07:24
    ♥ Do have faith in what you're doing.