如何判断运行我的python脚本的终端是否已关闭?如果用户关闭终端,我想安全地结束我的python脚本 . 我可以使用处理程序捕获SIGHUP,但不能在脚本以sudo身份运行时捕获 . 当我用sudo启动脚本并关闭终端时,python脚本继续运行 .
示例脚本:
import signal
import time
import sys
def handler(signum, frame):
fd = open ("tmp.txt", "a")
fd.write(str(signum) + " handled\n")
fd.close()
sys.exit(0)
signal.signal(signal.SIGHUP, handler)
signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGTERM, handler)
time.sleep(50)
有时,脚本将在以sudo身份运行时执行处理程序,但更常见的情况是不执行 . 在没有sudo的情况下运行时脚本总是写入文件 . 我在Raspberry Pi上运行它 . 我在LXTerminal和gnome-terminal中看到了相同的东西 . 这个示例脚本将在50秒后结束,但是我的冗长代码在无限循环中运行
最终目标是在Raspberry Pi上安装一个.desktop启动器来进行蓝牙扫描和查找设备 . 蓝牙扫描需要sudo,因为它使用4.0 BLE . 我不确定为什么bluez需要sudo,但确实如此 . 在pi上输入sudo时,它从不要求输入密码,这对我来说很好 . 问题是关闭终端后,扫描过程仍在运行 . 扫描由在终端中运行的python脚本完成 .
1 回答
sudo是为TIGHUP语义而设计的,当它是tty上某个其他进程的子进程时你会得到它 . 在这种情况下,当父进程退出时,所有进程都从内核获得自己的SIGHUP .
xterm -e sudo cmd
直接在伪终端上运行sudo . 这产生了与sudo期望的不同的SIGHUP语义 . 只有sudo从内核接收一个SIGHUP,并且只有当它的子进程也有它自己的时候才会从内核获得一个SIGHUP(因为sudo的父进程(例如bash)会这样做) .我reported the issue upstream和 it's now marked as fixed in sudo 1.8.15 and onwards .
解决方法:
如果
-c
参数是单个命令,则bash通过执行它来进行优化 . 解决另一个命令(在这种情况下是微不足道的true
),让bash坚持并在孩提时间运行sudo . 我测试过,使用这种方法,当你关闭xterm时,sig-counter从内核中获取一个SIGHUP . (对于任何其他终端仿真器,它应该是相同的 . )我测试了这个,它适用于bash和dash . 源代码包括一个方便的花花公子信号接收 - 不退出程序,您可以串行查看它收到的所有信号 .
本答案其余部分的某些部分可能略微不同步 . 在找出sudo作为控制过程与sudo作为shell差异的孩子之前,我经历了一些理论和测试方法 .
POSIX says,伪终端主端的
close()
导致:"a SIGHUP signal shall be sent to the controlling process, if any, for which the slave side of the pseudo-terminal is the controlling terminal."close()
的POSIX措辞意味着只有一个处理过程将pty作为其控制终端 .当bash是pty的slave端的控制进程时,它会导致所有其他进程接收SIGHUP . 这是sudo期待的语义 .
ssh localhost
,然后中止与~.
的连接或终止您的ssh客户端 .测试这个很棘手,因为在退出和关闭pty之前
xterm
也会自己发送一个SIGHUP . 其他终端仿真器(gnome-terminal,konsole)可能会也可能不会这样做 . 我不得不自己写一个信号测试程序,不仅仅是在第一次SIGHUP之后就死了 .除非xterm以root身份运行,否则它无法向sudo发送信号,因此sudo只能从内核获取信号 . (因为它是tty的控制进程,而在sudo下运行的进程不是 . )
sudo
手册页说:它在我看来像sudo 's double-signal avoidance logic for SIGHUP was designed for running as a child of an interactive shell. When there'没有涉及交互式shell(在交互式shell之后
exec sudo
,或者当首先没有涉及shell时),只有父进程(sudo)获得一个SIGHUP .即使在没有涉及shell的xterm中,sudo的行为也适用于SIGINT和SIGQUIT:在xterm中按^ C或^ \后,
sig-counter
只接收一个SIGINT或SIGQUIT .sudo
收到一个并且不转发它 .si_code=SI_KERNEL
在这两个过程中 .在Ubuntu 15.04上测试,
sudo --version
:1.8.9p5 .xterm -v
:XTerm(312) .在
sudo
下运行的命令仅在xterm关闭时才会看到SIGCONT .请注意,单击xterm Headers 栏上的窗口管理器关闭按钮只是让xterm手动发送一个SIGHUP . 通常这会导致xterm中的进程关闭,在这种情况下xterm将在此之后退出 . 同样,这只是xterm的行为 .
这是
bash
在获取SIGHUP时所做的事情,产生sudo
期望的行为:我不确定这部分工作是完成的 . 可能实际退出是诀窍,或者在启动命令之前它做了什么,因为
kill
和tcsetpgrp()
都失败了 .我自己尝试尝试的第一次尝试是:
(其中pts / 11是另一个终端 . )
sleep
在第一个SIGHUP之后退出,因此没有sudo的测试只显示xterm手动发送的SIGHUP .SIG-counter.c中:
我对此的第一次尝试是perl,但安装信号处理程序并没有阻止perl在信号处理程序返回后退出SIGHUP . 我看到xterm关闭之前就出现了这条消息 .
显然perl信号处理相当复杂,因为perl必须defer them until it's not in the middle of something that doesn't do proper locking .
C中的Unix系统调用是进行系统编程的“默认”方式,因此可以解决任何可能的混淆 . strace通常是一种廉价的方法来避免实际编写日志/打印代码来玩游戏 . :P