故障環境:
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發行版(比較高的版本)。