【问题标题】:How to exit function with signal on Windows?如何在 Windows 上使用信号退出功能?
【发布时间】:2017-11-10 10:25:09
【问题描述】:

我在 Windows 上使用 Python 2.7 编写了以下代码。我想检查当前python脚本的更新并更新它,如果有更新,通过ftp服务器保留文件名的新版本,然后在通过os.kill和@987654322终止当前后执行新的python脚本@。

我使用了exit 函数方法,但我读到在 Windows 中这仅适用于 atexit 库和默认 python exit 方法。所以我使用了atexit.register() 和信号处理程序的组合。

***necessary libraries***

filematch = 'test.py'
version = '0.0'
checkdir = os.path.abspath(".")
dircontent = os.listdir(checkdir)
r = StringIO()

def exithandler():
    try:
        try:
            if filematch in dircontent:
                os.remove(checkdir + '\\' + filematch)
        except Exception as e:
            print e

        ftp = FTP(ip address)
        ftp.login(username, password)
        ftp.cwd('/Test')

        for filename in ftp.nlst(filematch):
            fhandle = open(filename, 'wb')
            ftp.retrbinary('RETR ' + filename, fhandle.write)
            fhandle.close()

        subprocess.Popen([sys.executable, "test.py"])
        print 'Test file successfully updated.'
    except Exception as e:
        print e

ftp = FTP(ip address)
ftp.login(username, password)
ftp.cwd('/Test')

ftp.retrbinary('RETR version.txt', r.write)

if(r.getvalue() != version):
    atexit.register(exithandler)
    somepid = os.getpid()
    signal.signal(SIGTERM, lambda signum, stack_frame: exit(1))
    os.kill(somepid, signal.SIGTERM)

print 'Successfully replaced and started the file'

使用:

signal.signal(SIGTERM, lambda signum, stack_frame: exit(1))

我明白了:

Traceback (most recent call last):
  File "C:\Users\STiX\Desktop\Python Keylogger\test.py", line 50, in <module>
    signal.signal(SIGTERM, lambda signum, stack_frame: exit(1))
NameError: name 'SIGTERM' is not defined

但我可以毫无问题地完成工作,除非我在更复杂的脚本中使用当前代码,该脚本给我同样的错误但由于某种原因立即终止。

但另一方面,如果我以正确的方式使用它,signal.SIGTERM,进程会直接终止并且退出函数永远不会执行。这是为什么呢?

如何在 Windows 上进行这项工作并成功获得我上面描述的结果?

【问题讨论】:

  • signal.SIGTERM 或者只有 15 个应该可以工作
  • @Artyer 我使用了上面提到的那个。由于某种原因,退出函数永远不会使用 signal.SIGTERM 执行
  • Windows 没有 POSIX 信号。 C 运行时实现了当前进程中标准 C 所需的 6 个,以及非标准 SIGBREAK。 Python 在 Windows 上对 os.kill 的实现将两个完全不相关的函数混合在一起,它们的事件目标不是同一件事:GenerateConsoleCtrlEvent(将 Ctrl+C 或 Ctrl+Break 事件发送到进程 group附加到当前控制台)和TerminateProcess 以立即终止单个进程(如 Unix 上的 SIGKILL)。

标签: python windows python-2.7 signals


【解决方案1】:

您正在尝试做的事情似乎有点复杂(从信息安全的角度来看是危险的;-)。我建议处理功能的 reload-file-when-updated 部分是添加一个控制器类,该控制器类将您现在拥有的 python 脚本作为模块导入并启动它并在更新时重新加载它(基于一个函数return 或其他技术)- 从这里寻找灵感 - https://stackoverflow.com/a/1517072/1010991

编辑 - exe 呢?

另一种操纵当前运行程序文件的技巧是 shell ping 技巧。它可以用于所有编程语言。诀窍是在调用进程终止后发送一个未执行的 shell 命令。使用 ping 导致延迟并使用 & 链接其他命令。对于您的用例,它可能是这样的:

import subprocess
subprocess.Popen("ping -n 2 -w 2000 1.1.1.1 > Nul & del hack.py & rename hack_temp.py hack.py & hack.py ", shell=True)

编辑 2 - 原始问题的替代解决方案

由于 python 不会阻止对当前正在运行的脚本的写访问,解决原始问题的替代概念是:

import subprocess
print "hello"
a = open(__file__,"r")
running_script_as_string = a.read()
b = open(__file__,"w")
b.write(running_script_as_string)
b.write("\nprint 'updated version of hack'")
b.close()
subprocess.Popen("python hack.py")

【讨论】:

  • hmm 很有趣,但是如果我将脚本编译成 exe 会怎样?从 ftp 服务器获取新的 exe 后如何重新加载 exe?
  • 这完全是另一个问题。查看更新的答案
  • 目前我介于你的建议和os.execl() 之间,因为它用新的进程替换了当前进程。你的想法是什么?哪一个能完成工作(尤其是 exe 文件)?
  • os.execl 似乎很有希望,只要为更新的 .exe 文件取一个新名称是可以接受的。如果您想保持相同的名称,您可能必须使用 ping 技巧或类似的方法。
  • os.execl 几乎从来都不是你想在 Windows 上调用的东西。 Windows API 中没有像 Unix exec 系列系统调用那样的东西,即没有什么可以替换现有进程的内容。这个调用是由 C 运行时通过生成一个新进程(具有新 PID)并退出当前进程来伪造的。这标志着原始进程的每个句柄。例如,如果 cmd.exe 正在等待您的程序退出,它将假定它现在是控制台中的前台进程。如果你的程序也使用控制台,这将是一团糟。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-22
  • 2021-01-30
  • 2022-11-15
  • 1970-01-01
  • 2021-11-04
  • 2017-03-18
相关资源
最近更新 更多