本文目錄一覽:
python 守護進程
一、守護進程的特性
守護進程是一個在後台運行並且不受任何終端控制的進程(守護進程獨立於所有終端,之所以脫離於終端是為了避免進程被任何終端所產生的信息所打斷,其在執行過程中的信息也不在任何終端上顯示。)
二、守護進程的作用
守護進程是一類在後台執行,生命周期較長的進程,它一般隨系統啟動運行,在系統關閉的時候停止。所以守護進程一般用作系統後台服務。
三、如何編寫一個守護進程
編寫守護進程實際上是把一個普通進程按照守護進程的特性進行改造。
守護進程的開發涉及到子進程、進程組、會晤期、信號量、文件權限、目錄和控制終端等多個概念。
由於守護進程是脫離控制終端的,因此首先創建子進程,終止父進程,使得程序在shell終端里造成一個已經運行完畢的假象。之後所有的工作都在子進程中完成,而用戶在shell終端里則可以執行其他的命令,從而使得程序以殭屍進程形式運行,在形式上做到了與控制終端的脫離。
四、python 編寫守護進程
參考:
如何給腳本寫一個守護進程
在我們日常運維中,寫腳本監控一個進程是比較常見的操作,比如我要監控mysql進程是否消失,如果消失就重啟mysql,用下面這段代碼就可以實現:
#!/bin/sh
Date=` date ‘+%c’`
while :
do
if ! psaux | grep -w mysqld | grep -v grep /dev/null 21
then
/etc/init.d/mysqldstart
echo $Datemysqldwasreboot /var/log/reboot_mysql.log
fi
done
本篇這是以mysql為例子,但實際中如果是監控的腳本出了問題,報警沒發出來,那就比較尷尬了,所以為保證我們的檢查腳本能實時運行,我們需要一個進程來守護這個腳本,這就是我們今天要說的主題,如何給腳本寫一個daemon,我們先上代碼:
#!/usr/bin/python
import subprocess
from daemonimport runner
cmd = “/root/demo_script/restart_mysql.sh”
class App():
def __init__(self):
self.stdin_path = ‘/dev/null’
self.stdout_path = ‘/dev/tty’
self.stderr_path = ‘/dev/tty’
self.pidfile_path = ‘/tmp/hello.pid’
self.pidfile_timeout = 5
def start_subprocess(self):
return subprocess.Popen(cmd, shell=True)
def run(self):
p = self.start_subprocess()
while True:
res = p.poll()
if resis not None:
p = self.start_subprocess()
if __name__ == ‘__main__’:
app = App()
daemon_runner = runner.DaemonRunner(app)
daemon_runner.do_action()
腳本比較簡單,沒什麼特別的邏輯,關於daemon這個模塊如何使用,我這裡給出官方的解釋,注意喲,是英文的,我就不翻譯了,如果有生詞就查查字典,就當多學幾個了單詞吧。
__init__(self, app)
| Setuptheparametersof a new runner.
|
| The `app` argumentmusthavethefollowingattributes:
|
| * `stdin_path`, `stdout_path`, `stderr_path`: Filesystem
| pathsto openand replacetheexisting `sys.stdin`,
| `sys.stdout`, `sys.stderr`.
|
| * `pidfile_path`: Absolutefilesystempathto a filethat
| willbeusedas thePIDfilefor thedaemon. If
| “None“, noPIDfilewillbeused.
|
| * `pidfile_timeout`: Usedas thedefault acquisition
| timeoutvaluesuppliedto therunner’s PIDlockfile.
|
| * `run`: Callablethatwillbeinvokedwhenthedaemonis
| started.
|
| do_action(self)
| Performtherequestedaction.
|
| parse_args(self, argv=None)
| Parsecommand-linearguments.
這樣就完成了,守護進程的啟動比較高大上,輸入以上代碼後,可以直接在終端輸入:
#python monitor.py start
當然還有stop,restart等參數。
這裡我介紹的是其中一個應用場景,實際中可以靈活運用,比如1台服務器上啟動的程序過多,環境配置比較複雜,就可以先啟動daemon進程,然後通過daemon來啟動其它所有應用程序,就不用一個一個應用程序啟動了,這篇就到這裡,有問題可以給我留言。
如何在python腳本中新建一個守護子進程
函數實現
[html] view plaincopy
#!/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會記錄標準輸出日誌。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/242740.html