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