python多进程卡死问题排查

news2024/9/23 21:24:18

文章目录

    • 背景
      • 开发环境
      • 启动链路
    • 问题排查
      • pdb调试
      • 给文件加共享锁
      • 查看进程fd
      • strace追踪堆栈
      • <br />
      • GDB调试python
        • 安装gdb和python-dbg
        • python-dbg和python版本
        • 编译python3.9的dbg文件
        • gdb调试
      • pytorch多进程卡死问题
      • 多进程的fork和spawn模式
    • 其他解决方式
      • 使用fastapi自带的backgroudTask
      • 使用多线程模式
    • 个人建议
      • 使用celery多进程队列
      • 启动两个镜像
    • 总结
    • 补充问题
      • multiprocess多进程使用cuda的问题
      • 多次调用multiprocessing.set_start_method的问题
        • 解决方案

背景

由于一些原因,打算把点云算法的三维重建和定位打到一个docker镜像里面,统一对外提供服务接口。
算法嘛,需要加载比较大的模型文件,使用多核CPU,使用GPU等资源,因此一开始是打算使用多进程的方式去响应接口请求。
然后就有问题了,启动子进程去三维重建,直接卡死。。

开发环境

ubuntu22.04
python: 3.9
pytorch: 1.13.1
gunicorn+fastapi

启动链路

  1. 使用gunicorn启动fastapi服务
  2. 初始化空间定位算法,加载算法模型。初始化三维重建。
  3. 请求三维重建接口,使用fastapi的multiprocessing开启子进程响应。
  4. 问题:子进程读取三维重建模型卡死。

问题排查

推测是可能遇到了文件锁的东西,导致进程等待。具体需要排查原因。

pdb调试

遇事不决,可问春风(debug)。第一时间就上pdb,结果pdb到卡住的地方就报错了,报错如下:

  File "/usr/local/lib/python3.9/bdb.py", line 88, in trace_dispatch
    return self.dispatch_line(frame)
  File "/usr/local/lib/python3.9/bdb.py", line 113, in dispatch_line
    if self.quitting: raise BdbQuit
bdb.BdbQuit
ERROR:rokidloc_server:reconstruct failed, reason:

# 解释
bdb是pdb的底层库,用于提供断点、单步等功能。该错误消息 "bdb.BdbQuit" 通常出现在
调试器退出时。由于断点、单步等操作是通过向代码中注入信号来完成的,
在程序卡住或被无限循环阻塞时,这些信号无法恰当的注入或被接收处理,
pdb 所在的环境可能变得不可靠或不一致,调试器就会自动退出以防止进一步的问题。

这个博客 在 Python 中使用 GDB 来调试 转载_python gdb-CSDN博客里面也提到了,卡住的进程无法通过pdb进行调试,可以考虑使用GDB调试python程序。
行吧,小趴菜一个。。

给文件加共享锁

一开始总以为是文件锁导致的,就去给读取模型文件的部分加共享锁,读完就释放,如下:

            model_path = self.config['weight_path']
        with open(model_path, 'rb') as f:
            try:
                fcntl.flock(f, fcntl.LOCK_SH)  # 共享锁定
                state_dict = torch.load(f)
            finally:
                fcntl.flock(f, fcntl.LOCK_UN)  # 释放锁

然而并没有什么卵用。

查看进程fd

那么是否可以查看进程fd,看看是否打开了模型文件呢?确认下是否是读取模型文件导致的卡住。

ls -l /proc/pid/fd

# 结果没有看到直接打开模型文件的path
lrwx------ 1 root root 64 Mar 23 18:09 0 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 23 18:09 1 -> /dev/pts/1
l-wx------ 1 root root 64 Mar 23 18:09 10 -> 'pipe:[51968331]'
lr-x------ 1 root root 64 Mar 23 18:09 11 -> 'pipe:[51968350]'
l-wx------ 1 root root 64 Mar 23 18:20 12 -> 'pipe:[51968350]'

# 查看pid启动了多少个子线程
ps -T -p 256
    PID    SPID TTY          TIME CMD
    256     256 pts/1    00:00:09 gunicorn
    256     289 pts/1    00:00:00 iou-sqp-3015093
    256     291 pts/1    00:00:00 gunicorn
    256     292 pts/1    00:00:00 gunicorn
    256     293 pts/1    00:00:00 gunicorn
    256     294 pts/1    00:00:00 gunicorn
    256     295 pts/1    00:00:00 gunicorn
    256     296 pts/1    00:00:00 gunicorn
    256     297 pts/1    00:00:00 gunicorn
    256     298 pts/1    00:00:00 gunicorn

# 298就是持有private的锁进程,查看298对应的fd
ls -l /proc/298/fd
# 依然看不到有明确的打开模型文件,不清楚到底卡哪里了

ok,还是失败。

strace追踪堆栈

查看初始化的进程信息

strace -f -p 256
strace: Process 256 attached with 10 threads
[pid   298] futex(0x7fa1f0001200, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 0, NULL, FUTEX_BITSET_MATCH_ANY <unfinished ...>
[pid   297] futex(0x561186395444, FUTEX_WAIT_PRIVATE, 1064, NULL <unfinished ...>
[pid   296] futex(0x561186395444, FUTEX_WAIT_PRIVATE, 1064, NULL <unfinished ...>
[pid   294] futex(0x561186395444, FUTEX_WAIT_PRIVATE, 1064, NULL <unfinished ...>
[pid   293] futex(0x561186395444, FUTEX_WAIT_PRIVATE, 1064, NULL <unfinished ...>
[pid   292] futex(0x561186395444, FUTEX_WAIT_PRIVATE, 1064, NULL <unfinished ...>
[pid   295] futex(0x561186395444, FUTEX_WAIT_PRIVATE, 1064, NULL <unfinished ...>
[pid   256] epoll_pwait(18,  <unfinished ...>
[pid   291] futex(0x561186395444, FUTEX_WAIT_PRIVATE, 1064, NULL <unfinished ...>

# 解释
strace输出的这行信息表示,这条命令表示线程正在等待内存地址0x561186395444上的
futex的值变为1064,在此情况发生之前,这个线程会被阻塞。

FUTEX_WAIT_BITSET_PRIVATE: 这是等待操作。它会让调用进程睡眠,直到另一进程使用 
FUTEX_WAKE 操作来唤醒它。
_PRIVATE 后缀表示这个锁只能被同一进程中的其他线程所见,不能被其他进程的线程所见。


查看卡住的进程信息

strace -f -p 135
strace: Process 135 attached
futex(0x555989b08d84, FUTEX_WAIT_PRIVATE, 1064, NULL

# 解释
线程正在等待一个 futex 的值变为 1064。如果在无限等待期间 futex 的值不发生变化,那么线程将一直阻塞。

Futex是什么呢?参考:Futex系统调用,Futex机制,及具体案例分析-CSDN博客
简单来说,Futex是一种用户态和内核态混合的同步机制,一种轻量级的锁。而strace查看的出现的futex是代表进程被挂起等待唤醒。
问题是两个进程都等待唤醒,那就有问题了,表现上就是卡住了。


GDB调试python

安装gdb和python-dbg

参考:使用gdb调试Python程序-CSDN博客

  1. 查看python-dbg 是否和gdb结合起来了: /usr/share/gdb/auto-load/usr/bin/
  2. 可以手动设置gdb的auto-load : 参考:https://docs.python.org/3/howto/gdb_helpers.html
  3. 进入gdb,执行: 参考:https://www.lyyyuna.com/2018/01/01/python-internal4-lldb/
1.gdb python3.10
2.set args test.py
3.run
  1. gdb -p pid 查看python的堆栈。
    1. py-bt 查看python堆栈,py-list查看python代码

嗯,看起来很简单,然而实际上立马就遇到了坑。

python-dbg和python版本

python-dbg是gdb可以调试python的关键。而python-dbg和python版本是要一一对应的。
博主python版本是3.9,ubuntu22.04默认的python版本是3.10,就导致安装的python-dbg也是3.10的,因此调试失败。

编译python3.9的dbg文件
  1. 去github下载cpython,切换到3.9版本,按照自己的python版本来
  2. 执行编译
1. ./configure --with-pydebug --enable-optimizations
2. make install
3. 根目录看到生成了python, python-gdb.py
4. 默认编译完成之后,会把python复制到/usr/local/bin/
  注意: 查看是否把python-gdb.py或者python3.9-gdb.py给copy到/usr/local/bin/下
  如果没有copy的话,需要我们手动copy。 
  cp /xxx/cpython/python-gdb.py /usr/local/bin/python3.9-gdb.py
5. 安装完gdb, 设置gdb的启动配置文件:
  vim ~/.gdbinit
  add-auto-load-safe-path /usr/local/bin/python3.9-gdb.py
6. gdb python
  (gdb) info auto-load
  gdb-scripts:  No auto-load scripts.
  libthread-db:  No auto-loaded libthread-db.
  local-gdbinit:  Local .gdbinit file was not found.
  python-scripts:
  Loaded  Script
  Yes     /usr/local/bin/python3.9-gdb.py
  1. 运行python程序,通过gdb查看堆栈
1. ps aux 查看python程序的pid
2. gdb 调试pid : gdb -p pid
3.进入之后,py-bt查看堆栈,py-list查看python程序代码。例如:
(gdb) py-list
  39        loop = events.new_event_loop()
  40        try:
  41            events.set_event_loop(loop)
  42            if debug is not None:
  43                loop.set_debug(debug)
 >44            return loop.run_until_complete(main)
  45        finally:
  46            try:
  47                _cancel_all_tasks(loop)
  48                loop.run_until_complete(loop.shutdown_asyncgens())
  49                loop.run_until_complete(loop.shutdown_default_executor())
(gdb)

gdb调试
  1. gdb直接运行python程序: 参考:https://blog.51cto.com/u_16175509/6890013
1.gdb python
2.run test.py
3.b test.py:4
4.continue
  1. gdb调试在运行中的python程序,也就是通过pid调试
    1. gdb进去之后,查看当前代码卡在哪了,如图可以看到是44行image.png
    2. 查看python的堆栈
      1. 这个堆栈表示了 Python 应用在 Gunicorn 服务器下的运行路径。
      2. image.png
    3. 查看阻塞进程
      1. 看报错是pytorch加载模型的报错,加载参数失败,那么要么是模型问题,要么是pytorch版本的问题了。
      2. image.png

猜测是pytorch的数据加载问题。ok,那就查查pytorch。

pytorch多进程卡死问题

参考:https://blog.csdn.net/kelxLZ/article/details/114591236

  1. 查看程序是否使用openMp,可以看到是用到了
lsof -p 104  | grep libgomp
gunicorn 104 root  mem       REG               0,54    168721  9362919 /usr/local/lib/python3.9/site-packages/torch/lib/libgomp-a34b3233.so.1
gunicorn 104 root  mem       REG               0,54    298776  9185138 /usr/lib/x86_64-linux-gnu/libgomp.so.1.0.0
  1. 咨询了算法同学,他们在训练的时候,也遇到过这种问题,一般的解决方案是:
# 子进程启动方法设置成spawn模式
torch.multiprocessing.set_start_method('spawn')
  1. python官网给出的三种进程启动方法
根据不同的平台, multiprocessing 支持三种启动进程的方法。这些 启动方法 有

spawn
父进程会启动一个全新的 python 解释器进程。 子进程将只继承那些运行进程对象的 
run() 方法所必需的资源。 特别地,来自父进程的非必需文件描述符和句柄将不会被继承。
使用此方法启动进程相比使用 fork 或 forkserver 要慢上许多。
可在Unix和Windows上使用。 Windows上的默认设置。

fork
父进程使用 os.fork() 来产生 Python 解释器分叉。子进程在开始时实际上与父进程
相同。父进程的所有资源都由子进程继承。请注意,安全分叉多线程进程是棘手的。
只存在于Unix。Unix中的默认值。

forkserver
程序启动并选择 forkserver 启动方法时,将启动服务器进程。 从那时起,每当需要
一个新进程时,父进程就会连接到服务器并请求它分叉一个新进程。 分叉服务器进程是
单线程的,因此使用 os.fork() 是安全的。 没有不必要的资源被继承。

可在Unix平台上使用,支持通过Unix管道传递文件描述符。

在 3.8 版更改: 对于 macOS,spawn 启动方式是默认方式。 因为 fork 可能导致
subprocess崩溃,被认为是不安全的,查看 bpo-33725
  1. 更改成spawn启动,问题解决。
    1. spawn创建子进程很慢,只能用于耗时不敏感的程序
    2. spawn会导致有些全局变量无法使用,需要传入进去

多进程的fork和spawn模式

具体的概念就不说了, 主要是看一下通过这两种模式,启动的子进程是什么样子的,父子关系如何。

# apt install psmisc : 方便查看pid的tree

--  fork模式
ps aux
root          88      85  0 21:32 pts/1    00:00:00 /usr/local/bin/python3.9 /usr/local/bin/gunicorn --config=configs/gunicorn.conf.py server.service:app
root          89      88 40 21:32 pts/1    00:00:10 /usr/local/bin/python3.9 /usr/local/bin/gunicorn --config=configs/gunicorn.conf.py server.service:app
root         135			89  1 21:32 pts/1    00:00:00 /usr/local/bin/python3.9 /usr/local/bin/gunicorn --config=configs/gunicorn.conf.py server.service:app

# pstree -p -T 88
gunicorn(88)---gunicorn(89)---gunicorn(135)

pid=88是gunicorn主进程
pid=89是gunicorn的工作worker进程
pid=135是multiprocess fork出来的子进程


---- spawn模式
ps aux
root          59  0.0  0.0  30788 26540 pts/1    S+   21:16   0:00 /usr/local/bin/python3.9 /usr/local/bin/gunicorn --config=configs/gunicorn.conf.py server.ser
root          60  3.0  1.5 4664492 1026576 pts/1 Sl+  21:16   0:10 /usr/local/bin/python3.9 /usr/local/bin/gunicorn --config=configs/gunicorn.conf.py server.ser
root         104  0.0  0.0  13468 11612 pts/1    S+   21:17   0:00 /usr/local/bin/python3.9 -c from multiprocessing.resource_tracker import main;main(36)
root         105  672  1.2 6657676 799780 pts/1  Sl+  21:17  31:51 /usr/local/bin/python3.9 -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_

pid=104: 第一个进程是资源追踪器 (resource tracker)。资源追踪器的目的是保证当一个程序结束时,其创建的所有资源,如临时文件,都能被正确地清理掉。
pid=105: 第二个进程是实际的子进程,它是通过 spawn 启动方法创建的。在 spawn 模式下,一个全新的Python解释器进程被启动,
主程序的所有代码都需要在这个新的进程中重新导入。

# pstree -p -T 59
gunicorn(59)---gunicorn(60)-+-python3.9(104)
                            `-python3.9(105)

pid=59是主进程
pid=60是worker进程
pid=104是spawn的监控进程
pid=105是spawn模式启动的子进程

最明显的差别就是spawn启动的子进程是新的python解释器,有点6。

其他解决方式

其实发现程序有bug之后,第一时间就用其他方式解决掉了。这里也记录一下其他的解决方案,供大家参考。

使用fastapi自带的backgroudTask

  1. backgroundTasks可以完成任务
  2. 问题在于,无法控制,无法做优雅退出

使用多线程模式

from concurrent.futures import ThreadPoolExecutor
  1. 可以完成任务,htop发现也能用到多核
  2. 一样的问题,无法监控状态和优雅退出

个人建议

使用celery多进程队列

使用fastapi + celery的方式,参考:https://derlin.github.io/introduction-to-fastapi-and-celery/03-celery/
这种方式更适合做大型的服务,通过任务队列保证数据安全,通过flower来监控进程状态。
flower参考: https://medium.com/featurepreneur/flower-celery-monitoring-tool-50fba1c8f623

启动两个镜像

三维建图和空间定位都需要用到多核和GPU资源,多线程模式启动更好一些,因此可以考虑部署两个镜像。这也是docker一直主张的原则,粒度尽量小,指责单一,内聚。

总结

当发现这个问题之后,很快就通过多线程的方式暂时解决掉了,依然可以用到多核资源。只是遇到问题不应该退缩,也要把握住每一个解决难题的机会。
出于这种心理,一步步排查最终锁定问题,知其然知其所以然,才能有的放矢的解决根本问题。排查问题的路上也学习了不少东西,值得记录下来。

end

===== 20240327补充 =====

补充问题

multiprocess多进程使用cuda的问题

RuntimeError: Cannot re-initialize CUDA in forked subprocess. To use CUDA with multiprocessing, you must use the 'spawn' start method

解决方案,设置进程启动方式为spawn即可。

多次调用multiprocessing.set_start_method的问题

  File "/usr/local/lib/python3.9/multiprocessing/context.py", line 243, in set_start_method
    raise RuntimeError('context has already been set')
RuntimeError: context has already been set

文档上有解释:参考:https://docs.python.org/zh-cn/3.9/library/multiprocessing.html#multiprocessing.set_start_method
设置启动子进程的方法。 method 可以是 ‘fork’ , ‘spawn’ 或者 ‘forkserver’ 。

注意这最多只能调用一次,并且需要藏在 main 模块中,由 if name == ‘main’ 保护着。

解决方案

a. 如果使用spawn模式,那么只是把multiprocessing.set_start_method放到文件顶部是不够的,每次copy进程都会执行一次。解决方案就是加force参数:

import multiprocessing as mp
# 第二个参数为True
mp.set_start_method('spawn', True)

以上方案虽然解决了报错,但感觉并不完美,强扭的瓜不甜。

b. 通过context设置进程启动方法

import multiprocessing as mp
ctx = mp.get_context('spawn')
ctx.Process()

完美解决这个问题。每次启动子进程,通过上下文去获取进程的启动method,后续启动子进程延续即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1552244.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

康耐视visionpro-CogDataAnalyTool工具详细说明

CogDataAnalyTool功能: 数据分析工具,统计数据的平均值、标准差、最大值及最小值等。 CogDataAnalyTool操作说明: ①.打开工具栏,双击或点击鼠标拖拽添加CogDataAnalyTool ②.添加通道:根据需要添加多个输入通道,可同时统计多个输入数据。 ③.打开结果栏,点击运行可获…

Linux的学习之路:3、基础指令(2)

一、echo指令 这个指令在上篇文章我也用了但是忘了说了&#xff0c;这个指令的大概用法就是把后面跟的文本等输出在显示器上&#xff0c;如下代码所示打印的“Hello Linux” [rootVM-24-9-centos ~]# echo "Hello Linux" Hello Linux二、输出重定向与输入重定向 着…

金融案例:构建高效统一的需求登记与管理方案

在金融行业数字化转型背景下&#xff0c;银行等金融机构面临着业务模式创新与数据应用的深度融合。业务上所需要的不再是单纯的数据&#xff0c;而是数据背后映射的业务趋势洞察&#xff0c;只有和业务相结合转化为业务度量指标&#xff0c;经过数据分析处理呈现为报表进行展示…

MybatisPlus学习总结

MybatisPlus.xmind 一、MybatisPlus快速入门 1.基本介绍 官网: 简介 | MyBatis-Plus MyBatis Plus是一个基于MyBatis的增强工具&#xff0c;它简化了MyBatis的使用&#xff0c;提供了一系列的增强功能&#xff0c;使开发更加方便快捷。 MyBatis Plus的主要特点包括&#xff…

SQL,group by分组后分别计算组内不同值的数量

SQL&#xff0c;group by分组后分别计算组内不同值的数量 如现有一张购物表shopping 先要求小明和小红分别买了多少笔和多少橡皮&#xff0c;形成以下格式 SELECT name,COUNT(*) FROM shopping GROUP BY name;SELECT name AS 姓名,SUM( CASE WHEN cargo 笔 THEN 1 ELSE 0 END)…

PyPy 通过采用即时编译技术,能够显著提升 Python 代码的执行效率。

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 提升 Python 代码性能至接近 C 语言速度&#xff0c;无需修改源代码。遵循 Python 之父吉多・范罗苏姆的建议&#xff1a;“如果你想让你的代码神奇地运行得更快&#xff0c;你应该试试用 PyPy。” Yo…

二维前缀和与二维差分的表示

前缀和&#xff1a; 上述图片是求范围内的总和的图和公式 上述图片是初始化前缀和数组的图和公式 差分&#xff1a; 上图是差分公式 #include<iostream> #include<climits> #include<algorithm> #include<cstring> #include<cstdio> #include&l…

基于视图能力的县域治理视频基座数字化、智慧化解决方案

一、方案背景 县域治理方案是我国地方治理体系的重要组成部分&#xff0c;对于促进县域经济社会发展、维护社会稳定、推进全面深化改革具有重要意义。随着科技的不断进步&#xff0c;视频监管已经成为了现代社会治理的重要手段之一。县域治理视频监管方案是通过视频监控、数据…

SpringBoot国际化配置流程(超详细)

前言 最新第一次在做SpringBoot的国际化&#xff0c;网上搜了很多相关的资料&#xff0c;都是一些简单的使用例子&#xff0c;达不到在实际项目中使用的要求&#xff0c;因此本次将结合查询的资料以及自己的实践整理出SpringBoot配置国际化的流程。 SpringBoot国际化 "i…

关于svn安装报错2503问题的解决方法

问题&#xff1a; SVN在安装时&#xff0c;一直报错&#xff0c;安装失败 The installer has encountered an unexpected error installing this package.The error code is 2503 权限问题&#xff0c;右键以管理员权限运行。如果你也是像我一样&#xff0c;右键没有以管理员…

springboot 使用@profiles.active@多配置文件切换

项目配置文件结构&#xff1a; 主配置文件内容&#xff1a; pom配置文件&#xff1a; <profiles><profile><id>dev</id><properties><profiles.active>dev</profiles.active></properties></profile><profile>…

43 带 fixed 列的 el-table 不兼容于 sortablejs

前言 这是一个基于 sortablejs 来实现的 el-table 的拖拽功能的基础实现 然后 这个过程中遇到的一个比较特殊的问题是, 关于 el-table-column 的 fixed 的属性, 对于 sortablejs 这边来定位目标选择列 影响的一个问题 在基础的用例中, 使用 “.el-table__body-wrapper tbo…

数组的常见算法

数组的常见算法 数值型数组特征值统计 这里的特征值涉及到&#xff1a;平均值、最大值、最小值、总和等 举例1&#xff1a;数组统计&#xff1a;求总和、均值 public class TestArrayElementSum {public static void main(String[] args) {int[] arr {4,5,6,1,9};//求总和、…

污水处理迈入3D可视化新时代:智慧环保触手可及

在科技日新月异的今天&#xff0c;环保事业也迎来了前所未有的发展机遇。污水处理作为环保领域的重要组成部分&#xff0c;其技术的革新与进步&#xff0c;对于保护水资源、维护生态平衡具有重要意义。 传统的污水处理机组往往存在着操作复杂、监控困难等问题&#xff0c;使得污…

2024年【熔化焊接与热切割】报名考试及熔化焊接与热切割模拟试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 熔化焊接与热切割报名考试考前必练&#xff01;安全生产模拟考试一点通每个月更新熔化焊接与热切割模拟试题题目及答案&#xff01;多做几遍&#xff0c;其实通过熔化焊接与热切割作业考试题库很简单。 1、【单选题】…

ORA-04031 错误分析及处理方法

一、问题描述 使用普通用户登录数据库报ORA-04031错误 $ sqlplus / as sysdbaSQL*Plus: Release 11.2.0.1.0 Production on Mon Mar 25 09:14:59 2024Copyright (c) 1982, 2009, Oracle. All rights reserved.Connected to: Oracle Database 11g Enterprise Edition Releas…

学点儿Java_Day12_IO流

1 IO介绍以及分类 IO: Input Output 流是一组有顺序的&#xff0c;有起点和终点的字节集合&#xff0c;是对数据传输的总称或抽象。即数据在两设备间的传输称为流&#xff0c;流的本质是数据传输&#xff0c;根据数据传输特性将流抽象为各种类&#xff0c;方便更直观的进行数据…

详细分析Linux中的core dump异常(附 Demo排查)

目录 1. 基本知识2. 进阶知识3. Demo4. 彩蛋 1. 基本知识 Core dump 是指在程序异常终止时&#xff0c;操作系统将程序的内存映像保存到磁盘上的一种机制。 在 Linux 系统中&#xff0c;core dump 提供了一种调试程序错误的重要方式&#xff0c;它记录了程序在崩溃时的内存状态…

文献学习(自备)

收官大作&#xff0c;多组学融合的新套路发NC&#xff01;&#xff01; - 知乎 (zhihu.com) Hofbauer cell function in the term placenta associates with adult cardiovascular and depressive outcomes | Nature Communications 病理性胎盘炎症会增加几种成人疾病的风险&a…

CAD自动轻量化,工业仿真动画快速制作

随着现代工业的蓬勃发展&#xff0c;制造业企业在产品宣传展示、工作流程讲解、机械维修维护等方面对展示形式提出了更高的要求。工业动画以其直观、生动的特点&#xff0c;能够深入剖析产品的结构、工作原理和操作流程&#xff0c;为企业带来了全新的宣传展示方式。 但是由于…