📄 child.c
字号:
} do { clen = sizeof(sa_client); csd = accept(nsd, (struct sockaddr *) &sa_client, &clen); } while (csd < 0 && APR_STATUS_IS_EINTR(apr_get_netos_error())); if (csd < 0) { if (APR_STATUS_IS_ECONNABORTED(apr_get_netos_error())) { ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(), ap_server_conf, "accept: (client socket)"); } } else { add_job(csd); } } SetEvent(exit_event); return 0;}static PCOMP_CONTEXT win9x_get_connection(PCOMP_CONTEXT context){ apr_os_sock_info_t sockinfo; int len; if (context == NULL) { /* allocate the completion context and the transaction pool */ apr_allocator_t *allocator; apr_thread_mutex_lock(child_lock); context = apr_pcalloc(pchild, sizeof(COMP_CONTEXT)); apr_allocator_create(&allocator); apr_allocator_max_free_set(allocator, ap_max_mem_free); apr_pool_create_ex(&context->ptrans, pchild, NULL, allocator); apr_allocator_owner_set(allocator, context->ptrans); apr_pool_tag(context->ptrans, "transaction"); apr_thread_mutex_unlock(child_lock); } while (1) { apr_pool_clear(context->ptrans); context->ba = apr_bucket_alloc_create(context->ptrans); context->accept_socket = remove_job(); if (context->accept_socket == INVALID_SOCKET) { return NULL; } len = sizeof(struct sockaddr); context->sa_server = apr_palloc(context->ptrans, len); if (getsockname(context->accept_socket, context->sa_server, &len)== SOCKET_ERROR) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf, "getsockname failed"); continue; } len = sizeof(struct sockaddr); context->sa_client = apr_palloc(context->ptrans, len); if ((getpeername(context->accept_socket, context->sa_client, &len)) == SOCKET_ERROR) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf, "getpeername failed"); memset(&context->sa_client, '\0', sizeof(context->sa_client)); } sockinfo.os_sock = &context->accept_socket; sockinfo.local = context->sa_server; sockinfo.remote = context->sa_client; sockinfo.family = APR_INET; sockinfo.type = SOCK_STREAM; apr_os_sock_make(&context->sock, &sockinfo, context->ptrans); return context; }}/* Windows NT/2000 specific code... * Accept processing for on Windows NT uses a producer/consumer queue * model. An accept thread accepts connections off the network then issues * PostQueuedCompletionStatus() to awake a thread blocked on the ThreadDispatch * IOCompletionPort. * * winnt_accept() * One or more accept threads run in this function, each of which accepts * connections off the network and calls PostQueuedCompletionStatus() to * queue an io completion packet to the ThreadDispatch IOCompletionPort. * winnt_get_connection() * Worker threads block on the ThreadDispatch IOCompletionPort awaiting * connections to service. */#define MAX_ACCEPTEX_ERR_COUNT 250static unsigned int __stdcall winnt_accept(void *lr_) { ap_listen_rec *lr = (ap_listen_rec *)lr_; apr_os_sock_info_t sockinfo; PCOMP_CONTEXT context = NULL; DWORD BytesRead; SOCKET nlsd; int rv, err_count = 0; apr_os_sock_get(&nlsd, lr->sd); while (!shutdown_in_progress) { if (!context) { context = mpm_get_completion_context(); if (!context) { /* Temporary resource constraint? */ Sleep(0); continue; } } /* Create and initialize the accept socket */ if (context->accept_socket == INVALID_SOCKET) { context->accept_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (context->accept_socket == INVALID_SOCKET) { /* Another temporary condition? */ ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_netos_error(), ap_server_conf, "winnt_accept: Failed to allocate an accept socket. " "Temporary resource constraint? Try again."); Sleep(100); continue; } } /* AcceptEx on the completion context. The completion context will be * signaled when a connection is accepted. */ if (!AcceptEx(nlsd, context->accept_socket, context->buff, 0, PADDED_ADDR_SIZE, PADDED_ADDR_SIZE, &BytesRead, &context->Overlapped)) { rv = apr_get_netos_error(); if ((rv == APR_FROM_OS_ERROR(WSAEINVAL)) || (rv == APR_FROM_OS_ERROR(WSAENOTSOCK))) { /* We can get here when: * 1) the client disconnects early * 2) TransmitFile does not properly recycle the accept socket (typically * because the client disconnected) * 3) there is VPN or Firewall software installed with buggy AcceptEx implementation * 4) the webserver is using a dynamic address that has changed */ ++err_count; closesocket(context->accept_socket); context->accept_socket = INVALID_SOCKET; if (err_count > MAX_ACCEPTEX_ERR_COUNT) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, "Child %d: Encountered too many errors accepting client connections. " "Possible causes: dynamic address renewal, or incompatible VPN or firewall software. " "Try using the Win32DisableAcceptEx directive.", my_pid); err_count = 0; } continue; } else if ((rv != APR_FROM_OS_ERROR(ERROR_IO_PENDING)) && (rv != APR_FROM_OS_ERROR(WSA_IO_PENDING))) { ++err_count; closesocket(context->accept_socket); context->accept_socket = INVALID_SOCKET; if (err_count > MAX_ACCEPTEX_ERR_COUNT) { ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf, "Child %d: Encountered too many errors accepting client connections. " "Possible causes: Unknown. " "Try using the Win32DisableAcceptEx directive.", my_pid); err_count = 0; } continue; } /* Wait for pending i/o. * Wake up once per second to check for shutdown . * XXX: We should be waiting on exit_event instead of polling */ while (1) { rv = WaitForSingleObject(context->Overlapped.hEvent, 1000); if (rv == WAIT_OBJECT_0) { if (context->accept_socket == INVALID_SOCKET) { /* socket already closed */ break; } if (!GetOverlappedResult((HANDLE)context->accept_socket, &context->Overlapped, &BytesRead, FALSE)) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), ap_server_conf, "winnt_accept: Asynchronous AcceptEx failed."); closesocket(context->accept_socket); context->accept_socket = INVALID_SOCKET; } break; } /* WAIT_TIMEOUT */ if (shutdown_in_progress) { closesocket(context->accept_socket); context->accept_socket = INVALID_SOCKET; break; } } if (context->accept_socket == INVALID_SOCKET) { continue; } } err_count = 0; /* Inherit the listen socket settings. Required for * shutdown() to work */ if (setsockopt(context->accept_socket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char *)&nlsd, sizeof(nlsd))) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf, "setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed."); /* Not a failure condition. Keep running. */ } /* Get the local & remote address */ GetAcceptExSockaddrs(context->buff, 0, PADDED_ADDR_SIZE, PADDED_ADDR_SIZE, &context->sa_server, &context->sa_server_len, &context->sa_client, &context->sa_client_len); sockinfo.os_sock = &context->accept_socket; sockinfo.local = context->sa_server; sockinfo.remote = context->sa_client; sockinfo.family = APR_INET; sockinfo.type = SOCK_STREAM; apr_os_sock_make(&context->sock, &sockinfo, context->ptrans); /* When a connection is received, send an io completion notification to * the ThreadDispatchIOCP. This function could be replaced by * mpm_post_completion_context(), but why do an extra function call... */ PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_CONNECTION_ACCEPTED, &context->Overlapped); context = NULL; } if (!shutdown_in_progress) { /* Yow, hit an irrecoverable error! Tell the child to die. */ SetEvent(exit_event); } ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ap_server_conf, "Child %d: Accept thread exiting.", my_pid); return 0;}static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context){ int rc; DWORD BytesRead; DWORD CompKey; LPOVERLAPPED pol; mpm_recycle_completion_context(context); apr_atomic_inc(&g_blocked_threads); while (1) { if (workers_may_exit) { apr_atomic_dec(&g_blocked_threads); return NULL; } rc = GetQueuedCompletionStatus(ThreadDispatchIOCP, &BytesRead, &CompKey, &pol, INFINITE); if (!rc) { rc = apr_get_os_error(); ap_log_error(APLOG_MARK,APLOG_DEBUG, rc, ap_server_conf, "Child %d: GetQueuedComplationStatus returned %d", my_pid, rc); continue; } switch (CompKey) { case IOCP_CONNECTION_ACCEPTED: context = CONTAINING_RECORD(pol, COMP_CONTEXT, Overlapped); break; case IOCP_SHUTDOWN: apr_atomic_dec(&g_blocked_threads); return NULL; default: apr_atomic_dec(&g_blocked_threads); return NULL; } break; } apr_atomic_dec(&g_blocked_threads); return context;}/* * worker_main() * Main entry point for the worker threads. Worker threads block in * win*_get_connection() awaiting a connection to service. */static unsigned int __stdcall worker_main(void *thread_num_val){ static int requests_this_child = 0; PCOMP_CONTEXT context = NULL; int thread_num = (int)thread_num_val; ap_sb_handle_t *sbh; ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf, "Child %d: Worker thread %ld starting.", my_pid, thread_num); while (1) { conn_rec *c; apr_int32_t disconnected; ap_update_child_status_from_indexes(0, thread_num, SERVER_READY, NULL); /* Grab a connection off the network */ if (use_acceptex) { context = winnt_get_connection(context); } else { context = win9x_get_connection(context); } if (!context) { /* Time for the thread to exit */ break; } /* Have we hit MaxRequestPerChild connections? */ if (ap_max_requests_per_child) { requests_this_child++; if (requests_this_child > ap_max_requests_per_child) { SetEvent(max_requests_per_child_event); } } ap_create_sb_handle(&sbh, context->ptrans, 0, thread_num); c = ap_run_create_connection(context->ptrans, ap_server_conf, context->sock, thread_num, sbh, context->ba); if (c) { ap_process_connection(c, context->sock); apr_socket_opt_get(context->sock, APR_SO_DISCONNECTED, &disconnected); if (!disconnected) { context->accept_socket = INVALID_SOCKET; ap_lingering_close(c); } else if (!use_acceptex) { /* If the socket is disconnected but we are not using acceptex, * we cannot reuse the socket. Disconnected sockets are removed * from the apr_socket_t struct by apr_sendfile() to prevent the * socket descriptor from being inadvertently closed by a call * to apr_socket_close(), so close it directly. */ closesocket(context->accept_socket); context->accept_socket = INVALID_SOCKET; } } else { /* ap_run_create_connection closes the socket on failure */ context->accept_socket = INVALID_SOCKET; } } ap_update_child_status_from_indexes(0, thread_num, SERVER_DEAD, (request_rec *) NULL); ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf, "Child %d: Worker thread %ld exiting.", my_pid, thread_num); return 0;}static void cleanup_thread(HANDLE *handles, int *thread_cnt, int thread_to_clean){ int i; CloseHandle(handles[thread_to_clean]); for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++) handles[i] = handles[i + 1]; (*thread_cnt)--;}/* * child_main() * Entry point for the main control thread for the child process. * This thread creates the accept thread, worker threads and * monitors the child process for maintenance and shutdown
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -