Python之基於fork的多進程編程

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()  # 不需要客戶端的套接字

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章