进程的粗略理解
一, 程序和进程
-
进程: 正在进行的一个过程或者说一个任务.而负责执行任务的是cpu.
-
单核 + 多道,实现多个进程的并发
-
程序: 一堆代码,一堆文件
-
同一个程序执行两次,那也是两个进程,比如打开暴风影音,虽然都是同一个软件,但是一个可以播放天空之城,一个可以播放七龙珠
二, 并发与并行
-
无论是并行还是并发,在用户看来都是'同时'运行的,不管是进程还是线程,都只是一个任务而已,真是干活的是cpu,cpu来做这些任务,而一个cpu同一时刻只能执行一个任务
-
串行: 所有的进程由cpu一个一个的解决
-
并发: 单个cpu,同时执行多个进程(多道技术),看起来像是同时运行
-
并行: 多个cpu,真正的同时运行多个进程
三, 同步\异步and阻塞\非阻塞
- 同步: 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不会返回.按照这个定义,其实绝大多数函数都是同步调用.但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务
- 异步: 异步的概念和同步相对.当一个异步功能调用发出后,调用者不能立刻得到结果.当该异步功能完成后,通过状态、通知或回调来通知调用者.如果异步功能用状态来通知,那么调用者就需要每隔一定时间检查一次,效率就很低.如果是使用通知的方式,效率则很高,因为异步功能几乎不需要做额外的操作.至于回调函数,其实和通知没太多区别.
- 阻塞: 阻塞调用是指调用结果返回之前,当前线程会被挂起(如遇到I/O操作 ,recv, accept, read input,write, sleep,join等等),函数只有在得到结果之后才会将阻塞的线程激活.有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的.对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已
- 注: 当没有阻塞的进程被计算机强制挂起的时候,不叫阻塞
- 非阻塞: 非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前也会立刻返回,同时该函数不会阻塞当前线程.
- 总结:
- 同步与异步针对的是函数/任务的调用方式: 同步就是当一个进程发起一个函数(任务)调用的时候,一直等到函数(任务)完成,而进程继续处于激活状态.而异步情况下是当一个进程发起一个函数(任务)调用的时候,不会等函数返回,而是继续往下执行,当函数返回的时候通过状态、通知、事件等方式通知进程任务完成.
- 阻塞与非阻塞针对的是进程或线程: 阻塞是当请求不能满足的时候就将进程挂起,而非阻塞则不会阻塞当前进程.
四, 进程的创建
-
对于通用系统(跑很多应用程序),需要有系统运行过程中创建或撤销进程的能力,主要分为四种形式创建新的进程
- 系统初始化
- 一个进程在运行过程中开启了子进程
- 用户的交互式请求,而创建一个新进程(如用户双击暴风影音)
- 一个批处理作业的初始化(只在大型机的批处理系统中应用)
-
新进程的创建都是由一个已经存在的进程执行了一个用于创建进程的系统调用而创建的:
-
python中,如果一次想开启多个进程,必须是一个主进程,开启多个子进程
# 开启进程的2种方式 # 第一种方式 from multiprocessing import Process import time def task(name): print(f'{name} is running') time.sleep(9) print(f'{name} is done') if __name__ == '__main__': # windows必须在main下开启多进程 p = Process(target=task, args=('子进程',)) # args一定是一个元组 p.start() # 通知操作系统在内存种开辟一个空间,将p这个进程放进去,让cpu执行 print('===>主进程') ---------------------------------------------------- # 第二种方式 from multiprocessing import Process import time class MyProcess(Process): def __init__(self, name): super().__init__() # 必须继承父类的init self.name = name def run(self): # 必须是run print(f'{self.name} is running') time.sleep(9) print(f'{self.name} is done') if __name__ == '__main__': p = MyProcess('子进程') p.start() print('===>主进程')
-
-
关于创建的子进程,UNIX和windows
- 相同的是: 进程创建后,父进程和子进程有各自不同的地址空间(多道技术要求物理层面实现进程之间内存的隔离),任何一个进程在其地址空间中的修改都不会影响到另外一个进程.
- 不同的是: 在UNIX中,子进程的初始地址空间是父进程的一个副本,子进程和父进程是可以有只读的共享内存区的.但是对于windows系统来说,从一开始父进程与子进程的地址空间就是不同的.
五, 进程的终止
- 正常退出(自愿,如用户点击交互式页面的叉号,或程序执行完毕发起系统调用正常退出,在linux中用exit,在windows中用ExitProcess)
- 出错退出(自愿)
- 严重错误(非自愿,执行非法指令,如引用不存在的内存,1/0等,可以捕捉异常,try...except...)
- 被其他进程杀死(非自愿)
六, 进程的结构
- 无论linux还是windows,进程只有一个父进程,不同的是:
- 在linux中所有的进程,都是以init进程为根,组成树形结构.父子进程共同组成一个进程组,这样,当从键盘发出一个信号时,该信号被送给当前与键盘相关的进程组中的所有成员.
- 在windows中,没有进程层次的概念,所有的进程都是地位相同的,唯一类似于进程层次的暗示,是在创建进程时,父进程得到一个特别的令牌(称为句柄),该句柄可以用来控制子进程,但是父进程有权把该句柄传给其他子进程,这样就没有层次了.
七, 进程的状态
-
执行程序A,开启一个子进程,执行程序B,开启另外一个子进程,两个进程之间基于管道'|'通讯,将A的结果作为B的输入.进程B在等待输入(即I/O)时的状态称为阻塞,此时B命令都无法运行
-
其实在两种情况下会导致一个进程在逻辑上不能运行
- 进程挂起是自身原因,遇到I/O阻塞,便要让出CPU让其他进程去执行,这样保证CPU一直在工作
- 与进程无关,是操作系统层面,可能会因为一个进程占用时间过多,或者优先级等原因,而调用其他的进程去使用CPU
-
一个进程有三种状态:
八, 进程并发的实现
进程并发的实现在于,硬件中断一个正在运行的进程,把此时进程运行的所有状态保存下来,为此,操作系统维护一张表格,即进程表(process table),每个进程占用一个进程表项(这些表项也称为进程控制块),该表存放了进程状态的重要信息: 程序计数器、堆栈指针、内存分配状况、所有打开文件的状态、帐号和调度信息,以及其他在进程由运行态转为就绪态或阻塞态时,必须保存的信息,从而保证该进程在再次启动时,就像从未被中断过一样.