Python 进程的相关内容

进程:程序在计算机中一次执行的过程
程序: 是一个静态的描述,不占有计算机资源
进程: 是一个动态的过程,占有cpu内存的计算机资源,有一定的生命周期

多任务编程:通过应用程序利用多个计算及核心达到多任务同时执行的目的,以此来提升程序执行效率

同一个程序,每次执行都是不同的进程,因为分配的计算机资源不同

进程的创建流程:
    用户空间运行程序创建进程申请 ---> 调用操作系统内核接口创建进程--->分配计算机资源,确定进程状态---> 将新的进程提供用户使用

多个进程如何占用cpu
     一个内核同一时刻只能运行一个任务
     多个进程对内核资源进行争夺,操作系统决定哪个进程占有计算机核心
    占有计算机核心进程我们称为该进程占有cpu的时间片

进程如何保存相关信息:
   PCB(进程控制块):在unix,linux系统中进程创建后,会在内核开辟一块空间存放进程的相关信息,称为PCB

查看进程信息 ps -aux

进程的信息:用户  PID 占有内存 优先级 等
PID:在操作系统中进程的唯一标志,是大于0的整数,由系统自动分配

进程特征
  进程是操作系统资源分配的最小单位
  每个进程单独占有4G虚拟内存
  进程之间相互独立,运行不受影响


进程的状态
  三态
    就绪态 :进程具备运行条件,等待系统分配处理器运行
    运行态 :进程占有cpu处于运行的状态
    等待态 :又称为阻塞态,睡眠态,指进程暂时不具备运行的条件,需要阻塞等待(sleep accept...)
 五态
   新建态 :创建一个进程,获取资源,直接表现为运行一个程序,或者在程序中创建新的进程
   就绪态 :进程具备运行条件,等待系统分配处理器运行
   运行态 :进程占有cpu处于运行的状态
   等待态 :又称为阻塞态,睡眠态,指进程暂时不具备运行的条件,需要阻塞等待(sleep accept...)
   终止态 :进程执行结束,资源回收过程

进程状态的表示:

  D 等待态  (不可中断等待)
  S 等待态 (可中断等待)
  T 等待态 (暂停)
  R 运行态
  Z 僵尸态 (死亡状态)

    + 前台进程 (不带+即为后台进程)
    < 高优先级
    N 低优先级
    l 有进程链接
    s 会话组

进程优先级

  优先级决定了一个进程的执行权限和占有资源的优先程度

top  : 动态查看当前运行的进程的状态  q退出
          < > 可以翻页
         #!/usr/bin/env python3(加env会在top中显示文件名)

linux 中优先级范围  -20 ---- 19   -20最高

用户程序默认优先级为0

nice  : 以指定的优先级运行进程

示例:
  nice  -9   ./.py文件           以9的优先级运行此程序
  sudo nice  --9 ./.py文件   以-9的优先级运行此程序(小于0的优先级一般要加sudo)

renice : 改变某个进程的优先级

示例: 

   renice  2  PID(NI)

进程(process)
  父子进程:在系统出除了初始化进程其他进程都有一个父进程,可能有多个子进程

父子进程的创建过程:

import os 

fork()
      多程序:无法根据情况动态创建进程
      通过接口创建进程:便于控制,可以在程序中随时根据需要创建
      

  os.fork()
  功能 : 创建一个新的进程
  参数 : 无
  返回值 : 失败返回一个负数  -1
          成功  0   在子进程中fork的返回值  (成功即为创建新的进程成功了)

                >0的正整数(新的进程的PID)在父进程中的返回值

示例:

#os 为系统相关模块,不同操作系统使用情况不同
import os
print("准备创建进程")

pid=os.fork()
if pid<0:
    print("创建进程失败")

elif pid==0:
    print("创建了一个新的进程")
else:
    print("这是原有的进程")

print("进程进行完毕")

注意:

  父进程中fork创建之前的内容子进程同样会复制,但父子进程空间独立,fork创建之后的修改不会影响到对方
  父子进程在执行上互不影响,谁先执行,谁先执行完不确定(一般是父进程先执行)
  子进程虽然复制父进程的空间,但是有自己的特性,比如自己的PID,进程PCB,进程栈空间等

进程相关函数

获取PID号
  os.getpid()
    功能:获取当前进程的PID号
    返回值 : 当前进程PID

  os.getppid()
    功能 : 获取当前进程父进程的PID号
    返回值 : 父进程PID

  进程的退出
    os._exit(status) 
      功能 : 结束一个进程
      参数 : 表示进程的结束状态 是一个整数
             带一个下划线的不能被from os import * 引用

示例:

import os
from time import *

pid=os.fork()
if pid<0:
    print("创建子进程失败")
elif pid==0:
    print("创建子进程成功")
    print("getpid:",os.getpid())#子进程自己获取自己的pid
    print("getppid:",os.getppid())#子进程获取父进程的pid
else:
    sleep(1)
    print("这里是父进程")
    print("pid",pid)#父进程fork返回值就是子进程PID
    print("getpid",os.getpid())#父进程获取自己的pid

sys.exit([status])
功能 : 结束一个进程,抛出异常
参数 : 传入一个正整数表示结束状态
        传入字符串表示结束打印

示例:

import sys

try:
    sys.exit(1)#这时抛出一个异常,下面的print语句不会执行
    print("我要执行")
except:
    print("出错了")

孤儿进程 :父进程先于子进程执行完毕并退出,此时子进程变为孤儿进程

* 孤儿进程会被系统指定的进程所接收,该进程则成为孤儿进程新的父进程。在孤儿进程退出时,系统指定的进程会进行处理不会使其成为僵尸

僵尸进程 : 子进程先于父进程执行完毕并退出,但是父进程没有处理子进程的退出状况,子进程就会成为僵尸进程

* 僵尸进程会滞留PCB的部分信息在内存中,大量的僵尸进程会消耗系统资源,所以应该尽量避免僵尸进程的产生

避免僵尸进程产生的三种方法:
1.让父进程先退出 (不好控制,一般不会采用此方法)

2. 让父进程处理子进程的退出
     使用wait 或者 waitpid 函数(使用阻塞函数,等待并处理子进程执行完后,再执行并退出)

        os.wait() 阻塞函数,直到子进程退出才会结束阻塞
         功能:等待子进程的退出进行处理
         参数: 无
         返回值:一个二元元组,第一个值为退出的子进程pid
                           第二个值为子进程退出状态
       os.waitpid(pid,option)
         功能:处理子进程的退出
         参数:pid: -1 :表示等待任意子进程的退出

                  >0 :表示等待相应的PID号的子进程

            option: 0 :表示阻塞等待

            WNOHANG : 表示非阻塞等待 
         返回值:一个二元元组,第一个值为退出的子进程pid
                           第二个值为子进程退出状态

        os.waitpid(-1,0)等同于os.wait()

     使用信号处理

        import signal

       signal.signal(signum,handler)
         功能: 处理一个信号
         参数: signum: 要处理的信号

                     handler: 对该信号的处理方法
                                   取值:SIG_DFL 采用默认方法处理(等同于默认为signum)
                                              SIG_IGN 忽略这个信号
                                              func自己定义的方法处理(回调函数把一个函数当成参数传递过去)
                                                 func 格式要求:
                                                    def func(sig,frame):
                                                           ...
                                                          sig:接收到的信号
                                                          frame:信号对象

        *signal函数是一个异步处理信号函数,只要执行,在进程中就会按照指定方法处理信号
        *signal不能处理SIGSTOP SIGKILL信号

       在父进程中,忽略子进程的发送信号
            signal(SIGCHLD,SIG_IGH)

  3.创建二级子进程(创建二级子进程,这时一级子进程变为父进程,一级子进程退出时,将不再产生僵尸进程)
      父进程创建子进程后等待子进程退出
      子进程创建二级子进程后马上退出,二级子进程成为孤儿
      让父进程和二级子进程处理具体事件

示例:

import os

#创建以及子进程

pid1=os.fork()
if pid1<0:
    print("创建一级子进程失败")

elif pid1==0:
    #创建二级子进程
    pid2=os.fork()
    if pid2<0:
        print("创建二级子进程失败")
    elif pid2==0:
        print("我创建出来了")
    else:
        #一级子进程退出,使二级进程成为孤儿
        os._exit(0)
else:
    #等待一级子进程退出
    os.wait()
    print("做父进程该做的事情")

multiprocessing 模块创建进程  标准库模块
  需要将事件封装为函数
  使用multiprocessing提供的类创建新的进程
  新的进程和对应的函数相关联,进程启动会自动执行函数,完成事件
  进程回收


创建子进程类
multiprocessing.Process()
    功能:创建子进程
    参数:name : 给创建的进程起个名字
                            默认 process-1

           target:目标函数

           args: 元组 要给函数传递的参数

           kwargs: 字典 要给函数传递的参数 键值
  进程对象属性函数
  p.start()
      功能:启动子进程 此时进程真正创建
   p.join([timeout])
      功能:阻塞等待回收相应的子进程
      参数:默认为阻塞,timeout为超时时间
    p的其他属性
    p.name 进程名称
    p.pid 创建进程的PID号
    p.is_alive() 进程状态
   
    p.daemon
      默认值为False 表示主进程结束后 不影响子进程的执行 如果设置为True 则主进程执行完毕后 所有的子进程一同退出不执行

      注意:

       *设置必须在start()前
       *一般使用daemon=True时不用加join
        *该属性并不是linux/unix系统中所说的守护进程设置(只是一个标识位)

  守护进程:生命周期长,随系统创建 随系统销毁 不受前端控制,后台运行 操作系统进程,或者是自动化运行进程居多

示例:

import multiprocessing as mp
import os
import time
a=1
def fun():
    global a
    a=1000
    print(os.getppid(),"----",os.getpid())
    print("吃早饭")
    time.sleep(1)
    print("吃午饭")
    time.sleep(2)
    print("吃晚饭")
    time.sleep(3)
def fun2():
    print(os.getppid(),"----",os.getpid())
    print("睡午觉")
    time.sleep(2)
    time.sleep(3)
    print("睡觉")
    print(a)
def fun3():
    print(os.getppid(),"----",os.getpid())
    print("打豆豆")
    time.sleep(2)
    print("打小豆豆")
    time.sleep(2)

#创建三个新的进程,关联上面三个事件,达到运行的目的
#生成三个进程对象分别表示这三个进程

p1=mp.Process(target=fun)
p2=mp.Process(target=fun2)
p3=mp.Process(target=fun3)
#通过生成的进程对象启动子进程
#子进程有父进程的代码段 只不过只执行对应的函数

p1.start()
p2.start()
p3.start()
print("Parent PID",os.getpid())
#阻塞等待回收进程
p1.join()
p2.join()
p3.join()

 

多进程
  优点:
    并行运行多个任务,提高运行效率
    空间独立,数据安全,创建方便
  缺点:
    进程创建销毁的过程中消耗较多的计算机资源

进程池

  使用场景: 
    在需要频繁的创建删除较多的情况下,导致计算机资源消耗过多

  进程池创建步骤
    创建进程池,在池内放入适量的进程
    将事件加入进程池等待队列
    使用进程池中的进程不断处理事件
    所有事件处理后,回收关闭进程池

  from multiprocessing import Pool

  Pool()
    功能:创建进程池
    参数:procdsses:指定进程池中进程数量
    返回:得到进程池对象
  Poll.apply_async()
    功能:异步方式将事件放入进程池执行
    参数: func :要执行的事件函数
          args:同Process中args 给函数传参
          kwds:同Process中kwargs 给函数传参
    返回值:返回一个对象 该对象可以通过get()方法得到func函数的返回值

  Pool.close()
    功能:关闭进程池,使其无法加入新的事件

  Pool.join()
  功能:阻塞等待进程池退出(当所有事件处理完毕后)

  Pool.apply()
    用法和apply_asnc一样,只是需要顺序执行,一个事件结束再执行另一个事件
  Pool.map(func,iter)
    功能:类似于内建函数map 将第二个参数的迭代数传递给第一个参数的函数执行,同时兼容了使用进程池执行
    返回值:返回func的返回值列表

  r = pool.map(fun,test) 等同于
  r = []
  for i in test:
    res = pool.apply_async(fun,(i,))
    r.append(res.get())

示例:

from multiprocessing import Pool
from time import sleep
import os

def worker(msg):
    sleep(2)
    print(msg)
    return msg+' over'

#创建进程池
pool=Pool(processes=4)

result=[]
#放入事件到进程池中
for i in range(10):
    msg='hello %d'%i
    #加入事件后就会立即操作事件
    #apply_async的返回值对象,该对象可以获取worker返回结果
    r=pool.apply_async(worker,(msg,))
    #pool.apply(worker,(msg,))
    result.append(r)


sleep(3)
print("+++++++")
#关闭进程池,不能再加入事件
pool.close()
sleep(3)
print("********")
#阻塞等待回收
pool.join()
print("========")
#通过aply_async()返回对象get()方法获取返回值
for res in result:
    print(res.get())
 

进程间通信
  磁盘交互
    速度慢
    不安全

  套接字通信socket 本地套接字

  以上两种通常不用

  管道 消息队列 共享内存 信号 信号量 套接字

  管道通信(pipe)
    在内存中开辟一块空间,对多个进程可见,通过管道,多进程进行通信
  multpprocessing---->pipe

  fd1,fd2=Pipe(duplex=True)
    功能:创建一个管道
    参数:duplex 默认为True 表示双向管道
                设置为False 则表示单向管道
    返回值:返回两个管道流对象,表示管道的两端
            如果是双向管道则两个均可读写
            如果为单向管道则 fd1只能读,fd2只能写

    fd1.recv()
      功能:接收消息(每次接受一条)
      参数:无
      返回值:接收消息
      *如果管道没有消息会阻塞


    fd2.send(data)
      功能:发送消息  可以是字符串或者是其他类型
      参数:要发送的内容

from multiprocessing import Process,Pipe
import os,time
# #创建一个双向管道
fd1,fd2=Pipe()
#如果参数为False 则fd1只能recv fd2只能send
# fd1,fd2=Pipe(False)

def fun(i):
    time.sleep(3)
    #发字符串到管道
    fd1.send('hello'+ str(i))
    print(os.getppid(),"----",os.getpid())


jobs=[]
for i in range(5):
    p=Process(target=fun,args=(i,))
    jobs.append(p)
    p.start()
for i in range(5):
    data=fd2.recv()
    print(data)
for i in jobs:
    i.join()

  消息队列
    队列:先进先出
      在内存中开辟队列模型,用来存放消息,任何拥有队列的进程都可以存取消息
    创建队列
      q=Queue(maxsoze=0)
        功能:创建一个消息队列
        参数:maxsize默认为0 表示队列可存放多少消息有内存而定
                        >0 表示队列最多存放多少条消息
        返回值:返回消息队列对象

      q.put()
        功能:向队列中存放消息

        参数:要存的消息(字符串 整数 列表)
        *当队列满时会阻塞
      q.full()
        判断队列是否为满 满返回True
      q.get()
        功能:向队列中取出消息
        返回值:取出的消息
        *当队列满时会阻塞 
      q.empty()
        判断队列是否为空 空返回 True 
      q.qsize()
        得到当前队列中消息的个数
      q.close() 关闭队列
      *put get 中均有可选参数 block 和timeout  block 默认为True 表示阻塞函数 如设置为False则不阻塞
      timeout block 为True时设置超时时间 

示例:

from multiprocessing import Queue
from time import sleep
#创建队列
q=Queue(3)

q.put(1)
print(q.full())
q.put(2)
q.put(3)
print(q.full())
#超时时间为3
# q.put(4,True,3)
print("取出值为",q.get())
print("队列中有%d条消息"%q.qsize())
sleep(0.1)
print("队列是否为空:",q.empty())
q.close()#关闭队列

  共享内存
    在内存中开辟一段空间 ,存储数据,对多个进程可见,每次写入共享 内存的数据会覆盖之前的内容,由于对内存格式化较少,所以存取速度快 
    只能存储一条消息

    from multiprocessing import Vale,Array 
      obj=Value(ctype,obj)
        功能:开辟内存空间
        参数:ctype str 要转变的c类型 (只接受c语言) (对照ctype表)
            obj 写入共享内存的初始值
        返回值:返回一个共享内存对象
      obj.value 即可得到共享内存中的值
      obj=Array(ctype,obj)
        功能:开辟共享内存空间
        参数:ctype 要转换的类型
             obj  存入到共享内存的数据
                  是一个列表,要求列表中数类型一致
                  正整数,则表示开辟一个多大的序列空间

        返回值:返回一个共享内存对象

示例一:

from multiprocessing import Array,Process
import time
def fun(shm):
    for i in shm:
        print(i)
    shm[2]=1000
#开辟共享内存空间,可容纳6个整数
#初始值[1,2,3,4,5,6]
shm=Array('i',[1,2,3,4,5,6])
#表示在共享内存中开辟一个包含6个整数的空间
# shm=Array('i',6)
p=Process(target=fun,args=(shm,))
p.start()
p.join()
for i in shm:
    print(i)

示例二:

from multiprocessing import Value,Process
import time
import random
def deposite(money):
    for i in range(100):
        time.sleep(0.03)
        money.value+=random.randint(1,200)
def withdraw(money):
    for i in range(100):
        time.sleep(0.02)
        money.value-=random.randint(1,10)
#创建共享内存对象
money=Value('i',2000)
d=Process(target=deposite,args=(money,))
w=Process(target=withdraw,args=(money,))
d.start()
w.start()
d.join()
w.join()
print(money.value)

进程间通信的对比:

                      管道                 消息队列                共享内存
_________________________________________________________
开辟空间       内存                  内存                          内存
_________________________________________________________
读写方式    双向/单向             先进先出                  操作覆盖内存
_________________________________________________________
效率             一般                    一般                          较快
__________________________________________________________
应用      多用于亲缘进程       方便灵活广泛            较复杂
__________________________________________________________
是否需要       是                           否                         需要
互斥机制

信号
  一个进程向另一个进程通过信号传递某种讯息

  kill -l 查看信息
  kill -signame PID  给PID的进程发送一个信号

  信号的名称 :系统定义,信号的名字
  信号的含义 :系统定义的,信号的作用
  信号的默认处理方法 :系统定义的,信号给接受进程带来的行为一般有 终止 暂停 忽略

  python 如何处理
  发送
  os.kill(pid,sig)
        功能:向一个进程发送一个信号
        参数: pid:要发送信号的进程PID
              sig:要发送的信号
      signal.alarm(sec)
        功能:向自身发送一个时钟信号 SIGALRM
        参数:sec 时钟秒数
  *信号属于异步通信方式,信号的发送不会影响进程的持续执行
  *一个进程中只能同时有一个时钟,后面的时钟时间会覆盖前面的时钟时间

  处理
      signal.pause()
        功能:阻塞等待一个信号的发生

  信号量
    给定一定的信号数量,对多个进程可见,并且多个进程均可操作,进程根据信号量的多少,可以有不同的行为

    multiprocess------->Semaphore()
    Semaphore(num)
      功能:定义信号量
      参数:num : 给定信号量的初始个数
      返回值:返回信号量对象

      sem=Semaphore(num)
        sem.acquire() 将信号量 减一 信号量为0时调用会阻塞

        sem.release() 将信号量 加一

同步和互斥
  目的:对共有资源的操作会产生争夺,同步和互斥是一种解决争夺的方案
  临界资源:多个进程或者线程都可以操作的资源
  临界区: 操作临界资源的代码段
  同步:同步是一种合作关系,为完成某个任务多进程或多线程之间形成一种协调,按照条件次序执行,传递告知资源情况,这种协调可能是因为阻塞关系达成的
  互斥:互斥是一种制约关系,当一个进程或线程进入到临界区会进行加锁操作,此时其他进程(线程)在企图操作临界资源就会阻塞,只有当资源被释放后才能进行操作

  Event事件
    创建事件对象
    e=Event()
    提供事件阻塞
     e.wait()
    对事件对象进程设置,此时e.wait判断事件被set则结束阻塞
    e.set()
    清除该事件对象的set
    e.clear()
    检测对象是否被设置 设置返回True
     e.is_set()

示例:

from multiprocessing import Event
#创建事件对象
e=Event()
print(e.is_set())
e.set()
print(e.is_set())
#将设置清除 wait又阻塞
e.clear()
e.wait()

  Lock 锁
    multiprocessing------>Lock
    创建一个对象:
      lock=Lock()
    上锁
      lock.acquire()
    解锁

示例:

from  multiprocessing import Process,Lock
import sys
from time import sleep
def worker1():
    # with lock:
    lock.acquire()#上锁
    for i in range(5):
        sleep(1)
        sys.stdout.write("worker1 输出\n")
    lock.release()#解锁
def worker2():
    #with方式上锁
    with lock:
        for i in range(5):
            sleep(1)
            sys.stdout.write("worker2 输出\n")


#创建一个lock对象
lock=Lock()
#标准输入
sys.stdin
#标准输出
sys.stdout
w1=Process(target=worker1)
w2=Process(target=worker2)
w1.start()
w2.start()
w1.join()
w2.join()

      lock.release()
    with Lock  :------>给wait代码段上锁
      ...
      ...
    -------------------->with代码段结束自动解锁
    *在lock对象处于上锁状态的时候,在企图上锁则会阻塞直至锁被释放,才会继续执行上锁操作

猜你喜欢

转载自blog.csdn.net/Shen1105/article/details/81148687
今日推荐