我一直致力于为我作为无头服务器运行的覆盆子pi开发监控脚本。作为其中的一部分,我希望它对关闭事件作出反应。
我尝试使用signal
模块,它确实做出了反应并调用了我的关机例程,但它在关机例程发生的很晚才发生,我想尝试找到一种方法让它在很快后做出反应发出关闭请求,而不是等待操作系统要求python退出。
这是在树莓派1 B上运行,使用最新的jessie lite图像 我使用的是python 3,我的python脚本本身就是init脚本:
#!/usr/bin/python3
### BEGIN INIT INFO
# Provides: monitor
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start the monitor daemon
# Description: Start the monitor daemon during system boot
### END INIT INFO
import os, psutil, socket, sys, time
from daemon import Daemon
from RPLCD import CharLCD
from subprocess import Popen, PIPE
import RPi.GPIO as GPIO
GPIO.setwarnings(False)
def get_cpu_temperature():
process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE)
output, _error = process.communicate()
output = output.decode('utf8')
return float(output[output.index('=') + 1:output.rindex("'")])
class MyDaemon(Daemon):
def run(self):
lcd = CharLCD(pin_rs=7, pin_rw=4, pin_e=8, pins_data=[25, 24, 23, 18], numbering_mode=GPIO.BCM, cols=40, rows=2, dotsize=8)
while not self.exitflag:
gw = os.popen("ip -4 route show default").read().split()
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.connect((gw[2], 0))
ipaddr = s.getsockname()[0]
lcd.cursor_pos = (0, 0)
lcd.write_string("IP:" + ipaddr)
gateway = gw[2]
lcd.cursor_pos = (1, 0)
lcd.write_string("GW:" + gateway)
except IndexError:
lcd.cursor_pos = (0, 0)
lcd.write_string("IP:No Network")
lcd.cursor_pos = (1, 0)
lcd.write_string("GW:No Network")
host = socket.gethostname()
lcd.cursor_pos = (0, 20)
lcd.write_string("Host:" + host)
for num in range(10):
temp = get_cpu_temperature()
perc = psutil.cpu_percent()
lcd.cursor_pos = (1, 20)
lcd.write_string("CPU :{:5.1f}% {:4.1f}\u00DFC".format(perc, temp))
if (self.exitflag):
break
time.sleep(2)
lcd.clear()
## lcd.cursor_pos = (13, 0)
lcd.write_string("Shutting Down")
if __name__ == "__main__":
daemon = MyDaemon('/var/run/monitor.pid')
if len(sys.argv) == 2:
if 'start' == sys.argv[1]:
daemon.start()
elif 'stop' == sys.argv[1]:
daemon.stop()
elif 'restart' == sys.argv[1]:
daemon.restart()
elif 'run' == sys.argv[1]:
daemon.run()
else:
print("Unknown command")
sys.exit(2)
sys.exit(0)
else:
print("usage: %s start|stop|restart" % sys.argv[0])
sys.exit(2)
"""Generic linux daemon base class for python 3.x."""
import sys, os, time, signal
class Daemon:
"""A generic daemon class.
Usage: subclass the daemon class and override the run() method."""
def __init__(self, pidfile):
self.pidfile = pidfile
self.exitflag = False
signal.signal(signal.SIGINT, self.exit_signal)
signal.signal(signal.SIGTERM, self.exit_signal)
def daemonize(self):
"""Deamonize class. UNIX double fork mechanism."""
try:
pid = os.fork()
if pid > 0:
# exit first parent
sys.exit(0)
except OSError as err:
sys.stderr.write('fork #1 failed: {0}\n'.format(err))
sys.exit(1)
# decouple from parent environment
os.chdir('/')
os.setsid()
os.umask(0)
# do second fork
try:
pid = os.fork()
if pid > 0:
# exit from second parent
sys.exit(0)
except OSError as err:
sys.stderr.write('fork #2 failed: {0}\n'.format(err))
sys.exit(1)
# redirect standard file descriptors
sys.stdout.flush()
sys.stderr.flush()
si = open(os.devnull, 'r')
so = open(os.devnull, 'a+')
se = open(os.devnull, 'a+')
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
pid = str(os.getpid())
with open(self.pidfile,'w+') as f:
f.write(pid + '\n')
def start(self):
"""Start the daemon."""
# Check for a pidfile to see if the daemon already runs
try:
with open(self.pidfile,'r') as pf:
pid = int(pf.read().strip())
except IOError:
pid = None
if pid:
message = "pidfile {0} already exist. Daemon already running?\n"
sys.stderr.write(message.format(self.pidfile))
sys.exit(1)
# Start the daemon
self.daemonize()
self.run()
def stop(self):
"""Stop the daemon."""
# Get the pid from the pidfile
try:
with open(self.pidfile,'r') as pf:
pid = int(pf.read().strip())
except IOError:
pid = None
if not pid:
message = "pidfile {0} does not exist. Daemon not running?\n"
sys.stderr.write(message.format(self.pidfile))
return # not an error in a restart
# Try killing the daemon process
try:
while 1:
os.kill(pid, signal.SIGTERM)
time.sleep(0.1)
except OSError as err:
e = str(err.args)
if e.find("No such process") > 0:
if os.path.exists(self.pidfile):
os.remove(self.pidfile)
else:
print (str(err.args))
sys.exit(1)
def restart(self):
"""Restart the daemon."""
self.stop()
self.start()
def exit_signal(self, sig, stack):
self.exitflag = True
try:
os.remove(self.pidfile)
except FileNotFoundError:
pass
def run(self):
"""You should override this method when you subclass Daemon.
It will be called after the process has been daemonized by
start() or restart()."""
所以总之有什么方法我可以在关机时尽可能早地检测到关机,无论它如何调用,最好还能从python中检测到重启
答案 0 :(得分:1)
不要反应。 附表
Unixoid系统在启动和关闭时具有完善的启动和停止服务机制。只需在系统关闭时添加其中一个即可停止;您通常甚至可以定义可以调用这些关闭脚本的顺序。
现在,我不了解您使用Linux的这些系统中的哪一个。您可能正在使用
无论哪种方式,系统上都有很多此类服务文件的例子;如果您正在运行systemd,systemctl
将显示当前加载的服务,并显示您应该查看哪些文件进行复制并添加为您自己的服务。如果您正在运行SysV-Style init,请查看/etc/init.d以获取大量脚本。
您将找到有关如何为特定运行级别/系统目标添加和启用init脚本或systemd服务文件的大量信息。