前请回顾
本地套接字 : AF_UNIX 做本地的进程间通信
多任务编程中的进程
进程 : 程序在计算机中的一次执行过程
进程状态 : 等待态 运行态 就绪态
os.fork()
功能 创建新的进程
os.getpid() 获取当前进程的PID
os.getppid() 获取父进程的PID
退出进程
os._exit()
sys.exit()
import os
from time import sleep
pid = os.fork()
if pid < 0:
print(“Create process failed”)
elif pid == 0:
print(“Child Process”)
# 子进程打印父进程的PID
print(“Parent pid:”,os.getppid())
sleep(2) #等父进程退出
print(“Parent pid again:”,os.getppid())
else:
sleep(1) # 等待子进程打印自己的PID
print(“Parent Process”)
# Child Process
# Parent pid: 5267
# Parent Process
if pid < 0:
print(“Create process failed”)
elif pid == 0:
print(“Child Process :”,os.getpid())
print(“Child Process over”)
# 子进程打印父进程的PID
# print(“Parent pid:”,os.getppid())
# sleep(2) #等父进程退出
# print(“Parent pid again:”,os.getppid())
else:
sleep(2)
print(“Parent Process”)
while True:
pass
# sleep(1) # 等待子进程打印自己的PID
孤儿进程:父进程先于子进程退出,此时子进程就会成为
孤儿进程。
- 孤儿进程会被系统指定的进程收养,即系统进程会成为
孤儿进程新的父进程。当孤儿进程退出时“继父”,会处
理孤儿进程退出状态,使其不会成为僵尸。
僵尸进程:子进程先于父进程退出,父进程没有处理子进
程的退出状态,此时子进程就会成为僵尸进程
- 僵尸进程会滞留部分PCB信息在内存中,大量的僵尸进
程会消耗系统资源,应该尽量避免僵尸进程的产生
如何避免僵尸进程
* 父进程先退出 (不佳,因为不好控制)
* 父进程处理子进程的退出状态
pid,status = os.wait()
功能: 在父进程中阻塞等待子进程的退出
返回值 : 一个两个元素的元组
pid : 退出的子进程的PID号
status : 退出的子进程的退出状态
import os
from time import sleep
pid = os.fork()
if pid < 0:
print(“create process failed”)
elif pid == 0:
sleep(3)
print(“Child process over”)
os._exit(2)
else:
# 等子进程执行完毕
pid,status = os.wait()
print("++++++++++++++++++++")
print(pid,status)
print(os.WEXITSTATUS(status)) #获取退出状态
while True:
pass
#Child process over
#++++++++++++++++++++
#6818 768
#查看命令:ps -aux
pid,status = os.waitpid(pid,option)
功能 : 处理子进程的退出状态
参数 : pid -1 表示等待任意子进程退出
>0 整数 表示等待对应PID号的子进程退出
option : 0 表示阻塞等待
WNOHONG 表示非阻塞
返回值 : 同wait()
waitpid(-1,0) ==== wait()
watipid.py
import os
from time import sleep
pid = os.fork()
if pid < 0:
print(“create process failed”)
elif pid == 0:
sleep(3)
print(“Child process over”)
os._exit(2)
else:
while True:
sleep(1)
# 设置非阻塞状态
pid,status = os.waitpid(-1,os.WNOHANG)
print("++++++++++++++++++++")
print(pid,status)
print(os.WEXITSTATUS(status)) #获取退出状态
while True:
pass
tarena@tedu:~$ python3 03_waitpid.py
++++++++++++++++++++
0 0
0
++++++++++++++++++++
0 0
0
Child process over
++++++++++++++++++++
6530 512
2
Traceback (most recent call last):
File “03_waitpid.py”, line 17, in
pid,status = os.waitpid(-1,os.WNOHANG)
ChildProcessError: [Errno 10] No child processes
创建二级子进程
* 父进程创建子进程等待子进程的退出
* 子进程创建二级子进程后满上退出
* 二级子进程成为孤儿,处理具体事件
#创建二级子进程处理僵尸
import os
from time import sleep
def fun1():
sleep(3)
print(“第一件事情”)
def fun2():
sleep(4)
print(“第二件事情”)
pid = os.fork()
if pid < 0:
print(“create process error”)
elif pid == 0:
#创建二级子进程
pid0 = os.fork()
if pid0 < 0:
print(“create pid0 failed”)
elif pid0 == 0:
fun2() #做另一件事情
else:
os._exit(0)
else:
os.wait()
fun1() #做第一件事情
聊天室项目
功能 : 类似QQ群聊
- 进入聊天室前需要输入用户名
- 有人进入聊天室会向其他用户发起通知
xxx 进入了聊天室 - 一个人发消息,其他人都能收到
xxx 说:xxxxxxxx - 某个人退出聊天室 其他人也会收到通知
xxx 退出了聊天室 - 管理员喊话功能 :管理员发言所有客户端都能收到
管理员 说:xxxxxxx
需要什么技术:
socket 通信
存储用户 : 字典或列表
姓名 和 地址
用什么类型套接字 : udp套接字
怎么发消息:
- 转发 即客户端发给服务器然后服务器在发送给其他人
*在客户端让发送和接收需要相互独立,各使用一个进 程
*服务端,管理员喊话和请求的接收需要独立,各使用一个进程
注意点
- 功能封装 将每个功能模块化
- 测试,每个功能进行测试
代码编写流程
搭建网络连接----》多进程的创建—》每个进程功能的编写----》
项目功能的诸个编写
客户端可能发送的请求
进入聊天室: “L name”
服务端 : 识别请求 判定是否可以进入
回复客户端 保留用户
告知其他人谁进入
聊天请求 : “C name message”
服务端 : 识别请求 转发给其他人
退出聊天室: “Q name”
服务端 : 识别请求 告知其他人谁退出
将其从用户中删除
server.py
from socket import *
import os,sys
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[name] = addr
def do_chat(s,user,name,text):
msg = “\n%-4s 说:%s”%(name,text)
#发送给除了自己的所有人
for i in user:
if i != name:
s.sendto(msg.encode(),user[i])
def do_quit(s,user,name):
del user[name]
msg = “\n” + name + “离开了聊天室”
for i in user:
s.sendto(msg.encode(),user[i])
接收客户端请求并处理
def do_child(s):
#用于存储用户 {‘zhangsan’?‘172.60.50.51’,9687)}
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’:
do_chat(s,user,msgList[1],
’ '.join(msgList[2:]))
elif msgList[0] == ‘Q’:
do_quit(s,user,msgList[1])
发送管理员消息
def do_parent(s,addr):
while True:
msg = input(“管理员消息:”)
msg = "C 管理员 " + msg
s.sendto(msg.encode(),addr)
创建套接字,创建链接 ,创建父子进程
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:
#创建二级子进程
pid0 = os.fork()
if pid0 < 0:
sys.exit("创建进程失败")
elif pid0 == 0:
#执行子进程功能
do_child(s)
else:
os._exit(0)
else:
os.wait()
#执行父进程工能
do_parent(s,ADDR)
if name == “main”:
main()
client.py
‘’’
Project : Chatroom
name : Levi
chatroom server
‘’’
from socket import *
import sys,os
import signal
#发送消息
def do_child(s,name,addr):
while True:
text = input(“发言(quit退出):”)
#退出
if text.strip() == “quit”:
msg = "Q " + name
s.sendto(msg.encode(),addr)
#从子进程中杀死父进程
os.kill(os.getppid(),signal.SIGKILL)
sys.exit(“退出聊天室”)
#聊天
else:
msg = “C %s %s”%(name,text)
s.sendto(msg.encode(),addr)
#接收消息
def do_parent(s):
while True:
msg,addr = s.recvfrom(1024)
print(msg.decode()+"\n发言(quit退出):",end="")
#创建套接字,创建父子进程,登录
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)
#登录
while True:
name = input("请输入姓名:")
msg = "L " + name
s.sendto(msg.encode(),ADDR)
data,addr = s.recvfrom(1024)
if data.decode() == 'OK':
print("@进入聊天室@")
break
else:
print(data.decode())
pid = os.fork()
if pid < 0:
sys.exit("创建子进程失败")
elif pid == 0:
do_child(s,name,ADDR)
else:
do_parent(s)
if name == “main”:
main()
multiprocessing 模块创建进程
- 需要将要做的事件进行封装成函数
- 使用multiprocessing提供的类Process创建进程对象
- 通过对象和Process的初始化函数对进程进行设置以
及绑定要执行的事件 - 启动进程,会自动的执行函数代表的事件
- 完成进程的回收
创建进程对象
Process()
功能:创建进程对象
参数:name : 给创建的进程对象起一个名字
默认为Process-1
target : 绑定的函数
args :元组 用来给 target函数传参按位置传参
kwargs : 字典 按照键值传参
p.start()
功能:启动进程 进程被创建,自动运行对应函数
p.join([timeout])
功能 : 阻塞等待对应子进程的退出,回收子进程
参数 : 超时时间
- 如果不使用join 则子进程会成为僵尸进程
- 在使用multiprocessing创建进程中,一般父进程功能
就是创建子进程等待回收,不做过多其他事情 - 使用multiprocessing创建子进程,同样子进程复制父
进程空间,之后有自己独立的执行空间,互不干扰
import multiprocessing as mp
from time import sleep
a = 1
def fun():
global a
a = 1000
sleep(3)
print(“子进程事件”)
#创建进程对象
p = mp.Process(target = fun)
#启动进程
p.start()
sleep(2)
print(“这是父进程”)
#回收进程
p.join()
print("a = ",a)
from multiprocessing import Process
from time import sleep
import os
def th1():
sleep(3)
print(“吃饭”)
print(os.getppid(),"----",os.getpid())
def th2():
sleep(2)
print(“睡觉”)
print(os.getppid(),"----",os.getpid())
def th3():
sleep(4)
print(“打豆豆”)
print(os.getppid(),"----",os.getpid())
thing = [th1,th2,th3]
process = []
for th in thing:
p = Process(target = th)
process.append§ #将进程对象保存
p.start()
#回收进程
for i in process:
i.join()
作业: 1. 将聊天室代码进行梳理
2. 复习fork创建进程的内容进行整理
3. multiprocessing 函数巩固