---
title: fork方式创建子进程导致应用进程卡死
description: "多卡训练场景下，出现训练进程卡死，或者出现训练超时、用户dataloader卡死等现象。"
url: https://www.hiascend.com/document/detail/zh/CANNCommunityEdition/latest/maintenref/troubleshooting/troubleshooting_0075.html
sourcePath: /source/zh/CANNCommunityEdition/920beta1/maintenref/troubleshooting/troubleshooting_0075.html
indexId: 390de4f93cba6a0b659defb2ad63e2039b53af42dba2074598b97ad3db87831193
---
# fork方式创建子进程导致应用进程卡死

#### 问题现象

多卡训练场景下，出现训练进程卡死，或者出现训练超时、用户dataloader卡死等现象。


#### 原因分析

1. 查看Python堆栈，堆栈信息中包含fork关键字。
  使用pyspy工具查看Python堆栈信息的命令示例如下。

  使用pyspy命令前，需要安装gdb和py-spy。若环境中未安装gdb，可通过包管理（如apt-get install gdb命令、yum install gdb命令）进行安装，详细安装步骤及使用方法请参见GDB官方文档(https://sourceware.org/gdb/)；若环境中未安装py-spy，可使用pip3 install py-spy命令安装（若安装时提示pip版本低，例如You are using pip version 19.2.3, however version 24.0 is available，这时可按照提示使用pip3 install --upgrade pip命令升级pip即可）。

```
# 将指定进程的堆栈信息导出到指定文件中，
pid
表示卡住的用户进程ID，
pyspy.log
表示存放堆栈信息的文件，请根据实际情况替换
py-spy dump -p
pid >
pyspy.log
```


  堆栈信息示例如下（xxxx表示目录名称、trainApp表示训练程序，由实际业务情况决定，此处仅为示例）：

```
1 Process 16203: /train/
xxxx
/
xxxx
/
xxxx
/python3.8 -u -m
trainApp
--config-dir
2 Python  v3.8.19 (/train/
xxxx
/
xxxx
/
xxxx
/python3.8)
3
4 Thread 0xFFFF9CF35B50 (active): "MainThread"
5    poll (multiprocessing/
popen_fork.py
:27)
6    wait (multiprocessing/
popen_fork.py
:47)
7    join (multiprocessing/process.py:149)
8    _terminate_pool (multiprocessing/pool.py:729)
9    __call__ (multiprocessing/util.py:224)
10   _scale_down_hw (datasets/datasets.py:96)
11   __init__ (datasets/datasets.py:73)
......
```


2. 查看C/C++堆栈，堆栈信息中包含acquire_lock关键字。
  通过gdb命令观察卡住进程的调用栈信息，若环境中未安装gdb，则需要安装gdb，可通过包管理（如apt-get install gdb、yum install gdb）进行安装，详细安装步骤及使用方法请参见GDB官方文档(https://sourceware.org/gdb/)
  。

```
# 先执行gdb命令,
pid
表示卡住的用户进程ID，请根据实际情况替换
gdb -p
pid
# 再查看调用栈
(gdb)bt
```


  堆栈信息示例如下：

```
#0 0x0000ffffa9b2b268 in do_futex_wait.constprop () from /lib/aarch64-linux-gnu/libpthread.so.0
#1 0x0000ffffa9b2b39c in   new_sem_waut_slow.constprop.0 () from /lib/aarch64-linux-gnu/libpthread.so.0
#2 0x0000ffffa9e96eb8 in PyThread_
acquire_lock
_timed () from /usr/local/lib/libpython3.8.so.1.0
#3 0x0000ffffa9e865a8 in _PyThreadState_DeleteExcept () from /usr/local/lib/libpython3.8.so.1.0
#4 0x0000ffffa9eb94ac in _PyOS_AfterFork_Child () from /usr/local/lib/libpython3.8.so.1.0
#5 0x0000ffffa9eb9638 in ?? () from /usr/local/lib/libpython3.8.so.1.0
......
```


3. 通过Python堆栈的fork关键字以及C++堆栈的acquire_lock关键字，确认训练进程卡死是因为用fork方式启进程触发Python的bug而导致的问题。
  在Python3.8~Python3.11版本中如果不指定创建进程的方式，或者显式指定为fork时，在创建子进程时可能会复制主进程的锁状态，而在子进程里再触发获取锁时，就会导致死锁，进而导致业务进程卡死。

  Python社区也有相关说明：python社区有相同问题的issue：https://github.com/python/cpython/issues/74580(https://github.com/python/cpython/issues/74580)


#### 解决方法

两种解决方式，由用户根据业务情况选用：

- 方式一：按照Python官网(https://www.python.org/downloads/)的指导，升级Python3.8~Python3.11版本的补丁。
  在Python官网，针对Python3.8~Python3.11版本都出了补丁版本，解决fork方式引起的bug。

  在这些补丁版本中，也有针对fork问题的相应说明，如下：

- 方式二：修改客户业务代码，显式使用forkserver或者spawn方式。
  注意事项：如果涉及修改fork的地方比较多或工作量比较大，建议采用方式一，防止修改遗漏。
  1. 找到Python安装目录
    执行pip show torch命令查找Python安装目录，查询结果示例如下：

  2. 在Python安装目录下执行find -name popen_fork.py命令找到popen_fork.py文件，在fork启进程的地方都增加触发堆栈的代码。
    在_launch(self,process_obj)函数内添加代码，目的是走fork的子进程都触发堆栈：

    例如，在上图70行的位置增加如下代码，用于在fork启进程的地方触发堆栈消息打印：

```
import traceback
import time
timestamp = time.time()
timestamp_str = str(int(timestamp))
file_name = f"stack_{timestamp_str}.txt"
with open("/home/{}.txt".format(file_name),"a") as f:
traceback.print_stack(file=f)
```


  3. 复跑训练业务，再次查看Python堆栈。
    在堆栈信息中，先忽略CANN相关的堆栈，只要修改客户业务的fork，切换为“spawn”或“forkserver”启动方式。关于启动方式的详细使用说明，请参见Python官网文档(https://docs.python.org/zh-cn/3.8/library/multiprocessing.html)（此处可根据所用的Python版本，选择对应版本的文档）。
