1.进程与程序
进程:是正在进行的程序;
程序:是一堆代码,当这堆代码被系统加载到内存执行时,产生进程
注:一个程序是可以产生多个进程,就像我们可以同时运行多个qq,会形成多个进程
2.PID 与PPID
pid:进程编号,用于区分多ge进程个;
ppid:当前进程的父进程的进程编号,
可以用os模块来获取pid/ppid eg: os.getpid() os.getppid()
如果在pycharm中运行py文件,那pycharm就是这个python.exe的父进程,如果从cmd运行py文件,那cmd就是python.exe的父进程
3.进程与程序
1.并发与并行
并发:指的是多个事件同时发生,本质是任务间的切换给人的感觉是同时在进行,称之为伪并行 例如洗衣服和做饭,同时发生了
并行:指的是多个事件同时进行种,例如一个人在写代码另一个人在写书,这两件事件是同时在进行的,一个人是无法真正的并行执行任务
2.阻塞与非阻塞,是程序的状态
阻塞状态:是程序遇到io操作,比如sleep,print,input,导致后续的代码不能被CPU执行
非阻塞状态:与之相反,表示程序正在被CPU执行
3.进程的三种状态间的转换
就绪 到 执行 要 进程调度 , 执行 到 阻塞 要有io , 阻塞 到就绪 要完成io
执行 到 就绪 cpu运行过长,时间片用完
注:多道技术会在进程执行时间过长或遇到IO时自动切换其他进程,意味着IO操作与进程被剥夺CPU执行权都会造成进程无法继续执行
4.进程的创建
-
系统初始化(查看进程linux中用ps命令,windows中用任务管理器,前台进程负责与用户交互,后台运行的进程与用户无关,运行在后台并且只在需要时才唤醒的进程,称为守护进程,如电子邮件、web页面、新闻、打印)
-
一个进程在运行过程中开启了子进程(如nginx开启多进程,os.fork,subprocess.Popen等)
-
用户的交互式请求,而创建一个新进程(如用户双击暴风影音)
-
一个批处理作业的初始化(只在大型机的批处理系统中应用)
关于创建的子进程,UNIX和windows
1.相同的是:进程创建后,父进程和子进程有各自不同的地址空间(多道技术要求物理层面实现进程之间内存的隔离),任何一个进程的在其地址空间中的修改都不会影响到另外一个进程。
2.不同的是:在UNIX中,子进程的初始地址空间是父进程的一个副本,提示:子进程和父进程是可以有只读的共享内存区的。但是对于windows系统来说,会重新加载程序代码。
5.进程的销毁
-
正常退出(自愿,如用户点击交互式页面的叉号,或程序执行完毕调用发起系统调用正常退出,在linux中用exit,在windows中用ExitProcess)
-
出错退出(自愿,python a.py中a.py不存在)
-
严重错误(非自愿,执行非法指令,如引用不存在的内存,1/0等,可以捕捉异常,try...except...)
-
被其他进程杀死(非自愿,如kill -9)
进程的层次结构
无论UNIX还是windows是先相同之处都是 需要将数据copy一份给子进程 这样子进程才知道要干什么
linux 会将父进程的所有数据 完全copy ,
windows 会copy 一部分数据 同时会导入py文件来执行 这样一来递归开进程
linux 拿到父进程知道代码位置 继续执行
建议都加上判断 可以保证两个平台都能用
-
在UNIX中所有的进程,都是以init进程为根,组成树形结构。父子进程共同组成一个进程组,这样,当从键盘发出一个信号时,该信号被送给当前与键盘相关的进程组中的所有成员。
-
在windows中,没有进程层次的概念,所有的进程都是地位相同的,唯一类似于进程层次的暗示,是在创建进程时,父进程得到一个特别的令牌(称为句柄),该句柄可以用来控制子进程,但是父进程有权把该句柄传给其他子进程,这样就没有层次了。
6.python中开启子进程的两种方式
方式1:
实例化Process类
from multiprocessing import Process
import time
def task(name):
print('%s is running' %name)
time.sleep(3)
print('%s is done' %name)
if __name__ == '__main__':
# 在windows系统之上,开启子进程的操作一定要放到这下面
# Process(target=task,kwargs={'name':'egon'})
p=Process(target=task,args=('jack',))
p.start() # 向操作系统发送请求,操作系统会申请内存空间,然后把父进程的数据拷贝给子进程,作为子进程的初始状态
print('======主')
方式2:
继承Process类 并覆盖run方法
from multiprocessing import Process
import time
class MyProcess(Process):
def __init__(self,name):
super(MyProcess,self).__init__()
self.name=name
def run(self):
print('%s is running' %self.name)
time.sleep(3)
print('%s is done' %self.name)
if __name__ == '__main__':
p=MyProcess('jack')
p.start()
print('主')
需要注意的是
1.在windows下 开启子进程必须放到__main__下面,因为windows在开启子进程时会重新加载所有的代码造成递归创建进程
2.第二种方式中,必须将要执行的代码放到run方法中,子进程只会执行run方法其他的一概不管
进程间内存相互隔离
7.join函数
调用start函数后的操作就由操作系统来玩了,至于何时开启进程,进程何时执行,何时结束都与应用程序无关,所以当前进程会继续往下执行,join函数就可以是父进程等待子进程结束后继续执行
案例1:
from multiprocessing import Process
import time
x=1000
def task():
time.sleep(3)
global x
x=0
print('儿子死啦',x)
if __name__ == '__main__':
p=Process(target=task)
p.start()
p.join() # 让父亲在原地等
print(x)
案例2:
from multiprocessing import Process
import time,random
x=1000
def task(n):
print('%s is runing' %n)
time.sleep(n)
if __name__ == '__main__':
start_time=time.time()
p1=Process(target=task,args=(1,))
p2=Process(target=task,args=(2,))
p3=Process(target=task,args=(3,))
p1.start()
p2.start()
p3.start()
p3.join() #3s
p1.join()
p2.join()
print('主',(time.time() - start_time))
start_time=time.time()
p_l=[]
for i in range(1,4):
p=Process(target=task,args=(i,))
p_l.append(p)
p.start()
for p in p_l:
p.join()
print('主',(time.time() - start_time))
Process对象常用属性
from multiprocessing import Process
def task(n):
print('%s is runing' %n)
time.sleep(n)
if __name__ == '__main__':
start_time=time.time()
p1=Process(target=task,args=(1,),name='任务1')
p1.start() # 启动进程
print(p1.pid) # 获取进程pid
print(p1.name) # 获取进程名字
p1.terminate() # 终止进程
p1.join() # 提高优先级
print(p1.is_alive()) # 获取进程的存活状态
print('主')
孤儿进程与僵尸进程
孤儿进程指的是开启子进程后,父进程先于子进程终止了,那这个子进程就称之为孤儿进程 是无害的
例如:qq聊天中别人发给你一个链接,点击后打开了浏览器,那qq就是浏览器的父进程,然后退出qq,此时浏览器就成了孤儿进程
孤儿进程是无害的,有其存在的必要性,在父进程结束后,其子进程会被操作系统接管。
僵尸进程指的是,当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被操作系统接管,子进程退出后操作系统会回收其占用的相关资源!
僵尸进程的危害:,子进程先结束,保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生[僵死进程],将因为没有可用的进程号而导致系统不能产生新的进程。