This article focuses on Python daemons and scripts run singly, I think it's pretty good, now share it with you, but also give you a reference. Together follow the editor over to see it
I. Introduction
The most important characteristic of a daemon is that it runs in the background; it must be isolated from its pre-run environment, which consists of unclosed file descriptors, control terminals, session and process groups, working directories, and file creation masks; it can be started from the startup script /etc/ at system startup, either by the inetd daemon, or by the job planning process crond. It can also be executed by a user terminal (usually a shell).
Python sometimes needs to ensure that only one instance of a script is run to avoid data conflicts.
II. Python daemons
1、Function realization
#!/usr/bin/env python #coding: utf-8 import sys, os '''Fork the current process as a daemon Note: If your daemon was started by inetd, don't do this! inetd finishes! Inetd does everything you need to do, including redirecting standard file descriptors, and the only things you need to do are chdir() and umask(). ''' def daemonize (stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): # Redirect standard file descriptors (default to /dev/null) try: pid = () # The parent process (session group leader process) exits, which means that a non-session group leader process can never regain control of the terminal. if pid > 0: (0) # The parent process exits except OSError, e: ("fork #1 failed: (%d) %s\n" % (, ) ) (1) # Removed from the maternal environment ("/") #chdir makes sure that the process does not keep any directories in use, otherwise it cannot mount a filesystem. It is also possible to change to a directory that is important for the daemon to run. (0) # Call umask(0) in order to have full control over whatever is written, because sometimes it's not clear what kind of umask is inherited. () After a successful #setsid call, the process becomes the new session leader and the new process leader and is detached from the original login session and process group. # Execute the second fork try: pid = () if pid > 0: (0) # The second parent process exits except OSError, e: ("fork #2 failed: (%d) %s\n" % (, ) ) (1) #Process is already a daemon, redirecting standard file descriptors for f in , : () si = open(stdin, 'r') so = open(stdout, 'a+') se = open(stderr, 'a+', 0) os.dup2((), ()) The #dup2 function atomically closes and copies file descriptors os.dup2((), ()) os.dup2((), ()) # Sample function: print a number and timestamp every second def main(): import time ('Daemon started with pid %d\n' % ()) ('Daemon stdout output\n') ('Daemon stderr output\n') c = 0 while True: ('%d: %s\n' %(c, ())) () c = c+1 (1) if __name__ == "__main__": daemonize('/dev/null','/tmp/daemon_stdout.log','/tmp/daemon_error.log') main()
You can use the command ps -ef | grep to view the inheritance running in the background, in /tmp/daemon_error.log will record the error running log, in /tmp/daemon_stdout.log will record the standard output log.
2、Class realization
#!/usr/bin/env python #coding: utf-8 #python emulates linux daemons import sys, os, time, atexit, string from signal import SIGTERM class Daemon: def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): # Need to get debug information, change to stdin='/dev/stdin', stdout='/dev/stdout', stderr='/dev/stderr', run as root. = stdin = stdout = stderr = pidfile def _daemonize(self): try: pid = () # First fork, spawns child process, breaks away from parent process if pid > 0: (0) # Exit the main process except OSError, e: ('fork #1 failed: %d (%s)\n' % (, )) (1) ("/") # Modify the working directory () #Setup a new session connection (0) # Reset file creation permissions try: pid = () #Fork the process a second time to prevent it from opening a terminal. if pid > 0: (0) except OSError, e: ('fork #2 failed: %d (%s)\n' % (, )) (1) # Redirect file descriptors () () si = file(, 'r') so = file(, 'a+') se = file(, 'a+', 0) os.dup2((), ()) os.dup2((), ()) os.dup2((), ()) #Register the exit function to determine if a process exists based on the file pid. () pid = str(()) file(,'w+').write('%s\n' % pid) def delpid(self): () def start(self): # Check for the existence of a pid file to detect the presence of a process try: pf = file(,'r') pid = int(().strip()) () except IOError: pid = None if pid: message = 'pidfile %s already exist. Daemon already running!\n' (message % ) (1) #Start monitoring self._daemonize() self._run() def stop(self): # Get the pid from the pid file try: pf = file(,'r') pid = int(().strip()) () except IOError: pid = None if not pid: #Reboot without error message = 'pidfile %s does not exist. Daemon not running!\n' (message % ) return # Kill the process try: while 1: (pid, SIGTERM) (0.1) #(' stop datanode') #(' stop tasktracker') #() except OSError, err: err = str(err) if ('No such process') > 0: if (): () else: print str(err) (1) def restart(self): () () def _run(self): """ run your fun""" while True: #fp=open('/tmp/result','a+') #('Hello World\n') ('%s:hello world\n' % ((),)) () (2) if __name__ == '__main__': daemon = Daemon('/tmp/watch_process.pid', stdout = '/tmp/watch_stdout.log') if len() == 2: if 'start' == [1]: () elif 'stop' == [1]: () elif 'restart' == [1]: () else: print 'unknown command' (2) (0) else: print 'usage: %s start|stop|restart' % [0] (2)
Run results:
It is when Daemon is designed as a template, from daemon import Daemon in other files, and then define subclasses that override the run() method to implement their own functionality.
class MyDaemon(Daemon): def run(self): while True: fp=open('/tmp/','a+') ('Hello World\n') (1)
Weaknesses: signal handling (, cleanup_handler) is not installed at the moment, and the callback function delpid() when the registered program exits is not called.
Then, write a shell command to add a boot startup service that detects if the daemon is started every 2 seconds, and starts it if it is not, and automatically monitors the recovery process.
#/bin/sh while true do count=`ps -ef | grep "" | grep -v "grep"` if [ "$?" != "0" ]; then start fi sleep 2 done
III. python guarantees that only one instance of the script can be run
1. Open the file itself with a lock
#!/usr/bin/env python #coding: utf-8 import fcntl, sys, time, os pidfile = 0 def ApplicationInstance(): global pidfile pidfile = open((__file__), "r") try: (pidfile, fcntl.LOCK_EX | fcntl.LOCK_NB) # Create an exclusive lock, and other locked processes will not block. except: print "another instance is running..." (1) if __name__ == "__main__": ApplicationInstance() while True: print 'running...' (1)
Note: the open() parameter cannot use w, otherwise it will overwrite its own file; pidfile must be declared as a global variable, otherwise the life cycle of the local variable is over and the file descriptor will be reclaimed by the system due to a reference count of 0. (If the whole function is written in the main function, it doesn't need to be defined as global).
2. Open the customized file and lock it
#!/usr/bin/env python #coding: utf-8 import fcntl, sys, time pidfile = 0 def ApplicationInstance(): global pidfile pidfile = open("", "w") try: (pidfile, fcntl.LOCK_EX | fcntl.LOCK_NB) # Create an exclusive lock, and other locked processes will not block. except IOError: print "another instance is running..." (0) if __name__ == "__main__": ApplicationInstance() while True: print 'running...' (1)
3. Detection of PID in documents
#!/usr/bin/env python #coding: utf-8 import time, os, sys import signal pidfile = '/tmp/' def sig_handler(sig, frame): if (pidfile): (pidfile) (0) def ApplicationInstance(): (, sig_handler) (, sig_handler) (, sig_handler) try: pf = file(pidfile, 'r') pid = int(().strip()) () except IOError: pid = None if pid: ('instance is running...\n') (0) file(pidfile, 'w+').write('%s\n' % ()) if __name__ == "__main__": ApplicationInstance() while True: print 'running...' (1)
4. Detect specific folders or files
#!/usr/bin/env python #coding: utf-8 import time, commands, signal, sys def sig_handler(sig, frame): if ("/tmp/test"): ("/tmp/test") (0) def ApplicationInstance(): (, sig_handler) (, sig_handler) (, sig_handler) if ("mkdir /tmp/test")[0]: print "instance is running..." (0) if __name__ == "__main__": ApplicationInstance() while True: print 'running...' (1)
It is also possible to detect a specific file and determine if the file exists:
import os import import time #class used to handle one application instance mechanism class ApplicationInstance: #specify the file used to save the application instance pid def __init__( self, pid_file ): self.pid_file = pid_file () () #check if the current application is already running def check( self ): #check if the pidfile exists if not ( self.pid_file ): return #read the pid from the file pid = 0 try: file = open( self.pid_file, 'rt' ) data = () () pid = int( data ) except: pass #check if the process with specified by pid exists if 0 == pid: return try: ( pid, 0 ) #this will raise an exception if the pid is 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' ) ( str( () ) ) () #called when the single instance exit ( remove pid file ) def exitApplication( self ): try: ( self.pid_file ) except: pass if __name__ == '__main__': #create application instance appInstance = ApplicationInstance( '/tmp/' ) #do something here print "Start MyApp" (5) #sleep 5 seconds print "End MyApp" #remove pid file ()
The above ( pid, 0 ) is used to check if a process with pid is still alive, and throws an exception if the process with pid has stopped, and does not send a kill signal if it is running.
5. socket listens on a specific port
#!/usr/bin/env python #coding: utf-8 import socket, time, sys def ApplicationInstance(): try: global s s = () host = () ((host, 60123)) except: print "instance is running..." (0) if __name__ == "__main__": ApplicationInstance() while True: print 'running...' (1)
The function can be implemented using a decorator for easy reuse (same effect as above):
#!/usr/bin/env python #coding: utf-8 import socket, time, sys import functools # Implemented using decorators def ApplicationInstance(func): @(func) def fun(*args,**kwargs): import socket try: global s s = () host = () ((host, 60123)) except: print('already has an instance...') return None return func(*args,**kwargs) return fun @ApplicationInstance def main(): while True: print 'running...' (1) if __name__ == "__main__": main()
IV. Summary
(1) daemon and single script run in the actual application is more important, the method is also more, you can choose the appropriate to modify, you can make them into a separate class or template, and then subclassing to achieve customization.
(2) daemon monitoring process automatic recovery to avoid the use of nohup and & and with shell scripts can save a lot of untimely startup hangs the server trouble.
This is the whole content of this article.