python使用fork實(shí)現(xiàn)守護(hù)進(jìn)程的方法
os模塊中的fork方法可以創(chuàng)建一個(gè)子進(jìn)程。相當(dāng)于克隆了父進(jìn)程
os.fork()
子進(jìn)程運(yùn)行時(shí),os.fork方法會(huì)返回0;
而父進(jìn)程運(yùn)行時(shí),os.fork方法會(huì)返回子進(jìn)程的PID號(hào)。
所以可以使用PID來區(qū)分兩個(gè)進(jìn)程:
#!/usr/bin/env python #coding=utf8 from time import sleep import os try: pid = os.fork() except OSError, e: pass sleep(30)
運(yùn)行代碼,查看進(jìn)程:
[root@localhost ~]# python test2.py & [1] 2464 [root@localhost ~]# ps -l F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 4 S 0 2379 2377 0 80 0 - 28879 wait pts/1 00:00:00 bash 0 S 0 2464 2379 0 80 0 - 31318 poll_s pts/1 00:00:00 python 1 S 0 2465 2464 0 80 0 - 31318 poll_s pts/1 00:00:00 python 0 R 0 2466 2379 0 80 0 - 37227 - pts/1 00:00:00 ps
可以看出第二條python進(jìn)程就是第一條的子進(jìn)程。
如剛剛所說os.fork()方法區(qū)分子進(jìn)程和父進(jìn)程
#-*- coding:utf-8 -*-
from time import sleep
import os
print('start+++++++++++++')
#創(chuàng)建子進(jìn)程之前聲明的變量
source = 10
try:
pid = os.fork()
print('pid=',pid)
if pid == 0: #子進(jìn)程
print("this is child process.")
source = source - 1 #在子進(jìn)程中source減1
else: #父進(jìn)程
print("this is parent process." )
print(source)
except (OSError,e):
pass
print('END---------------')
面代碼中,在子進(jìn)程創(chuàng)建前,聲明了一個(gè)變量source,然后在子進(jìn)程中減1,最后打印出source的值,顯然父進(jìn)程打印出來的值應(yīng)該為10,子進(jìn)程打印出來的值應(yīng)該為9。
[root@localhost ~]# python test3.py start+++++++++++++ pid= 2550 this is parent process. 10 END--------------- pid= 0 this is child process. 9 END---------------
簡(jiǎn)單守護(hù)進(jìn)程例子:
def main():
''' 程序要執(zhí)行的邏輯代碼 '''
pass
# 創(chuàng)建守護(hù)進(jìn)程函數(shù)
def createDaemon():
''' 第一塊(創(chuàng)建第一個(gè)子進(jìn)程) '''
# fork 第一個(gè)子進(jìn)程(如果fork成功,父進(jìn)程自殺,只留下第一個(gè)子進(jìn)程繼續(xù)向下運(yùn)行)
try:
if os.fork() > 0:
sys.exit(0)
except OSError, error:
print '(fork第一個(gè)子進(jìn)程失?。ゝork #1 failed: %d (%s)' % (error.errno, error.strerror)
sys.exit(1)
''' 第一塊結(jié)束 '''
###### 第一個(gè)進(jìn)程創(chuàng)建成功后,它的ppid = 1,已是一個(gè)守護(hù)里程了,但有些功能上還是有被限制。
###### 所以下面再來創(chuàng)建一個(gè)子進(jìn)程。第二次創(chuàng)建的子進(jìn)程限制就沒那多了,有可能沒有,所以最安全。
###### 下面來創(chuàng)建第二個(gè)子進(jìn)程?!?
os.chdir('/') # 把第一個(gè)子進(jìn)程的工作目錄切換到 / (根目錄)
os.setsid() # 第一個(gè)子進(jìn)程取得程序的權(quán)限
os.umask(0) # 第一個(gè)子進(jìn)程取得工作目錄的所有操作(目錄的rwx)
''' 第二塊(創(chuàng)建第二個(gè)子進(jìn)程) '''
# fork 第二個(gè)子進(jìn)程(如果fork成功,第一個(gè)子進(jìn)程自殺,只留下新創(chuàng)建的第二個(gè)子進(jìn)程)
try:
pid = os.fork()
if pid > 0:
print 'Daemon PID %d' % pid
sys.exit(0)
except OSError, error:
print '(fork第二個(gè)子進(jìn)程失?。ゝork #2 failed: %d (%s)' % (error.errno, error.strerror)
sys.exit(1)
''' 第二塊結(jié)束 '''
####### 通過上面兩個(gè) try 語句塊,只留下了第二個(gè)子進(jìn)程在運(yùn)行了。這時(shí)第二個(gè)子進(jìn)程的ppid=1。
####### 創(chuàng)建的第二個(gè)子進(jìn)程,可以說是一個(gè)不受限的守護(hù)進(jìn)程了。
# 重定向標(biāo)準(zhǔn)IO(因?yàn)橹挥械诙€(gè)子進(jìn)程在運(yùn)行了,所以也就是指定整個(gè)程序的輸入、輸出、錯(cuò)誤流)
# sys.stdout.flush() # 清除程序運(yùn)行空間的輸出流
# sys.stderr.flush() # 清除程序運(yùn)行空間的錯(cuò)誤流
# inputS = file("/dev/null", 'r') # 定義一個(gè) inputS 文件對(duì)象
# outputS = file("/dev/null", 'a+') # 定義一個(gè) outputS 文件對(duì)象
# errorS = file("/dev/null", 'a+', 0) # 定義一個(gè) errorS 文件對(duì)象
# os.dup2(inputS.fileno(), sys.stdin.fileno()) # 把程序的輸入流重定向到上面定義的 inputS 文件對(duì)象上。
# os.dup2(so.fileno(), sys.stdout.fileno()) # 把程序的 輸出流 重定向到上面定義的 outputS 文件對(duì)象上。
# os.dup2(se.fileno(), sys.stderr.fileno()) # 把程序的 錯(cuò)誤流 重定向到上面定義的 errorS 文件對(duì)象上。
main() # main函數(shù)為真正程序邏輯代碼
if __name__ == "__main__":
if platform.system() == "Linux":
createDaemon()
else:
sys.exit()
帶控制參數(shù)的例子:
編寫守護(hù)進(jìn)程的基類,用于繼承:
# coding: utf-8
import os
import sys
import time
import atexit
import signal
class Daemon:
def __init__(self, pidfile='/tmp/daemon.pid', stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
self.stdin = stdin
self.stdout = stdout
self.stderr = stderr
self.pidfile = pidfile
def daemonize(self):
if os.path.exists(self.pidfile):
raise RuntimeError('Already running.')
# First fork (detaches from parent)
try:
if os.fork() > 0:
raise SystemExit(0)
except OSError as e:
raise RuntimeError('fork #1 faild: {0} ({1})\n'.format(e.errno, e.strerror))
os.chdir('/')
os.setsid()
os.umask(0o22)
# Second fork (relinquish session leadership)
try:
if os.fork() > 0:
raise SystemExit(0)
except OSError as e:
raise RuntimeError('fork #2 faild: {0} ({1})\n'.format(e.errno, e.strerror))
# Flush I/O buffers
sys.stdout.flush()
sys.stderr.flush()
# Replace file descriptors for stdin, stdout, and stderr
with open(self.stdin, 'rb', 0) as f:
os.dup2(f.fileno(), sys.stdin.fileno())
with open(self.stdout, 'ab', 0) as f:
os.dup2(f.fileno(), sys.stdout.fileno())
with open(self.stderr, 'ab', 0) as f:
os.dup2(f.fileno(), sys.stderr.fileno())
# Write the PID file
with open(self.pidfile, 'w') as f:
print(os.getpid(), file=f)
# Arrange to have the PID file removed on exit/signal
atexit.register(lambda: os.remove(self.pidfile))
signal.signal(signal.SIGTERM, self.__sigterm_handler)
# Signal handler for termination (required)
@staticmethod
def __sigterm_handler(signo, frame):
raise SystemExit(1)
def start(self):
try:
self.daemonize()
except RuntimeError as e:
print(e, file=sys.stderr)
raise SystemExit(1)
self.run()
def stop(self):
try:
if os.path.exists(self.pidfile):
with open(self.pidfile) as f:
os.kill(int(f.read()), signal.SIGTERM)
else:
print('Not running.', file=sys.stderr)
raise SystemExit(1)
except OSError as e:
if 'No such process' in str(e) and os.path.exists(self.pidfile):
os.remove(self.pidfile)
def restart(self):
self.stop()
self.start()
def run(self):
#繼承類重寫該方法
pass
編寫自己的類:
#導(dǎo)入剛剛編寫的基類
from daemon import Daemon
#繼承
class MyTestDaemon(Daemon):
#重寫run方法,就是你要后臺(tái)運(yùn)行的函數(shù)
def run(self):
#后臺(tái)運(yùn)行的函數(shù),比如shell輸出到自己定義的文件
sys.stdout.write('Daemon started with pid {}\n'.format(os.getpid()))
while True:
sys.stdout.write('Daemon Alive! {}\n'.format(time.ctime()))
sys.stdout.flush()
time.sleep(5)
if __name__ == '__main__':
PIDFILE = '/tmp/daemon-example.pid'
LOG = '/tmp/daemon-example.log'
daemon = MyTestDaemon(pidfile=PIDFILE, stdout=LOG, stderr=LOG)
if len(sys.argv) != 2:
print('Usage: {} [start|stop]'.format(sys.argv[0]), file=sys.stderr)
raise SystemExit(1)
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 {!r}'.format(sys.argv[1]), file=sys.stderr)
raise SystemExit(1)
關(guān)于兩次fork
第二個(gè)fork不是必須的,只是為了防止進(jìn)程打開控制終端。
打開一個(gè)控制終端的條件是該進(jìn)程必須是session leader。第一次fork,setsid之后,子進(jìn)程成為session leader,進(jìn)程可以打開終端;第二次fork產(chǎn)生的進(jìn)程,不再是session leader,進(jìn)程則無法打開終端。
也就是說,只要程序?qū)崿F(xiàn)得好,控制程序不主動(dòng)打開終端,無第二次fork亦可。
代碼實(shí)現(xiàn)
# coding: utf-8
import os
import sys
import time
import atexit
import signal
class Daemon:
def __init__(self, pidfile='/tmp/daemon.pid', stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
self.stdin = stdin
self.stdout = stdout
self.stderr = stderr
self.pidfile = pidfile
def daemonize(self):
if os.path.exists(self.pidfile):
raise RuntimeError('Already running.')
# First fork (detaches from parent)
try:
if os.fork() > 0:
raise SystemExit(0)
except OSError as e:
raise RuntimeError('fork #1 faild: {0} ({1})\n'.format(e.errno, e.strerror))
os.chdir('/')
os.setsid()
os.umask(0o22)
# Second fork (relinquish session leadership)
try:
if os.fork() > 0:
raise SystemExit(0)
except OSError as e:
raise RuntimeError('fork #2 faild: {0} ({1})\n'.format(e.errno, e.strerror))
# Flush I/O buffers
sys.stdout.flush()
sys.stderr.flush()
# Replace file descriptors for stdin, stdout, and stderr
with open(self.stdin, 'rb', 0) as f:
os.dup2(f.fileno(), sys.stdin.fileno())
with open(self.stdout, 'ab', 0) as f:
os.dup2(f.fileno(), sys.stdout.fileno())
with open(self.stderr, 'ab', 0) as f:
os.dup2(f.fileno(), sys.stderr.fileno())
# Write the PID file
with open(self.pidfile, 'w') as f:
print(os.getpid(), file=f)
# Arrange to have the PID file removed on exit/signal
atexit.register(lambda: os.remove(self.pidfile))
signal.signal(signal.SIGTERM, self.__sigterm_handler)
# Signal handler for termination (required)
@staticmethod
def __sigterm_handler(signo, frame):
raise SystemExit(1)
def start(self):
try:
self.daemonize()
except RuntimeError as e:
print(e, file=sys.stderr)
raise SystemExit(1)
self.run()
def stop(self):
try:
if os.path.exists(self.pidfile):
with open(self.pidfile) as f:
os.kill(int(f.read()), signal.SIGTERM)
else:
print('Not running.', file=sys.stderr)
raise SystemExit(1)
except OSError as e:
if 'No such process' in str(e) and os.path.exists(self.pidfile):
os.remove(self.pidfile)
def restart(self):
self.stop()
self.start()
def run(self):
pass
使用測(cè)試
import os
import sys
import time
from daemon import Daemon
class MyTestDaemon(Daemon):
def run(self):
sys.stdout.write('Daemon started with pid {}\n'.format(os.getpid()))
while True:
sys.stdout.write('Daemon Alive! {}\n'.format(time.ctime()))
sys.stdout.flush()
time.sleep(5)
if __name__ == '__main__':
PIDFILE = '/tmp/daemon-example.pid'
LOG = '/tmp/daemon-example.log'
daemon = MyTestDaemon(pidfile=PIDFILE, stdout=LOG, stderr=LOG)
if len(sys.argv) != 2:
print('Usage: {} [start|stop]'.format(sys.argv[0]), file=sys.stderr)
raise SystemExit(1)
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 {!r}'.format(sys.argv[1]), file=sys.stderr)
raise SystemExit(1)
[daemon] python test.py start 23:45:42 [daemon] cat /tmp/daemon-example.pid 23:45:49 8532 [daemon] ps -ef|grep 8532 | grep -v grep 23:46:07 502 8532 1 0 11:45下午 ?? 0:00.00 python test.py start [daemon] tail -f /tmp/daemon-example.log 23:46:20 Daemon started with pid 8532 Daemon Alive! Fri Dec 2 23:45:49 2016 Daemon Alive! Fri Dec 2 23:45:54 2016 Daemon Alive! Fri Dec 2 23:45:59 2016 Daemon Alive! Fri Dec 2 23:46:04 2016 Daemon Alive! Fri Dec 2 23:46:09 2016 Daemon Alive! Fri Dec 2 23:46:14 2016 Daemon Alive! Fri Dec 2 23:46:19 2016 Daemon Alive! Fri Dec 2 23:46:24 2016 Daemon Alive! Fri Dec 2 23:46:29 2016 Daemon Alive! Fri Dec 2 23:46:34 2016 [daemon] python test.py stop 23:46:36 [daemon] ps -ef|grep 8532 | grep -v grep 23:46:43
- Python全棧之進(jìn)程和守護(hù)進(jìn)程
- python 如何設(shè)置守護(hù)進(jìn)程
- Python 創(chuàng)建守護(hù)進(jìn)程的示例
- Python守護(hù)進(jìn)程實(shí)現(xiàn)過程詳解
- python實(shí)現(xiàn)守護(hù)進(jìn)程、守護(hù)線程、守護(hù)非守護(hù)并行
- Python如何實(shí)現(xiàn)守護(hù)進(jìn)程的方法示例
- python daemon守護(hù)進(jìn)程實(shí)現(xiàn)
- Python守護(hù)進(jìn)程用法實(shí)例分析
- Python實(shí)現(xiàn)日志備份守護(hù)進(jìn)程的示例
相關(guān)文章
python數(shù)據(jù)庫開發(fā)之MongoDB安裝及Python3操作MongoDB數(shù)據(jù)庫詳細(xì)方法與實(shí)例
這篇文章主要介紹了python數(shù)據(jù)庫開發(fā)之MongoDB安裝及Python3操作MongoDB數(shù)據(jù)庫詳細(xì)方法與實(shí)例,需要的朋友可以參考下2020-03-03
Python機(jī)器學(xué)習(xí)入門(一)序章
這篇文章主要介紹了Python機(jī)器學(xué)習(xí)入門知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08
關(guān)于python pygame游戲進(jìn)行聲音添加的技巧
這篇文章主要給大家分享的是pygame游戲進(jìn)行聲音添加的方法,這文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!2021-10-10
Python bisect_left 函數(shù)使用場(chǎng)景詳解
在Python的編程世界中,數(shù)據(jù)處理和搜索操作是非常常見的任務(wù),bisect_left函數(shù)是Python標(biāo)準(zhǔn)庫bisect模塊中的一個(gè)強(qiáng)大工具,接下來,我們將詳細(xì)探討bisect_left函數(shù)的使用場(chǎng)景,需要的朋友可以參考下2024-11-11
Django中STATIC_ROOT和STATIC_URL及STATICFILES_DIRS淺析
這篇文章主要給大家介紹了關(guān)于Django中STATIC_ROOT和STATIC_URL及STATICFILES_DIRS的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧2018-05-05
Python字符串內(nèi)置函數(shù)功能與用法總結(jié)
這篇文章主要介紹了Python字符串內(nèi)置函數(shù)功能與用法,結(jié)合實(shí)例形式總結(jié)分析了Python常見字符串操作函數(shù)的功能、分類、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-04-04
Numpy中np.random.rand()和np.random.randn() 用法和區(qū)別詳解
這篇文章主要介紹了Numpy中np.random.rand()和np.random.randn() 用法和區(qū)別詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
pytorch更新tensor中指定index位置的值scatter_add_問題
這篇文章主要介紹了pytorch更新tensor中指定index位置的值scatter_add_問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06

