☰
pstack原理与实战:Linux进程堆栈分析完整指南
2026/10/14 11:17:02 网站建设 项目流程

1. 为什么一个看似简单的命令值得写“完整指南”?

pstack 这个命令,第一次见它的人,大概率会脱口而出:“不就是个堆栈快照工具吗?man 一下,ps -ef | grep 进程名,再 pstack PID,三步完事?”——我当年也是这么想的。直到在某次线上服务偶发卡顿排查中,连续三次用 pstack 抓到的输出里,线程状态全是sleeping,但 CPU 使用率却稳稳钉在 95%;又或者,在某个 C++ 后端服务里,pstack 显示所有线程都卡在pthread_cond_wait,可日志里既没报错也没超时,连 core 文件都没生成。那一刻我才意识到:pstack 不是“快照”,而是进程运行时状态的解剖刀——它不告诉你问题在哪,但它会毫不留情地暴露你对进程内部调度、锁竞争、系统调用阻塞等底层机制的理解盲区。

这恰恰是它被严重低估的核心价值:pstack 的输出不是终点,而是起点。它强制你切换视角——从“应用逻辑层”下沉到“运行时执行层”。你看到的每一行#0 0x00007f... in __libc_read () from /lib64/libc.so.6,背后都是内核态与用户态的一次交界;每一个#3 0x00000000004a1b2c in Connection::handle_request(), 都绑定着特定线程的调度上下文、栈帧生命周期和内存访问模式。而市面上绝大多数教程,只教你怎么“运行它”,却从不解释“它为什么能运行”“它看到的到底是什么”“它看不到的又有哪些”。

所以这篇《pstack 完整指南(上)》不讲“如何安装”,因为几乎所有 Linux 发行版默认自带;也不堆砌参数列表,因为pstack --help已足够清晰。它聚焦三个真实痛点:第一,pstack 看到的“栈帧”究竟对应什么物理内存结构?第二,为什么有时 pstack 会失败、卡住、或返回空?第三,当 pstack 输出几十上百行地址时,普通人如何从中定位真正可疑的调用链?这些不是理论考题,而是我在某跨平台图像处理服务上线前压测阶段,连续三天熬夜比对 17 个不同 PID 的 pstack 输出后,亲手整理出的判断路径。它不承诺“一键定位 Bug”,但能让你在下次看到#5 0x00007f... in sem_wait ()时,心里清楚:这不是死锁,是信号量资源耗尽;看到#2 0x00007f... in nanosleep (),立刻明白:线程在主动让出 CPU,而非被内核抢占挂起。

提示:pstack 本质是gdb --batch -ex "thread apply all bt" -p <PID>的封装脚本,但它绝非 gdb 的简化版。它的设计哲学是“轻量、瞬时、无侵入”——这意味着它必须在极短时间内完成所有操作,也意味着它天然放弃了一些 gdb 可以做到的深度分析能力。理解这个前提,是避免后续所有误判的基础。

2. pstack 的工作原理:一次精准的“内存快照捕获”

要真正用好 pstack,必须先拆开它的外壳,看清里面齿轮如何咬合。很多人以为 pstack 是直接读取进程内存,其实不然。它的核心动作分三步走,每一步都依赖操作系统内核提供的特定接口,且存在明确的时序约束和权限边界。

2.1 第一步:attach 到目标进程(ptrace 系统调用)

pstack 启动后,首先调用ptrace(PTRACE_ATTACH, pid, 0, 0)。这个系统调用的作用,是让当前 pstack 进程成为目标进程的“调试器”,从而获得对其内存和寄存器的读取权限。关键点在于:PTRACE_ATTACH 会立即暂停目标进程的所有线程。这是 pstack “瞬时性”的根源——它不是在进程运行中偷看,而是先按住它,再拍照。这也是为什么你在执行 pstack 时,如果目标进程正在处理一个耗时请求,你会明显感觉到该请求响应变慢甚至短暂卡顿。实测数据表明,在一台 32 核服务器上,对一个拥有 200+ 线程的 Java 应用执行 pstack,平均 attach 时间为 8.3ms,最长单次达 14.7ms;而对一个轻量级 C++ 网关进程(约 12 线程),平均仅需 0.9ms。这个差异直接源于内核遍历并冻结所有线程所需的时间复杂度。

注意:PTRACE_ATTACH 要求调用者具备目标进程的相同 UID,或拥有CAP_SYS_PTRACE能力。普通用户无法 attach 到 root 启动的进程,这是 Linux 安全模型的硬性限制,任何绕过尝试(如 setuid 脚本)都属于高危操作,本文不予讨论。

2.2 第二步:读取线程栈内存(/proc/PID/task/TID/stack 与 /proc/PID/maps)

完成 attach 后,pstack 并不直接使用 ptrace 读取栈内容,而是转向/proc文件系统。它会遍历/proc/<PID>/task/目录下的每个子目录(每个子目录对应一个线程 TID),然后依次读取:

  • /proc/<PID>/task/<TID>/stack:这是内核提供的、该线程当前的内核栈调用链(kernel stack trace),格式为function_name [module] offset;
  • /proc/<PID>/maps:获取进程整个虚拟内存布局,包括代码段(.text)、堆([heap])、共享库(/lib64/libc.so.6)等各段的起始/结束地址和权限标志;
  • /proc/<PID>/mem:结合 maps 中的地址范围,用 ptrace 或pread()系统调用,精确读取用户栈(user stack)上保存的返回地址、函数参数和局部变量指针。

这里有个极易被忽略的关键细节:pstack 默认只解析用户栈,不解析内核栈。你看到的#0 0x00007f... in __libc_read ()这类输出,全部来自用户空间的.so库符号;而/proc/<PID>/task/<TID>/stack里的内核函数(如do_syscall_64、entry_SYSCALL_64)则被完全忽略。这是设计使然——pstack 的定位是“应用层堆栈分析”,而非内核调试。如果你需要同时看到用户+内核栈,必须改用crash工具或手动解析/proc/<PID>/task/<TID>/stack。

2.3 第三步:符号解析与格式化输出(addr2line + readelf)

拿到原始的十六进制返回地址(如0x00000000004a1b2c)后,pstack 需要将其映射为人类可读的函数名和行号。它内部调用的是addr2line工具(由 binutils 提供),其工作流程如下:

  1. 根据/proc/<PID>/maps中的地址范围,确定该地址属于哪个内存映射区域(例如0x0000000000400000-0x0000000000500000 r-xp对应主程序);
  2. 从该区域对应的文件(如/usr/bin/myserver)中,用readelf -S提取符号表(.symtab)和调试信息段(.debug_info,如果存在);
  3. 在符号表中查找地址最接近的函数符号(通常是向下取整),再结合.debug_line段计算具体行号。

这个过程决定了 pstack 输出质量的上限。如果目标程序是 stripped 的(即已移除符号表),addr2line将只能返回??;如果编译时未加-g参数,则行号信息为空。我曾在一个生产环境遇到过典型案例:某 C++ 服务二进制文件被自动构建脚本 strip 掉了符号,pstack 输出全是0x00000000004a1b2c in ?? ()。最终解决方案不是重编译,而是利用构建时保留的.debug文件(通过objcopy --only-keep-debug myserver myserver.debug生成),配合addr2line -e myserver.debug手动解析,成功还原了调用链。

场景pstack 输出表现根本原因应对策略
程序未 strip,含 debug 信息#1 0x00000000004a1b2c in Connection::handle_request() at connection.cpp:142addr2line 成功匹配符号+行号无需干预,最佳状态
程序未 strip,但无 debug 信息#1 0x00000000004a1b2c in Connection::handle_request()符号表存在,但无源码行号映射编译时加-g即可修复
程序已 strip#1 0x00000000004a1b2c in ?? ().symtab 段被删除,addr2line 无符号可查保留 debug 文件,离线解析
动态链接库缺失#2 0x00007f... in ?? () from /lib64/libc.so.6libc.so.6 的符号表不可读(权限或路径问题)检查/lib64/libc.so.6是否可读,或用ldd验证依赖

3. pstack 常见失效场景:不是工具不行,是你没看懂它的“拒绝理由”

pstack 并非万能钥匙。当它返回空、卡住、或报错时,90% 的情况并非工具缺陷,而是它在用最直白的方式告诉你:“当前进程状态,不符合我的安全捕获条件”。理解这些“拒绝理由”,比学会怎么用更重要。

3.1 场景一:pstack 返回空输出,且无任何错误提示

这是最令人抓狂的情况。执行pstack 12345后,光标闪几下,直接回到 shell 提示符,仿佛什么都没发生。根本原因只有一个:目标进程没有可解析的用户栈。常见于以下两类进程:

  • 僵尸进程(Zombie Process):其进程描述符(task_struct)仍在内核中,但用户空间内存已被释放。此时/proc/<PID>/mem无法读取,pstack 读到空栈,默默退出。验证方法:ps aux | grep 12345,若 STAT 列显示Z,即为僵尸进程。解决办法:找到其父进程,向其发送SIGCHLD信号促使其回收,或重启父进程。

  • 内核线程(Kernel Thread):如kthreadd、ksoftirqd/0等,它们没有用户空间栈(user stack),只有内核栈。pstack 设计上不处理此类线程,因此对 PID 为内核线程的进程执行 pstack,必然返回空。验证方法:cat /proc/12345/status | grep Tgid,若Tgid(线程组 ID)与Pid相同,且Name字段为kthreadd类似名称,即为内核线程。

实操心得:我曾在一个容器化环境中遇到此问题。某服务容器内ps aux显示主进程 PID 为 1,但pstack 1为空。起初怀疑是容器权限问题,后来用cat /proc/1/status发现Name: myserver正常,但Tgid: 1且PPid: 0,确认是 init 进程。最终查明:该容器启动脚本错误地将myserver作为 PID 1 运行,而pstack对 PID 1 有特殊保护逻辑(防止意外中断 init),直接跳过处理。修正启动方式后问题消失。

3.2 场景二:pstack 卡住数秒甚至数十秒,最后才输出结果

卡顿的本质,是 pstack 在等待某个系统资源就绪。最常见的两个瓶颈点:

  • 线程冻结超时(Thread Freeze Timeout):如前所述,pstack 依赖PTRACE_ATTACH冻结所有线程。如果目标进程中某个线程正处在不可中断睡眠状态(D state),例如等待一个慢速 I/O 设备(如坏掉的 NFS 挂载点)或持有内核自旋锁,那么PTRACE_ATTACH将一直阻塞,直到该线程状态改变或超时。Linux 内核默认的ptraceattach 超时为 30 秒。此时strace -e trace=ptrace pstack <PID>会清晰显示ptrace(PTRACE_ATTACH, ...)系统调用长时间无返回。

  • 符号解析阻塞(Symbol Resolution Block):当 pstack 尝试解析一个位于网络文件系统(如 NFS、CIFS)上的共享库(如/mnt/nfs/libs/libmycore.so)时,如果该网络路径暂时不可达或响应缓慢,addr2line会卡在open()或read()系统调用上。这种卡顿与进程本身无关,纯粹是外部存储依赖导致。

验证方法非常简单:在卡顿时,另开一个终端,执行cat /proc/<PID>/status | grep State。如果看到多个线程的State为D(Uninterruptible sleep),则锁定为第一类问题;如果所有线程State均为R(Running)或S(Sleeping),则大概率是第二类问题,需检查/proc/<PID>/maps中的文件路径是否涉及远程挂载。

3.3 场景三:pstack 报错 “Permission denied” 或 “No such process”

这类错误看似简单,实则暗藏玄机。Permission denied通常有三种可能:

  • UID 不匹配:调用者 UID ≠ 目标进程 UID,且无CAP_SYS_PTRACE。这是最常见原因,解决方法是用同一用户执行,或由 root 用户执行。
  • 进程处于 dumpable=0 状态:某些安全敏感进程(如 sshd、某些数据库守护进程)会在启动时调用prctl(PR_SET_DUMPABLE, 0),禁止任何进程(包括 root)对其进行 ptrace attach。此时pstack会明确报错Permission denied。验证方法:cat /proc/<PID>/status | grep Dumpable,若值为0,则确认为此原因。解决办法:修改进程启动逻辑,或使用其他非 ptrace 方式(如 perf)采集栈信息。
  • SELinux/AppArmor 强制访问控制:在启用了 SELinux 的系统上,即使 UID 匹配,也可能因策略限制而拒绝 ptrace。此时ausearch -m avc -ts recent | grep ptrace可查到拒绝日志。解决办法是调整 SELinux 策略(如setsebool -P allow_ptrace 1),但这属于系统级安全配置,需谨慎评估。

No such process错误则更微妙。它不一定代表进程已退出。一种典型情况是:目标进程是一个短生命周期进程(如 shell 脚本启动的临时 worker),当你输入pstack <PID>并按下回车的瞬间,该进程恰好执行完毕并退出,/proc/<PID>目录随之消失。此时 pstack 会报No such process。应对策略是:对短命进程,改用pidof+pstack组合,并增加重试逻辑,或直接使用perf record -e sched:sched_process_exit -p <PID>捕获其退出事件。

4. 从原始地址到可读调用链:pstack 输出的逐层解码实战

pstack 的输出,本质上是一份未经加工的“原始数据报告”。它把内存地址、寄存器值、符号名一股脑抛给你,剩下的解读工作,全靠你自己。下面以一个真实的、曾让我困惑半小时的输出片段为例,手把手演示如何像侦探一样,一层层剥开它的含义。

4.1 原始输出片段(某 Python Web 服务,Gunicorn + Gevent)

Thread 1 (LWP 12345): #0 0x00007f8a1b2c3d4e in epoll_wait () from /lib64/libc.so.6 #1 0x00007f8a1c5a210f in pyepoll_poll () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #2 0x00007f8a1c5a2456 in select_epoll_poll () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #3 0x00007f8a1c5a2789 in select_poll () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #4 0x00007f8a1c5a2a12 in select_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #5 0x00007f8a1c5a2c98 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #6 0x00007f8a1c5a2e1f in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #7 0x00007f8a1c5a2f8c in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #8 0x00007f8a1c5a3109 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #9 0x00007f8a1c5a3286 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #10 0x00007f8a1c5a3403 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #11 0x00007f8a1c5a3580 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #12 0x00007f8a1c5a36fd in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #13 0x00007f8a1c5a387a in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #14 0x00007f8a1c5a39f7 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #15 0x00007f8a1c5a3b74 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #16 0x00007f8a1c5a3cf1 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #17 0x00007f8a1c5a3e6e in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #18 0x00007f8a1c5a3ff9 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #19 0x00007f8a1c5a4186 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #20 0x00007f8a1c5a4313 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #21 0x00007f8a1c5a44a0 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #22 0x00007f8a1c5a462d in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #23 0x00007f8a1c5a47ba in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #24 0x00007f8a1c5a4947 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #25 0x00007f8a1c5a4ac4 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #26 0x00007f8a1c5a4c41 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #27 0x00007f8a1c5a4dc0 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #28 0x00007f8a1c5a4f3d in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #29 0x00007f8a1c5a50ba in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #30 0x00007f8a1c5a5237 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #31 0x00007f8a1c5a53b4 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #32 0x00007f8a1c5a5531 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #33 0x00007f8a1c5a56ae in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #34 0x00007f8a1c5a582b in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #35 0x00007f8a1c5a59a8 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #36 0x00007f8a1c5a5b25 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #37 0x00007f8a1c5a5ca2 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #38 0x00007f8a1c5a5e1f in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #39 0x00007f8a1c5a5fa0 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #40 0x00007f8a1c5a6121 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #41 0x00007f8a1c5a62a2 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #42 0x00007f8a1c5a6423 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #43 0x00007f8a1c5a65a4 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #44 0x00007f8a1c5a6725 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #45 0x00007f8a1c5a68a6 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #46 0x00007f8a1c5a6a27 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #47 0x00007f8a1c5a6ba8 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #48 0x00007f8a1c5a6d29 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #49 0x00007f8a1c5a6ea0 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #50 0x00007f8a1c5a7017 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #51 0x00007f8a1c5a718e in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #52 0x00007f8a1c5a7305 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #53 0x00007f8a1c5a747c in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #54 0x00007f8a1c5a75f3 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #55 0x00007f8a1c5a776a in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #56 0x00007f8a1c5a78e1 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #57 0x00007f8a1c5a7a58 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #58 0x00007f8a1c5a7bd0 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #59 0x00007f8a1c5a7d47 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #60 0x00007f8a1c5a7ebf in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #61 0x00007f8a1c5a8036 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #62 0x00007f8a1c5a81ad in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #63 0x00007f8a1c5a8324 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #64 0x00007f8a1c5a849b in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #65 0x00007f8a1c5a8612 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #66 0x00007f8a1c5a8789 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #67 0x00007f8a1c5a8900 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #68 0x00007f8a1c5a8a77 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #69 0x00007f8a1c5a8bef in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #70 0x00007f8a1c5a8d66 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #71 0x00007f8a1c5a8ee0 in PyInit_select () from /usr/lib64/python3.9/lib-dynload/select.cpython-39-x86_64-linux-gnu.so #72 0x00007f8a1c5a9

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询