01-进程与程序的概念
并发:多进程和多线程
进程的概念:进程就是正在执行的过程,一个应用程序不是进程,只有应用程序启动以后才能说是进程,进程是一个抽象的概念, 起源于操作系统
02-操作系统介绍
应用程序的调用都是由操作系统实现的,操作系统会管理多个应用程序,具体执行程序是由硬件CPU管理的
多个应用程序启动会先给操作系统请求,由操作系统的算法判断硬件是否执行
操作系统的两个核心功能
1.封装好硬件的复杂接口,给应用程序调用
2.管理应用程序上的启动的多个进程(分配给CPU资源)
03-操作系统发展史-第一代计算机
【特点】
没有操作系统的概念
所有的程序设计都是直接操控硬件
【工作过程】
程序员在墙上的机时表预约一段时间,然后程序员拿着他的插件版到机房里,将自己的插件板接到计算机里,这几个小时内他独享整个计算机资源,后面的一批人都得等着(两万多个真空管经常会有被烧坏的情况出现)。
后来出现了穿孔卡片,可以将程序写在卡片上,然后读入机而不用插件板
【优点】
程序员在申请的时间段内独享整个资源,可以即时地调试自己的程序(有bug可以立刻处理)
【缺点】
浪费计算机资源,一个时间段内只有一个人用。
注意:同一时刻只有一个程序在内存中,被cpu调用执行,比方说10个程序的执行,是串行的
04-操作系统发展史-批处理系统
第二代计算机的批处理系统
第二代如何解决第一代的问题/缺点:
1.把一堆人的输入攒成一大波输入,
2.然后顺序计算(这是有问题的,但是第二代计算也没有解决)
3.把一堆人的输出攒成一大波输出
优点:
批处理,节省了机时
缺点:
1.整个流程需要人参与控制,将磁带搬来搬去(中间俩小人)
2.计算的过程仍然是顺序计算-》串行(仍然是一个一个执行的,把一堆程序的结果一起执行,最后全部执行完了以后输出)
3.程序员原来独享一段时间的计算机,现在必须被统一规划到一批作业中,等待结果和重新调试的过程都需要等同批次的其他程序都运作完才可以(这极大的影响了程序的开发效率,无法及时调试程序)
05-操作系统发展史-多道技术
第三代计算机(1965~1980):集成电路芯片和多道程序设计
如何解决第二代计算机的问题1:
卡片被拿到机房后能够很快的将作业从卡片读入磁盘,于是任何时刻当一个作业结束时,操作系统就能将一个作业从磁带读出,装进空出来的内存区域运行,这种技术叫做同时的外部设备联机操作:SPOOLING,该技术同时用于输出。当采用了这种技术后,就不在需要IBM1401机了,也不必将磁带搬来搬去了(中间俩小人不再需要)
如何解决第二代计算机的问题2:
第三代计算机的操作系统广泛应用了第二代计算机的操作系统没有的关键技术:多道技术
cpu在执行一个任务的过程中,若需要操作硬盘,则发送操作硬盘的指令,指令一旦发出,硬盘上的机械手臂滑动读取数据到内存中,这一段时间,cpu需要等待,时间可能很短,但对于cpu来说已经很长很长,长到可以让cpu做很多其他的任务,如果我们让cpu在这段时间内切换到去做其他的任务,这样cpu不就充分利用了吗。这正是多道技术产生的技术背景
多道技术:
多道技术中的多道指的是多个程序,多道技术的实现是为了解决多个程序竞争或者说共享同一个资源(比如cpu)的有序调度问题,解决方式即多路复用,多路复用分为时间上的复用和空间上的复用。
空间上的复用:将内存分为几部分,每个部分放入一个程序,这样,同一时间内存中就有了多道程序。
时间上的复用:当一个程序在等待I/O时,另一个程序可以使用cpu,如果内存中可以同时存放足够多的作业,则cpu的利用率可以接近100%,类似于我们小学数学所学的统筹方法。(操作系统采用了多道技术后,可以控制进程的切换,或者说进程之间去争抢cpu的执行权限。这种切换不仅会在一个进程遇到io时进行,一个进程占用cpu时间过长也会切换,或者说被操作系统夺走cpu的执行权限)
本质上CPU只能运行一个程序,但是不断切换的时候让人的感觉就是CPU在不断运行并发程序
串行:程序一个一个执行
并行:多个程序一起执行
空间上复用的最大问题:内存级别在物理层面上就要保证进程间的数据是互相独立的,第三代计算机仍然是批处理系统
06-操作系统发展史-分时操作系统
如何解决第二代计算机的问题3:
分时操作系统:多个联机终端+多道技术
20个客户端同时加载到内存,有17在思考,3个在运行,cpu就采用多道的方式处理内存中的这3个程序,由于客户提交的一般都是简短的指令而且很少有耗时长的,索引计算机能够为许多用户提供快速的交互式服务,所有的用户都以为自己独享了计算机资源
07-总结操作系统功能与多道技术
必备的理论基础:
一 操作系统的作用:
1:隐藏丑陋复杂的硬件接口,提供良好的抽象接口
2:管理、调度进程,并且将多个进程对硬件的竞争变得有序
二 多道技术:
1.产生背景:针对单核,实现并发
ps:
现在的主机一般是多核,那么每个核都会利用多道技术
有4个cpu【并行】,运行于cpu1的某个程序遇到io阻塞,会等到io结束再重新调度,会被调度到4个
cpu中的任意一个,具体由操作系统调度算法决定。
2.空间上的复用:如内存中同时有多道程序
3.时间上的复用:复用一个cpu的时间片
强调:遇到io切,占用cpu时间过长也切,核心在于切之前将进程的状态保存下来,
这样才能保证下次切换回来时,能基于上次切走的位置继续运行
08-进程理论
什么是进程?
进程就是一个正在执行的过程或者是一个任务,而复杂执行的任务的是CPU
进程与程序的区别?
程序仅仅是一堆代码而已,而进程指的是程序的运行过程
强调:同一个程序执行两次,那也是两个进程。
并发:是伪并行,即看起来是同时运行。单个cpu+多道技术就可以实现并发
并行:同时运行,只有具备多个cpu才能实现并行
进程的创建
- 系统初始化(查看进程linux中用ps命令,windows中用任务管理器,前台进程负责与用户交互,后台运行的进程与用户无关,运行在后台并且只在需要时才唤醒的进程,称为守护进程,如电子邮件、web页面、新闻、打印)
- 一个进程在运行过程中开启了子进程(如nginx开启多进程,os.fork,subprocess.Popen等)
- 用户的交互式请求,而创建一个新进程(如用户双击暴风影音)
- 一个批处理作业的初始化(只在大型机的批处理系统中应用)
【注意】Unix开子进程就是拷贝一份父进程的数据到子进程作为初始数据,windows开子进程也是拷贝一份父进程的数据,但是数据不是和父进程完全相同的数据
进程的状态
其实在两种情况下会导致一个进程在逻辑上不能运行,
- 进程挂起是自身原因,遇到I/O阻塞,便要让出CPU让其他进程去执行,这样保证CPU一直在工作
- 与进程无关,是操作系统层面,可能会因为一个进程占用时间过多,或者优先级等原因,而调用其他的进程去使用CPU。
进程的三个状态:
就绪状态: 当进程已分配到除CPU以外的所有必要资源后,只要再获得CPU,便可立即执行,进程这时的状态就称为就绪状态
执行状态: 进程已获得CPU,其程序正在执行。在单处理机系统中,只有一个进程处于执行状态;在多处理机系统中,则有多个进程处于执行状态。
阻塞状态: 正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可有多种,例如,等待I/O完成、申请缓冲区不能满足、等待信件(信号)等。
09-开启子进程的两种方式
方式一
# 开启子进程的目的是执行一个任务# 方式一from multiprocessing import Processimport timedef task(name): print('%s is running' % name) time.sleep(4) print('%s is done' % name)# windows操作系统一定要在main下执行Processif __name__ == '__main__': p1 = Process(target=task, args=('panda',)) #实例化一个对象 p2 = Process(target=task, kwargs={ 'name':'boy'}) #实例化一个对象 p1.start() #仅仅是给操作系统发送了一个信号 p2.start() print('主进程结束')
方式二
# 方式二# 改写默认的一个类,提供功能from multiprocessing import Processimport timeclass MyProcess(Process): def __init__(self, name): super().__init__() self.name = name def run(self): print('%s is running' % self.name) time.sleep(4) print('%s is done' % self.name)if __name__ == '__main__': p1 = MyProcess('panda') p1.start() print('主进程已经结束了')
10-查看进程的pid与ppid
操作系统对进程的管理也是需要编号的,这个编号就是进程的ID,简称为pid
import osfrom multiprocessing import Processimport timedef task(): print('%s is running' % os.getpid()) time.sleep(4) print('%s is done' % os.getpid())# windows操作系统一定要在main下执行Processif __name__ == '__main__': p1 = Process(target=task) #实例化一个对象 p2 = Process(target=task) #实例化一个对象 p1.start() #仅仅是给操作系统发送了一个信号 p2.start() print('主进程结束',os.getpid())
输出结果
import osfrom multiprocessing import Processimport timedef task(): print('%s is running.Parent is is %s' % (os.getpid(),os.getppid())) time.sleep(4) print('%s is done.Parent is is %s' % (os.getpid(),os.getppid()))# windows操作系统一定要在main下执行Processif __name__ == '__main__': p1 = Process(target=task) #实例化一个对象 p2 = Process(target=task) #实例化一个对象 p1.start() #仅仅是给操作系统发送了一个信号 p2.start() print('主进程结束',os.getpid())
输出结果
CMD命令查看应用程序的进程id
tasklist |findstr ProcessName
11-僵尸进程与孤儿进程
12-Process对象的其他属性或方法
13-练习题讲解
14-守护进程
15-互斥锁
16-模拟抢票
17-互斥锁与join的区别
18-队列的使用
19-生产者消费者模型
20-JoinableQueue的使用
21-什么是线程
22-开启线程的两种方式
23-进程与线程的区别
24-Thread对象的其他属性或方法
25-守护线程
26-互斥锁
27-GIL的基本概念
28-GIL与自定义互斥锁的区别
29-GIL与多线程
30-死锁与递归锁
31-信号量
32-Event事件
33-定时器
34-线程queue
35-多线程实现并发的套接字通信
36-进程池线程池
37-异步调用与回调机制
38-进程池线程池小练习
39-协程介绍
40-协程实现与总结
41-greenlet模块
42-gevent模块
43-gevent异步提交任务
44-基于gevent模块实现并发的套接字通信
45-IO模型介绍
46-阻塞IO模型
47-非阻塞IO模型
48-多路复用IO模型
49-异步IO模型