Python daemon and script single-instance run

Source: Internet
Author: User
This article mainly introduces the Python daemon and script run, small series feel very good, and now share to everyone, but also for everyone to do a reference. Let's take a look at it with a little knitting.

First, Introduction

The most important feature of the daemon is running in the background, which must be isolated from the environment before it is run, including non-closed file descriptors, control terminals, session and process groups, working directories, and file creation masks, and so on, which can be/etc/from startup scripts at system startup RC.D, which can be started by the inetd daemon, can be started by the job planning process Crond, and can be executed by the user terminal (usually the shell).

Python sometimes needs to ensure that only one instance of a script is run to avoid data conflicts.

Second, the Python daemon

1. Function implementation

#!/usr/bin/env python #coding: utf-8 import sys, OS "fork the current process into a daemon note: If your daemon is initiated by inetd, do not do so! INETD has done all the things that need to be done, including redirecting standard file descriptors, the only thing that needs to be done is chdir () and Umask () "Def daemonize (stdin= '/dev/null ', stdout= '/dev/null ', Stderr= '/dev/null '): #重定向标准文件描述符 (by default directed to/dev/null) Try:pid = Os.fork () #父进程 (conversation group leader process) exits, which means that a non-conversation group leader process never     The control terminal can be re-acquired. If PID > 0:sys.exit (0) #父进程退出 except OSError, E:sys.stderr.write ("Fork #1 failed: (%d)%s\n"% (E.errn O, E.strerror)) Sys.exit (1) #从母体环境脱离 Os.chdir ("/") #chdir确认进程不保持任何目录于使用状态, otherwise you cannot umount a file system.   It can also be changed to the directory that is important for the daemon to run Os.umask (0) #调用umask (0) in order to have complete control over anything written, because sometimes it is not known what kind of umask to inherit.      Os.setsid () #setsid调用成功后, the process becomes the new session leader and the new process leader, and is detached from the original logon session and process group. #执行第二次fork try:pid = os.fork () if PID > 0:sys.exit (0) #第二个父进程退出 except OSError, e:sys.stde Rr.write ("Fork #2 failed: (%d)%s\n"% (E.errno, e.strerror)) Sys.exit (1) #进程已经是守护进程了, redirect standard file descriptor for F in S Ys.stdout, SYS.STderr:f.flush () si = open (stdin, ' r ') so = open (stdout, ' A + ') se = open (stderr, ' A + ', 0) os.dup2 (Si.fileno (), SYS . Stdin.fileno ()) #dup2函数原子化关闭和复制文件描述符 os.dup2 (So.fileno (), Sys.stdout.fileno ()) os.dup2 (Se.fileno (), Sys.stderr.fileno ()) #示例函数: Prints a number and timestamp def main (): Import time Sys.stdout.write (' Daemon started with PID%d\n '% OS     . Getpid ()) sys.stdout.write (' Daemon stdout output\n ') sys.stderr.write (' Daemon stderr output\n ') c = 0 while True: Sys.stdout.write ('%d:%s\n '% (c, time.ctime ())) Sys.stdout.flush () c = c+1 Time.sleep (1) if __name__ = = " __main__ ": daemonize ('/dev/null ', '/tmp/daemon_stdout.log ', '/tmp/daemon_error.log ') main ()

can be ps-ef by command | grep daemon.py views the inheritance running in the background,/tmp/daemon_error.log logs the error run log, and the standard output log is logged in/tmp/daemon_stdout.log.

2. Class implementation

#!/usr/bin/env python #coding: utf-8 #python模拟linux的守护进程 import sys, OS, time, atexit, string from signal import Sigte RM class Daemon:def __init__ (self, pidfile, stdin= '/dev/null ', stdout= '/dev/null ', stderr= '/dev/null '): #需要获取调试信息, change   For stdin= '/dev/stdin ', stdout= '/dev/stdout ', stderr= '/dev/stderr ', run as root.     Self.stdin = stdin Self.stdout = stdout Self.stderr = stderr self.pidfile = Pidfile def _daemonize (self): try: PID = Os.fork () #第一次fork, generating child processes, leaving parent process if PID > 0:sys.exit (0) #退出主进程 except OSError, E:SYS.STDERR.WR Ite (' Fork #1 failed:%d (%s) \ n '% (E.errno, e.strerror)) Sys.exit (1) os.chdir ("/") #修改工作目录 Os.setsid () #设置新 Session connection Os.umask (0) #重新设置文件创建权限 try:pid = Os.fork () #第二次fork, disable the process from opening the terminal if PID > 0:sys.exit (0) Exce PT OSError, E:sys.stderr.write (' fork #2 failed:%d (%s) \ n '% (E.errno, e.strerror)) Sys.exit (1) #重定向文件描述符 s  Ys.stdout.flush () sys.stderr.flush () si = file (Self.stdin, ' R ') so = File (Self.stdout, ' A + ') se = file (self.stderr, ' A + ', 0) os.dup2 (Si.fileno (), Sys.stdin.fileno ()) os.dup2 (so.fi Leno (), Sys.stdout.fileno ()) os.dup2 (Se.fileno (), Sys.stderr.fileno ()) #注册退出函数, according to the file PID to determine whether there is a process atexit.register (s ELF.DELPID) pid = str (os.getpid ()) file (Self.pidfile, ' w+ '). Write ('%s\n '% pid) def delpid (self): Os.remove (self. Pidfile) def start (self): #检查pid文件是否存在以探测是否存在进程 try:pf = File (self.pidfile, ' r ') pid = Int (Pf.read (). Strip () ) Pf.close () except ioerror:pid = None if pid:message = ' pidfile%s already exist. Daemon already running!\n ' sys.stderr.write (message% self.pidfile) sys.exit (1) #启动监控 self._daemonize () sel F._run () def stop (self): #从pid文件中获取pid try:pf = File (self.pidfile, ' r ') pid = Int (Pf.read (). Strip ()) pf.cl OSE () except ioerror:pid = None if not pid: #重启不报错 message = ' Pidfile%s does not exist. Daemon not running!\n ' sys.stderr.write (message% self.pidfile)    return #杀进程 try:while 1:os.kill (PID, SIGTERM) Time.sleep (0.1) #os. System (' hadoop-daemon.sh stop  Datanode ') #os. System (' hadoop-daemon.sh stop Tasktracker ') #os. Remove (self.pidfile) except OSError, Err:err    = str (ERR) if Err.find (' No such process ') > 0:if os.path.exists (self.pidfile): Os.remove (Self.pidfile) Else:print Str (ERR) sys.exit (1) def restart (self): Self.stop () Self.start () def _run (self): "" "Run Your fun "" and "while True: #fp =open ('/tmp/result ', ' A + ') #fp. Write (' Hello world\n ') sys.stdout.write ('%s:hello World \ n '% (Time.ctime ()) Sys.stdout.flush () Time.sleep (2) if __name__ = = ' __main__ ': daemon = Daemon ('/tmp/wa Tch_process.pid ', stdout = '/tmp/watch_stdout.log ') If Len (sys.argv) = = 2:if ' start ' = = Sys.argv[1]: daemon.s Tart () elif ' stop ' = = Sys.argv[1]: daemon.stop () elif ' restart ' = = Sys.argv[1]: Daemon.restart () E Lse:print ' Unknown Command ' sys.exit (2) sys.exit (0) Else:print ' usage:%s start|stop|restart '% sys.argv[0] Sys.exit (2)

Operation Result:

It is when Daemon is designed into a template, in other files from Daemon Import daemon, and then defines subclasses, overriding the run () method to implement its own functionality.

Class Mydaemon (Daemon):  def run (self):    while True:      fp=open ('/tmp/run.log ', ' A + ')      fp.write (' Hello world\n ')      time.sleep (1)

Insufficient: Signal processing signal.signal (signal. SIGTERM, Cleanup_handler) is not installed temporarily, the callback function Delpid () is not called when the registration program exits.

Then, write a shell command, join the start-up service, every 2 seconds to detect whether the daemon is started, if not started, automatically monitor the recovery program.

#/bin/shwhile truedo count= ' Ps-ef | grep "daemonclass.py" | Grep-v "grep" ' If ["$?"! = "0"]; Then   daemonclass.py start fi sleep 2done

Third, Python guarantees that only one script instance can be run

1. Open the file itself and lock

#!/usr/bin/env python#coding:utf-8import fcntl, sys, time, ospidfile = 0  def applicationinstance ():  Global Pidfile  pidfile = open (Os.path.realpath (__file__), "R")  try:    fcntl.flock (Pidfile, Fcntl. LOCK_EX | Fcntl. LOCK_NB) #创建一个排他锁 and locked other processes will not block  except:    print "another instance is running ..."    sys.exit (1)  if __ name__ = = "__main__":  applicationinstance () while  True:    print ' running ... '    time.sleep (1)

Note: The open () parameter cannot use W, otherwise it overwrites itself; The pidfile must be declared as a global variable, otherwise the local variable life cycle ends, and the file descriptor is reclaimed by the system because the reference count is 0 (if the entire function is written in the main function, it does not need to be defined as global).

2. Open the customization file and lock it

#!/usr/bin/env python#coding:utf-8import fcntl, sys, timepidfile = 0  def applicationinstance ():  Global Pidfile  pidfile = open ("Instance.pid", "W")  try:    fcntl.lockf (Pidfile, Fcntl. LOCK_EX | Fcntl. LOCK_NB) #创建一个排他锁 and locked other processes will not block  except IOError:    print "another instance is running ..."    sys.exit (0)  if __name__ = = "__main__":  applicationinstance () while  True:    print ' running ... '    time.sleep (1)

3, the detection file PID

#!/usr/bin/env python#coding:utf-8import time, OS, sysimport signal  pidfile = '/tmp/process.pid '  def sig_ Handler (sig, frame):  if Os.path.exists (pidfile):    os.remove (pidfile)  sys.exit (0)  def ApplicationInstance ():  signal.signal (signal. SIGTERM, Sig_handler)  signal.signal (signal. SIGINT, Sig_handler)  signal.signal (signal. Sigquit, Sig_handler)    try:   pf = file (pidfile, ' R ')   pid = Int (Pf.read (). Strip ())   pf.close ()  Except IOError:   pid = None     if pid:   sys.stdout.write (' instance is running...\n ')   sys.exit (0)    file (pidfile, ' w+ '). Write ('%s\n '% os.getpid ())  if __name__ = = "__main__":  applicationinstance () While  True:    print ' running ... '    time.sleep (1)

4. Detecting specific folders or files

#!/usr/bin/env python#coding:utf-8import time, commands, Signal, SYS  def sig_handler (SIG, frame):  if Os.path.exists ("/tmp/test"):    os.rmdir ("/tmp/test")  sys.exit (0)  def applicationinstance ():  Signal.signal (signal. SIGTERM, Sig_handler)  signal.signal (signal. SIGINT, Sig_handler)  signal.signal (signal. Sigquit, Sig_handler)  if Commands.getstatusoutput ("Mkdir/tmp/test") [0]:    print "instance is running ..."    sys.exit (0)  if __name__ = = "__main__":  applicationinstance () while  True:    print ' running ... '    time.sleep (1)

You can also detect a specific file to determine if the file exists:

Import Osimport os.pathimport time #class used to handle one application instance Mechanismclass applicationinstance: #specify the file used to save the application instance PID def __init__ (Self, pid_file): Self.pid_file = Pid_f Ile Self.check () self.startapplication () #check If the current application are already running Def check (self)    : #check If the pidfile exists if not Os.path.isfile (self.pid_file): Return #read the PID from the file    PID = 0 try:file = open (Self.pid_file, ' rt ') data = File.read () file.close () pid = int (data) Except:pass #check If the process with specified by PID exists if 0 = = Pid:return Try:os.    Kill (PID, 0) #this would raise an exception if the PID was not valid Except:return #exit the application     Print "The application is already running ..." exit (0) #exit raise an exception so don ' t put it in a try/except block #called when the SinGLE instance starts to save it's PID def startapplication (self): File = open (self.pid_file, ' wt ') file.write (s TR (Os.getpid ())) File.close () #called when the single instance exit (remove PID file) def exitapplication (SE LF): Try:os.remove (self.pid_file) except:pass if __name__ = = ' __main__ ': #create application in  Stance appinstance = applicationinstance ('/tmp/myapp.pid ') #do something here print "Start MyApp" Time.sleep (5) #sleep 5 Seconds print "End MyApp" #remove pid file Appinstance.exitapplication ()

The above Os.kill (PID, 0) is used to detect whether a PID process is still alive, if the PID process has stopped the exception is thrown, if it is running, do not send a kill signal.

5, socket monitoring a specific port

#!/usr/bin/env python#coding:utf-8import socket, time, SYS    def applicationinstance ():  try:      global S    s = Socket.socket ()    host = Socket.gethostname ()    S.bind ((host, 60123))  except:    print "instance is Running ... "    sys.exit (0)  if __name__ = =" __main__ ":  applicationinstance () while  True:    print ' Running '    time.sleep (1)

This function can be implemented using adorners for ease of reuse (the effect is the same as above):

#!/usr/bin/env python#coding:utf-8import socket, time, sysimport functools  #使用装饰器实现def applicationinstance (func) :  @functools. Wraps (func)  def Fun (*args,**kwargs):    import Socket    Try:      global s      s = Socket.socket ()      host = Socket.gethostname ()      S.bind ((host, 60123))    except:      print (' already have an Instance ... ')      return None    return func (*args,**kwargs) return fun  @ApplicationInstancedef Main ( ): While  True:    print ' running ... '    time.sleep (1)  if __name__ = = "__main__":  Main ()

Iv. Summary

(1) Daemon and single script running in the actual application of the more important, the method is more, you can choose the appropriate to modify, you can make them into a separate class or template, and then sub-class implementation of the Custom.

(2) Daemon monitoring process Auto-recovery avoids the use of nohup and &, and with shell scripts can save a lot of time to start the trouble of hanging off the server.

The above is the whole content of this article, I hope that everyone's learning has helped, but also hope that we support topic.alibabacloud.com.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.