fork使用
pid = os.fork()
功能: 創建新的進程
返回值:整數,如果創建進程失敗返回一個負數,如果成功則在原有進程中返回新進程的PID,在新進程中返回0
import os
from time import sleep
# 創建子進程
pid = os.fork()
if pid < 0:
print("Create process failed")
elif pid == 0:
# 子進程執行部分
sleep(3)
print("The new process")
else:
# 父進程執行部分
sleep(2)
print("The old process")
print("Fork test over") # 父子進程都執行
注意:
子進程會複製父進程全部內存空間,從fork下一句開始執行。
父子進程各自獨立運行,運行順序不一定。
利用父子進程fork返回值的區別,配合if結構讓父子進程執行不同的內容幾乎是固定搭配。
父子進程有各自特有特徵比如PID PCB 命令集等。
父進程fork之前開闢的空間子進程同樣擁有,父子進程對各自空間的操作不會相互影響。
a = 1
pid = os.fork()
if pid < 0:
print("Error")
elif pid == 0:
print("Child process")
print("a = ", a) # 從父進程空間獲取的a
a = 10000 # 修改自己空間的a,不影響父進程的a
else:
sleep(1)
print("Parent process")
print('a:', a)
print("all a = ", a) # 父進程a不變 a=1
進程相關函數
os.getpid()
功能: 獲取一個進程的PID值
返回值: 返回當前進程的PID
os.getppid()
功能: 獲取父進程的PID號
返回值: 返回父進程PID
os._exit(status)
功能: 結束一個進程
參數:進程的終止狀態
sys.exit([status])
功能:退出進程
參數:整數 表示退出狀態
字符串 表示退出時打印內容
import os
import time
pid = os.fork()
if pid < 0:
print("Error")
elif pid == 0: # 子進程
time.sleep(1)
print("Child PID:", os.getpid()) # 子進程自己的PID
print("Get parent PID:", os.getppid()) # 子進程獲取父進程的PID
else:
time.sleep(3)
print("Get child PID:", pid) # 父進程獲取生成的子進程PID
print("Parent PID:", os.getpid()) # 父進程自己的PID
孤兒和殭屍
1. 孤兒進程 : 父進程先於子進程退出,此時子進程成爲孤兒進程。
特點: 孤兒進程會被系統進程收養,此時系統進程就會成爲孤兒進程新的父進程,孤兒進程退出該進程會自動處理。
2. 殭屍進程 : 子進程先於父進程退出,父進程又沒有處理子進程的退出狀態,此時子進程就會稱爲殭屍進程。
特點: 殭屍進程雖然結束,但是會存留部分PCB在內存中,大量的殭屍進程會浪費系統的內存資源。
3. 如何避免殭屍進程產生
方法一:使用wait函數處理子進程退出
pid,status = os.wait()
功能:在父進程中阻塞等待處理子進程退出
返回值: pid 退出的子進程的PID
status 子進程退出狀態
import os
from time import sleep
pid = os.fork()
if pid < 0:
print("Error")
elif pid == 0:
print("Child process:", os.getpid())
sleep(2)
os._exit(3) # 進程退出
else:
pid, status = os.wait() # 阻塞等待回收子進程
print("pid:", pid)
print("status:", os.WEXITSTATUS(status))
while True: # 讓父進程不退出
pass
方法二、創建二級子進程處理殭屍
【1】 父進程創建子進程,等待回收子進程
【2】 子進程創建二級子進程然後退出
【3】 二級子進程稱爲孤兒,和原來父進程一同執行事件
方法三(推薦)、通過信號處理子進程退出
原理: 子進程退出時會發送信號給父進程,如果父進程忽略子進程信號,則系統就會自動處理子進程退出。
方法: 使用signal模塊在父進程創建子進程前寫如下語句 :
import signal
signal.signal(signal.SIGCHLD,signal.SIG_IGN)
特點 : 非阻塞,不會影響父進程運行。可以處理所有子進程退出
# signal 信號方法處理殭屍進程
import os
import signal
# 信號處理殭屍
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
# 創建子進程
pid = os.fork()
if pid < 0:
print("Create process failed")
elif pid == 0:
# 子進程執行部分
print("Child process:", os.getpid())
else:
# 父進程執行部分
print("Process process")
while True:
pass
注意:fork在windows下無法使用,會出現異常 module 'os' has no attribute 'fork'
示例:
"""
基於fork的多進程網絡併發
"""
from socket import *
import os, sys
import signal
HOST = '0.0.0.0'
PORT = 8888
ADDR = (HOST, PORT)
# 創建TCP套接字
s = socket()
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind(ADDR)
s.listen(3)
# 殭屍進程處理
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
print("Listen the port 8888...")
def handle(con):
print("客戶端:", c.getpeername())
while True:
data = c.recv(1024)
if not data:
break
print(data.decode())
con.close()
# 循環接收客戶端連接
while True:
try:
c, addr = s.accept()
except Exception as e:
print(e)
continue
# 創建子進程處理客戶端請求
pid = os.fork()
if pid == 0:
s.close() # 不需要父進程的套接字
handle(c) # 具體處理客戶端請求
os._exit(0)
else:
# 父進程只處理客戶端連接
c.close() # 不需要客戶端的套接字