操作系统发展史

  1. 穿孔卡片:一个机房,一次只能被一个卡片使用

    缺点:CPU利用率最低

  2. 批处理系统:主机自动对作业成批处理,减少了人工操作时间

    • 联机批处理系统:主机联合控制作业的输入和输出,同时在主机与输入机之间增加磁带存储作业,进一步减少了作业建立时间
    • 脱机批处理系统:作业输入输出脱离主机控制,由并行的卫星机控制

,在主机与输出端再增加磁带存储作业结果,缓解了高速主机与慢速外设矛盾

单道与多道技术

  • 单道:多个作业使用CPU时是串行的
  • 多道技术:
    • 空间上的复用:一个CPU可以提供给多个用户去使用
    • 时间上的复用:CPU根据情况在不同时间点切换作业 + 保存切换前保存作业状态
      • 第一种切换情况:若CPU遇IO操作,发送IO指令后会立即切换CPU执行权限,从而提高CPU利用率
      • 第二种切换情况:若一个CPU使用的时间过长,也会立即切换CPU执行权限,但是这种切换会使程序的执行效率降低

并发与并行

并发与并行

  • 并发:一段时间同时运行多个程序,即在多个程序间切换 + 保存状态
  • 并行:多个CPU在同一时刻同时运行多个程序

程序与进程

  • 程序是一堆代码
  • 进程是一堆代码运行的过程

进程调度

  1. 先来先服务调度
    • 缺点:如果正在运行的程序执行时间很长,对其他程序不公平
  2. 短作业调度:优先调度用时短的程序使用CPU
    • 缺点:如果运行的程序很多,对执行时间长的程序不公平
  3. 时间片轮转法调度:将CPU的单位时间均分给N个加载的程序
  4. 分级反馈队列调度:将程序的执行优先级分为多个级别

当代操作系统进程调度:时间片轮转法调度 + 分级反馈队列调度

进程的三个状态

  • 就绪态:
    1. 进程创建时进入
    2. 运行的时间片用完进入
    3. 阻塞态结束进入
  • 运行态:进程被调度后进入
  • 阻塞态:CPU执行程序遇到IO操作时进入

创建进程的两种方式

新进程的创建都是由一个已经存在的进程执行了一个用于创建进程的系统调用而创建的。

# 方式一:从多进程模块导入进程类创建进程
from multiprocessing import Process
import time


# 子进程执行的目标任务函数
def task(name):
    print(f'{name}子进程开始')
    time.sleep(1)
    print(f'子进程结束')


# 实例化子进程对象
sub_process = Process(
    target=task,  # 控制子进程执行目标
    args=('蔡启龙',)  # 控制执行目标所需参数
)

# 向操作系统申请创建子进程
if __name__ == '__main__':  # 防止递归申请子进程内存空间处理
    sub_process.start()  # 子进程对象向操作系统申请子进程内存空间

print('主进程结束')

# windows创建子进程需导入fork函数,会将当前父进程代码重新加载执行一次
'''
主进程结束
主进程结束
蔡启龙子进程开始
子进程结束
'''
# 方式二:自定义进程类创建进程
from multiprocessing import Process
import time


# 自定义进程类继承多进程模块中的进程类
class MyProcess(Process):
    def __init__(self, name):  # name控制子进程执行目标中的参数
        super(MyProcess, self).__init__()
        self.name = name

    # 必须重写父类中的run函数---控制子进程执行目标
    def run(self):
        print(f'{self.name}子进程开始运行')
        time.sleep(1)
        print('子进程结束')


if __name__ == '__main__':
    sub_process = MyProcess('蔡启龙')
    sub_process.start()  # 自定义子进程对象向操作系统申请创建子进程

    time.sleep(2)  # 主进程阻塞使子进程先运行完
    print('主进程结束')

'''
蔡启龙子进程开始运行
子进程结束
主进程结束
'''

进程对象的方法

  • join方法

    from multiprocessing import Process
    import time
    
    
    def task(name):
        print(f'{name}子进程开始')
        time.sleep(1)
        print(f'子进程结束')
    
    
    sub_process = Process(target=task, args=('蔡启龙',))
    
    if __name__ == '__main__':
        sub_process.start()
        sub_process.join()  # join方法控制父进程在原地等待子进程结束后再执行下面代码
    
    print('主进程结束')
    
    '''
    主进程结束
    蔡启龙子进程开始
    子进程结束
    主进程结束
    '''
    
  • isalive 和 terminate方法

    from multiprocessing import Process
    import time
    
    
    def task(name):
        print(f'{name}子进程开始')
        time.sleep(1)
        print(f'子进程结束')
    
    
    sub_process = Process(target=task, args=('蔡启龙',))
    
    if __name__ == '__main__':
        sub_process.start()
    
        print(sub_process.is_alive())  # 判断子进程是否存活  True
        sub_process.terminate()  # 终止子进程
        time.sleep(1)  # 阻塞主进程使子进程对象完成终止子进程操作
        print(sub_process.is_alive())  # 判断进程是否存活   False
    
  • 不同的查看进程id的方法

    import multiprocessing
    import os
    import time
    
    sub_process = multiprocessing.Process()
    
    if __name__ == '__main__':
        sub_process.start()
    
        print(multiprocessing.current_process().pid)  # current_processing().pid方法获取当前进程号 20292   
    
        print(os.getpid())  # os.getpid方法从操作系统中获取进程号 20292
    
        print(os.getppid())  # 获取父进程id 6608
    
        time.sleep(100)  # 保持进程不终止
    
    '''
    cmd中查看进程: tasklist |findstr 进程号
    python.exe 8524
    pycharm64.exe 6608
    '''
    
  • 将子进程以守护进程模式申请创建

    import multiprocessing
    import time
    
    
    def task():
        print('蔡启龙开始')
        time.sleep(1)
        print('蔡启龙结束')
    
    
    sub_process = multiprocessing.Process(target=task)
    
    if __name__ == '__main__':
        # 子程序对象添加守护进程参数,需在子进程创建前添加
        sub_process.daemon = True  # 将子进程变成守护进程
    
        sub_process.start()
        time.sleep(0.5)
        print('主程序结束')
    
    '''
    蔡启龙开始
    主程序结束
    '''
    

同步和异步:指的是作业的提交方式

  • 同步:必须要等当前任务执行结束后才能提交下一个任务
  • 异步:不需要等当前任务执行结束就能提交下一个任务

阻塞非阻塞

  • 阻塞:CPU执行程序遇到IO操作时阻塞
  • 非阻塞:进程处于就绪态和运行态

面试题:同步和异步,阻塞和非阻塞是否为同一概念?---否

最大化提高CPU使用率:尽可能减少不必要的IO操作

验证进程间数据相互隔离

import multiprocessing

x = 10


def change():
    global x
    x = 20


sub_process = multiprocessing.Process(target=change)

if __name__ == '__main__':
    sub_process.start()
    sub_process.join()  # 主进程原地等待子进程先完成修改
    print(x)

进程号回收的两个条件

  1. 一个子进程结束之后由父进程调用wait/waitpid回收
  2. 一个进程没有父进程或父进程提前结束则由init(1号)进程回收

僵尸进程与孤儿进程(了解)

僵尸进程:

  • 每一个进程在结束后默认都会留下一个称为僵尸进程的数据结构,记录退出状态等信息,以供父进程进行可能的查询

  • 当子进程先于父进程结束时,如果父进程没有调用wait/waitpid方法清理僵尸进程或提前设置不产生僵尸进程,那么子进程会一直保持僵尸状态占用进程号

  • 解决办法

    1. 终止僵尸进程的父进程使僵尸进程成为孤儿进程,然后由init(1号)进程接管回收
    2. 大批量则重启

孤儿进程:

父进程先于子进程结束时,子进程成为孤儿进程,,孤儿进程将被init(1号)进程接管回收,孤儿进程无危害