国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > PHP > 正文

Python守護(hù)進(jìn)程和腳本單例運(yùn)行詳解

2024-05-04 21:50:41
字體:
供稿:網(wǎng)友

本篇文章主要介紹了Python守護(hù)進(jìn)程和腳本單例運(yùn)行,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

一、簡(jiǎn)介

守護(hù)進(jìn)程最重要的特性是后臺(tái)運(yùn)行;它必須與其運(yùn)行前的環(huán)境隔離開來,這些環(huán)境包括未關(guān)閉的文件描述符、控制終端、會(huì)話和進(jìn)程組、工作目錄以及文件創(chuàng)建掩碼等;它可以在系統(tǒng)啟動(dòng)時(shí)從啟動(dòng)腳本/etc/rc.d中啟動(dòng),可以由inetd守護(hù)進(jìn)程啟動(dòng),也可以有作業(yè)規(guī)劃進(jìn)程crond啟動(dòng),還可以由用戶終端(通常是shell)執(zhí)行。

Python有時(shí)需要保證只運(yùn)行一個(gè)腳本實(shí)例,以避免數(shù)據(jù)的沖突。

二、Python守護(hù)進(jìn)程

1、函數(shù)實(shí)現(xiàn)

  1. #!/usr/bin/env python 
  2. #coding: utf-8 
  3. importsys, os 
  4.    
  5. '''將當(dāng)前進(jìn)程fork為一個(gè)守護(hù)進(jìn)程 
  6.   注意:如果你的守護(hù)進(jìn)程是由inetd啟動(dòng)的,不要這樣做!inetd完成了 
  7.   所有需要做的事情,包括重定向標(biāo)準(zhǔn)文件描述符,需要做的事情只有chdir()和umask()了 
  8. ''
  9.    
  10. defdaemonize (stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): 
  11.    #重定向標(biāo)準(zhǔn)文件描述符(默認(rèn)情況下定向到/dev/null) 
  12.   try:  
  13.     pid=os.fork()  
  14.      #父進(jìn)程(會(huì)話組頭領(lǐng)進(jìn)程)退出,這意味著一個(gè)非會(huì)話組頭領(lǐng)進(jìn)程永遠(yuǎn)不能重新獲得控制終端。 
  15.     ifpid >0: 
  16.       sys.exit(0) #父進(jìn)程退出 
  17.   exceptOSError, e:  
  18.     sys.stderr.write ("fork #1 failed: (%d) %s/n"%(e.errno, e.strerror) ) 
  19.     sys.exit(1) 
  20.    
  21.    #從母體環(huán)境脫離 
  22.   os.chdir("/")#chdir確認(rèn)進(jìn)程不保持任何目錄于使用狀態(tài),否則不能umount一個(gè)文件系統(tǒng)。也可以改變到對(duì)于守護(hù)程序運(yùn)行重要的文件所在目錄 
  23.   os.umask(0) #調(diào)用umask(0)以便擁有對(duì)于寫的任何東西的完全控制,因?yàn)橛袝r(shí)不知道繼承了什么樣的umask。 
  24.   os.setsid() #setsid調(diào)用成功后,進(jìn)程成為新的會(huì)話組長和新的進(jìn)程組長,并與原來的登錄會(huì)話和進(jìn)程組脫離。 
  25.    
  26.    #執(zhí)行第二次fork 
  27.   try:  
  28.     pid=os.fork()  
  29.     ifpid >0: 
  30.       sys.exit(0) #第二個(gè)父進(jìn)程退出 
  31.   exceptOSError, e:  
  32.     sys.stderr.write ("fork #2 failed: (%d) %s/n"%(e.errno, e.strerror) ) 
  33.     sys.exit(1) 
  34.    
  35.    #進(jìn)程已經(jīng)是守護(hù)進(jìn)程了,重定向標(biāo)準(zhǔn)文件描述符 
  36.    
  37.   forfinsys.stdout, sys.stderr: f.flush() 
  38.   si=open(stdin,'r'
  39.   so=open(stdout,'a+'
  40.   se=open(stderr,'a+',0) 
  41.   os.dup2(si.fileno(), sys.stdin.fileno()) #dup2函數(shù)原子化關(guān)閉和復(fù)制文件描述符 
  42.   os.dup2(so.fileno(), sys.stdout.fileno()) 
  43.   os.dup2(se.fileno(), sys.stderr.fileno()) 
  44.    
  45. #示例函數(shù):每秒打印一個(gè)數(shù)字和時(shí)間戳 
  46. defmain(): 
  47.   importtime 
  48.   sys.stdout.write('Daemon started with pid %d/n'%os.getpid()) 
  49.   sys.stdout.write('Daemon stdout output/n'
  50.   sys.stderr.write('Daemon stderr output/n'
  51.   c=0 
  52.   whileTrue: 
  53.     sys.stdout.write('%d: %s/n'%(c, time.ctime())) 
  54.     sys.stdout.flush() 
  55.     c=c+1 
  56.     time.sleep(1) 
  57.    
  58. if__name__=="__main__"
  59.    daemonize('/dev/null','/tmp/daemon_stdout.log','/tmp/daemon_error.log'
  60.    main() 

2、類實(shí)現(xiàn)

  1. #!/usr/bin/env python 
  2. #coding: utf-8 
  3.    
  4. #python模擬linux的守護(hù)進(jìn)程 
  5.    
  6. importsys, os, time, atexit, string 
  7. fromsignalimportSIGTERM 
  8.    
  9. classDaemon: 
  10.  def__init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): 
  11.    #需要獲取調(diào)試信息,改為stdin='/dev/stdin', stdout='/dev/stdout', stderr='/dev/stderr',以root身份運(yùn)行。 
  12.   self.stdin=stdin 
  13.   self.stdout=stdout 
  14.   self.stderr=stderr 
  15.   self.pidfile=pidfile 
  16.     
  17.  def_daemonize(self): 
  18.   try: 
  19.    pid=os.fork() #第一次fork,生成子進(jìn)程,脫離父進(jìn)程 
  20.    ifpid >0: 
  21.     sys.exit(0)  #退出主進(jìn)程 
  22.   exceptOSError, e: 
  23.    sys.stderr.write('fork #1 failed: %d (%s)/n'%(e.errno, e.strerror)) 
  24.    sys.exit(1) 
  25.     
  26.   os.chdir("/")  #修改工作目錄 
  27.   os.setsid()   #設(shè)置新的會(huì)話連接 
  28.   os.umask(0)   #重新設(shè)置文件創(chuàng)建權(quán)限 
  29.     
  30.   try: 
  31.    pid=os.fork()#第二次fork,禁止進(jìn)程打開終端 
  32.    ifpid >0: 
  33.     sys.exit(0) 
  34.   exceptOSError, e: 
  35.    sys.stderr.write('fork #2 failed: %d (%s)/n'%(e.errno, e.strerror)) 
  36.    sys.exit(1) 
  37.     
  38.    #重定向文件描述符 
  39.   sys.stdout.flush() 
  40.   sys.stderr.flush() 
  41.   si=file(self.stdin,'r'
  42.   so=file(self.stdout,'a+'
  43.   se=file(self.stderr,'a+',0) 
  44.   os.dup2(si.fileno(), sys.stdin.fileno()) 
  45.   os.dup2(so.fileno(), sys.stdout.fileno()) 
  46.   os.dup2(se.fileno(), sys.stderr.fileno()) 
  47.     
  48.    #注冊(cè)退出函數(shù),根據(jù)文件pid判斷是否存在進(jìn)程 
  49.   atexit.register(self.delpid) 
  50.   pid=str(os.getpid()) 
  51.   file(self.pidfile,'w+').write('%s/n'%pid) 
  52.     
  53.  defdelpid(self): 
  54.   os.remove(self.pidfile) 
  55.    
  56.  defstart(self): 
  57.    #檢查pid文件是否存在以探測(cè)是否存在進(jìn)程 
  58.   try: 
  59.    pf=file(self.pidfile,'r'
  60.    pid=int(pf.read().strip()) 
  61.    pf.close() 
  62.   exceptIOError: 
  63.    pid=None 
  64.     
  65.   ifpid: 
  66.    message='pidfile %s already exist. Daemon already running!/n' 
  67.    sys.stderr.write(message%self.pidfile) 
  68.    sys.exit(1) 
  69.      
  70.   #啟動(dòng)監(jiān)控 
  71.   self._daemonize() 
  72.   self._run() 
  73.    
  74.  defstop(self): 
  75.   #從pid文件中獲取pid 
  76.   try: 
  77.    pf=file(self.pidfile,'r'
  78.    pid=int(pf.read().strip()) 
  79.    pf.close() 
  80.   exceptIOError: 
  81.    pid=None 
  82.     
  83.   ifnotpid: #重啟不報(bào)錯(cuò) 
  84.    message='pidfile %s does not exist. Daemon not running!/n' 
  85.    sys.stderr.write(message%self.pidfile) 
  86.    return 
  87.    
  88.    #殺進(jìn)程 
  89.   try: 
  90.    while1: 
  91.     os.kill(pid, SIGTERM) 
  92.     time.sleep(0.1) 
  93.     #os.system('hadoop-daemon.sh stop datanode'
  94.     #os.system('hadoop-daemon.sh stop tasktracker'
  95.     #os.remove(self.pidfile) 
  96.   exceptOSError, err: 
  97.    err=str(err) 
  98.    iferr.find('No such process') >0: 
  99.     ifos.path.exists(self.pidfile): 
  100.      os.remove(self.pidfile) 
  101.    else
  102.     printstr(err) 
  103.     sys.exit(1) 
  104.    
  105.  defrestart(self): 
  106.   self.stop() 
  107.   self.start() 
  108.    
  109.  def_run(self): 
  110.   """ run your fun""" 
  111.   whileTrue: 
  112.    #fp=open('/tmp/result','a+'
  113.    #fp.write('Hello World/n'
  114.    sys.stdout.write('%s:hello world/n'%(time.ctime(),)) 
  115.    sys.stdout.flush()  
  116.    time.sleep(2) 
  117.      
  118.    
  119. if__name__=='__main__'
  120.   daemon=Daemon('/tmp/watch_process.pid', stdout='/tmp/watch_stdout.log'
  121.   iflen(sys.argv)==2: 
  122.     if'start'==sys.argv[1]: 
  123.       daemon.start() 
  124.     elif'stop'==sys.argv[1]: 
  125.       daemon.stop() 
  126.     elif'restart'==sys.argv[1]: 
  127.       daemon.restart() 
  128.     else
  129.       print'unknown command' 
  130.       sys.exit(2) //Vevb.com 
  131.     sys.exit(0) 
  132.   else
  133.     print'usage: %s start|stop|restart'%sys.argv[0] 
  134.     sys.exit(2) 

它是當(dāng)Daemon設(shè)計(jì)成一個(gè)模板,在其他文件中from daemon import Daemon,然后定義子類,重寫run()方法實(shí)現(xiàn)自己的功能。

  1. classMyDaemon(Daemon): 
  2.   defrun(self): 
  3.     whileTrue: 
  4.       fp=open('/tmp/run.log','a+'
  5.       fp.write('Hello World/n'
  6.       time.sleep(1) 

不足:信號(hào)處理signal.signal(signal.SIGTERM, cleanup_handler)暫時(shí)沒有安裝,注冊(cè)程序退出時(shí)的回調(diào)函數(shù)delpid()沒有被調(diào)用。

然后,再寫個(gè)shell命令,加入開機(jī)啟動(dòng)服務(wù),每隔2秒檢測(cè)守護(hù)進(jìn)程是否啟動(dòng),若沒有啟動(dòng)則啟動(dòng),自動(dòng)監(jiān)控恢復(fù)程序。

  1. #/bin/sh 
  2. whiletrue 
  3. do 
  4.  count=`ps-ef |grep"daemonclass.py"|grep-v"grep"
  5.  if["$?"!="0"];then 
  6.    daemonclass.py start 
  7.  fi 
  8.  sleep2 
  9. done 

三、python保證只能運(yùn)行一個(gè)腳本實(shí)例

1、打開文件本身加鎖

  1. #!/usr/bin/env python 
  2. #coding: utf-8 
  3. importfcntl, sys, time, os 
  4. pidfile=0 
  5.    
  6. defApplicationInstance(): 
  7.   globalpidfile 
  8.   pidfile=open(os.path.realpath(__file__),"r"
  9.   try: 
  10.     fcntl.flock(pidfile, fcntl.LOCK_EX | fcntl.LOCK_NB)#創(chuàng)建一個(gè)排他鎖,并且所被鎖住其他進(jìn)程不會(huì)阻塞 
  11.   except: 
  12.     print"another instance is running..." 
  13.     sys.exit(1) 
  14.    
  15. if__name__=="__main__"
  16.   ApplicationInstance() 
  17.   whileTrue: 
  18.     print'running...' 
  19.     time.sleep(1) 

注意:open()參數(shù)不能使用w,否則會(huì)覆蓋本身文件;pidfile必須聲明為全局變量,否則局部變量生命周期結(jié)束,文件描述符會(huì)因引用計(jì)數(shù)為0被系統(tǒng)回收(若整個(gè)函數(shù)寫在主函數(shù)中,則不需要定義成global)。

2、打開自定義文件并加鎖

  1. #!/usr/bin/env python 
  2. #coding: utf-8 
  3. importfcntl, sys, time 
  4. pidfile=0 
  5.    
  6. defApplicationInstance(): 
  7.   globalpidfile 
  8.   pidfile=open("instance.pid","w"
  9.   try: 
  10.     fcntl.lockf(pidfile, fcntl.LOCK_EX | fcntl.LOCK_NB)#創(chuàng)建一個(gè)排他鎖,并且所被鎖住其他進(jìn)程不會(huì)阻塞 
  11.   exceptIOError: 
  12.     print"another instance is running..." 
  13.     sys.exit(0) 
  14.    
  15. if__name__=="__main__"
  16.   ApplicationInstance() 
  17.   whileTrue: 
  18.     print'running...' 
  19.     time.sleep(1) 

3、檢測(cè)文件中PID

  1. #!/usr/bin/env python 
  2. #coding: utf-8 
  3. importtime, os, sys 
  4. importsignal 
  5.    
  6. pidfile='/tmp/process.pid' 
  7.    
  8. defsig_handler(sig, frame): 
  9.   ifos.path.exists(pidfile): 
  10.     os.remove(pidfile) 
  11.   sys.exit(0) 
  12.    
  13. defApplicationInstance(): 
  14.   signal.signal(signal.SIGTERM, sig_handler) 
  15.   signal.signal(signal.SIGINT, sig_handler) 
  16.   signal.signal(signal.SIGQUIT, sig_handler) 
  17.    
  18.   try: 
  19.    pf=file(pidfile,'r'
  20.    pid=int(pf.read().strip()) 
  21.    pf.close() 
  22.   exceptIOError: 
  23.    pid=None 
  24.     
  25.   ifpid: 
  26.    sys.stdout.write('instance is running.../n'
  27.    sys.exit(0) 
  28.    
  29.   file(pidfile,'w+').write('%s/n'%os.getpid()) 
  30.    
  31. if__name__=="__main__"
  32.   ApplicationInstance() 
  33.   whileTrue: 
  34.     print'running...' 
  35.     time.sleep(1) 

4、檢測(cè)特定文件夾或文件

  1. #!/usr/bin/env python 
  2. #coding: utf-8 
  3. importtime, commands, signal, sys 
  4.    
  5. defsig_handler(sig, frame): 
  6.   ifos.path.exists("/tmp/test"): 
  7.     os.rmdir("/tmp/test"
  8.   sys.exit(0) 
  9.    
  10. defApplicationInstance(): 
  11.   signal.signal(signal.SIGTERM, sig_handler) 
  12.   signal.signal(signal.SIGINT, sig_handler) 
  13.   signal.signal(signal.SIGQUIT, sig_handler) 
  14.   ifcommands.getstatusoutput("mkdir /tmp/test")[0]: 
  15.     print"instance is running..." 
  16.     sys.exit(0) 
  17.    
  18. if__name__=="__main__"
  19.   ApplicationInstance() 
  20.   whileTrue: 
  21.     print'running...' 
  22.     time.sleep(1) 

也可以檢測(cè)某一個(gè)特定的文件,判斷文件是否存在:

  1. importos 
  2. importos.path 
  3. importtime 
  4.     
  5.     
  6. #class used to handle one application instance mechanism 
  7. classApplicationInstance: 
  8.     
  9.   #specify the file used to save the application instance pid 
  10.   def__init__(self, pid_file ): 
  11.     self.pid_file=pid_file 
  12.     self.check() 
  13.     self.startApplication() 
  14.     
  15.   #check if the current application is already running 
  16.   defcheck(self): 
  17.     #check if the pidfile exists 
  18.     ifnotos.path.isfile(self.pid_file ): 
  19.       return 
  20.     #read the pid from the file 
  21.     pid=0 
  22.     try: 
  23.       file=open(self.pid_file,'rt'
  24.       data=file.read() 
  25.       file.close() 
  26.       pid=int( data ) 
  27.     except: 
  28.       pass 
  29.     #check if the process with specified by pid exists 
  30.     if0==pid: 
  31.       return 
  32.     
  33.     try: 
  34.       os.kill( pid,0) #this will raise an exception if the pid is not valid 
  35.     except: 
  36.       return 
  37.     
  38.     #exit the application 
  39.     print"The application is already running..." 
  40.     exit(0)#exit raise an exception so don't put it in a try/except block 
  41.     
  42.   #called when the single instance starts to save it's pid 
  43.   defstartApplication(self): 
  44.     file=open(self.pid_file,'wt'
  45.     file.write(str( os.getpid() ) ) 
  46.     file.close() 
  47.     
  48.   #called when the single instance exit ( remove pid file ) 
  49.   defexitApplication(self): 
  50.     try: 
  51.       os.remove(self.pid_file ) 
  52.     except: 
  53.       pass 
  54.     
  55.     
  56. if__name__=='__main__'
  57.   #create application instance 
  58.   appInstance=ApplicationInstance('/tmp/myapp.pid'
  59.     
  60.   #do something here 
  61.   print"Start MyApp" 
  62.   time.sleep(5) #sleep 5 seconds 
  63.   print"End MyApp" 
  64.     
  65.   #remove pid file 
  66.   appInstance.exitApplication() 

上述os.kill( pid, 0 )用于檢測(cè)一個(gè)為pid的進(jìn)程是否還活著,若該pid的進(jìn)程已經(jīng)停止則拋出異常,若正在運(yùn)行則不發(fā)送kill信號(hào)。

5、socket監(jiān)聽一個(gè)特定端口

  1. #!/usr/bin/env python 
  2. #coding: utf-8 
  3. importsocket, time, sys 
  4.    
  5.    
  6. defApplicationInstance(): 
  7.   try:   
  8.     globals 
  9.     s=socket.socket() 
  10.     host=socket.gethostname() 
  11.     s.bind((host,60123)) 
  12.   except: 
  13.     print"instance is running..." 
  14.     sys.exit(0) 
  15.    
  16. if__name__=="__main__"
  17.   ApplicationInstance() 
  18.   whileTrue: 
  19.     print'running...' 
  20.     time.sleep(1) 

可以將該函數(shù)使用裝飾器實(shí)現(xiàn),便于重用(效果與上述相同):

  1. #!/usr/bin/env python 
  2. #coding: utf-8 
  3. importsocket, time, sys 
  4. importfunctools 
  5.    
  6. #使用裝飾器實(shí)現(xiàn) 
  7. defApplicationInstance(func): 
  8.   @functools.wraps(func) 
  9.   deffun(*args,**kwargs): 
  10.     importsocket 
  11.     try: 
  12.       globals 
  13.       s=socket.socket() 
  14.       host=socket.gethostname() 
  15.       s.bind((host,60123)) 
  16.     except: 
  17.       print('already has an instance...'
  18.       returnNone 
  19.     returnfunc(*args,**kwargs) 
  20.   returnfun 
  21.    
  22. @ApplicationInstance 
  23. defmain(): 
  24.   whileTrue: 
  25.     print'running...' 
  26.     time.sleep(1) 
  27.    
  28. if__name__=="__main__"
  29.   main() 

四、總結(jié)

(1)守護(hù)進(jìn)程和單腳本運(yùn)行在實(shí)際應(yīng)用中比較重要,方法也比較多,可選擇合適的來進(jìn)行修改,可以將它們做成一個(gè)單獨(dú)的類或模板,然后子類化實(shí)現(xiàn)自定義。

(2)daemon監(jiān)控進(jìn)程自動(dòng)恢復(fù)避免了nohup和&的使用,并配合shell腳本可以省去很多不定時(shí)啟動(dòng)掛掉服務(wù)器的麻煩。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 温泉县| 齐河县| 河南省| 九龙城区| 金门县| 新源县| 洞口县| 江津市| 岳阳市| 偏关县| 延川县| 涞源县| 兴义市| 永福县| 渝北区| 阳曲县| 饶河县| 石城县| 潢川县| 高密市| 西华县| 思茅市| 西华县| 万山特区| 博客| 游戏| 武冈市| 始兴县| 阳西县| 扶沟县| 赞皇县| 突泉县| 正安县| 泉州市| 本溪| 定陶县| 宝丰县| 伊宁市| 台北市| 甘肃省| 嫩江县|