守護進程
1、定義:
守護進程:脫離終端並且在後臺運行的進程
守護進程脫離終端:避免進程在執行過程中的信息在任何終端上顯示;進程不會被任何終端所產生的終端信息所打斷
2、創建步驟:
創建子進程,退出父進程:
使用fork()函數和if判斷語句,使子進程變爲孤兒進程,交給init進程管理
pid = os.fork() #調用fork()函數 if pid < 0: #小於0,表明調用fork失敗 print 'invoke fork() failure' sys.exit(1) #退出腳本 elif pid > 0: #大於0,表明是父進程 sys.exit(0) #退出父進程
在子進程中創建新會話:
這個步驟是創建守護進程中最重要的一步;調用fork函數創建子進程時,子進程繼承了父進程的全部資源環境(包括會話期、進程組、控制終端等),雖然父進程退出了,但會話期、進程組、控制終端等並沒有改變,因此,子進程並沒有真正的獨立出來,而setsid函數能夠使子進程完全獨立出來
os.setsid() #調用setsid()函數
改變當前目錄爲根目錄:
調用fork()函數創建子進程,子進程也會繼承父進程的當前工作目錄,若子進程使用父進程的當前工作目錄可能會有一些問題
os.chdir("/")
重設文件權限掩碼:
把文件權限掩碼設置爲0,可以大大增強守護進程的靈活性
os.umask(0)
3、一個創建守護進程的實例:
vi /tmp/dir1/file.py #encoding:utf-8 import os import sys import time import commands pid = os.fork() #調用fork()函數 if pid < 0: #小於0,表面調用fork失敗 print 'invoke fork() failure' sys.exit(1) #退出腳本 elif pid > 0: #大於0,表面是父進程 sys.exit(0) #退出父進程,下面所有命令都是在子進程下執行 os.setsid() #在子進程中調用setsid函數 os.chdir("/") os.umask(0) while True: os.system('echo `date +%F-%H%M%S` >> /tmp/dir1/file') time.sleep(1)
[root@scj dir1]# python /tmp/dir1/file.py [root@scj dir1]# ps -ef | grep file.py root 3282 1 0 02:46 ? 00:00:00 python /tmp/dir1/file.py root 3311 1575 0 02:46 pts/1 00:00:00 grep file.py
由上發現:file.py被放在後臺作爲守護進程運行,父進程號是1(也就是init進程)
注意:守護進程由init進程管理
[root@scj dir1]# tail -f /tmp/dir1/file 2015-06-23-025041 2015-06-23-025042 2015-06-23-025043 2015-06-23-025044 2015-06-23-025045 2015-06-23-025046 2015-06-23-025047 2015-06-23-025048 2015-06-23-025049 2015-06-23-025050 2015-06-23-025051 2015-06-23-025052 2015-06-23-025053 2015-06-23-025054
每秒執行一次
4、停止守護進程的方法:
使用kill殺死
[root@scj dir1]# ps -ef | grep file.py root 3282 1 0 02:46 ? 00:00:00 python /tmp/dir1/file.py root 4195 1575 0 02:51 pts/1 00:00:00 grep file.py [root@scj dir1]# kill -9 3282
python守護進程的完整實例:
# -*-coding:utf-8-*- import sys, os '''將當前進程fork爲一個守護進程 注意:如果你的守護進程是由inetd啓動的,不要這樣做!inetd完成了 所有需要做的事情,包括重定向標準文件描述符,需要做的事情只有 chdir() 和 umask()了 ''' def daemonize(stdin='/dev/null',stdout= '/dev/null', stderr= 'dev/null'): '''Fork當前進程爲守護進程,重定向標準文件描述符 (默認情況下定向到/dev/null) ''' #Perform first fork. try: pid = os.fork() if pid > 0: sys.exit(0) #first parent out except OSError, e: sys.stderr.write("fork #1 failed: (%d) %s\n" %(e.errno, e.strerror)) sys.exit(1) #從母體環境脫離 os.chdir("/") os.umask(0) os.setsid() #執行第二次fork try: pid = os.fork() if pid > 0: sys.exit(0) #second parent out 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 = file(stdin, 'r') so = file(stdout,'a+') se = file(stderr,'a+',0) os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) def _example_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','/home/hzhida/daemon.log','home/hzhida/daemon.log') _example_main()
#第一個fork是爲了讓shell返回,同時讓你完成setsid(從你的控制終端移除,這樣就不會意外地收到信號)。setsid使得這個進程成爲“會話領導(session leader)”,即如果這個進程打開任何終端,該終端就會成爲此進程的控制終端。我們不需要一個守護進程有任何控制終端,所以我們又fork一次。在第二次fork之後,此進程不再是一個“會話領導”,這樣它就能打開任何文件(包括終端)且不會意外地再次獲得一個控制終端
另外說明:
umask()函數爲進程設置文件模式創建屏蔽字,並返回以前的值
在shell命令行輸入:umask 就可知當前文件模式創建屏蔽字
常見的幾種umask值是002,022和027,002阻止其他用戶寫你的文件,022阻止同組成員和其他用戶寫你的文件,027阻止同組成員寫你的文件以及其他用戶讀寫或執行你的文件
rwx-rwx-rwx 代表是777 所有的人都具有權限讀寫與執行
chmod()改變文件的權限位
int dup(int filedes) 返回新文件描述符一定是當前文件描述符中的最小數值
int dup2(int filedes, int filedes2);這兩個函數返回的新文件描述符與參數filedes共享同一個文件表項。
sys.stdout.flush():python的輸出(stdout,stderr)是有緩衝區的
flush()方法會直接把內部緩衝區的數據立刻寫入文件,而不是被動的等待輸出緩衝區被寫入
obj.fileno():獲取打開文件的描述符
os.dup2(f1,f2):複製f1的文件描述符到f2