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

 

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