Python 进程总结

关于进程的理论知识这里简要带过,本文主要通过具体示例对Python中多进程的处理进行一个简要总结。

进程


进程是操作系统进行资源分配和调度的基本单位。Python 标准库 os 和 multiprocessing 中都包含了对进程的操作。multiprocessing 包中进程的处理对开发人员更加友好,其底层其实就是应用 os 包中进程处理的函数,而 os 包中的进程处理函数与C中标准函数对应。

multiprocessing


from multiprocessing import Process

def call(msg):
    print(msg)  
process = Process(target=call, args=("hello",))
process.start()
process.join()

我们可以看到,创建一个进程如此方便。

os


import os

process = os.fork()
if process == 0:
    print("child process")
elif process > 0:
    print("parent process")
    os.wait()

孤儿进程


如果子进程执行还没有结束,父进程就被杀死,那么子进程将变成孤儿进程,挂到init进程下。

import os

process = os.fork()
if process == 0:
    print("child process")
    sleep(100)
    print("child process end!")
elif process>0:
    print("parent process")
    sleep(10)
    print("parent process end!")

你可以通过ps -el看到开始时父子进程都存在,10秒之后,父进程结束,子进程被挂到 init 进程下面。

如果你不想产生孤儿进程,想在父进程结束时子进程也随之结束,你可以使用第三方库prctl,这个库对操作系统C标准库进行了封装,可以对进程进行定制处理。

import signal
import os
from time import sleep
import prctl

p = os.fork()

if p == 0:
    prctl.prctl(prctl.PDEATHSIG, signal.SIGKILL)
    print("child process")
    sleep(100)
    print("child end")
elif p > 0:
    print("parent process")
    sleep(10)
    print("parent end")

Note:
除非你故意去kill父进程,否则使用 multiprocessing 不会产生孤儿进程。处理细节涉及到 daemon 这个属性,这个 daemon 属性与我们所说的守护进程没有半毛钱关系。daemon 属性是布尔类型,设置为 True 的时候,父进程退出之前会先杀死子进程,设置为 False 的时候,父进程退出之前会对子进程进行 wait。

僵死进程


子进程结束之后,在操作系统中还会记录它的一些运行信息,如返回状态、运行时间和进程号。此时,需要父进程进行资源清理回收。如果没有回收,就成了僵死进程。僵死进程生成了,而程序中又没有处理的话,它会一直存在,直到父进程结束。你可以通过杀死其父进程来回收掉僵死进程。虽然僵死进程占用的资源很少,但是如果僵死进程数堆积得越来越多,也会对系统性能产生影响。我们在程序中还是最好有对僵死进程的相关处理。有三种方式来避免僵死进程,其本质上都是wait回收。

僵死处理:join


直接在父进程中调用 join 函数,如上面示例中。但这样会阻塞父进程。

僵死处理:signal+wait


子进程结束后会发送一个SIGCHLD信号给父进程,所以可以在父进程中设置一个信号处理函数处理对子进程进行回收。

from multiprocessing import Process
from time import sleep
import os
import signal


def sigchld_handler(signum, action):
    try:
        while True:
            pid, info = os.waitpid(-1, os.WNOHANG)
            if pid == 0:
                break
            status = info >> 8
            print("subprocess: {0}, status: {1}".format(pid, status))
    except OSError as e:
        print(e.message)

signal.signal(signal.SIGCHLD, sigchld_handler)

def call(msg):
    for i in range(2):
        print(msg)  

process = Process(target=call, args=("hello",))
process.start()
sleep(10)

输出:

hello
hello
subprocess: 11560, status: 0

NOTE: sleep(10) 在信号中断处理返回后将跳过而执行下一条指令;

僵死处理:init 托管


如果你不想自己等待回收,你也可以把它托管给init进程,让它处理僵死进程。只要在父进程中将 SIGCHLD 信号的处理函数设置成 SIG_IGN 就能将僵死态的进程直接交给 init 进程进行回收。

from signal import signal, SIGCHLD, SIG_IGN

signal(SIGCHLD, SIG_IGN)

守护进程


守护进程是指脱离了控制终端的后台运行进程。

import os
import sys


def daemonize (stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
  try:
    pid = os.fork()
    if pid > 0:
      sys.exit(0)
  except OSError as e:
    print >> sys.stderr, ("step1 - fork failed, errno: {}, str: {}\n".format(e.errno, e.strerror) )
    sys.exit(1)

  os.chdir(os.getcwd())
  os.umask(0)
  os.setsid()

  try:
    pid = os.fork()
    if pid > 0:
      sys.exit(0)
  except OSError as e:
    print >> sys.stderr, ("step2 - fork failed, errno: {}, str: {}\n" % (e.errno, e.strerror) )
    sys.exit(1)

  sys.stdout.flush()
  sys.stderr.flush()
  tmpin = open(stdin, 'r')
  tmpout = open(stdout, 'a+')
  tmperr = open(stderr, 'a+')
  os.dup2(tmpin.fileno(), sys.stdin.fileno())
  os.dup2(tmpout.fileno(), sys.stdout.fileno())
  os.dup2(tmperr.fileno(), sys.stderr.fileno())
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值