[PHP] PHP5.4 + Apache 2.4でSegmentation fault

PHP5.4.0 + Apache2.4.1で、PHPプログラムを動かした際、数回アクセスすると500エラーが発生しているケースがちらほら。

 

Apacheのエラーログを確認すると、以下のようなメッセージが出ていました。

[Fri Apr 13 22:50:04.385611 2012] [core:notice] [pid 2499:tid 140578174879488] AH00051: child pid 3659 exit signal Segmentation fault (11)

 

同じページにアクセスした場合でも必ず発生するというわけではなく、いまいち規則性も見えない状況です。

 

利用してるApacheモジュール群が怪しいんじゃないかと思い、不要そうなmoduleのLoadModuleをコメントアウトして再起動してみるも、状況はあまり変わらず。

そもそもApache2.4系になってデフォルトで最小限のmodule読み込みになっているので、不要なモジュールが少ない、というものありました。

 

となると、通常のエラーログからはわからない「Segmentation fault」の根本原因を探らないとなのですが、いろいろ調べてみると、coreファイルを吐き出してデバッグ、がセオリーのようです。

http://d.hatena.ne.jp/sarface2012/20101027

手順を参考にさせてもらって、とにかくやってみます。

coreファイルの出力制限を解除

現在のcoreファイルの出力制限を確認します。

$ ulimit -a | grep "core file size"

値が0の場合は、制限を解除します。

$ vi /etc/profile
# ulimit -S -c 0 > /dev/null 2>&1
ulimit -c unlimited > /dev/null 2>&1

変更後は再ログインします。

確認して出力制限が外れていればOKです。

$ ulimit -a
core file size          (blocks, -c) unlimited。

Apacheにcoreファイル出力の設定を追加

httpd.confに設定を追加します。

$ vi httpd.conf
CoreDumpDirectory /tmp

追加後、再起動します。

apachectl restart

coreファイルのデバッグ

ここで、再度「Segmentation fault」を発生させます。
すると、/tmp下にcoreファイルが吐かれました。

$ ls -lr /tmp/core.*
core.3086

coreファイルをデバッグするためにはgdbコマンドを使います(CentOSの利用環境に入っていなかったので、yumインストールしました)

gdb <command> -c <core file>

で利用できるということで

$ gdb httpd -c /tmp/core.3086
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
...
Reading symbols from /XXXXX/httpd...done.

warning: exec file is newer than core file.

  :
  略
  :

(gdb)

ここで、「where」をタイプするとエラー発生箇所がでてきます。

(gdb) where
#0  0x00007fdae71d14ed in read () from /XXX/libpthread.so.0
#1  0x00000000004657f7 in ap_worker_pod_check (pod=) at pod.c:57
#2  0x0000000000463154 in child_main (child_num_arg=1) at worker.c:1332
#3  0x0000000000463fb6 in make_child (s=0x1dab4c0, slot=1) at worker.c:1417
#4  0x0000000000464b57 in perform_idle_server_maintenance (_pconf=, plog=, s=) at worker.c:1618
#5  server_main_loop (_pconf=, plog=, s=) at worker.c:1739
#6  worker_run (_pconf=, plog=, s=) at worker.c:1810
#7  0x000000000042de6e in ap_run_mpm (pconf=0x1d82138, plog=0x1de5528, s=0x1dab4c0) at mpm_common.c:98
#8  0x0000000000428874 in main (argc=3, argv=0x7fff9696e8c8) at main.c:777

正直わからない部分もあるのですが、

#1  0x00000000004657f7 in ap_worker_pod_check (pod=) at pod.c:57

あたりを見ると、workerでの実行に関係がありそうです。

 

そこで、現在のApache環境を確認してみると

$ apachectl -V | grep "Server MPM"
Server MPM:     worker

workerで動いてました。

もともとPHPはpreforkが基本の認識でいたのですが、今回Apacheのインストール時にMPMをworkerにしてしまっていたようです。

Apacheを自前インストールしていて、コンパイルをやり直す必要があるため、configure時に

--with-mpm=prefork

をつけて、Apacheを再インストール。
設定をもろもろして、起動。

結果、、、

 

「Segmentation fault」が発生しなくなりました!

 

調べてみたところ、PHPをworkerで動かす際はFastCGIなどを組み合わせるのが通例のようです。

マニュアルにも非推奨とありました。

http://www.php.net/manual/ja/faq.installation.php#faq.installation.apache2

ただ、Apache、PHPのバージョンが古い自前環境ではworkerでも問題なく動く環境もあったので、バージョン依存のある問題かもしれません。

workerのほうが、待機時のメモリ利用を抑えられるなど利点も大きいと思ってますが、基本PHPはpreforkという認識はあっていたみたいです。

 

今回は、

  • workerが悪さをしていたと思われる(詳細な原因の判明まではできず)
  • preforkにすることで解決できた

という結論になりました。

coreファイルのデバッグを実践できたのは収穫でしたが、コンパイル時に注意していればPHP+preforkで問題なく動いてくれて、調査もいらなかったんじゃないか・・・とも思ってしまいます。