前言:
昨天我们已经了解了多进程的原理以及它的实际使用
Unix/Linux 操作系统提供了一个 fork()系统调用, 它非常特殊. 普通的函数调用, 调用一次, 返回一次,
但是 fork()调用一次, 返回两次, 因为操作系统自动把当前进程 (称为父进程) 复制了一份(称为子进程), 然后, 分别在父进程和子进程内返回.
子进程永远返回 0, 而父进程返回子进程的 ID. 这样做的理由是, 一个父进程可以 fork 出很多子进程, 所以,
父进程要记下每个子进程的 ID, 而子进程只需要调用 getppid()就可以拿到父进程的 ID.
Python 的 os 模块封装了常见的系统调用, 其中就包括 fork, 可以在 Python 程序中轻松创建子进程
既然是进程那么就会有运行和退出
接下来我们就来了解一下进程的退出以及处理
孤儿进程:
当父进程优先于子进程退出, 此时子进程就会成为孤儿进程
特征:
孤儿进程会被系统指定进程收养, 即系统进程会成为
这个孤儿进程新的父进程, 系统进程会自动处理进程退出状态
僵尸进程:
当子进程优先于父进程退出, 父进程没有处理子进程的退出状态
此时子进程就会成为僵尸进程
僵尸进程会滞留部分 PCB 信息在内存中, 大量的僵尸进程会消耗系统给的内存资源,
所以要尽量避免僵尸进程的产生
如何避免僵尸进程的产生:
1. 父进程先退出
2. 父进程处理子进程状态
PID,status = os.wait
功能:
在父进程中阻塞等待处理子进程的退出
返回值:
pid : 退出的那个子进程的 PID 号
status : 子进程的退出状态
获取原来的退出状态:
- wait(status)
- pid,status = os.waitpid(pid,option)
功能: 在父进程阻塞等待处理子进程的退出
参数 :
pid -1 表示等待任意子进程退出
>0 表示等待对应 PID 号的子进程退出 option
0 表示阻塞等待 WNOHANG 表示非阻塞
返回值: pid
退出的那个子进程的 PID 号 status 子进程的退出状态
创建二级子进程
父进程创建子进程等待进程退出
子进程创建下一个进程, 然后立即退出
二级子进程成为孤儿进程 处理具体工作
multiprocessing 模块创建进程
1. 需要将要做的事情封装为函数
2. 使用 multiprocessing 提供的 process 创建进程对象
3. 通过进程对象和 process 初始化进程进行进程的设置, 绑定函数
4. 启动进程, 会自动执行绑定的函数
5. 完成进程的回收
创建进程对象:
process()
功能:
创建进程对象
参数:
target: 函数对象
name 给进程新名称()
args: 元组 用来给 target 函数位置传参
kwargs: 字典 用来给 target 函数键值传参
p.start()
功能:
启动进程, 自动运行 terget 绑定函数,
此时进程被创建
p.join([timeout])
功能:
等待阻塞子进程退出
参数: 超时检测
如果不使用 join 回收可能后产生僵尸进程
使用 multiprocessing 创建进程子进程同样复制父进程的全部内存空间
之后有自己的独立空间 执行上互不干扰 子进程也有自己的 PID 特有资源等
使用 multiprocessing 创建子进程, 一般父进程功能就是创建子进程
回收子进程, 返回事件交给子进程完成
简单群聊:
功能:
类似 QQ 群聊
1. 进入聊天室需要输入姓名 姓名不能重复
2. 有人进入聊天室会向其他人发起通知 xxx 进入聊天室
3. 如果一个人发消息则其他人都能收到 xxx 说:...
4. 如果某个人退出聊天室也会收到通知 xxx 退出聊天室
5. 服务端可以喊话: 此时群里所有的都能收到服务端消息 管理员说:...
服务器端:
- from socket import *
- import os, sys
- # 发送管理员消息
- def do_child(s, addr):
- while True:
- msg = input("管理员消息:")
- msg = "C 管理员" + msg
- s.sendto(msg.encode(), addr)
- # 用户登录
- def do_login(s, user, name, addr):
- if (name in user) or name == "管理员":
- s.sendto("该用户已存在".encode(), addr)
- return
- s.sendto(b'OK', addr)
- # 通知所有人
- msg = "\n 欢迎 %s 进入聊天室" % name
- for i in user:
- s.sendto(msg.encode(), user[i])
- # 插入 user
- user[name] = addr
- def do_chat(s, user, name, data):
- msg = "\n{} 说: {}".format(name, data)
- for i in user:
- if i != name:
- s.sendto(msg.encode(), user[i])
- def do_quit(s, user, name):
- msg = "\n%s 离开了聊天室" % name
- for i in user:
- if i == name:
- s.sendto(b'EXIT', user[i])
- else:
- s.sendto(msg.encode(), user[i])
- del user[name] # 删除离开的用户
- # 接收客户端请求并处理
- def do_parent(s):
- # 用于存储用户 {'Alex':('127.0.0.1',8888)}
- user = {}
- while True:
- msg, addr = s.recvfrom(1024)
- msgList = msg.decode().split(' ')
- if msgList[0] == 'L':
- do_login(s, user, msgList[1], addr)
- elif msgList[0] == 'C':
- # "C Levi [I miss you]"
- data = ' '.join(msgList[2:])
- do_chat(s, user, msgList[1], data)
- elif msgList[0] == 'Q':
- do_quit(s, user, msgList[1])
- # 创建套接字, 网络连接, 创建父子进程
- def main():
- # server address
- ADDR = ('0.0.0.0', 8888)
- # 创建套接字
- s = socket(AF_INET, SOCK_DGRAM)
- s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
- s.bind(ADDR)
- # 创建父子进程
- pid = os.fork()
- if pid < 0:
- sys.exit("创建进程失败")
- elif pid == 0:
- do_child(s, ADDR)
- else:
- do_parent(s)
- if __name__ == "__main__":
- main()
客户端:
- from socket import *
- import sys, os
- def login(s, ADDR):
- while True:
- name = input("请输入用户名:")
- msg = "L" + name
- s.sendto(msg.encode(), ADDR)
- # 接收登录结果
- data, addr = s.recvfrom(1024)
- if data.decode() == 'OK':
- print("@进入聊天室 @")
- return name
- else:
- print(data.decode())
- # 发送消息
- def do_child(s, name, addr):
- while True:
- text = input("发言(quit 退出):")
- # 退出
- if text.strip() == "quit":
- msg = "Q" + name
- s.sendto(msg.encode(), addr)
- sys.exit("退出聊天室")
- msg = "C %s %s" % (name, text)
- s.sendto(msg.encode(), addr)
- # 接收消息
- def do_parent(s):
- while True:
- msg, addr = s.recvfrom(1024)
- if msg.decode() == 'EXIT':
- sys.exit(0)
- print(msg.decode() + "\n 发言(quit 退出):",end="")
- # main 控制套接字的创建
- def main():
- if len(sys.argv) < 3:
- print("argv is error")
- return
- HOST = sys.argv[1]
- PORT = int(sys.argv[2])
- ADDR = (HOST, PORT)
- s = socket(AF_INET, SOCK_DGRAM)
- name = login(s, ADDR)
- if name:
- pid = os.fork()
- if pid < 0:
- sys.exit("创建子进程失败")
- elif pid == 0:
- do_child(s, name, ADDR)
- else:
- do_parent(s)
- else:
- return
- if __name__ == "__main__":
- main()
来源: https://www.cnblogs.com/ParisGabriel/p/9458056.html