在 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}) = "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) = "htmlcode">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 进程,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]