(1) ap_process_http_async_connection関数
呼び出し元の関数ではリクエストを読込み(ap_read_request())、ap_process_async_request()処理を実行している。(httpd-2.4.9/modules/http/http_core.c)
124 static int ap_process_http_async_connection(conn_rec *c)
125 {
126 request_rec *r;
127 conn_state_t *cs = c->cs;
:
132 while (cs->state == CONN_STATE_READ_REQUEST_LINE) {
133 ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_READ, c);
134
135 if ((r = ap_read_request(c))) {
リクエスト行とリクエストヘッダを読み込み、request_rec情報に格納する。
ここで直接呼び出されるフック関数の実行関数には次のものがある
* ap_run_create_request
* ap_run_pre_read_request
* ap_run_post_read_request
* ap_run_log_transaction(エラー発生の場合)
:
141 if (r->status == HTTP_OK) {
142 cs->state = CONN_STATE_HANDLER;
143 ap_process_async_request(r);
:
151 r = NULL;
152 }
153
154 if (cs->state != CONN_STATE_WRITE_COMPLETION &&
155 cs->state != CONN_STATE_SUSPENDED) {
156 /* Something went wrong; close the connection */
157 cs->state = CONN_STATE_LINGER;
158 }
159 }
160 else { /* ap_read_request failed - client may have closed */
161 cs->state = CONN_STATE_LINGER;
162 }
163 }
164
165 return OK;
166 }
ここで conn_state_t という構造体が出てくる。
これは、コネクションの状態を管理している。
(httpd-2.4.9/include/httpd.h)
1187 struct conn_state_t {
1188 /** Current state of the connection */
1189 conn_state_e state;
1190 /** Whether to read instead of write, or write instead of read */
1191 conn_sense_e sense;
1192 };
ここにあるconn_state_e(列挙型)変数 stateは次の通り
(httpd-2.4.9/include/httpd.h)
1167 typedef enum {
1168 CONN_STATE_CHECK_REQUEST_LINE_READABLE,
1169 CONN_STATE_READ_REQUEST_LINE,
1170 CONN_STATE_HANDLER,
1171 CONN_STATE_WRITE_COMPLETION,
1172 CONN_STATE_SUSPENDED,
1173 CONN_STATE_LINGER, /* connection may be closed with lingering */
1174 CONN_STATE_LINGER_NORMAL, /* MPM has started lingering close with normal timeout */
1175 CONN_STATE_LINGER_SHORT /* MPM has started lingering close with short timeout */
1176 } conn_state_e;
ap_process_http_async_connection()でチェックされている CONN_STATE_READ_REQUEST_LINE は、その名の通り、リクエストの読み込みということだ。
リクエストの読み込み後、CONN_STATE_HANDLERに更新される(142行目)。
そして、ap_process_async_request()が実行される。
その処理を終えると、CONN_STATE_WRITE_COMPLETIONか、CONN_STATE_SUSPENDEDかチェックされ、いずれでもなければ、CONN_STATE_LINGERで更新される(157行目)。
この時点で、cs->stateは、CONN_STATE_WRITE_COMPLETIONか、CONN_STATE_SUSPENDEDか、CONN_STATE_LINGERということになるので、132行目からのwhile (cs->state == CONN_STATE_READ_REQUEST_LINE)ループからは抜ける。
この処理だけに着目する限り、whileループを実査にループする経路はなさそうだ。
(2)process_connectionフック関数
さて、もう一段、呼び出し元を辿ると、次の関数に辿り着く。(httpd-2.4.9/modules/http/http_core.c)
225 static int ap_process_http_connection(conn_rec *c)
226 {
227 if (async_mpm && !c->clogging_input_filters) {
228 return ap_process_http_async_connection(c);
229 }
230 else {
231 return ap_process_http_sync_connection(c);
232 }
233 }
これはフック関数だ。
以下で登録されている。process_connectionフック関数である。
(httpd-2.4.9/modules/http/http_core.c)
275 ap_hook_process_connection(ap_process_http_connection, NULL, NULL,
276 APR_HOOK_REALLY_LAST);
上記のap_process_http_connection()で使用されているフラグasync_mpmはap_mpm_query()関数で設定されている。
263 if (ap_mpm_query(AP_MPMQ_IS_ASYNC, &async_mpm) != APR_SUCCESS) {
264 async_mpm = 0;
265 }
このMPMの情報を取得する関数自体が内部でmpm_queryフック関数を呼び出している。
MPMによって、登録されているフック関数が異なり、実際にセットされる値も変わるという訳だ。
event MPMの場合は、このmpm_queryフック関数は、event_query()だ。
この関数が、AP_MPMQ_IS_ASYNCというコードを与えられたときに、引数async_mpmにセットする値は、1である。
ちなみに、worker MPMにおける、mpm_queryフック関数はworker_query()だが、AP_MPMQ_IS_ASYNCに対して返す値の指定がない。ここでは、APR_ENOTIMPLがエラーとして返されるので、結果として、264行目によって値が0となる。
prefork MPMも worker MPMと同様だった。
clogging_input_filtersは、2.4.9では使用されていないので、初期値の0のままだ。
従って、ap_process_http_connection()関数は、event MPMでは、ap_process_http_async_connection()が実行され、worker MPM/prefork MPMでは、ap_process_http_sync_connection()が実行される。
clogging_input_filtersをセットすると、worker MPMと同様にリクエスト処理でワーカスレッドを占有する、というのは、要はこの処理の分岐の話だ。
続いて、このap_process_http_connection()を実行するprocess_connectionフック関数の呼び出し箇所を見てみる。
event MPMでは process_socket()関数の処理で呼ばれている。
これは、event MPMのworkerスレッドで実行される。
listenerスレッドが受け付けたリクエストの処理を行うのが、workerスレッドのprocess_socket()関数だ。
もう少し細かく見ると、listenerスレッドは、listenソケットのacceptを監視し、既に接続済みのソケットが読込可能/書込可能となるのを監視している。
またapr_skiplistを利用したタイマー処理のタイムアップも監視しており、そのほかにも4種類のタイムアウト処理を実行する。
読込可能チェックには、以下の状態がある。
書込可能チェックは、以下の状態だ。
そして、acceptされたソケットの処理、KeepAlive接続の読込処理、書込み可能になったソケットの書込処理がprocess_socket関数で行われる。
clogging_input_filtersは、2.4.9では使用されていないので、初期値の0のままだ。
従って、ap_process_http_connection()関数は、event MPMでは、ap_process_http_async_connection()が実行され、worker MPM/prefork MPMでは、ap_process_http_sync_connection()が実行される。
clogging_input_filtersをセットすると、worker MPMと同様にリクエスト処理でワーカスレッドを占有する、というのは、要はこの処理の分岐の話だ。
(3)event MPM: process_socket関数
続いて、このap_process_http_connection()を実行するprocess_connectionフック関数の呼び出し箇所を見てみる。
event MPMでは process_socket()関数の処理で呼ばれている。
これは、event MPMのworkerスレッドで実行される。
listenerスレッドが受け付けたリクエストの処理を行うのが、workerスレッドのprocess_socket()関数だ。
もう少し細かく見ると、listenerスレッドは、listenソケットのacceptを監視し、既に接続済みのソケットが読込可能/書込可能となるのを監視している。
またapr_skiplistを利用したタイマー処理のタイムアップも監視しており、そのほかにも4種類のタイムアウト処理を実行する。
読込可能チェックには、以下の状態がある。
- KeepAlive接続における次リクエストの受信待ち
- lingeringクローズでの通信相手ソケットからのクローズ(FIN)待ち
書込可能チェックは、以下の状態だ。
- クライアントに送ろうとしたデータがブロックされてしまった場合(送信できなかった場合)、再び、書込可能なるのを待っている状態
そして、acceptされたソケットの処理、KeepAlive接続の読込処理、書込み可能になったソケットの書込処理がprocess_socket関数で行われる。
タイマー処理は、process_socket関数では処理されない。
新たなソケットの処理とKeepAlive接続の読込処理では、conn_state_tがCONN_STATE_CHECK_REQUEST_LINE_READABLE状態になる。
書込み可能となったソケットの場合は、conn_state_tがCONN_STATE_WRITE_COMPLETION状態だ。
ここで、process_socket関数でap_run_process_connection()を実行するのは、CONN_STATE_CHECK_REQUEST_LINE_READABLE状態の場合の処理となる。
この処理は、listenerスレッドと相互に関係しながら処理を進めるので、少し分かりにくい気がする。
が、リクエスト処理の流れを追うという意味では、ここが最上流、源流にあたる。
そういうわけで、上流から下流に簡単な図を書いてみた。
フック関数を中心に追ってみた。今日は終わり。
新たなソケットの処理とKeepAlive接続の読込処理では、conn_state_tがCONN_STATE_CHECK_REQUEST_LINE_READABLE状態になる。
書込み可能となったソケットの場合は、conn_state_tがCONN_STATE_WRITE_COMPLETION状態だ。
ここで、process_socket関数でap_run_process_connection()を実行するのは、CONN_STATE_CHECK_REQUEST_LINE_READABLE状態の場合の処理となる。
881 static void process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * sock, 882 event_conn_state_t * cs, int my_child_num, 883 int my_thread_num) 884 { : 893 if (cs == NULL) { /* This is a new connection */ 新しくacceptされたソケットの処理の場合、csはNULLとなっている。 KeepAlive接続の処理の場合はcsは作成済みなので、ここの処理を経由しない。 ここでコネクション情報の初期設定やevent_conn_state_t情報の初期設定 などの初期処理が実行される。
897 c = ap_run_create_connection(p, ap_server_conf, sock,
898 conn_id, sbh, cs->bucket_alloc);
:
920 ap_update_vhost_given_ip(c);
921
922 rc = ap_run_pre_connection(c, sock);
942 cs->pub.state = CONN_STATE_READ_REQUEST_LINE;
943
944 cs->pub.sense = CONN_SENSE_DEFAULT;
945 }
:
952 if (c->clogging_input_filters && !c->aborted) {
clogging_input_filtersフラグが立っている場合の処理。
実行されるのはap_run_process_connection()で、フラグが立っていない場合と
同じだが、終了時のconn_state_tに設定される状態が、CONN_STATE_LINGER状態
に限定されている(SUSPENDでなければ)。
:
959 apr_atomic_inc32(&clogged_count);
960 ap_run_process_connection(c);
961 if (cs->pub.state != CONN_STATE_SUSPENDED) {
962 cs->pub.state = CONN_STATE_LINGER;
963 }
964 apr_atomic_dec32(&clogged_count);
965 }
:
967 read_request:
968 if (cs->pub.state == CONN_STATE_READ_REQUEST_LINE) {
1件のリクエスト処理はここから始まる。
969 if (!c->aborted) {
970 ap_run_process_connection(c);
これこそ、今まで見てきた処理を実行する process_connectionフック関数の
実行だ。
:
976 }
977 else {
978 cs->pub.state = CONN_STATE_LINGER;
979 }
980 }
981
982 if (cs->pub.state == CONN_STATE_WRITE_COMPLETION) {
process_connectionフック関数からreturnすると、通常、conn_state_tは
CONN_STATE_WRITE_COMPLETION状態となっている。
ap_process_async_request()関数の最後に実行される 1件のリクエスト処理の
終了処理であるap_process_request_after_handler関数内で、
CONN_STATE_WRITE_COMPLETIONがセットされる。
そして、ここでは bucket brigadeが NULLで出力フィルタに引き渡されている。
内部に保持されているbucket brigadeが処理対象ということだ。
983 ap_filter_t *output_filter = c->output_filters;
:
989 rv = output_filter->frec->filter_func.out_func(output_filter, NULL);
:
1065 return;
1066 }
この処理は、listenerスレッドと相互に関係しながら処理を進めるので、少し分かりにくい気がする。
が、リクエスト処理の流れを追うという意味では、ここが最上流、源流にあたる。
そういうわけで、上流から下流に簡単な図を書いてみた。
フック関数を中心に追ってみた。今日は終わり。

0 件のコメント:
コメントを投稿