一、操作系统介绍
1.1、什么是操作系统
操作系统是一个协调、管理和控制计算机硬件资源和软件资源的控制程序。
1、操作系统的内核
运行于内核态,管理硬件资源
2、系统调用
运行于用户态,为程序员写的应用程序提供系统调用接口。
1.2、操作系统的功能
1、隐藏了复杂的硬件调用接口,为程序员提供调用硬件资源的更好、更简单、更清晰的模型(系统调用接口)
例如:用操作文件的方式,代替关于磁盘读写的操作
2、将应用程序对硬件资源的请求变得有序化
1.3、操作系统与普通软件的区别
1、操作系统由硬件保护,不能被用户修改
2、操作系统是一个大型、复杂、长寿的软件
大型:Windows和Linux仅内核源码就有500多万行,用户程序,如GUI、库、以及基本应用软件(如Windows explorer 等)很容易再扩充10到20倍。
长寿:操作系统一旦完成,不会轻易替换,而是在原有基础上改进
1.4、操作系统发展史
1.4.1、第一代计算机:真空管和穿孔卡片
特点:
1、没有操作系统概念
2、所有的程序设计都是直接操控硬件
优点:
1、程序员在申请的时间段内独享整个资源,可以即时的调试自己的程序(随时处理bug)
缺点:
1、浪费计算机资源,一个时间段内只有一个人使用
2、同一时刻只有一个程序在内存中运行,多个程序的执行是串行的
1.4.2、第二代计算机:晶体管和批处理系统
特点:
1、设计人员、生产人员、操作人员、程序人员和维护人员直接有了明确的分工,计算机被所在专用空调房间中,由专业操作人员运行。
2、有了操作系统的概念
3、有了程序设计语言。将FORTRAN语言或汇编语言写在纸上,然后穿孔打成卡片,将多个人的卡片盒拿到输入室,由操作人员操作计算机统一运算,结果输出到磁带上。
优点:
1、批处理,节省了机时
缺点:
1、整个流程需要人为参与控制,不停运输磁带
2、计算过程仍然是串行
3、执行的程序被统一规划到一批作业中,无法及时调试程序,影响了开发效率。
1.4.3、第三代计算机:集成电路芯片和多道程序设计
多道技术:用多路复用的方法控制多个程序有序的竞争或共享一个资源(如CPU)。
多路复用:
1、空间上的复用:将内存分为几个部分,每个部分放入一个程序,这样同一时间内存中就有了多道程序
2、时间上的复用:当一个程序在等待 I/O 时,另一个程序可以使用CPU,让CPU的利用率接近百分百。一个程序长时间占用CPU也会被操作系统自动切换。
分时操作系统:多个联机终端+多道技术
1.4.4、个人计算机
二、进程理论
2.1、什么是进程
正在进行的一个过程或一个任务,由CPU执行。
2.2、进程与程序的区别
程序是一堆代码,进程是程序的运行过程
2.3、并发与并行
1、并发:
伪并行,由单个CPU+多道技术即可实现
2、并行:
同时运行,需要具备多个CPU
2.4、进程的层次结构:
1、Linux:所有进程以 init 为根,组成树形结构,父子进程公用一个进程组。
2、Windows:没有进程层次概念,所有进程地位相同。
2.5、进程的状态:
阻塞态、运行态、就绪态
进程在逻辑上无法运行的两种原因:
1、自身原因,遇到 I/O 阻塞,让出CPU执行其他程序
2、操作系统原因,一个进程占用时间过多,或者优先级的原因,让出CPU。
三、开启进程的两种方式
3.1、multiprocessing 模块
功能:开启子进程,并在子进程中执行定制的任务。提供了Process、Queue、Pipe、Lock等组件
3,2、Process 类
由该类实例化的到的对象,可用来开启一个子进程
3.2.1、参数
group 参数未使用,始终为None
target 表示调用对象,即子进程要执行的任务
args 表示调用对象的位置参数,写作元组形式。
kargs 表示调用对象的字典.
name 表示子进程名字
3.2.2、方法
p.start() 启动进程,调用该子进程中的 p.run()
p.run() 进程启动运行的方法,正是它去调用 target 指定的函数。
p.terminate() 强制终止进程 p,不会进行任何清理操作,如果 p 创建了子进程,该进程就成了僵尸进程。如果p保留了锁,也不会被释放,而成为死锁。
p.is_alive() 判断 p 是否仍然运行
p.join([timeout]) 主线程等待 p 终止,timeout 是可选的超时时间
3.2.3、属性
p.deamon 默认值为 False,如果设为 True,代表 p 为后台运行的守护进程,当 p 的父进程终止时,p 也随之终止。设定为True后,p 还不能创建自己
的新进程,必须在 p.start() 之前设置。
p.name 进程的名称
p.pid 进程的端口地址
3.3、Process 的使用
注意:在 Windows 中 Process() 必须放到 if __name__ == '__main__': 下
1、
import time import random from multiprocessing import Process def study(name): print('%s study' % name) time.sleep(random.randrange(1, 5)) print('%s study end' % name) if __name__ == '__main__': p1 = Process(target=study, args=('a',)) p2 = Process(target=study, args=('b',)) p3 = Process(target=study, args=('c',)) p1.start() p2.start() p3.start() print('主')
2、
import time import random from multiprocessing import Process class Study(Process): def __init__(self, name): super().__init__() self.name = name def run(self): print('%s study' % self.name) time.sleep(random.randrange(1, 5)) print('%s study end' % self.name) if __name__ == '__main__': p1 = Study('a') p2 = Study('b') p3 = Study('c') p1.start() p2.start() p3.start() print('主')
四、join方法
4.1、process 对象的 join 方法
作用:如果主进程的任务在执行到某一阶段时,需要等待子进程执行完毕后才能继续执行,就需要有一种机制能够让主进程检测子进程是否运行完毕,
在子进程执行完毕后才继续执行,否则一直在原地阻塞。
from multiprocessing import Process import time import random import os def task(): print('%s is studying' % os.getpid()) time.sleep(random.randrange(1, 3)) print('%s is sleeping' % os.getpid()) if __name__ == '__main__': p = Process(target=task) p.start() p.join() print('主')
五、守护进程
5.1、特点:
1、守护进程会在主进程代码结束后终止
2、守护进程内无法再开启子进程,否则抛出异常
5.2、
from multiprocessing import Process import time import random
def task(name): print('%s is study' % name) time.sleep(random.randrange(1, 3)) print('%s is sleeping' % name) if __name__ == '__main__': p = Process(target=task, args=('a',)) p.daemon = True p.start() print('主')
六、互斥锁
6.1、进程之间数据不共享,但是共享同一套文件系统,也由此带来了竞争,导致输出混乱。
锁必须在主进程中产生,交给子进程使用。
互斥锁特点:能将任务中某一段代码串行。
1、并发运行,竞争同一终端导致打印错乱
from multiprocessing import Process import os, time def work(): print('%s is running' %os.getpid()) time.sleep(2) print('%s is done' %os.getpid()) if __name__ == '__main__': for i in range(3): p = Process(target=work) p.start()
2、加锁后变成串行,牺牲了运行效率,但避免了竞争
def work(mutex): mutex.acquire() print('%s is running' %os.getpid()) time.sleep(2) print('%s is done' %os.getpid()) mutex.release() if __name__ == '__main__': mutex = Lock() for i in range(3): p = Process(target=work, args=(mutex,)) p.start()
6.2、模拟抢票练习
1、在执行文件目录下创建名为 'data.txt' 的文件,用 json 的字典格式写入 {"ticket": 1}
2、并发运行,效率高,但竞争写同一文件,数据写入错乱,一张票10个人都抢到
from multiprocessing import Process import time import json #查票 def search(i): with open('data.txt', 'r', encoding='utf-8') as f: data = f.read() ticket_num = json.loads(data) print('查询余票为:%s' % ticket_num.get('ticket')) #买票 def buy(i): with open('data.txt', 'r', encoding='utf-8') as f: data = f.read() ticket_num = json.loads(data) time.sleep(3) if ticket_num.get('ticket') > 0: ticket_num['ticket'] -= 1 with open('data', 'w', encoding='utf-8') as f: json.dump(ticket_num, f) f.flush() print('%s用户抢票成功' % i) else: print('没票了') def run(i): search(i) buy(i) if __name__ == '__main__': for i in range(10): p = Process(target=run, args=(i,)) p.start()
3、加锁处理,购票行为由并发变为串行,牺牲运行效率,保证了数据安全。
from multiprocessing import Process, Lock import json import time def check(): with open('data.txt', 'r', encoding='utf-8') as f: dict = json.load(f) ticket_num = dict['ticket'] print('还剩%s张票' % ticket_num) def buy(i): with open('data.txt', 'r', encoding='utf-8') as f: dict = json.load(f) ticket_num = dict['ticket'] time.sleep(1) if ticket_num > 0: dict['ticket'] -= 1 with open ('data.txt', 'w', encoding='utf-8') as f: json.dump(dict, f) f.flush() print('用户%s抢票成功' % i) else: print('余票不足') def run(i, mutex): check() mutex.acquire() buy(i) mutex.release() if __name__ == '__main__': mutex = Lock() for i in range(10): p = Process(target=run, args=(i, mutex)) p.start()