创建进程大致过程
multiprocessing.Process()
Process类
# Process类源码
class Process(process.BaseProcess):
_start_method = None
@staticmethod
def _Popen(process_obj):
return _default_context.get_context().Process._Popen(process_obj)
BaseProcess类
init方法
def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
*, daemon=None):
assert group is None, 'group argument must be None for now'
count = next(_process_counter) # 获取已有进程数量
self._identity = _current_process._identity + (count,) #进程号为父进程序号+序号
self._config = _current_process._config.copy() # 复制父进程的config信息,很明显此处是深拷贝
self._parent_pid = os.getpid() # 设置父进程id
self._parent_name = _current_process.name # 设置父进程名称
self._popen = None # 设置_popen
self._closed = False # 设置_closed
self._target = target # 设置 _target
self._args = tuple(args) # 设置 _args 参数
self._kwargs = dict(kwargs) # 设置 _kwargs 参数
self._name = name or type(self).__name__ + '-' + \
':'.join(str(i) for i in self._identity) # 如果没有名字,初始化名字
if daemon is not None:
self.daemon = daemon # 设置当前进程是否需要守护
_dangling.add(self) # 将进程放进一个集合
start方法
def start(self):
# 检查进程是或执行过,_close属性是否为True
self._check_closed()
# 一个进程不能执行两次
assert self._popen is None
# 进程对象 只能 由创建对象的进程 去就绪
assert self._parent_pid == os.getpid()
# 守护进程不能拥有子进程
assert not _current_process._config.get('daemon')
# 检查哪个进程已经结束了???目前不知道干啥的
_cleanup()
#此处的self._Popen(self) 不同平台返回的对象不一样
# win 平台 .popen_spawn_win32.Popen
# mac 平台 .popen_spawn_posix.Popen
# 其他 平台 .popen_fork.Popen
# import multiprocessing 章节,描述了一下具体原因
self._popen = self._Popen(self)
self._sentinel = self._popen.sentinel
# Avoid a refcycle if the target function holds an indirect
# reference to the process object (see bpo-30775)
del self._target, self._args, self._kwargs
_children.add(self)
import multiprocessing
分析进程之前我们要看一下导入进程模块之后发生了什么,以下是multiprocessing模块中的context.py模块中部分代码
if sys.platform != 'win32':
class ForkProcess(process.BaseProcess):
_start_method = 'fork'
@staticmethod
def _Popen(process_obj):
from .popen_fork import Popen
return Popen(process_obj)
class SpawnProcess(process.BaseProcess):
_start_method = 'spawn'
@staticmethod
def _Popen(process_obj):
from .popen_spawn_posix import Popen
return Popen(process_obj)
class ForkServerProcess(process.BaseProcess):
_start_method = 'forkserver'
@staticmethod
def _Popen(process_obj):
from .popen_forkserver import Popen
return Popen(process_obj)
class ForkContext(BaseContext):
_name = 'fork'
Process = ForkProcess
class SpawnContext(BaseContext):
_name = 'spawn'
Process = SpawnProcess
class ForkServerContext(BaseContext):
_name = 'forkserver'
Process = ForkServerProcess
def _check_available(self):
if not reduction.HAVE_SEND_HANDLE:
raise ValueError('forkserver start method not available')
_concrete_contexts = {
'fork': ForkContext(),
'spawn': SpawnContext(),
'forkserver': ForkServerContext(),
}
if sys.platform == 'darwin':
# bpo-33725: running arbitrary code after fork() is no longer reliable
# on macOS since macOS 10.14 (Mojave). Use spawn by default instead.
_default_context = DefaultContext(_concrete_contexts['spawn'])
else:
_default_context = DefaultContext(_concrete_contexts['fork'])
else:
class SpawnProcess(process.BaseProcess):
_start_method = 'spawn'
@staticmethod
def _Popen(process_obj):
from .popen_spawn_win32 import Popen
return Popen(process_obj)
class SpawnContext(BaseContext):
_name = 'spawn'
Process = SpawnProcess
_concrete_contexts = {
'spawn': SpawnContext(),
}
_default_context = DefaultContext(_concrete_contexts['spawn'])
由于本人是win32平台,找到else ,此处定义了两个类,以及两个变量
-
变量 _concrete_contexts
此变量定一个了一个字典,关键字 ‘spawn’对应属性是SpawnContext类的实例
-
变量 _default_context
此变量是DefaultContext类的一个实例,初始化DefaultContext,参数是也是 SpawnContext类的实例,那下面分析SpawnContext的初始化做了哪些工作,以及 初始化DefaultContext都有哪些工作
SpawnContext类
该类中仅仅定义了两个属性,**注意:**Process属性是SpawnProcess类
- _name = ‘spawn’
- Process = SpawnProcess
SpawnProcess类
这个类只有一个静态方法_Popen,注意:返回的是.popen_spawn_win32.Popen对象
DefaultContext类
代码如下,注意: get_context()返回SpawnContext类的实例
class DefaultContext(BaseContext):
Process = Process
def __init__(self, context):
self._default_context = context
self._actual_context = None
def get_context(self, method=None):
if method is None:
if self._actual_context is None:
self._actual_context = self._default_context
return self._actual_context
else:
return super().get_context(method)
Popen类
下面大致分析一下.popen_spawn_win32.Popen类
class Popen(object):
'''
Start a subprocess to run the code of a process object
'''
method = 'spawn'
# 此处的 process_obj 正是用户创建的Process类实例
def __init__(self, process_obj):
#spawn.get_preparation_data方法的注释大概是
# 获取子进程 所需 父进程的必要信息
# spawn对象貌似是解释器加载的时候初始化的东西,目前先不深究
prep_data = spawn.get_preparation_data(process_obj._name)
# 创建一个匿名pipe ,返回的二元组是该 pipe读和写的终点?
rhandle, whandle = _winapi.CreatePipe(None, 0)
# 根据 这个写句柄 创建一个c语言的运行时 文件描述符
wfd = msvcrt.open_osfhandle(whandle, 0)
# 返回用于生成子进程的命令行前缀
cmd = spawn.get_command_line(parent_pid=os.getpid(),
pipe_handle=rhandle)
cmd = ' '.join('"%s"' % x for x in cmd)
# 获取命令行可执行的 python命令字符串
python_exe = spawn.get_executable()
# bpo-35797: When running in a venv, we bypass the redirect
# executor and launch our base Python.
if WINENV and _path_eq(python_exe, sys.executable):
python_exe = sys._base_executable
env = os.environ.copy()
env["__PYVENV_LAUNCHER__"] = sys.executable
else:
env = None
# 以二进制写模式 打开写句柄
with open(wfd, 'wb', closefd=True) as to_child:
# start process
try:
hp, ht, pid, tid = _winapi.CreateProcess(
python_exe, cmd,
None, None, False, 0, env, None, None)
_winapi.CloseHandle(ht)
except:
_winapi.CloseHandle(rhandle)
raise
# set attributes of self
self.pid = pid
self.returncode = None
self._handle = hp
self.sentinel = int(hp)
self.finalizer = util.Finalize(self, _close_handles,
(self.sentinel, int(rhandle)))
# send information to child
set_spawning_popen(self)
try:
reduction.dump(prep_data, to_child)
reduction.dump(process_obj, to_child)
finally:
set_spawning_popen(None)
def duplicate_for_child(self, handle):
assert self is get_spawning_popen()
return reduction.duplicate(handle, self.sentinel)
def wait(self, timeout=None):
if self.returncode is None:
if timeout is None:
msecs = _winapi.INFINITE
else:
msecs = max(0, int(timeout * 1000 + 0.5))
res = _winapi.WaitForSingleObject(int(self._handle), msecs)
if res == _winapi.WAIT_OBJECT_0:
code = _winapi.GetExitCodeProcess(self._handle)
if code == TERMINATE:
code = -signal.SIGTERM
self.returncode = code
return self.returncode
def poll(self):
return self.wait(timeout=0)
def terminate(self):
if self.returncode is None:
try:
_winapi.TerminateProcess(int(self._handle), TERMINATE)
except OSError:
if self.wait(timeout=1.0) is None:
raise
kill = terminate
def close(self):
self.finalizer()