(1)SOCKETデータバケットタイプ情報
SOCKETデータバケットのバケットタイプ情報は次の通り。(apr-util-1.5.4/buckets/apr_buckets_socket.c)
107 APU_DECLARE_DATA const apr_bucket_type_t apr_bucket_type_socket = {
108 "SOCKET", 5, APR_BUCKET_DATA,
109 apr_bucket_destroy_noop,
110 socket_bucket_read,
111 apr_bucket_setaside_notimpl,
112 apr_bucket_split_notimpl,
113 apr_bucket_copy_notimpl
114 };
(2)apr_socket_t 情報のセットアップ
SOCKETデータバケットで、独自の実装を持っているのはread関数だけだ。
それをみておきたいが、その前に、ソケット情報(apr_socket_t)を生成する listenerスレッドのacceptの処理部分を見たい。
listenerスレッドのListenソケットの処理ではacceptの処理について触れた。
(httpd-2.4.9/server/mpm/event/event.c) 1368 static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy) 1369 { : 1616 rc = lr->accept_func(&csd, lr, ptrans);
accept_func()の実体は、ap_unixd_accept()だ。
(httpd-2.4.9/server/mpm/event/event.c)
1122 static apr_status_t init_pollset(apr_pool_t *p)
1123 {
:
1151 lr->accept_func = ap_unixd_accept;
ap_unixd_accept()関数で、ソケットがacceptされ、apr_socket_t情報が作成される。
このapr_socket_t情報は、listenerスレッドの、void*型の変数csdにアドレスがセットされる。
(httpd-2.4.9/os/unix/unixd.c) 291 AP_DECLARE(apr_status_t) ap_unixd_accept(void **accepted, ap_listen_rec *lr, 292 apr_pool_t *ptrans) 293 { 294 apr_socket_t *csd; : 300 *accepted = NULL; 301 status = apr_socket_accept(&csd, lr->sd, ptrans); 302 if (status == APR_SUCCESS) { 303 *accepted = csd;この*acceptedが、listenerスレッドの処理のcsdとなっている。
apr_socket_t情報は以下の通り。
(apr-1.5.1/include/arch/unix/apr_arch_networkio.h) 103 struct apr_socket_t { 104 apr_pool_t *pool; 105 int socketdes; 106 int type; 107 int protocol; 108 apr_sockaddr_t *local_addr; 109 apr_sockaddr_t *remote_addr; 110 apr_interval_time_t timeout; 111 #ifndef HAVE_POLL : 113 #endif 114 int local_port_unknown; 115 int local_interface_unknown; 116 int remote_addr_unknown; 117 apr_int32_t options; 118 apr_int32_t inherit; 119 sock_userdata_t *userdata; 120 #ifndef WAITIO_USES_POLL : 123 #endif 124 };
もう少し先を辿る。
apr_socket_accept()だ。
これは、APRライブラリに実装されている。
ここでは、第2引数に指定されたsock->socketdesをacceptし、新しい通信ソケットを第1引数apr_socket_t *newに返す。
(apr-1.5.1/network_io/unix/sockets.c) 201 apr_status_t apr_socket_accept(apr_socket_t **new, apr_socket_t *sock, 202 apr_pool_t *connection_context) 203 { 204 int s; 205 apr_sockaddr_t sa; 206 207 sa.salen = sizeof(sa.sa); 208 209 #ifdef HAVE_ACCEPT4 210 { 211 int flags = SOCK_CLOEXEC; : 222 s = accept4(sock->socketdes, (struct sockaddr *)&sa.sa, &sa.salen, flags); 223 }
手元の実装ではaccept4が利用できるようだ。
man accept4 を見ると、第4引数のflagsにはSOCK_NONBLOCK、SOCK_CLOEXECが指定できるとある(flags==0の場合は、通常のacceptと同じ動きとなる)。
ここではSOCK_CLOEXECフラグが指定されている。
このフラグがONの場合 execv()の実行時にファイルディスクリプタが自動的にクローズされるようになる。
つまり、これを実行中の別のスレッドでfork() & execv()が実行された場合、このフラグが無効だと、accept()されたソケットがオープン状態のまま残される可能性があるが、これをONにすることで、その場合に自動的にクローズされるようにできる。
224 #else : 226 #endif 227 228 if (s < 0) { 229 return errno; 230 } : 237 alloc_socket(new, connection_context); 238 239 /* Set up socket variables -- note that it may be possible for 240 * *new to be an AF_INET socket when sock is AF_INET6 in some 241 * dual-stack configurations, so ensure that the remote_/local_addr 242 * structures are adjusted for the family of the accepted 243 * socket: */ 244 set_socket_vars(*new, sa.sa.sin.sin_family, SOCK_STREAM, sock->protocol); 245 : 249 (*new)->timeout = -1; 250 251 (*new)->remote_addr_unknown = 0; 252 253 (*new)->socketdes = s; 254 255 /* Copy in peer's address. */ 256 (*new)->remote_addr->sa = sa.sa; 257 (*new)->remote_addr->salen = sa.salen; 258 259 *(*new)->local_addr = *sock->local_addr; 260 261 /* The above assignment just overwrote the pool entry. Setting the local_addr 262 pool for the accepted socket back to what it should be. Otherwise all 263 allocations for this socket will come from a server pool that is not 264 freed until the process goes down.*/ 265 (*new)->local_addr->pool = connection_context; 266 267 /* fix up any pointers which are no longer valid */ 268 if (sock->local_addr->sa.sin.sin_family == AF_INET) { 269 (*new)->local_addr->ipaddr_ptr = &(*new)->local_addr->sa.sin.sin_addr; 270 } 271 #if APR_HAVE_IPV6 272 else if (sock->local_addr->sa.sin.sin_family == AF_INET6) { 273 (*new)->local_addr->ipaddr_ptr = &(*new)->local_addr->sa.sin6.sin6_addr; 274 } 275 #endif 276 (*new)->remote_addr->port = ntohs((*new)->remote_addr->sa.sin.sin_port); 277 if (sock->local_port_unknown) { 278 /* not likely for a listening socket, but theoretically possible :) */ 279 (*new)->local_port_unknown = 1; 280 } 281 282 #if APR_TCP_NODELAY_INHERITED 283 if (apr_is_option_set(sock, APR_TCP_NODELAY) == 1) { 284 apr_set_option(*new, APR_TCP_NODELAY, 1);
APR_TCP_NODELAYをONにする処理はリクエスト処理の流れのcore_pre_connectionの処理でも見た。
Nagle(ネーグル)アルゴリズムを無効にする。
285 } 286 #endif /* TCP_NODELAY_INHERITED */ : 292 293 if (sock->local_interface_unknown || 294 !memcmp(sock->local_addr->ipaddr_ptr, 295 generic_inaddr_any, 296 sock->local_addr->ipaddr_len)) { 297 /* If the interface address inside the listening socket's local_addr wasn't 298 * up-to-date, we don't know local interface of the connected socket either. 299 * 300 * If the listening socket was not bound to a specific interface, we 301 * don't know the local_addr of the connected socket. 302 */ 303 (*new)->local_interface_unknown = 1; 304 } 305 : 318 319 (*new)->inherit = 0; 320 apr_pool_cleanup_register((*new)->pool, (void *)(*new), socket_cleanup, 321 socket_cleanup); 322 return APR_SUCCESS; 323 }新しいapr_socket_t情報を見ると、
249行目でtimeoutは-1がセットされている。
また、初期状態ではソケットはブロックモードになっている。
apr_socket_t情報のoptions変数には、次のようなフラグがセットされている(一部)。
(apr-1.5.1/include/apr_network_io.h) 63 #define APR_SO_LINGER 1 /**< Linger */ 64 #define APR_SO_KEEPALIVE 2 /**< Keepalive */ 65 #define APR_SO_DEBUG 4 /**< Debug */ 66 #define APR_SO_NONBLOCK 8 /**< Non-blocking IO */ 67 #define APR_SO_REUSEADDR 16 /**< Reuse addresses */ 68 #define APR_SO_SNDBUF 64 /**< Send buffer */ 69 #define APR_SO_RCVBUF 128 /**< Receive buffer */ 70 #define APR_SO_DISCONNECTED 256 /**< Disconnected */ 71 #define APR_TCP_NODELAY 512 /**< For SCTP sockets, this is mapped 72 * to STCP_NODELAY internally. 73 */ 74 #define APR_TCP_NOPUSH 1024 /**< No push */ :fcntl()やsetsockopt()でソケットのオプションを設定した場合、このoptionsも同期してフラグをON/OFFしている。
optionsは初期状態では512、つまり、APR_TCP_NODELAYのフラグが立っている。
apr_socket_accept()の283行目の処理でこのオプションが有効化されている状態だ。
このソケット情報は引き続きworkerスレッドで処理される。
process_socket処理はすでにみた。
ここで見た、pre_connectinoフック関数である、core_pre_connection関数で幾つかのソケットオプションが指定される。
その部分を再掲する。
(httpd-2.4.9/server/core.c) 4637 static int core_pre_connection(conn_rec *c, void *csd) 4638 { : 4649 rv = apr_socket_opt_set(csd, APR_TCP_NODELAY, 1); : 4664 rv = apr_socket_timeout_set(csd, c->base_server->timeout);4649行目は、既に有効化されているAPR_TCP_NODELAYフラグの処理だ。
4664行目は、ソケット情報のtimeout変数を設定のTimeoutディレクティブの値で更新する。
デフォルトの場合、Timeoutの値は60秒になっている。
(apr-1.5.1/network_io/unix/sockopt.c)
75 apr_status_t apr_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t)
76 {
77 apr_status_t stat;
78
79 /* If our new timeout is non-negative and our old timeout was
80 * negative, then we need to ensure that we are non-blocking.
81 * Conversely, if our new timeout is negative and we had
82 * non-negative timeout, we must make sure our socket is blocking.
83 * We want to avoid calling fcntl more than necessary on the
84 * socket.
85 */
86 if (t >= 0 && sock->timeout < 0) {
87 if (apr_is_option_set(sock, APR_SO_NONBLOCK) != 1) {
88 if ((stat = sononblock(sock->socketdes)) != APR_SUCCESS) {
89 return stat;
90 }
91 apr_set_option(sock, APR_SO_NONBLOCK, 1);
92 }
93 }
94 else if (t < 0 && sock->timeout >= 0) {
95 if (apr_is_option_set(sock, APR_SO_NONBLOCK) != 0) {
96 if ((stat = soblock(sock->socketdes)) != APR_SUCCESS) {
97 return stat;
98 }
99 apr_set_option(sock, APR_SO_NONBLOCK, 0);
100 }
101 }
ここでは、元のtimeout | 変更後のtimeout(t) | 処理 |
timeout <0 | t >=0 | ソケットを非ブロックに変更する 内部変数のoptionsの値を変更(APR_SO_NONBLOCKをオン) |
timeout >=0 | t <0 | ソケットをブロックに変更する 内部変数のoptionsの値を変更(APR_SO_NONBLOCKをオフ) |
102 /* must disable the incomplete read support if we disable 103 * a timeout 104 */ 105 if (t <= 0) { 106 sock->options &= ~APR_INCOMPLETE_READ;
変更後の値が<=0の場合、optionsのAPR_INCOMPLETE_READをオフにする。
107 } 108 sock->timeout = t;
そして、内部変数のtimeout値を変更する。
109 return APR_SUCCESS; 110 }ここで、デフォルトへの変更を考えると、元のtimeout値は-1、これを60000000マイクロ秒に更新する。
従って、ここでソケットを非ブロックに変更する処理が実行されることになる。
optionsの値は520になる。つまり、(APR_TCP_NODELAY|APR_SO_NONBLOCK)だ。
ここまででソケットの初期設定が完了する。
(3)apr_socket_t 情報の破棄
apr_socket_accept関数の末尾を再掲する。(apr-1.5.1/network_io/unix/sockets.c) 201 apr_status_t apr_socket_accept(apr_socket_t **new, apr_socket_t *sock, 202 apr_pool_t *connection_context) 203 { : 320 apr_pool_cleanup_register((*new)->pool, (void *)(*new), socket_cleanup, 321 socket_cleanup); 322 return APR_SUCCESS; 323 }apr_pool_cleanup_register()は指定されたメモリプールが破棄されるとき(apr_pool_clear()やapr_pool_destroy())に実行される関数を登録する。
ここでの実行関数はsocket_cleanup()。引数に、(void *)(*new)が渡される。
つまり、(*new)->poolが破棄されるときに、socket_cleanupが実行される。
socket_cleanup関数は下記の通り。
(apr-1.5.1/network_io/unix/sockets.c) 31 static apr_status_t socket_cleanup(void *sock) 32 { 33 apr_socket_t *thesocket = sock; 34 int sd = thesocket->socketdes; 35 36 /* Set socket descriptor to -1 before close(), so that there is no 37 * chance of returning an already closed FD from apr_os_sock_get(). 38 */ 39 thesocket->socketdes = -1; 40 41 if (close(sd) == 0) { 42 return APR_SUCCESS; 43 } 44 else { 45 /* Restore, close() was not successful. */ 46 thesocket->socketdes = sd; 47 48 return errno; 49 } 50 }apr_socket_t自体はそのままで、socketdes変数にセットされているソケットディスクリプタに対してclose()を実行し、
変数には-1をセットしている。
なお、ソケットのクローズには次のapr_socket_close関数も利用される。
(apr-1.5.1/network_io/unix/sockets.c) 172 apr_status_t apr_socket_close(apr_socket_t *thesocket) 173 { 174 return apr_pool_cleanup_run(thesocket->pool, thesocket, socket_cleanup); 175 }apr_pool_cleanup_run()関数は、第1引数のpoolに登録されている、socket_cleanup関数を削除し、その後、
socket_cleanup(thesocket)を実行する処理だ。
これを利用することで、元のpoolが破棄される際にsocket_cleanup()が二重に呼び出されることを回避できる。
(4)socket_bucket_read関数
次に、SOCKETデータバケットのread関数の実装を確認する。(apr-util-1.5.4/buckets/apr_buckets_socket.c) 19 static apr_status_t socket_bucket_read(apr_bucket *a, const char **str, 20 apr_size_t *len, apr_read_type_e block) 21 { 22 apr_socket_t *p = a->data;
apr_bucket_t 情報のバケットタイプ依存情報は、SOCKETの場合、apr_socket_tとなる。
23 char *buf; 24 apr_status_t rv; 25 apr_interval_time_t timeout; 26 27 if (block == APR_NONBLOCK_READ) {ここは、非ブロックモード指定の場合の処理だ。
28 apr_socket_timeout_get(p, &timeout);
apr_socket_t情報のtimeout変数の値をtimeoutに返す
29 apr_socket_timeout_set(p, 0);
apr_socket_timeout_set関数は、先に見ておいた。
ここでは、apr_socket_tのtimeout情報を0にしている。
元のTImeoutがデフォルトの60秒の場合、
- 29行目 60→ 0
- 39行目 0→60
という変更になる。
この場合変更前の値も変更後の値も0以上なので、ブロックモードの処理は行われない(非ブロック状態)。
30 }
31
32 *str = NULL;
33 *len = APR_BUCKET_BUFF_SIZE;
34 buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */
ここで受信用のバッファを確保している。
35
36 rv = apr_socket_recv(p, buf, len);
ソケットの受信処理を実行する。
(apr-1.5.1/network_io/unix/sendrecv.c) 70 apr_status_t apr_socket_recv(apr_socket_t *sock, char *buf, apr_size_t *len) 71 { 72 apr_ssize_t rv; 73 apr_status_t arv; 74 75 if (sock->options & APR_INCOMPLETE_READ) {
APR_INCOMPLETE_READフラグはソケットに対する操作が行われない、apr_socket_tのoptions独自のフラグだ。
非ブロックモードのソケット(timeout!=0)に対して、
直前の読み込みでバッファを満たすだけのデータの読み込みが行われなかった場合に、
その次のこのapr_socket_recv()処理において、いきなりread(81行目)を行うのではなく、まずpoll(87行目)を行わせるための
フラグだ。
このフラグは、この関数の103行目で条件を満たすとONになり、ここ(次のapr_socket_recv)でOFFになる。
その他、apr_socket_timeout_set()で変更後の値が0以下の場合にもOFFになる。
なお、プログラムで個別にON/OFFが適用される例もある。
76 sock->options &= ~APR_INCOMPLETE_READ; 77 goto do_select; 78 } 79 80 do { 81 rv = read(sock->socketdes, buf, (*len));
ソケットの読込処理。
82 } while (rv == -1 && errno == EINTR);
83
84 while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
85 && (sock->timeout > 0)) {
86 do_select:
87 arv = apr_wait_for_io_or_timeout(NULL, sock, 1);
このapr_wait_for_io_or_timeout関数では、sock->socketdesをPOLLINに指定してpoll()を実行する。
timeoutはapr_socket_t情報のtimeoutを使用する。
戻り値は次の通り。
APR_TIMEUP : タイムアウト
APR_SUCCESS: 受信可能
エラーコード(errno): エラー時
88 if (arv != APR_SUCCESS) {
ここは、pollタイムアウトまたはエラー時の処理となる。
89 *len = 0; 90 return arv; 91 } 92 else {
read()が実行されている。
93 do {
94 rv = read(sock->socketdes, buf, (*len));
95 } while (rv == -1 && errno == EINTR);
96 }
97 }
98 if (rv == -1) {
ここは、read()エラー時の処理となる。
99 (*len) = 0; 100 return errno; 101 } 102 if ((sock->timeout > 0) && (rv < *len)) {
timeout>0の場合でかつ、受信したデータがバッファサイズより小さかった場合にAPR_INCOMPLETE_READフラグを立てる。
103 sock->options |= APR_INCOMPLETE_READ; 104 } 105 (*len) = rv;
読み込みデータ長を引数*lenにセットする。
106 if (rv == 0) {
read()の返り値が0の場合は、APR_EOFを返す。
*lenも0になっている。
107 return APR_EOF; 108 }
その他の場合はAPR_SUCCESSで返る。
109 return APR_SUCCESS; 110 }
37 38 if (block == APR_NONBLOCK_READ) {
非ブロックモード指定の場合の処理。
39 apr_socket_timeout_set(p, timeout);
29行目で0にしたapr_socket_t情報のtimeout値を、28行目で退避した元の値に戻している。
40 } 41 42 if (rv != APR_SUCCESS && rv != APR_EOF) {
APR_EOF以外のエラーの場合の経路。
確保したバッファを解放して、エラーを返している。
43 apr_bucket_free(buf); 44 return rv; 45 } 46 /* 47 * If there's more to read we have to keep the rest of the socket 48 * for later. XXX: Note that more complicated bucket types that 49 * refer to data not in memory and must therefore have a read() 50 * function similar to this one should be wary of copying this 51 * code because if they have a destroy function they probably 52 * want to migrate the bucket's subordinate structure from the 53 * old bucket to a raw new one and adjust it as appropriate, 54 * rather than destroying the old one and creating a completely 55 * new bucket. 56 * 57 * Even if there is nothing more to read, don't close the socket here 58 * as we have to use it to send any response :) We could shut it 59 * down for reading, but there is no benefit to doing so. 60 */ 61 if (*len > 0) {
データを受信した場合の処理となる。
この場合、作成した受信バッファをベースにHEAPバケットを作成して、bucket aを置き換えている。
62 apr_bucket_heap *h; 63 /* Change the current bucket to refer to what we read */ 64 a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free); 65 h = a->data; 66 h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */
HEAPデータバケットのバケットタイプ依存データは apr_bucket_heap情報で、
このalloc_lenを更新している。
67 *str = buf;
引数で渡された *strには、確保したバッファのアドレスを返す。
68 APR_BUCKET_INSERT_AFTER(a, apr_bucket_socket_create(p, a->list));
ここでは、元のSOCKETバケットから取り出したapr_socket_t情報 p をもとにSOCKETデータバケットを新たに作成しなおし、
それを、aを置き換えたHEAPデータバケットの後ろ(next)に追加している。
69 } 70 else {
*len>=0の場合の処理(EOFの場合の処理)
確保したバッファを解放して、空文字列のIMMORTALデータバケットを生成し、bucket aを置き換えている(上書き)。
これにより、SOCKETデータバケットが失われている。
EOFのソケットはread処理の対象ではないが、データ送信には使われる可能性があるので、特に
ここではソケットに対するクローズ処理などは行われていない。
コメントには、read側をshutdownすることはできるが、意味はない、と書かれている。
71 apr_bucket_free(buf); 72 a = apr_bucket_immortal_make(a, "", 0);
IMMORTALデータバケットを生成している。
apr_bucket_immortal_createの場合、apr_bucket_t領域を確保し、各値をIMMORTALバケットタイプに設定するが、
apr_buclet_immortal_makeの場合、apr_bucket_t領域としては、aを使いまわし、ここでは内部のデータに定数空文字列("")をセットしている。
IMMORTALデータバケットのデータは、destroy()では破棄されない。定数データなどで使用されている。
他方、HEAPバケットの場合は、destroy()関数で、使用しているメモリ領域を解放する。
ちなみに、TRANSIENTデータバケットはIMMORTALデータバケットと似ているが、setaside関数が実装されている、これによりHEAPデータバケットに変換できる点が異なっている。
これにより、aに存在していたSOCKETデータバケットはIMMORTALデータバケットで上書きされた(上記の通り、特にクローズなど行われない)。
73 *str = a->data;
a->dataには "" がポイントされている。
74 } 75 return APR_SUCCESS; 76 }
今回はここまで。
0 件のコメント:
コメントを投稿