分析运行中的 Python 进程详细解析
作者:四火的唠叨 发布时间:2021-09-19 14:47:30
在 Java 中打印当前线程的方法栈,可以用 kill -3 命令向 JVM 发送一个 OS 信号,JVM 捕捉以后会自动 dump 出来;当然,也可以直接使用 jstack 工具完成,这些方法好几年前我在这篇性能分析的文章 中介绍过。这样的需求可以说很常见,比如定位死锁,定位一个不工作的线程到底卡在哪里,或者定位为什么 CPU 居高不下等等问题。
现在工作中我用的是 Python,需要线上问题定位的缘故,也有了类似的需求——想要知道当前的 Python 进程“在干什么”。但是没有了 JVM 的加持,原有的命令或者工具都不再适用。传统的 gdb 的 debug * 在线上也不好操作。于是我寻找了一些别的方法,来帮助定位问题,我把它们记录在这里。
signal
在代码中,我们可以使用 signal 为进程预先注册一个信号 * ,在进程接收到特定信号的时候,可以打印方法栈:
import traceback, signal
class Debugger():
def __init__(self, logger):
self._logger = logger
def log_stack_trace(self, sig, frame):
d={'_frame':frame}
d.update(frame.f_globals)
d.update(frame.f_locals)
messages = "Signal received. Stack trace:\n"
messages += ''.join(traceback.format_stack(frame))
self._logger.warn(messages)
def listen(self):
signal.signal(signal.SIGUSR1, self.log_stack_trace)
通过调用上面的 listen 方法(比如 new Debug(logger).listen()),就将一个可以接收 SIGUSR1 并打印方法栈的 * 注册到当前进程了。这里是打印方法栈,但是实际上可以做任何事,因为方法执行的当前,上下文已经跑到进程里面了。
那么怎么向进程发送信号呢?和 JVM 的方法类似,可以通过操作系统命令来发送:
kill -30 pid
这里的信号为什么是 30?这是因为 SIGUSR1 被当前操作系统定义成 30(请注意不同的操作系统这个映射表是可能不同的),这点可以通过 man signal 查看:
No Name Default Action Description
SIGHUP terminate process terminal line hangup
SIGINT terminate process interrupt program
SIGQUIT create core image quit program
SIGILL create core image illegal instruction
SIGTRAP create core image trace trap
SIGABRT create core image abort program (formerly SIGIOT)
SIGEMT create core image emulate instruction executed
SIGFPE create core image floating-point exception
SIGKILL terminate process kill program
SIGBUS create core image bus error
SIGSEGV create core image segmentation violation
SIGSYS create core image non-existent system call invoked
SIGPIPE terminate process write on a pipe with no reader
SIGALRM terminate process real-time timer expired
SIGTERM terminate process software termination signal
SIGURG discard signal urgent condition present on socket
SIGSTOP stop process stop (cannot be caught or ignored)
SIGTSTP stop process stop signal generated from keyboard
SIGCONT discard signal continue after stop
SIGCHLD discard signal child status has changed
SIGTTIN stop process background read attempted from control terminal
SIGTTOU stop process background write attempted to control terminal
SIGIO discard signal I/O is possible on a descriptor (see fcntl(2))
SIGXCPU terminate process cpu time limit exceeded (see setrlimit(2))
SIGXFSZ terminate process file size limit exceeded (see setrlimit(2))
SIGVTALRM terminate process virtual time alarm (see setitimer(2))
SIGPROF terminate process profiling timer alarm (see setitimer(2))
SIGWINCH discard signal Window size change
SIGINFO discard signal status request from keyboard
SIGUSR1 terminate process User defined signal 1
SIGUSR2 terminate process User defined signal 2
当然,也可以写一点点 python 脚本来发送这个信号:
import os, signal
os.kill($PID, signal.SIGUSR1)原理是一样的。
strace
如果进程已经无响应了,或者上面的信号 * 没有注册,那么就要考虑别的方法来或者“进程在干什么”这件事情了。其中,一个有用的命令是 strace:
strace -p pid
比如,我自己写了一个测试脚本 t.py,使用 python 执行,然后调用 sleep,再给它发送一个 SIGUSR1 的消息,它打印方法栈并退出。这整个过程,我使用 strace 可以得到这样的结果:
strace -p 9157
strace: Process 9157 attached
select(0, NULL, NULL, NULL, {9999943, 62231}) = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=9273, si_uid=9007} ---
rt_sigreturn({mask=[]}) = -1 EINTR (Interrupted system call)
stat("t.py", {st_mode=S_IFREG|0644, st_size=1281, ...}) = 0
open("t.py", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=1281, ...}) = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=1281, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f631e866000
read(3, "import traceback, signal, time\n "..., 8192) = 1281
read(3, "", 4096) = 0
close(3) = 0
munmap(0x7f631e866000, 4096) = 0
stat("t.py", {st_mode=S_IFREG|0644, st_size=1281, ...}) = 0
write(1, "Signal received. Stack trace:\n "..., 134) = 134
write(1, "\n", 1) = 1
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f631e06f5d0}, {0x7f631e392680, [], SA_RESTORER, 0x7f631e06f5d0}, 8) = 0
rt_sigaction(SIGUSR1, {SIG_DFL, [], SA_RESTORER, 0x7f631e06f5d0}, {0x7f631e392680, [], SA_RESTORER, 0x7f631e06f5d0}, 8) = 0
exit_group(0) = ?
+++ exited with 0 +++
可以看到从 strace attached 开始,到进程退出,所有重要的调用都被打印出来了。
在 iOS 下,没有 strace,但是可以使用类似的(更好的)命令 dtruss。
lsof
lsof 可以打印某进程打开的文件,而 Linux 下面一切都是文件,因此查看打开的文件列表有时可以获取很多额外的信息。比如,打开前面提到的这个测试进程:
lsof -p 16872
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
Python 16872 xxx cwd DIR 1,5 2688 1113586 /Users/xxx
Python 16872 xxx txt REG 1,5 51744 10627527 /System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
Python 16872 xxx txt REG 1,5 52768 10631046 /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload/_locale.so
Python 16872 xxx txt REG 1,5 65952 10631134 /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload/time.so
Python 16872 xxx txt REG 1,5 841440 10690598 /usr/lib/dyld
Python 16872 xxx txt REG 1,5 1170079744 10705794 /private/var/db/dyld/dyld_shared_cache_x86_64h
Python 16872 xxx 0u CHR 16,2 0t39990 649 /dev/ttys002
Python 16872 xxx 1u CHR 16,2 0t39990 649 /dev/ttys002
Python 16872 xxx 2u CHR 16,2 0t39990 649 /dev/ttys002
它有几个参数很常用,比如-i,用来指定网络文件(如果是“-i: 端口号”这样的形式还可以指定端口)。
总结
以上所述是小编给大家介绍的分析运行中的 Python 进程,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!
来源:https://www.raychase.net/5358


猜你喜欢
- 询问度娘搭好appium和python环境,开启移动app自动化的探索(基于Android),首先来记录下如何启动待测的app吧!如何启动A
- 在pycharm中我们有时需要切换python的版本,这里需要注意的是我们是在PyCharm中的Preferences中切换的,在File的
- 从 Google 的一个细节说起:整个虚线框都是“Next”的可点击区域。看似不经意,却直接提升了细节的可用性。其它页码也巧妙地和上面的字母
- 1 Python自动绘图在这里我主要运用了pyautocad库进行AutoCAD的自动化操作,pyautocad是一款功能非常强大的Auto
- 我们进行CSS网页布局的时候,都知道它需要符合XHTML1.0规范。如果我们在进行CSS网页布局的时候,还在使用被W3C废弃的元素,那就失去
- 一.windows系统的解决方法1.首先以系统管理员身份登陆系统。2.停止MySQL的服务。3.进入命令窗口,然后进入MySQL的安装目录,
- 在模板中往往要加载静态文件,如CSS, JavaScript,图片等。那么这些文件在django中如何才能正确加载呢?首先要在setting
- 在进行python数据分析的时候,首先要进行数据预处理。有时候不得不处理一些非数值类别的数据,嗯, 今天要说的就是面对这些数据该如何处理。目
- 前言MySQL是关系性数据库中的一种,查询功能强,数据一致性高,数据安全性高,支持二级索引。但性能方面稍逊于非关系性数据库,特别是百万级别以
- PHP在运行时, 针对严重程度不同的错误,会给以不同的提示。 eg:在$a没声明时,直接相加,值为NULL,相加时当成0来算.但是,却提示N
- 1 什么是K8ssandraCassandra是一款非常优秀的开源的分布式NoSQL数据库,被许多优秀的大公司采用,具有高可用、弹性扩展、性
- 下面继续为大家带来XHTML与HTML兼容的16条指引!1.避免将页面声明为XML类型,页面使用UTF-8或者UTF-16字符集。2.在空元
- 在IE中,在使用checkbox或radio时,你会发现有时不能通过CheckBoxObject.checked = true或CheckB
- 首先找到要下载的歌曲排行榜的链接,这里用的是:https://music.163.com/discover/toplist?id=37786
- SQL中的单记录函数 1.ASCII 返回与指定的字符对应的十进制数; SQL> select ascii('A')
- 常见的数据库对象对象描述表(TABLE)表是存储数据的逻辑单元,以行和列的形式存在,列就是字段,行就是记录数据字典就是系统表,存放数据库相关
- 关于并发、并行、同步阻塞、异步非阻塞、线程、进程、协程等这些概念,单纯通过文字恐怕很难有比较深刻的理解,本文就通过代码一步步实现这些并发和异
- new fun的执行过程分析,学习面向对象的朋友可以参考下。(1)创建一个新的对象,并让this指针指向它;(2)将函数的prototype
- 基于Ubuntu16.04 + Python3 + nginx + mysql + Django接下来先安装这些必要的环境,这些操作都是在你
- 环境Python 3.7.4pymysql8.0.11 MySQL Community Server读取图片以二进制格式读取图片with o