故障环境:
Red Hat Enterprise Linux AS release 3 (Taroon Update 4)
kernel-2.4.21-27.EL
glibc-2.3.2-95.30
apache-2.0.53
[root@test10 root]# /usr/apache2/bin/httpd -V
Server version: Apache/2.0.53
Server built: Sep 9 2005 16:27:28
Server's Module Magic Number: 20020903:9
Architecture: 32-bit
Server compiled with....
-D APACHE_MPM_DIR="server/mpm/prefork"
-D APR_HAS_SENDFILE
-D APR_HAS_MMAP
-D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
-D APR_USE_SYSVSEM_SERIALIZE
-D APR_USE_PTHREAD_SERIALIZE
-D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
-D APR_HAS_OTHER_CHILD
-D AP_HAVE_RELIABLE_PIPED_LOGS
-D HTTPD_ROOT="/usr/apache2"
-D SUEXEC_BIN="/usr/apache2/bin/suexec"
-D DEFAULT_PIDLOG="logs/httpd.pid"
-D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
-D DEFAULT_LOCKFILE="logs/accept.lock"
-D DEFAULT_ERRORLOG="logs/error_log"
-D AP_TYPES_CONFIG_FILE="conf/mime.types"
-D SERVER_CONFIG_FILE="conf/httpd.conf"
故障现象:
在Linux AS 3上使用压力测试工具进行测试有时出现httpd子进程CPU占用率100%,且压力撤除后无法恢复,始终有个别httpd子进程CPU占用率很高。但在Linux AS 4上测试始终不会出现此问题,另外使用Jmeter测试在两种系统上均未出现该现象。
问题初步分析:
由于在Linux AS 4上未出现问题,所以可以确定测试工具和测试脚本不会存在问题。打开Apache监视页面发现CPU占用率高的子进程的状态为Reading,在Apache官方Bugzilla上也有人出现的同样的问题不过有一例已经解决(是由于自身模块的原因,但是没有具体说明)。
问题的进一步分析:
由于不能通过一些外部表现来继续跟踪,所以只能借助于GDB了。在Apache帮助中有调试相关的介绍,所以就按部就班当故障出现时使用GDB把故障进程的调用堆栈显示出来(如下):
(gdb) bt
#0 0x002738a0 in _IO_un_link_internal () from /lib/tls/libc.so.6
#1 0x0027496c in _IO_default_finish_internal () from /lib/tls/libc.so.6
#2 0x002713ad in _IO_new_file_finish () from /lib/tls/libc.so.6
#3 0x002666d5 in fclose@@GLIBC_2.1 () from /lib/tls/libc.so.6
#4 0x004f3523 in CPakReader::Close () from /usr/lib/httpd/modules/mod_pak.so
#5 0x004f2c50 in CPakReader::~CPakReader () from /usr/lib/httpd/modules/mod_pak.so
#6 0x004f3e29 in mod_pak_method_handler () from /usr/lib/httpd/modules/mod_pak.so
#7 0x0807c2f2 in ap_run_handler (r=0x8a56518) at config.c:153
#8 0x0807c80a in ap_invoke_handler (r=0x8a56518) at config.c:364
#9 0x0806bf8f in ap_process_request (r=0x8a56518) at http_request.c:249
#10 0x08068049 in ap_process_http_connection (c=0x8a523e0) at http_core.c:251
#11 0x0808556e in ap_run_process_connection (c=0x8a523e0) at connection.c:43
#12 0x0807ae6b in child_main (child_num_arg=145082000) at prefork.c:610
#13 0x0807af88 in make_child (s=0x897f800, slot=0) at prefork.c:704
#14 0x0807b06f in startup_children (number_to_start=5) at prefork.c:722
#15 0x0807b77d in ap_mpm_run (_pconf=0x897b0a8, plog=0x89b3188, s=0x897f800) at prefork.c:941
#16 0x08080732 in main (argc=1, argv=0xbfffb324) at main.c:618
接着就是在调用堆栈中自己的模块和相关的函数,然后针对调试该函数,输出更详细的错误信息。此例中出现故障的httpd进程在C库的fclose调用内部可能发生了一个未结束的循环,由于该故障在AS4环境下不存在,据此推定可能是glibc的线程局域存储实现在AS3特有的NPTL环境下有BUG而在某个边界条件下被触发,由堆栈信息可知引发该故障的原因是CPakReader对象析构时调用的fclose,通过增加调试代码发现该处fclose返回了-1(EOF),基本判定为重复关闭已关闭的FILE流指针引发了该故障,因此马上修改后重新测试,问题得以修正。
经验总结:
1、测试时尽量多采用不同的测试工具进行测试,避免由于测试工具的缺陷导致问题潜藏在系统中;
2、对于不经常出现的问题和故障,在发生时尽量保留现场进行分析,并详细记录每次测试的配置条件
3、由于资源泄露和过度释放属代码中高频度发生的BUG,建议开发人员引入一些必要的自动化源代码扫描覆盖手段,尽可能提前发现此类BUG;
4、由于Linux-2.6的核心在原生内核线程支持、进程调度抢占及代码维护等级等方面均优于Linux-2.4核心,且JAVA支持也更加高效,应用部署平台如无特殊要求请尽量使用AS4等使用kernel-2.6.x发行版(比较高的版本)。