操作系统发展史
1.UDP协议 (了解)
UDP协议:称之为数据包协议。
特点:
1) 不需要建立链接。 2) 不需要知道对方是否收到。 3) 数据不安全 4) 传输速度快 5)能支持并发 6) 不会粘包 7) 无需先启动服务端再启动客户端
优点:
- 传输速度快 - 能支持并发 - 不会粘包
缺点
- 数据不安全, 容易丢失
应用场景:
早期的QQ聊天室。
2.TCP协议
- TCP协议(称为流式协议):
优点
数据安全
缺点:
传输速度慢 粘包
面试:
TCP与UDP的区别, 简述优缺点即可
3.并发编程
1) 操作系统发展史
1.1 穿孔卡片
- 读取数据速度特别慢 - CPU的利用率极低 - 单用户(一份代码)使用
1.2 批处理
- 读取数据速度特别慢 - CPU的利用率极低 - 联机(多份代码)使用 - 效率还是很低
1.3 脱机批处理(现代操作系统的设计原理)
- 读取数据速度提高 - CPU的利用率提高
2) 多道技术 (基于单核背景下产生的):
单道
- 单道: 一条道走到黑 ----> 串行 - 比如: a,b需要使用cpu,a先使用,b等待a使用完毕后,b才能使用cpu。
多道
比如: a,b需要使用cpu,a先使用,b等待a,直到a进入“IO或执行时间过长” a会(切换 + 保存状态),然后b可以使用cpu,待b执行遇到 “IO或执行时间过长” 再将cpu执行权限交给a,直到两个程序结束。
空间上的复用
多个程序使用一个CPU。
时间上的复用
切换 + 保存状态 1) 当执行程序遇到IO时,操作系统会将CPU的执行权限剥夺。 优点: - CPU的执行效率提高 2) 当执行程序执行时间过长时,操作系统会将CPU的执行权限剥夺。 缺点: - 程序的执行效率低
并发与并行
并发:
在单核(一个cpu)情况下,当执行两个a,b程序时,a先执行,当a遇到IO时,b开始争抢cpu的执行权限,再让b执行,他们看起像同时运行。
并行:
在多核(多个cpu)的情况下,当执行两个a,b程序时,a与b同时执行。他们是真正意义上的同时运行。
面试题:
在单核情况下能否实现并行? 不行
3) 进程
1.什么是进程?
进程是一个资源单位。
2.进程与程序:
- 程序: 一对代码文件。 - 进程: 执行代码的过程,称之为进程。
3 进程调度: (了解)
1) 先来先服务调度算法(了解) - 比如程序 a,b,若a先来,则让a先服务,待a服务完毕后,b再服务。 - 缺点: 执行效率低。 2) 短作业优先调度算法(了解) - 执行时间越短,则先先调度。 缺点: 导致执行时间长的程序,需要等待所有时间短的程序执行完毕后,才能执行。 注意: 3) 时间片轮转法 - 比如同时有10个程序需要执行,操作系统会给你10秒,然后时间片轮转法会将10秒分成10等分。 4) 多级反馈队列: 1级队列: 优先级最高,先执行次队列中程序。 2级队列: 优先级以此类推 3级队列:
4) 同步与异步
同步与异步指的是 “提交任务的方式”。
同步(串行)
两个a,b程序都要提交并执行,假如a先提交执行,b必须等a执行完毕后,才能提交任务。
异步(并发)
两个a,b程序都要提交并执行,假如a先提交并执行,b无需等a执行完毕,就可以直接提交任务。
4) 阻塞与非阻塞
阻塞(等待):
- 凡是遇到IO都会阻塞。 - IO: input() output() time.sleep(3) 文件的读写 数据的传输
非阻塞 (不等待) :
- 除了IO都是非阻塞 (比如: 从1+1开始计算到100万)
5) 进程的三种状态
就绪态:
同步与异步
运行态:
程序的执行时间过长 ----> 将程序返回给就绪态。 - 非阻塞
阻塞态:
- 遇到IO
面试题:
阻塞与同步是一样的吗?非阻塞与异步是一样的吗?
- 同步与异步: 提交任务的方式 - 阻塞与非阻塞: 进程的状态。 - 异步非阻塞: ----> cpu的利用率最大化!
6)创建进程的两种方式:
同步演示
import time def func(): print('start...') time.sleep(3) print('end...') if __name__ == '__main__': # 任务1: func() # 任务2: print('程序结束')
进程的两种创建方式
方式一
from multiprocessing import Process import time # 方式1: 直接调用Process def task(name): # 任务 # name == 'jason_sb' print(f'start...{name}的子进程') time.sleep(3) print(f'end...{name}的子进程') if __name__ == '__main__': # target=任务(函数地址) ---> 创建一个子进程 # 异步提交了三个任务 p_obj1 = Process(target=task, args=('jason_sb', )) p_obj1.start() # 告诉操作系统,去创建一个子进程 p_obj1.join() # 告诉主进程,等待子进程结束后,再结束 p_obj2 = Process(target=task, args=('sean_sb', )) p_obj2.start() # 告诉操作系统,去创建一个子进程 p_obj2.join() # 告诉主进程,等待子进程结束后,再结束 p_obj3 = Process(target=task, args=('大饼_sb', )) p_obj3.start() # 告诉操作系统,去创建一个子进程 p_obj3.join() # 告诉主进程,等待子进程结束后,再结束 print('正在执行当前主进程...') list1 = [] for line in range(10): p_obj = Process(target=task, args=('jason_sb',)) p_obj.start() list1.append(p_obj) for obj in list1: obj.join() print('主进程')
方式二
# 方式二: from multiprocessing import Process import time class MyProcess(Process): def run(self): print(f'start...{self.name}的子进程') time.sleep(3) print(f'end...{self.name}的子进程') if __name__ == '__main__': list1 = [] for line in range(10): obj = MyProcess() obj.start() list1.append(obj) for obj in list1: obj.join() print('主进程...')
进程号回收的两种条件:
'''windows: 创建子进程,windows会将当前父进程代码重新加载执行一次。 linux/mac: 会将当前父进程代码重新拷贝一份,再去执行。'''
from multiprocessing import Process import time ''' 创建进程方式一: ''' # 1.定义一个任务 def task(name): print(f'{name}的任务开始执行') time.sleep(1) print(f'{name}的任务已经结束') # 在linux/mac系统下不会报错 p = Process(target=task, args=('jason',)) if __name__ == '__main__': # target=执行函数的地址 p = Process(target=task, args=('jason',)) # 向操作系统提交创建进程的任务 p.start() print('主进程') 方法二: # 创建进程方式二: # 1.自定义一个类,并继承Process class MyProcess(Process): # 父类的方法 def run(self): print('任务开始执行') time.sleep(1) print('任务已经结束') if __name__ == '__main__': p = MyProcess() p.start() # p.start() print('主进程') 主进程 任务开始执行 任务已经结束
#### 1.join,可以回收子进程与主进程。
join方法: 用来告诉操作系统,让子进程结束后,父进程再结束。 from multiprocessing import Process import time def task(name, n): print(f'{name} start...') time.sleep(n) print(f'{name} over..') if __name__ == '__main__': p1 = Process(target=task, args=('jason', 1)) p2 = Process(target=task, args=('egon', 2)) p3 = Process(target=task, args=('sean', 3)) p1.start() p2.start() # 告诉操作系统,开启子进程 p3.start() # 告诉操作系统,开启子进程 p1.join() # 告诉操作系统,等子进程结束后,父进程再结束。 p2.join() p3.join() print('主进程') jason start... sean start... egon start... jason over.. egon over.. sean over.. 主进程
#### 2.主进程正常结束,子进程与主进程也会被回收。
current_process().pid: 获取子进程号 os.getpid(): 获取主进程pid号 cmd中查看进程号: tasklist |findstr 进程号 进程号回收的两种条件: 1.join,可以回收子进程与主进程。 2.主进程正常结束,子进程与主进程也会被回收。 os.getppid() ''' from multiprocessing import Process from multiprocessing import current_process import os # 与操作系统交互 import time def task(name): print(f'{name} start...', current_process().pid) time.sleep(1) print(f'{name} over..', current_process().pid) if __name__ == '__main__': p = Process(target=task, args=('jason', )) p.start() # 告诉操作系统,开启子进程 # 判断子进程是否存活 print(p.is_alive()) # 直接告诉操作系统,终止 子进程 p.terminate() time.sleep(0.1) # 判断子进程是否存活 print(p.is_alive()) p.join() # 告诉操作系统,等子进程结束后,父进程再结束。 print('主进程', os.getpid()) print('主主进程', os.getppid()) time.sleep(100)
僵尸进程与孤儿进程(了解):
僵尸进程: 指的是子进程已经结束,但PID号还存在,未销毁. 缺点: 占用PID号,占用操作系统资源.
孤儿进程: 指的是子进程还在执行,但父进程意外结束. 操作系统优化机制: 提供一个福利院,帮你回收没有父亲的子进程.
守护进程:
指的是主进程结束后,该主进程产生的所有子进程跟着结束,并回收.
• from multiprocessing import Process from multiprocessing import current_process import time def task(name): print(f'{name} start...', current_process().pid) time.sleep(5) print(f'{name} over..', current_process().pid) print(f'管家{name}') if __name__ == '__main__': p1 = Process(target=task, args=('jason', )) # 添加守护进程参数 p1.daemon = True # True代表该进程是守护进程 p1.start() print(f'egon 驾鹤西去...') •