前情回顾
1.httpserver (HTTP协议)
*接收浏览器的连接
*将接收内容解析
*组织响应内容
*将响应内容回发给浏览器
2.阻塞IO 默认形态
3.非阻塞IO 和超时检测
4.IO多路复用
目的 : 当程序中有多个IO事件的时候提高IO的执行效率
方法 :同时监控多个IO事件,当有IO 事件可以运行则处理可执行的IO
代码实现:select poll epoll
epoll
使用方法 : 代码基本与poll相同
- 将生成对象的 poll() 函数 变为 epoll()
- 将register注册IO事件时 关注的事件类别改为epoll类别
区别:
epoll 效率要高于 poll和select
epoll 的关注触发方式多一些
01_epoll_server.py
from socket import *
from select import *
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind((“127.0.0.1”,8888))
s.listen(5)
#创建epoll对象
p = epoll()
建立通过fileno查找IO对象的地图
fdmap = {s.fileno()?}
#添加关注
p.register(s,EPOLLIN | EPOLLERR)
while True:
#进行监控
events = p.poll()
for fd,event in events:
if fd == s.fileno():
c,addr = fdmap[fd].accept()
print(“Connect from”,addr)
#注册新的套接字
p.register(c,EPOLLIN)
#维护地图更新
fdmap[c.fileno()] = c
elif event & EPOLLIN:
data = fdmap[fd].recv(1024)
if not data:
p.unregister(fd)
fdmap[fd].close()
del fdmap[fd]
else:
print(data.decode())
fdmap[fd].send(‘收到了’.encode())
本地套接字
linux下文件类型:
b(块设备文件) c(字符设备文件) d(目录)
-(普通文件) l(链接文件) s(套接字文件) p(管道文件)
作用 : 用于本地不同的程序间进行通信
本地套接字创建流程:
1.创建套接字对象
sockfd = socket(AF_UNIX,SOCK_STREAM)
2.建立套接字文件
sockfd.bind(path) 绑定一个文件
3.监听
4.接收发送消息
cookie
os.path.exists(path)
功能: 判断一个文件是否存在
参数: 文件位置
返回值: 存在返回True 否则返回False
os.unlink() os.remove()
功能:删除一个文件
参数:要删除的文件
In [2]: os.path.exists("./re")
Out[2]: True
In [3]: os.unlink("./re")
02_unix_recv.py
from socket import *
import os
#确定使用哪个套接字文件
sock_file = ‘./sock_file’
#如果已经存在同名的文件则删除
if os.path.exists(sock_file):
os.unlink(sock_file)
#创建本地套接字
sockfd = socket(AF_UNIX,SOCK_STREAM)
#绑定套接字文件
sockfd.bind(sock_file)
#监听
sockfd.listen(5)
#消息收发
while True:
c,addr = sockfd.accept()
while True:
data = c.recv(1024)
if data:
print(data.decode())
c.send(b"Receive your message")
else:
break
c.close()
sockfd.close()
02_unix_send.py
from socket import *
#确保和另一端使用相同的套接字文件
sock_file = “./sock_file”
#本地套接字
sockfd = socket(AF_UNIX,SOCK_STREAM)
#链接另外一端
sockfd.connect(sock_file)
#发收消息
while True:
msg = input(">>")
if msg:
sockfd.send(msg.encode())
print(sockfd.recv(1024).decode())
else:
break
sockfd.close()
多任务编程
意义 : 充分的利用计算机资源提高程序的运行效率
定义 : 通过应用程序利用计算机的多个核心达到同时执行多个
任务的目的,以此来提升程序的执行效率
实施方案 : 多进程 多线程
并行: 多个计算机核心在同时处理多个任务,这多个任务间是并行关系
并发:同时处理多个任务,内核在任务间不断的切换,达到好像
都在处理运行的效果
进程:程序在计算机中的一次执行过程
程序 : 是一个可执行文件,是静态的,占有磁盘,不占计算机
的运行资源
进程 : 进程是一个动态的过程描述,占有计算机的资源,有一
定的生命周期
- 同一个程序的不同运行过程是不同的进程。因为分配的计算机资源不同,
生命周期也不同
进程的创建流程
1.用户空间运行一个程序,发起进程的创建
2.操作系统接受用户申请开启进行创建
3.操作系统分配计算机资源,确定进程状态
4.将新创建的进程交给用户使用
cpu时间片
如果一个进程占有计算机核心,我们称为该进程占有cpu时间片。
多个任务实际会对cpu内核进行争夺,由操作系统分配cpu资源
进程信息
PCB(进程控制块):在*nix操作系统中,进程创建后会自动在内存
中产生一个空间存放进程信息,称为PCB
进程信息:进程的ID 进程占有内存位置 创建时间 创建用户…
查看命令:ps -aux
PID(process ID) : 在操作系统中进程的唯一标志,大于0的整数,
由系统自动分配
进程的特征
* 进程是操作系统分配计算机资源的最小单位
* 每个进程有自己单独的虚拟内存空间
* 进程之间的执行上相互独立,互不影响
进程的状态
三态
* 就绪态:进程具备执行条件,等待系统分配处理器资源
* 运行态:进程占有cpu处于运行的状态
* 等待态:进程暂时不具备运行条件,需要阻塞等待
五态 (在三态基础上增加新建态和终止态)
* 新建态 :创建一个新的进程,获取资源的过程
* 终止态 :进程执行结束,资源释放回收的过程
ps -aux ----> STAT 表示进程状态
D 等待态 (不可终端等待)
S 等待态 (可终端等待)
T 等待态 (暂停状态)
R 运行态 (包含就绪态)
Z 僵尸态
+ 前台进程 在终端运行
< 高优先级
N 低优先级
l 有进程链接
s 会话组
进程优先级
优先级决定了一个进程的执行权限和占有资源的优先程度
查看优先级 :
top : 动态查看进程优先级 摁 < > 翻页
优先级取值范围 -20 — 19 -20最高
nice : 以指定的优先级运行一个程序
e.g. nice -9 ./while.py
nice --9 ./while.py -9的优先级运行
#!/usr/bin/python3
from time import sleep,ctime
while True:
sleep(2)
print(ctime())
父子进程
在系统中除了初始化进程每个进程都有一个父进程,可能有0个
或者多个子进程。由此形成进程的父子关系。我们认为每个进程
都是父进程创造的。
查看进程树 : pstree
查看父进程PID : ps -ajx
要求 : 什么是进程,进程和程序的区别
了解进程的特征
清楚进程每种状态,及状态转换
需求 : 编写一个程序能够同时做多件任务
方案 : 写一个程序,根据需要在程序内不可以创建多个进程完成任务
import os
pid = os.fork()
功能 : 创建一个新的进程
参数 : 无
返回值 :失败返回一个负数 -1
成功 : 在原有进程中返回新进程的PID号
在新进程中返回0
- 子进程会复制父进程全部代码段,包括fork之前产生的内存空间
- 子进程从fork的下一句开始执行
- 父子进程通常会根据fork返回值的差异选择执行不同的代码
- 子进程虽然复制父进程的代码空间,但是有自己的特有属性
比如 PID号 PCB等 - 父子进程在执行上互补干扰,执行顺序不确定
- 父子进程空间独立,在本进程中对空间的操作不会影响到其他进程
import os
from time import sleep
fork之前的代码只有父进程会执行
print("*******************")
#fork之前产生内存空间的存储,子进程也会有
a = 1
#创建新的进程
pid = os.fork()
if pid < 0:
print(“创建进程失败”)
只有子进程执行的部分
elif pid == 0:
sleep(1)
print("a = ",a)
print(“新创建的进程”)
只有父进程会运行的部分
else:
sleep(5)
print(“原来的进程”)
#if结构外的代码父子进程都会执行
print(“程序执行完毕”)
*******************
a = 1
新创建的进程
程序执行完毕
原来的进程
程序执行完毕
import os
from time import sleep
pid = os.fork()
a = 1
if pid < 0:
print(“Create Process failed”)
elif pid == 0:
#子进程中变量修改不会影响父进程
print("a = ",a)
a = 10000
print(“This is Child process”)
else:
sleep(1)
print(“This is parent process”)
print(“parent a =”,a)
a = 1
This is Child process
This is parent process
parent a = 1
进程相关函数使用
获取进程号
os.getpid()
功能 :获取当前进程的进程号
返回值 : 返回进程号
os.getppid()
功能:获取父进程的进程号
返回值 : 返回进程号
import os
pid = os.fork()
if pid < 0:
print(“Create Process failed”)
elif pid == 0:
#获取当前进程进程号
print(“子进程PID”,os.getpid())
#获取父进程进程号
print(“parent PID”,os.getppid())
else:
print(“父进程PID”,os.getpid())
print(“child PID”,pid) #子进程的PID
#父进程PID 13267
#child PID 13268
#子进程PID 13268
#parent PID 13267
进程的退出
os._exit(status)
功能 : 退出进程
参数 : 进程的退出状态 整数
sys.exit([status])
功能: 退出进程
参数: 不写默认为0
传入一个整数表示退出状态
传入一个字符串,则在进程退出时会打印该字符串
- sys.exit 可以通过捕获 SystemExit 异常阻止其退出
import os
import sys
#进程结束
os._exit(0)
try:
sys.exit(1)
except SystemExit as e:
# e为异常退出状态
print(e)
print(“Process end”)
作业 :
写一个聊天室
功能 : 类似QQ群聊
- 进入聊天室前需要输入用户名
- 有人进入聊天室会向其他用户发起通知
xxx 进入了聊天室 - 一个人发消息,其他人都能收到
xxx 说:xxxxxxxx - 某个人退出聊天室 其他人也会收到通知
xxx 退出了聊天室
服务端 客户端
- 使用什么技术
- 每个功能的实现方案