Python守護進程和腳本單例運行

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

一、簡介

? ???守護進程最重要的特性是后臺運行;它必須與其運行前的環境隔離開來,這些環境包括未關閉的文件描述符、控制終端、會話和進程組、工作目錄以及文件創建掩碼等;它可以在系統啟動時從啟動腳本/etc/rc.d中啟動,可以由inetd守護進程啟動,也可以有作業規劃進程crond啟動,還可以由用戶終端(通常是shell)執行。
???????Python有時需要保證只運行一個腳本實例,以避免數據的沖突。?

二、Python守護進程

1、函數實現

#!/usr/bin/env?python??
#coding:?utf-8??
import?sys,?os??'''將當前進程fork為一個守護進程??注意:如果你的守護進程是由inetd啟動的,不要這樣做!inetd完成了??所有需要做的事情,包括重定向標準文件描述符,需要做的事情只有chdir()和umask()了??
'''??def?daemonize?(stdin='/dev/null',?stdout='/dev/null',?stderr='/dev/null'):??#重定向標準文件描述符(默認情況下定向到/dev/null)??try:???pid?=?os.fork()???#父進程(會話組頭領進程)退出,這意味著一個非會話組頭領進程永遠不能重新獲得控制終端。??if?pid?>?0:??sys.exit(0)???#父進程退出??except?OSError,?e:???sys.stderr.write?("fork?#1?failed:?(%d)?%s\n"?%?(e.errno,?e.strerror)?)??sys.exit(1)??#從母體環境脫離??os.chdir("/")??#chdir確認進程不保持任何目錄于使用狀態,否則不能umount一個文件系統。也可以改變到對于守護程序運行重要的文件所在目錄??os.umask(0)????#調用umask(0)以便擁有對于寫的任何東西的完全控制,因為有時不知道繼承了什么樣的umask。??os.setsid()????#setsid調用成功后,進程成為新的會話組長和新的進程組長,并與原來的登錄會話和進程組脫離。??#執行第二次fork??try:???pid?=?os.fork()???if?pid?>?0:??sys.exit(0)???#第二個父進程退出??except?OSError,?e:???sys.stderr.write?("fork?#2?failed:?(%d)?%s\n"?%?(e.errno,?e.strerror)?)??sys.exit(1)??#進程已經是守護進程了,重定向標準文件描述符??for?f?in?sys.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())??#示例函數:每秒打印一個數字和時間戳??
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()??

??????? 可以通過命令ps -ef | grep daemon.py查看后臺運行的繼承,在/tmp/daemon_error.log會記錄錯誤運行日志,在/tmp/daemon_stdout.log會記錄標準輸出日志。

2、類實現

#!/usr/bin/env?python??
#coding:?utf-8??#python模擬linux的守護進程??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'):??#需要獲取調試信息,改為stdin='/dev/stdin',?stdout='/dev/stdout',?stderr='/dev/stderr',以root身份運行。??self.stdin?=?stdin??self.stdout?=?stdout??self.stderr?=?stderr??self.pidfile?=?pidfile??def?_daemonize(self):??try:??pid?=?os.fork()????#第一次fork,生成子進程,脫離父進程??if?pid?>?0:??sys.exit(0)??????#退出主進程??except?OSError,?e:??sys.stderr.write('fork?#1?failed:?%d?(%s)\n'?%?(e.errno,?e.strerror))??sys.exit(1)??os.chdir("/")??????#修改工作目錄??os.setsid()????????#設置新的會話連接??os.umask(0)????????#重新設置文件創建權限??try:??pid?=?os.fork()?#第二次fork,禁止進程打開終端??if?pid?>?0:??sys.exit(0)??except?OSError,?e:??sys.stderr.write('fork?#2?failed:?%d?(%s)\n'?%?(e.errno,?e.strerror))??sys.exit(1)??#重定向文件描述符??sys.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.fileno(),?sys.stdout.fileno())??os.dup2(se.fileno(),?sys.stderr.fileno())??#注冊退出函數,根據文件pid判斷是否存在進程??atexit.register(self.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()??self._run()??def?stop(self):??#從pid文件中獲取pid??try:??pf?=?file(self.pidfile,'r')??pid?=?int(pf.read().strip())??pf.close()??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"""??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/watch_process.pid',?stdout?=?'/tmp/watch_stdout.log')??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()??else:??print?'unknown?command'??sys.exit(2)??sys.exit(0)??else:??print?'usage:?%s?start|stop|restart'?%?sys.argv[0]??sys.exit(2)??

運行結果:

?????? 可以參考:http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/,它是當Daemon設計成一個模板,在其他文件中from?daemon?import?Daemon,然后定義子類,重寫run()方法實現自己的功能。

class?MyDaemon(Daemon):??def?run(self):??while?True:??fp=open('/tmp/run.log','a+')??fp.write('Hello?World\n')??time.sleep(1)??

??????? 不足:信號處理signal.signal(signal.SIGTERM, cleanup_handler)暫時沒有安裝,注冊程序退出時的回調函數delpid()沒有被調用。
?????? 然后,再寫個shell命令,加入開機啟動服務,每隔2秒檢測守護進程是否啟動,若沒有啟動則啟動,自動監控恢復程序。 ?????

#/bin/sh??
while?true??
do??count=`ps?-ef?|?grep?"daemonclass.py"?|?grep?-v?"grep"`??if?[?"$?"?!=?"0"?];?then??daemonclass.py?start??fi??sleep?2??
done??

三、python保證只能運行一個腳本實例

1、打開文件本身加鎖

#!/usr/bin/env?python??
#coding:?utf-8??
import?fcntl,?sys,?time,?os??
pidfile?=?0??def?ApplicationInstance():??global?pidfile??pidfile?=?open(os.path.realpath(__file__),?"r")??try:??fcntl.flock(pidfile,?fcntl.LOCK_EX?|?fcntl.LOCK_NB)?#創建一個排他鎖,并且所被鎖住其他進程不會阻塞??except:??print?"another?instance?is?running..."??sys.exit(1)??if?__name__?==?"__main__":??ApplicationInstance()??while?True:??print?'running...'??time.sleep(1)??

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

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

#!/usr/bin/env?python??
#coding:?utf-8??
import?fcntl,?sys,?time??
pidfile?=?0??def?ApplicationInstance():??global?pidfile??pidfile?=?open("instance.pid",?"w")??try:??fcntl.lockf(pidfile,?fcntl.LOCK_EX?|?fcntl.LOCK_NB)??#創建一個排他鎖,并且所被鎖住其他進程不會阻塞??except??IOError:??print?"another?instance?is?running..."??sys.exit(0)??if?__name__?==?"__main__":??ApplicationInstance()??while?True:??print?'running...'??time.sleep(1)??

3、檢測文件中PID

#!/usr/bin/env?python??
#coding:?utf-8??
import?time,?os,?sys??
import?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、檢測特定文件夾或文件

#!/usr/bin/env?python??
#coding:?utf-8??
import?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)??

?????? 也可以檢測某一個特定的文件,判斷文件是否存在:

import?os??
import?os.path??
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??self.check()??self.startApplication()??#check?if?the?current?application?is?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?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'?)??file.write(?str(?os.getpid()?)?)??file.close()??#called?when?the?single?instance?exit?(?remove?pid?file?)??def?exitApplication(?self?):??try:??os.remove(?self.pid_file?)??except:??pass??if?__name__?==?'__main__':??#create?application?instance??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()??

??????? 上述os.kill( pid, 0 )用于檢測一個為pid的進程是否還活著,若該pid的進程已經停止則拋出異常,若正在運行則不發送kill信號。

5、socket監聽一個特定端口

#!/usr/bin/env?python??
#coding:?utf-8??
import?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)??

可以將該函數使用裝飾器實現,便于重用(效果與上述相同):
?

#!/usr/bin/env?python??
#coding:?utf-8??
import?socket,?time,?sys??
import?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?has?an?instance...')??return?None??return?func(*args,**kwargs)??return?fun??@ApplicationInstance??
def?main():??while?True:??print?'running...'??time.sleep(1)??if?__name__?==?"__main__":??main()??

四、總結

(1)守護進程和單腳本運行在實際應用中比較重要,方法也比較多,可選擇合適的來進行修改,可以將它們做成一個單獨的類或模板,然后子類化實現自定義。
(2)daemon監控進程自動恢復避免了nohup和&的使用,并配合shell腳本可以省去很多不定時啟動掛掉服務器的麻煩。
(3)若有更好的設計和想法,可隨時留言,在此先感謝!

轉載于:https://my.oschina.net/mickelfeng/blog/968356

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/258394.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/258394.shtml
英文地址,請注明出處:http://en.pswp.cn/news/258394.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

分析access.log

cat access.log | awk {print $4,$1,$9} | awk -F/ {print $3}| awk -F: {print $2 ":" $3,$4} | awk {print $1,$3,$4} | uniq -c | sort -n轉載于:https://www.cnblogs.com/olderblue/p/4778339.html

AD20學習筆記4---網表導入及模塊化布局設計

前言: 本文學習視頻是B站點擊率第一的凡億教育《Altium Designer 20 19(入門到精通全38集)四層板智能車PCB設計視頻教程》,視頻地址:Altium Designer 20 19(入門到精通全38集)四層板智能車PCB設…

Paoding-Rose學習

* HttpServletRequest.getContextPath 獲取web程序root。如果是默認位置,返回””空串,否則返回 /根路徑名 * rose是如何掃描到資源的 利用spring提供的類掃描類和jar* rose建立匹配樹的過程 傳入根節點和List,按照路徑建立每個節點 * Module…

楪祈機器人_饑荒 Inori楪祈人物MOD V20161211

使用說明:1.解壓縮2.復制所有文件到游戲目錄mods3.啟動游戲,點擊mods(模組)加載MOD適用游戲版本:理論上支持所有版本的饑荒(普通,巨人,海難,聯機版)MOD說明:饑荒 Inori楪祈人物MOD;由…

javascript 模塊化

2019獨角獸企業重金招聘Python工程師標準>>> 一直好奇像node.js,require.js的模塊化是怎么做的,在看了《你不知道的javascript》后,對js的模塊化有了一些簡單的了解。這本書真的還不錯。 書里講述了js的模塊化的原理 和 現代js實現模塊化的簡…

AD20學習筆記5---PCB設計規則設置及PCB手工布線

前言: 本文學習視頻是B站點擊率第一的凡億教育《Altium Designer 20 19(入門到精通全38集)四層板智能車PCB設計視頻教程》,視頻地址:Altium Designer 20 19(入門到精通全38集)四層板智能車PCB設…

理論物理極礎9:相空間流體和吉布斯-劉維爾定理

萊尼喜歡看河,尤其喜歡看漂浮物順流而下。他猜想漂浮物如何穿過礁石,如何陷入漩渦。但是河流整體,水量,流切變,河的分流和匯聚,這是萊尼所看不到的。 相空間流體 在經典力學里,注視一個特別的初…

ComponentName的意思

ComponentName是用來打開其它應用程序中的Activity或服務的。 使用方法: Intent inew Intent();i.setComponent(new ComponentName(String packageName,String activityName ));startActivity(i); 轉載于:https://www.cnblogs.com/hrhguanli/p/3861243.html

nginx沒有worker進程_如何優雅地關閉worker進程?

點擊上方“武培軒”,選擇“設為星標”技術文章第一時間送達!之前我們講解 Nginx 命令行的時候,可以看到 Nginx 停止有兩種方式,分別是 nginx -s quit 和 nginx -s stop,其中 stop 是指立即停止 Nginx,而 qu…

[Python3]Python面向對象的程序設計

[Python3]Python面向對象的程序設計 一、面向對象的程序設計的由來 1.第一階段:面向機器,1940年以前 最早的程序設計都是采用機器語言來編寫的,直接使用二進制碼來表示機器能夠識別和執行的指令和數據。 簡單來說,就是直接編寫 0 和 1 的序列來代表程序語…

RC電路輸出波形的時域與頻域分析

RC一階電路為例進行時域和頻域分析,激勵Us為方波,以Uc作為輸出的波形相當于積分電路的輸出曲線,以Ur作為輸出的波形相當于微分電路的輸出曲線。電容對輸入電壓具有平滑作用,平滑程度與時間常數有關,衰減程度與帶寬設計…

JQuery之編寫彈窗

演示地址&#xff1a;http://sandbox.runjs.cn/show/irefekbs <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Document</title><style>*{ margin: 0; padding: 0;}#login { z-index:9999…

[ext/iconv/iconv.lo] Error 1

辦法1&#xff1a; wget http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.13.1.tar.gz tar -zxvf libiconv-1.13.1.tar.gz cd libiconv- 1.13.1 ./configure make make install 辦法2&#xff1a; wget http://down.wdlinux.cn/in/iconv_ins.sh sh iconv_ins.sh轉載于:https:/…

多麥克風做拾音的波束_麥克風陣列是什么 有哪些關鍵技術?

麥克風陣列是什么 有哪些關鍵技術&#xff1f;亞馬遜Echo和谷歌Home爭奇斗艷&#xff0c;除了云端服務&#xff0c;他們在硬件上到底有哪些差異&#xff1f;我們先將Echo和Home兩款音箱拆開來看&#xff0c;區別最大的還是麥克風陣列技術。Amazon Echo采用的是環形61麥克風陣列…

如何用AD20打開ddb文件

用AD20直接打開ddb文件會報錯&#xff0c;在AD20中使用導入向導才是ddb文件的正確打開方式。 1.用AD20直接打開ddb文件的報錯提示 2.使用導入向導打開ddb文件 除了以下兩處關鍵設置的地方&#xff0c;一路next就行。

Codeforces Round #419 (Div. 2)

1.題目A&#xff1a;Karen and Morning 題意&#xff1a; 給出hh:mm格式的時間&#xff0c;問至少經過多少分鐘后&#xff0c;該時刻為回文字符串&#xff1f; 思路&#xff1a; 簡單模擬&#xff0c;從當前時刻開始&#xff0c;如果hh的回文rh等于mm則停止累計。否則&#xff…

Java NIO 系列教程

Java NIO&#xff08;New IO&#xff09;是從Java 1.4版本開始引入的一個新的IO API&#xff0c;可以替代標準的Java IO API。本系列教程將有助于你學習和理解Java NIO。感謝并發編程網的翻譯和投遞。 &#xff08;關注ITeye官微&#xff0c;隨時隨地查看最新開發資訊、技術文章…

使用語句修改數據表結構

查詢表信息&#xff1a; sp_help dbo.T_User; 修改columnName 字段為空 alter table dbo.T_User alter column columnName datetime null; 修改columnName 默認值 ALTER TABLE dbo.T_User ADD CONSTRAINT [DF_T_User_columnName ] DEFAULT 0 FOR [columnName ];轉載于:https://…

變換上三角矩陣_關于馬爾可夫矩陣的一些個人研究成果、思考過程及相關解釋...

在幾個月以前&#xff0c;曾經有一位知乎好友邀請我回答一個問題&#xff1a;“如何證明馬爾可夫矩陣至少存在一個所有分量均不小于零的特征向量。”當時我思考了大概半個小時&#xff0c;給出了嚴謹的證明。事后由該問題引發我至少三度思考&#xff0c;對于此問題&#xff0c;…

Multisim14仿真入門筆記

本文是B站北京郵電大學鄧剛老師《Multisim仿真入門》的學習筆記&#xff0c;視頻地址&#xff1a;【電路仿真】Multisim仿真入門&#xff08;北京郵電大學 鄧剛主講&#xff09;_嗶哩嗶哩_bilibili。 1.Multisim簡介 Multisim14是一種專門用于電路仿真和設計的軟件之一&#x…