在Python中覆盖基本信号(SIGINT,SIGQUIT,SIGKILL??)
我正在写一个程序,用来根据我们公司的政策添加普通的UNIX账户(也就是修改/etc/passwd、/etc/group和/etc/shadow这些文件)。这个程序还做了一些稍微复杂的事情,比如给用户发送邮件。
我已经把所有代码都写好了,但有三段代码非常关键,它们负责更新上面提到的三个文件。这些代码已经相当稳健,因为它们会锁定这些文件(比如说,/etc/passwd.lock),先写入一个临时文件(比如说,/etc/passwd.tmp),然后再用临时文件覆盖原来的文件。我对这个过程很满意,因为它不会干扰到我程序的其他运行版本,或者系统的useradd、usermod、passwd等程序。
我最担心的事情是,在这些关键代码执行的过程中,可能会意外按下ctrl+c、ctrl+d,或者使用kill命令。这让我关注到了信号模块,它似乎正好可以做到我想要的:在“关键”区域内忽略某些信号。我使用的是一个较旧版本的Python,没有signal.SIG_IGN,所以我写了一个很棒的“pass”函数:
def passer(*a):
pass
我遇到的问题是,信号处理程序的工作方式并不是我预期的那样。给定以下测试代码:
def passer(a=None, b=None):
pass
def signalhander(enable):
signallist = (signal.SIGINT, signal.SIGQUIT, signal.SIGABRT, signal.SIGPIPE, signal.SIGALRM, signal.SIGTERM, signal.SIGKILL)
if enable:
for i in signallist:
signal.signal(i, passer)
else:
for i in signallist:
signal.signal(i, abort)
return
def abort(a=None, b=None):
sys.exit('\nAccount was not created.\n')
return
signalhander(True)
print('Enabled')
time.sleep(10) # ^C during this sleep
这个代码的问题在于,在time.sleep(10)调用期间按下^C(SIGINT)会导致这个函数停止,然后我的信号处理程序按预期接管。然而,这并没有解决我上面提到的“关键”区域问题,因为我不能容忍遇到信号的任何语句失败。
我需要某种信号处理程序,能够完全忽略SIGINT和SIGQUIT。Fedora/RH的命令“yum”是用Python写的,基本上做的就是我想要的。如果在它安装任何东西时按下^C,它会打印一条消息,比如“在两秒内按^C强制结束。”否则,^C会被忽略。我并不在乎这个两秒的警告,因为我的程序在几分之一秒内就能完成。
有人能帮我实现一个CPython 2.3的信号处理程序吗?这个程序在信号被忽略之前不会导致当前的语句/函数被取消。
如往常一样,提前感谢大家。
编辑:在S.Lott的回答后,我决定放弃信号模块。
我打算回到try: except:
块。看着我的代码,每个关键区域都有两件事情是不能中断的:用file.tmp覆盖文件和完成后移除锁(否则其他工具将无法修改文件,直到手动移除)。我把这两件事放在各自的函数里,并放在try:
块中,except:
则简单地再次调用这个函数。这样一来,如果发生KeyBoardInterrupt
或EOFError
,这个函数就会在关键代码完成之前不断自我调用。
我觉得这样应该不会出太多问题,因为我只是捕获用户提供的退出命令,而且就只有两到三行代码。理论上,如果这些异常能被快速触发,我想我可能会遇到“最大递归深度超出”的错误,但这似乎不太可能。
还有其他担忧吗?
伪代码:
def criticalRemoveLock(file):
try:
if os.path.isFile(file):
os.remove(file)
else:
return True
except (KeyboardInterrupt, EOFError):
return criticalRemoveLock(file)
def criticalOverwrite(tmp, file):
try:
if os.path.isFile(tmp):
shutil.copy2(tmp, file)
os.remove(tmp)
else:
return True
except (KeyboardInterrupt, EOFError):
return criticalOverwrite(tmp, file)
1 个回答
其实没有什么办法能让你的脚本完全安全。当然,你可以忽略一些信号,或者用 try: except:
来捕捉键盘中断,但这还是得看你的应用程序能不能处理这些中断。它必须能够在遇到中断后,从某个保存点继续操作。
你能做的就是先在临时文件上工作,而不是直接在原文件上。等你完成工作后,再把这些临时文件移动到最终的位置。我觉得从文件系统的角度来看,这种文件操作应该是“原子”的。否则,如果发生中断,你就得从头开始处理,使用干净的数据。