Apache 2.3/2.4系の新機能を見てみよう その3 〜MPMの動的ロード〜
さて、Apache 2.3探索シリーズの第3回になります今回、MPM(Multi-Processing Modules)に話題を移します。
従来Apache 2.0/2.2ではMPMはconfigureでのオプション指定によって選択し、ビルドでスタティックリンクされます。そのため、もしMPMを変える場合はconfigureでの指定を変えてビルドする所からやり直しになります。2.3/2.4からはこれがApacheモジュールと同じようにLoadModuleディレクティブで実行時に選ぶことができるようになるそうです。
この仕組みを利用する為には、前回のビルドでのconfigureオプションに項目を1つ追加する必要があります。--enable-mpms-sharedオプションを追加し、引数にallを与えてもう一度ビルド&インストールしてみましょう。
$ configure --help
...
--enable-mpms-shared=MPM-LIST
Space-separated list of MPM modules to enable for
dynamic loading. MPM-LIST=list | "all"
...
$ ./configure --enable-mpms-shared=all --enable-mods-shared=all \ --with-included-apr --prefix=/usr/local/app/apache-2.3.5 \ --with-mysql
なお、httpd本体にスタティックリンクされたモジュールは-lオプション指定での起動で確認することができますね。
$ /usr/local/app/apache-2.3.5/bin/httpd -l Compiled in modules: core.c mod_so.c http_core.c event.c ← MPM
たとえば、前回のビルド版のhttpdでは上記のような結果になりますが、これを今回新たに--enable-mpms-sharedオプションを追加したビルド版ですと、event MPMがスタティックリンクから外れます。
$ /usr/local/app/apache-2.3.5/bin/httpd -l Compiled in modules: core.c mod_so.c http_core.c
起動にはMPMの指定が必要ですが、LoadModuleディレクティブを以下のように追加して行います。
LoadModule mpm_event_module modules/mod_mpm_event.so
さてこのMPM周り、どう変わったのでしょうか。2.2.14と2.3.5-alphaの間でhttpdのmain()を持つserver/main.cのdiffを取ってMPMを呼び出す箇所を検証してみましょう。
***************
*** 737,743 ****
ap_run_optional_fn_retrieve();
! if (ap_mpm_run(pconf, plog, server_conf))
break;
apr_pool_lock(pconf, 0);
--- 766,772 ----
ap_run_optional_fn_retrieve();
! if (ap_run_mpm(pconf, plog, ap_server_conf) != OK)
break;
apr_pool_lock(pconf, 0);
ap_mpm_run()からap_run_mpm()へと関数名が変わったようです。
2.2系ではap_mpm_run()関数が各MPMにて実装されていて、これをスタティックリンクすることでserver/main.cからMPMの処理へと移行させていました。
912 /*****************************************************************
913 * Executive routines.
914 */
915
916 int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
917 {
918 int index;
919 int remaining_children_to_start;
920 apr_status_t rv;
921
922 ap_log_pid(pconf, ap_pid_fname);
923
924 first_server_limit = server_limit;
925 if (changed_limit_at_restart) {
926 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
927 "WARNING: Attempt to change ServerLimit "
928 "ignored during restart");
929 changed_limit_at_restart = 0;
930 }
...
1258
1259 return 0;
1260 }
それが2.3系ではap_run_mpm()関数に名前が変わっています。この関数名でgrepで全ソースを検索してみてもどこにも見当たりません。実は、ap_run_mpm()という関数はマクロによって生成されているので、その関数名のままではソース上から見つけることはできません。
82 /**
83 * Pass control to the MPM for steady-state processing. It is responsible
84 * for controlling the parent and child processes. It will run until a
85 * restart/shutdown is indicated.
86 * @param pconf the configuration pool, reset before the config file is read
87 * @param plog the log pool, reset after the config file is read
88 * @param server_conf the global server config.
89 * @return DONE for shutdown OK otherwise.
90 */
91 AP_DECLARE_HOOK(int, mpm, (apr_pool_t *pconf, apr_pool_t *plog, server_rec *server_conf))
このマクロを展開するとap_run_mpm()関数と一緒にap_hook_mpm()関数も用意されます。ap_hook_...という関数名はApacheモジュールの開発経験があれば一度は目にしたことのある方ばかりかと思います。
お分かりのように、ap_hook_xxx()関数は各イベントフックにイベントハンドラを登録する関数です。一方ap_run_xxx()関数は、同じxxxの名前を持つイベントフックを起動する役割を持ちます。つまり、2.3系からは直接MPM内の関数をリンクしての呼び出しではなく、イベントフックの仕組みに基づいて"mpm"イベントフックを定義し、このイベントハンドラとしてMPMの処理に委譲する形をとるようになったのです。
2718 static void event_hooks(apr_pool_t * p)
2719 {
2720 /* Our open_logs hook function must run before the core's, or stderr
2721 * will be redirected to a file, and the messages won't print to the
2722 * console.
2723 */
2724 static const char *const aszSucc[] = { "core.c", NULL };
2725 one_process = 0;
2726
2727 ap_hook_open_logs(event_open_logs, NULL, aszSucc, APR_HOOK_REALLY_FIRST);
2728 /* we need to set the MPM state before other pre-config hooks use MPM query
2729 * to retrieve it, so register as REALLY_FIRST
2730 */
2731 ap_hook_pre_config(event_pre_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
2732 ap_hook_check_config(event_check_config, NULL, NULL, APR_HOOK_MIDDLE);
2733 ap_hook_mpm(event_run, NULL, NULL, APR_HOOK_MIDDLE);
2734 ap_hook_mpm_query(event_query, NULL, NULL, APR_HOOK_MIDDLE);
2735 ap_hook_mpm_note_child_killed(event_note_child_killed, NULL, NULL, APR_HOOK_MIDDLE);
2736 ap_hook_mpm_register_timed_callback(event_register_timed_callback, NULL, NULL,
2737 APR_HOOK_MIDDLE);
2738 ap_hook_mpm_get_name(event_get_name, NULL, NULL, APR_HOOK_MIDDLE);
2739 }
...
2844 module AP_MODULE_DECLARE_DATA mpm_event_module = {
2845 MPM20_MODULE_STUFF,
2846 NULL, /* hook to run before apache parses args */
2847 NULL, /* create per-directory config structure */
2848 NULL, /* merge per-directory config structures */
2849 NULL, /* create per-server config structure */
2850 NULL, /* merge per-server config structures */
2851 event_cmds, /* command apr_table_t */
2852 event_hooks /* register_hooks */
2853 };
event MPMの内容を見ると、AP_MODULE_DECLARE_DATA構造体mpm_event_moduleで、当モジュールが読み込まれたときに呼び出されるコールバック関数event_hooks()関数を登録しています。このevent_hooks()中で、他のApacheモジュールでも見受けられたようにap_hook_xxx()関数によってイベントフックを設定しています。その中にap_hook_mpm()関数の呼び出しもあります。
LoadModuleディレクティブによってこのモジュールがロードされると、即座にこのevent_hooks()関数が呼び出されます。そこでap_hook_mpm()関数によりevent_run()関数がハンドラとして登録され、ap_run_mpm()関数が呼び出された際に起動されるのです。
ではこのイベントフックの仕組みと言うのは、そもそもどのような実装になっているのでしょう。次回は番外編として、イベントフックの実装詳細を確認し、現行の2.2系も含めたApacheイベントフックの仕組みそのものをおさらいしていきたいと思います。上に出たap_hook_mpm()/ap_run_mpm()両関数を生成するAP_DECLARE_HOOKマクロについて、その展開の仕組みを追う所から入ります。
