📄 mpm_winnt.c
字号:
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef WIN32
#define CORE_PRIVATE
#include "httpd.h"
#include "http_main.h"
#include "http_log.h"
#include "http_config.h" /* for read_config */
#include "http_core.h" /* for get_remote_host */
#include "http_connection.h"
#include "apr_portable.h"
#include "apr_thread_proc.h"
#include "apr_getopt.h"
#include "apr_strings.h"
#include "apr_lib.h"
#include "apr_shm.h"
#include "apr_thread_mutex.h"
#include "ap_mpm.h"
#include "ap_config.h"
#include "ap_listen.h"
#include "mpm_default.h"
#include "mpm_winnt.h"
#include "mpm_common.h"
#include <malloc.h>
#include "apr_atomic.h"
/* scoreboard.c does the heavy lifting; all we do is create the child
* score by moving a handle down the pipe into the child's stdin.
*/
extern apr_shm_t *ap_scoreboard_shm;
server_rec *ap_server_conf;
/* Definitions of WINNT MPM specific config globals */
static HANDLE shutdown_event; /* used to signal the parent to shutdown */
static HANDLE restart_event; /* used to signal the parent to restart */
static char ap_coredump_dir[MAX_STRING_LEN];
static int one_process = 0;
static char const* signal_arg = NULL;
OSVERSIONINFO osver; /* VER_PLATFORM_WIN32_NT */
static DWORD parent_pid;
DWORD my_pid;
int ap_threads_per_child = 0;
int use_acceptex = 1;
static int thread_limit = DEFAULT_THREAD_LIMIT;
static int first_thread_limit = 0;
static int changed_limit_at_restart;
int winnt_mpm_state = AP_MPMQ_STARTING;
/* ap_my_generation are used by the scoreboard code */
ap_generation_t volatile ap_my_generation=0;
/* shared by service.c as global, although
* perhaps it should be private.
*/
apr_pool_t *pconf;
/* definitions from child.c */
void child_main(apr_pool_t *pconf);
/* used by parent to signal the child to start and exit
* NOTE: these are not sophisticated enough for multiple children
* so they ultimately should not be shared with child.c
*/
extern apr_proc_mutex_t *start_mutex;
extern HANDLE exit_event;
/* Only one of these, the pipe from our parent, ment only for
* one child worker's consumption (not to be inherited!)
* XXX: decorate this name for the trunk branch, was left simplified
* only to make the 2.2 patch trivial to read.
*/
static HANDLE pipe;
/* Stub functions until this MPM supports the connection status API */
AP_DECLARE(void) ap_update_connection_status(long conn_id, const char *key, \
const char *value)
{
/* NOP */
}
AP_DECLARE(void) ap_reset_connection_status(long conn_id)
{
/* NOP */
}
AP_DECLARE(apr_array_header_t *) ap_get_status_table(apr_pool_t *p)
{
/* NOP */
return NULL;
}
/*
* Command processors
*/
static const char *set_threads_per_child (cmd_parms *cmd, void *dummy, char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
ap_threads_per_child = atoi(arg);
if (ap_threads_per_child > thread_limit) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"WARNING: ThreadsPerChild of %d exceeds ThreadLimit "
"value of %d threads,", ap_threads_per_child,
thread_limit);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" lowering ThreadsPerChild to %d. To increase, please"
" see the", thread_limit);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" ThreadLimit directive.");
ap_threads_per_child = thread_limit;
}
else if (ap_threads_per_child < 1) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"WARNING: Require ThreadsPerChild > 0, setting to 1");
ap_threads_per_child = 1;
}
return NULL;
}
static const char *set_thread_limit (cmd_parms *cmd, void *dummy, const char *arg)
{
int tmp_thread_limit;
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
tmp_thread_limit = atoi(arg);
/* you cannot change ThreadLimit across a restart; ignore
* any such attempts
*/
if (first_thread_limit &&
tmp_thread_limit != thread_limit) {
/* how do we log a message? the error log is a bit bucket at this
* point; we'll just have to set a flag so that ap_mpm_run()
* logs a warning later
*/
changed_limit_at_restart = 1;
return NULL;
}
thread_limit = tmp_thread_limit;
if (thread_limit > MAX_THREAD_LIMIT) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"WARNING: ThreadLimit of %d exceeds compile time limit "
"of %d threads,", thread_limit, MAX_THREAD_LIMIT);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" lowering ThreadLimit to %d.", MAX_THREAD_LIMIT);
thread_limit = MAX_THREAD_LIMIT;
}
else if (thread_limit < 1) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"WARNING: Require ThreadLimit > 0, setting to 1");
thread_limit = 1;
}
return NULL;
}
static const char *set_disable_acceptex(cmd_parms *cmd, void *dummy, char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
if (use_acceptex) {
use_acceptex = 0;
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
"Disabled use of AcceptEx() WinSock2 API");
}
return NULL;
}
static const command_rec winnt_cmds[] = {
LISTEN_COMMANDS,
AP_INIT_TAKE1("ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF,
"Number of threads each child creates" ),
AP_INIT_TAKE1("ThreadLimit", set_thread_limit, NULL, RSRC_CONF,
"Maximum worker threads in a server for this run of Apache"),
AP_INIT_NO_ARGS("Win32DisableAcceptEx", set_disable_acceptex, NULL, RSRC_CONF,
"Disable use of the high performance AcceptEx WinSock2 API to work around buggy VPN or Firewall software"),
{ NULL }
};
/*
* Signalling Apache on NT.
*
* Under Unix, Apache can be told to shutdown or restart by sending various
* signals (HUP, USR, TERM). On NT we don't have easy access to signals, so
* we use "events" instead. The parent apache process goes into a loop
* where it waits forever for a set of events. Two of those events are
* called
*
* apPID_shutdown
* apPID_restart
*
* (where PID is the PID of the apache parent process). When one of these
* is signalled, the Apache parent performs the appropriate action. The events
* can become signalled through internal Apache methods (e.g. if the child
* finds a fatal error and needs to kill its parent), via the service
* control manager (the control thread will signal the shutdown event when
* requested to stop the Apache service), from the -k Apache command line,
* or from any external program which finds the Apache PID from the
* httpd.pid file.
*
* The signal_parent() function, below, is used to signal one of these events.
* It can be called by any child or parent process, since it does not
* rely on global variables.
*
* On entry, type gives the event to signal. 0 means shutdown, 1 means
* graceful restart.
*/
/*
* Initialise the signal names, in the global variables signal_name_prefix,
* signal_restart_name and signal_shutdown_name.
*/
#define MAX_SIGNAL_NAME 30 /* Long enough for apPID_shutdown, where PID is an int */
char signal_name_prefix[MAX_SIGNAL_NAME];
char signal_restart_name[MAX_SIGNAL_NAME];
char signal_shutdown_name[MAX_SIGNAL_NAME];
void setup_signal_names(char *prefix)
{
apr_snprintf(signal_name_prefix, sizeof(signal_name_prefix), prefix);
apr_snprintf(signal_shutdown_name, sizeof(signal_shutdown_name),
"%s_shutdown", signal_name_prefix);
apr_snprintf(signal_restart_name, sizeof(signal_restart_name),
"%s_restart", signal_name_prefix);
}
int volatile is_graceful = 0;
AP_DECLARE(int) ap_graceful_stop_signalled(void)
{
return is_graceful;
}
AP_DECLARE(void) ap_signal_parent(ap_signal_parent_e type)
{
HANDLE e;
char *signal_name;
if (parent_pid == my_pid) {
switch(type) {
case SIGNAL_PARENT_SHUTDOWN:
{
SetEvent(shutdown_event);
break;
}
/* This MPM supports only graceful restarts right now */
case SIGNAL_PARENT_RESTART:
case SIGNAL_PARENT_RESTART_GRACEFUL:
{
is_graceful = 1;
SetEvent(restart_event);
break;
}
}
return;
}
switch(type) {
case SIGNAL_PARENT_SHUTDOWN:
{
signal_name = signal_shutdown_name;
break;
}
/* This MPM supports only graceful restarts right now */
case SIGNAL_PARENT_RESTART:
case SIGNAL_PARENT_RESTART_GRACEFUL:
{
signal_name = signal_restart_name;
is_graceful = 1;
break;
}
default:
return;
}
e = OpenEvent(EVENT_MODIFY_STATE, FALSE, signal_name);
if (!e) {
/* Um, problem, can't signal the parent, which means we can't
* signal ourselves to die. Ignore for now...
*/
ap_log_error(APLOG_MARK, APLOG_EMERG, apr_get_os_error(), ap_server_conf,
"OpenEvent on %s event", signal_name);
return;
}
if (SetEvent(e) == 0) {
/* Same problem as above */
ap_log_error(APLOG_MARK, APLOG_EMERG, apr_get_os_error(), ap_server_conf,
"SetEvent on %s event", signal_name);
CloseHandle(e);
return;
}
CloseHandle(e);
}
/*
* Passed the following handles [in sync with send_handles_to_child()]
*
* ready event [signal the parent immediately, then close]
* exit event [save to poll later]
* start mutex [signal from the parent to begin accept()]
* scoreboard shm handle [to recreate the ap_scoreboard]
*/
void get_handles_from_parent(server_rec *s, HANDLE *child_exit_event,
apr_proc_mutex_t **child_start_mutex,
apr_shm_t **scoreboard_shm)
{
HANDLE hScore;
HANDLE ready_event;
HANDLE os_start;
DWORD BytesRead;
void *sb_shared;
apr_status_t rv;
/* *** We now do this was back in winnt_rewrite_args
* pipe = GetStdHandle(STD_INPUT_HANDLE);
*/
if (!ReadFile(pipe, &ready_event, sizeof(HANDLE),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -