V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
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 · 2018-06-19 11:27:04 +08:00 · 4189 次点击
    这是一个创建于 2384 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我想用 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 条回复    2018-06-20 09:56:49 +08:00
    msg7086
        1
    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
        2
    wwqgtxx  
       2018-06-19 16:07:19 +08:00
    从 python 规范中只是说__del__会在被垃圾回收的时候调用,而且还说这种用法容易造成死锁、内存泄漏等问题,貌似从来没说过可以当做析构函数使用
    lolizeppelin
        3
    lolizeppelin  
       2018-06-19 16:11:30 +08:00 via Android
    kill 是信号发送
    signal_handler 接受信号


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

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

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

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

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

    终结化器不一定非要运行,包括程序正常退出的情况。终结化器不一定按照你想像的顺序运行,比如,当终结化器运行的时候,或许你的一个字段引用的对象已经被终结化了( C# 也有该现象)。
    ryd994
        21
    ryd994  
       2018-06-20 03:01:39 +08:00 via Android
    @msg7086
    @sNullp
    看来是我记错了,抱歉
    ruoge3s
        22
    ruoge3s  
    OP
       2018-06-20 09:56:49 +08:00
    @geelaw 受教了,我去多读读 python 的关于这点的文档
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2404 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 39ms · UTC 02:14 · PVG 10:14 · LAX 18:14 · JFK 21:14
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.