作为系统管理员的你,是否以为一切尽在你的掌握和控制之中呢?但现实往往事与愿违,你认为那些正在运行和收集数据的进程却已经有两个小时没有向你汇报任何消息了,也许是你的监控客户端失去了响应,也许是服务器端出了问题,总之你不能第一时间知道问题出在哪里,而最要命的是这种情况每隔几天就会重复一次,心态再好的人遇到这种情况也会火冒三丈。那么如何找出运行中的进程究竟发生了什么呢?其实不用高深知识,也不用高级的监控套件,用系统自带的一些工具就够了,下面一起来看看如何利用Linux系统自带的一些工具诊断服务器故障吧。
Top和其它系统工具
要想知道进程在运行期间发生了什么,我们最好先获得该进程的ID,如果你知道被“卡住”或资源消耗不断上升进程的名称,那反查该进程的ID就好办了,使用ps aux | grep processname命令即可,不知道进程名称也没关系,我们还可以使用top来查看高CPU利用率或高内存占有率的进程。
Tasks: 114 total, 1 running, 113 sleeping, 0 stopped, 0 zombie
Cpu(s): 1.2%us, 0.6%sy, 0.6%ni, 96.0%id, 1.6%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 4053756k total, 1059196k used, 2994560k free, 305236k buffers
Swap: 2249060k total, 0k used, 2249060k free, 465112k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3055 akkana 20 0 160m 39m 18m S 39 1.0 0:02.83 plugin-containe
2223 akkana 20 0 330m 107m 26m S 16 2.7 0:51.33 firefox-bin
65 root 20 0 0 0 0 S 2 0.0 0:00.34 kondemand/0
1586 root 20 0 71712 22m 8244 S 2 0.6 0:24.87 Xorg
1 root 20 0 2748 1612 1216 S 0 0.0 0:00.37 init
2 root 20 0 0 0 0 S 0 0.0 0:00.00 kthreadd
3 root RT 0 0 0 0 S 0 0.0 0:00.00 migration/0
...and so on
默认情况下,top命令的结果按“吃”CPU多少进行倒序排列,在上面的例子中,Firefox卡住了,它正在运行Flash,浏览器和它的辅助程序一起占用了45%的CPU时钟周期,这还不算什么,如果你看到一个CPU利用率达到99%的进程,那它一定有问题。
当你发现有问题的进程后,该如何了解它正在做什么呢?这个时候就要请出strace了。
strace
strace是一个很有用的程序,它可以显示正在发生的系统调用,系统调用包括文件操作,如读、写和打开,超时设定和发送信号,网络操作以及其它各种获得或设置系统信息的操作,你可以阅读man 2 intro查看概述,或man 2 syscalls查看所有可用系统调用的详细列表。
这一切听起来有点神秘,但有时看strace输出可以准确地知道一个程序为什么出故障了,也许是等待网络,也许是重复打开一个根本就不存在的文件等等。
你可以在strace下运行一个程序,如strace firefox,但更多时候,你想连接到一个正在运行的进程,当然没问题,首先使用ps或top得到进程ID,然后使用“strace –p 进程id”即可。
假设我有一个程序看起来已经挂了,top命令显示它没有使用任何CPU,但它确实已经卡住,至少有半个小时没有做任何事情了,我们使用strace –p来跟踪一下。
$ strace -p 3672
Process 3672 attached - interrupt to quit
recv(3,
strace就停在这里,光标在中间那行闪动,这是怎么回事呢?
其实它在等待recv系统调用,按照Ctrl-C退出strace,然后使用apropos。
$ apropos recv
recv (2) - receive a message from a socket
recvfrom (2) - receive a message from a socket
recvmsg (2) - receive a message from a socket
从上面的结果可以看出,进程是在等待读取网络套接字中的内容,显然我们对进程挂起有了进一步了解。
如何模拟故障?
你在建立一个诊断工具库时,有时你可能希望有一个简单办法来体验它们,这时我们要模仿进程挂起故障,如果你有一个Web服务器就好办了,编写一个脚本,内容如下:
#! /usr/bin/env python
import time
print """Content-Type: text/html
Hello, world. Now we'll hang for a bit ...
"""
for i in range(50) : # Don't run forever and clog up the server
time.sleep(300) # sleep for 5 minutes
print "<p>nAnother line"
你可以使用wget,curl或自己编写一个Python脚本测试它。
#!/usr/bin/env python
import urllib2
response = urllib2.urlopen("")
当然,如果你想要某个程序占用所有可用的CPU资源,只需要在脚本中添加下面的代码:
while /bin/true; do
echo x
done
用其它语言也可以,只要达到这种目的即可。
如果用top和strace的帮助仍然不大,下面还有个最终法宝——
使用gdb获得堆栈跟踪信息
好吧,我承认即使知道了进程ID和strace输出结果,但帮助仍然不大,不用急,我还使出绝招呢,绝招就是使用gdb获得堆栈跟踪信息,堆栈跟踪不仅会告诉你程序当前正在做什么,有底层的信息(如等待网络套接字),也有较高级别的信息(如正在执行什么类型的网络操作)。
和strace的使用方法一样,gdb也使用-p加进程id的命令格式,启动后你会获得一个gdb提示符,输入where就可以获得一个堆栈跟踪。下面是Firefox在运行一些有问题的JavaScript代码时的堆栈跟踪信息。
#0 0x01ad9794 in gfxPangoFontGroup::GetFontAt (this=0xa74e8160, i=0)
at gfxPangoFonts.cpp:1936
#1 0x01ad1c11 in GetFontOrGroup (this=0xa51466b4, aKey=0xbfab1e2c)
at gfxTextRunWordCache.cpp:899
#2 TextRunWordCache::CacheHashEntry::KeyEquals (this=0xa51466b4,
aKey=0xbfab1e2c) at gfxTextRunWordCache.cpp:910
#3 0x01a5cb74 in SearchTable (table=0xb45ce2d0, key=,
keyHash=, op=PL_DHASH_ADD) at pldhash.c:472
#4 0x01a5cc50 in PL_DHashTableOperate (table=0xb45ce2d0, key=0xbfab1e2c,
op=) at pldhash.c:661
#5 0x01ad2421 in nsTHashtable::PutEntry (
this=0xb45ce2c0, aTextRun=0xa7ee0ae0, aFirstFont=0xad613d30, aStart=8,
aEnd=10, aHash=821, aDeferredWords=0x0)
at ../../../dist/include/nsTHashtable.h:188
#6 TextRunWordCache::LookupWord (this=0xb45ce2c0, aTextRun=0xa7ee0ae0,
aFirstFont=0xad613d30, aStart=8, aEnd=10, aHash=821, aDeferredWords=0x0)
at gfxTextRunWordCache.cpp:358
... etc.
即使你不熟悉Firefox源代码也能从上面的堆栈跟踪结果看出,它正在处理一些和字体有关的事情。
如果程序正在循环,那么它可能一直在做相同的事情,当你运行gdb –p时,它会暂时停止程序以便你进行检查,在提示符后按下c就可以让它继续运行,按下Ctrl-C会再次停止,再输入一次where就会获得第二个堆栈输出。
(gdb) where
#0 0xb686db07 in ?? () from /usr/lib/firefox-3.6.12/libmozjs.so
#1 0xb684bec9 in ?? () from /usr/lib/firefox-3.6.12/libmozjs.so
#2 0xb685cf66 in js_Invoke () from /usr/lib/firefox-3.6.12/libmozjs.so
#3 0xb6b6231b in ?? () from /usr/lib/firefox-3.6.12/libxul.so
这一次结果不一样了,它只暗示Firefox正在处理JavaScript(JS)和XUL相关的事情,反复停止又启动程序,你就会发现它在处理什么事情上花费的时间最多,这些有用的信息可以一并附在你要提交的BUG中,或在搜索引擎中使用其中一些作为关键字进行搜索,看看是否有现成的解决方案。
堆栈跟踪应用到等待资源而挂起的程序上也很好使,下面是我前面使用的Python程序的跟踪结果。
(gdb) where
#0 0x006a2422 in __kernel_vsyscall ()
#1 0x0095d241 in recv () at ../sysdeps/unix/sysv/linux/i386/socket.S:61
#2 0x081301ba in ?? ()
#3 0x081303b4 in ?? ()
#4 0x080e0a21 in PyEval_EvalFrameEx ()
#5 0x080e2807 in PyEval_EvalCodeEx ()
#6 0x080e0c8b in PyEval_EvalFrameEx ()
... etc.
Gdb显示了strace做的一些事情:recv,接下来的内容只告诉你你正在运行Python,但没有告诉你身在Python脚本何处,想知道怎么发掘更多的信息吗?请继续关注下一期文章吧,我将介绍调式Python程序的一些技术,以及在出问题的机器上没有安装gdb等花俏的开发工具时该怎么做。(免责声明:文章内容如涉及作品内容、版权和其它问题,请及时与我们联系,我们将在第一时间删除内容,文章内容仅供参考)