⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 prefork.c

📁 Apache V2.0.15 Alpha For Linuxhttpd-2_0_15-alpha.tar.Z
💻 C
📖 第 1 页 / 共 3 页
字号:
#ifdef SIGHUP    apr_signal(SIGHUP, restart);#endif /* SIGHUP */#ifdef SIGWINCH    apr_signal(SIGWINCH, restart);#endif /* SIGWINCH */#ifdef SIGPIPE    apr_signal(SIGPIPE, SIG_IGN);#endif /* SIGPIPE */#endif}/***************************************************************** * Child process main loop. * The following vars are static to avoid getting clobbered by longjmp(); * they are really private to child_main. */static int srv;static apr_socket_t *csd;static int requests_this_child;static fd_set main_fds;int ap_graceful_stop_signalled(void){    ap_sync_scoreboard_image();    if (deferred_die ||	ap_scoreboard_image->global.running_generation != ap_my_generation) {	return 1;    }    return 0;}static void child_main(int child_num_arg){    ap_listen_rec *lr;    ap_listen_rec *last_lr;    ap_listen_rec *first_lr;    apr_pool_t *ptrans;    conn_rec *current_conn;    apr_status_t stat = APR_EINIT;    int sockdes;    ap_my_pid = getpid();    csd = NULL;    my_child_num = child_num_arg;    requests_this_child = 0;    last_lr = NULL;    /* Get a sub context for global allocations in this child, so that     * we can have cleanups occur when the child exits.     */    apr_pool_create(&pchild, pconf);    apr_pool_create(&ptrans, pchild);    /* needs to be done before we switch UIDs so we have permissions */    reopen_scoreboard(pchild);    SAFE_ACCEPT(accept_mutex_child_init(pchild));    if (unixd_setup_child()) {	clean_child_exit(APEXIT_CHILDFATAL);    }    ap_run_child_init(pchild, ap_server_conf);    (void) ap_update_child_status(AP_CHILD_THREAD_FROM_ID(my_child_num), SERVER_READY, (request_rec *) NULL);    apr_signal(SIGHUP, just_die);    apr_signal(SIGTERM, just_die);    while (!ap_graceful_stop_signalled()) {	/* Prepare to receive a SIGWINCH due to graceful restart so that	 * we can exit cleanly.	 */	usr1_just_die = 1;	apr_signal(SIGWINCH, usr1_handler);	/*	 * (Re)initialize this child to a pre-connection state.	 */	current_conn = NULL;	apr_pool_clear(ptrans);	if ((ap_max_requests_per_child > 0	     && requests_this_child++ >= ap_max_requests_per_child)) {	    clean_child_exit(0);	}	(void) ap_update_child_status(AP_CHILD_THREAD_FROM_ID(my_child_num), SERVER_READY, (request_rec *) NULL);	/*	 * Wait for an acceptable connection to arrive.	 */	/* Lock around "accept", if necessary */	SAFE_ACCEPT(accept_mutex_on());	for (;;) {	    if (ap_listeners->next) {		/* more than one socket */		memcpy(&main_fds, &listenfds, sizeof(fd_set));		srv = select(listenmaxfd + 1, &main_fds, NULL, NULL, NULL);		if (srv < 0 && errno != EINTR) {		    /* Single Unix documents select as returning errnos		     * EBADF, EINTR, and EINVAL... and in none of those		     * cases does it make sense to continue.  In fact		     * on Linux 2.0.x we seem to end up with EFAULT		     * occasionally, and we'd loop forever due to it.		     */		    ap_log_error(APLOG_MARK, APLOG_ERR, errno, ap_server_conf, "select: (listen)");		    clean_child_exit(1);		}		if (srv <= 0)		    continue;		/* we remember the last_lr we searched last time around so that		   we don't end up starving any particular listening socket */		if (last_lr == NULL) {		    lr = ap_listeners;		}		else {		    lr = last_lr->next;		    if (!lr)			lr = ap_listeners;		}		first_lr=lr;		do {                    apr_os_sock_get(&sockdes, lr->sd);		    if (FD_ISSET(sockdes, &main_fds))			goto got_listener;		    lr = lr->next;		    if (!lr)			lr = ap_listeners;		}		while (lr != first_lr);		/* FIXME: if we get here, something bad has happened, and we're		   probably gonna spin forever.		*/		continue;	got_listener:		last_lr = lr;		sd = lr->sd;	    }	    else {		/* only one socket, just pretend we did the other stuff */		sd = ap_listeners->sd;	    }	    /* if we accept() something we don't want to die, so we have to	     * defer the exit	     */	    usr1_just_die = 0;	    for (;;) {		if (deferred_die) {		    /* we didn't get a socket, and we were told to die */		    clean_child_exit(0);		}		stat = apr_accept(&csd, sd, ptrans);		if (stat == APR_SUCCESS || stat != APR_EINTR)		    break;	    }	    if (stat == APR_SUCCESS)		break;		/* We have a socket ready for reading */	    else {/* TODO: this accept result handling stuff should be abstracted... * it's already out of date between the various unix mpms */		/* Our old behaviour here was to continue after accept()		 * errors.  But this leads us into lots of troubles		 * because most of the errors are quite fatal.  For		 * example, EMFILE can be caused by slow descriptor		 * leaks (say in a 3rd party module, or libc).  It's		 * foolish for us to continue after an EMFILE.  We also		 * seem to tickle kernel bugs on some platforms which		 * lead to never-ending loops here.  So it seems best		 * to just exit in most cases.		 */                switch (stat) {#ifdef EPROTO		    /* EPROTO on certain older kernels really means		     * ECONNABORTED, so we need to ignore it for them.		     * See discussion in new-httpd archives nh.9701		     * search for EPROTO.		     *		     * Also see nh.9603, search for EPROTO:		     * There is potentially a bug in Solaris 2.x x<6,		     * and other boxes that implement tcp sockets in		     * userland (i.e. on top of STREAMS).  On these		     * systems, EPROTO can actually result in a fatal		     * loop.  See PR#981 for example.  It's hard to		     * handle both uses of EPROTO.		     */                case EPROTO:#endif#ifdef ECONNABORTED                case ECONNABORTED:#endif		    /* Linux generates the rest of these, other tcp		     * stacks (i.e. bsd) tend to hide them behind		     * getsockopt() interfaces.  They occur when		     * the net goes sour or the client disconnects		     * after the three-way handshake has been done		     * in the kernel but before userland has picked		     * up the socket.		     */#ifdef ECONNRESET                case ECONNRESET:#endif#ifdef ETIMEDOUT                case ETIMEDOUT:#endif#ifdef EHOSTUNREACH		case EHOSTUNREACH:#endif#ifdef ENETUNREACH		case ENETUNREACH:#endif                    break;#ifdef ENETDOWN		case ENETDOWN:		     /*		      * When the network layer has been shut down, there		      * is not much use in simply exiting: the parent		      * would simply re-create us (and we'd fail again).		      * Use the CHILDFATAL code to tear the server down.		      * @@@ Martin's idea for possible improvement:		      * A different approach would be to define		      * a new APEXIT_NETDOWN exit code, the reception		      * of which would make the parent shutdown all		      * children, then idle-loop until it detected that		      * the network is up again, and restart the children.		      * Ben Hyde noted that temporary ENETDOWN situations		      * occur in mobile IP.		      */		    ap_log_error(APLOG_MARK, APLOG_EMERG, stat, ap_server_conf,			"apr_accept: giving up.");		    clean_child_exit(APEXIT_CHILDFATAL);#endif /*ENETDOWN*/#ifdef TPF		case EINACT:		    ap_log_error(APLOG_MARK, APLOG_EMERG, stat, ap_server_conf,			"offload device inactive");		    clean_child_exit(APEXIT_CHILDFATAL);		    break;		default:		    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, ap_server_conf,			"select/accept error (%u)", stat);		    clean_child_exit(APEXIT_CHILDFATAL);#else		default:		    ap_log_error(APLOG_MARK, APLOG_ERR, stat, ap_server_conf,				"apr_accept: (client socket)");		    clean_child_exit(1);#endif		}	    }	    if (ap_graceful_stop_signalled()) {		clean_child_exit(0);	    }	    usr1_just_die = 1;	}	SAFE_ACCEPT(accept_mutex_off());	/* unlock after "accept" */	/* We've got a socket, let's at least process one request off the	 * socket before we accept a graceful restart request.  We set	 * the signal to ignore because we don't want to disturb any	 * third party code.	 */	apr_signal(SIGWINCH, SIG_IGN);	/*	 * We now have a connection, so set it up with the appropriate	 * socket options, file descriptors, and read/write buffers.	 */        apr_os_sock_get(&sockdes, csd);        if (sockdes >= FD_SETSIZE) {            ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, NULL,                         "new file descriptor %d is too large; you probably need "                         "to rebuild Apache with a larger FD_SETSIZE "                         "(currently %d)",                          sockdes, FD_SETSIZE);	    apr_socket_close(csd);	    continue;        }#ifdef TPF	if (sockdes == 0)                   /* 0 is invalid socket for TPF */	    continue;#endif	ap_sock_disable_nagle(csd);	current_conn = ap_new_connection(ptrans, ap_server_conf, csd,                                          my_child_num);        if (current_conn) {            ap_process_connection(current_conn);            ap_lingering_close(current_conn);        }    }    clean_child_exit(0);}static int make_child(server_rec *s, int slot){    int pid;    if (slot + 1 > ap_max_daemons_limit) {	ap_max_daemons_limit = slot + 1;    }    if (one_process) {	apr_signal(SIGHUP, just_die);	apr_signal(SIGINT, just_die);#ifdef SIGQUIT	apr_signal(SIGQUIT, SIG_DFL);#endif	apr_signal(SIGTERM, just_die);	child_main(slot);    }    (void) ap_update_child_status(AP_CHILD_THREAD_FROM_ID(slot), SERVER_STARTING, (request_rec *) NULL);#ifdef _OSD_POSIX    /* BS2000 requires a "special" version of fork() before a setuid() call */    if ((pid = os_fork(unixd_config.user_name)) == -1) {#elif defined(TPF)    if ((pid = os_fork(s, slot)) == -1) {#else    if ((pid = fork()) == -1) {#endif	ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, "fork: Unable to fork new process");	/* fork didn't succeed. Fix the scoreboard or else	 * it will say SERVER_STARTING forever and ever	 */	(void) ap_update_child_status(AP_CHILD_THREAD_FROM_ID(slot), SERVER_DEAD, (request_rec *) NULL);	/* In case system resources are maxxed out, we don't want	   Apache running away with the CPU trying to fork over and	   over and over again. */	sleep(10);	return -1;    }    if (!pid) {#ifdef AIX_BIND_PROCESSOR/* by default AIX binds to a single processor * this bit unbinds children which will then bind to another cpu */#include <sys/processor.h>	int status = bindprocessor(BINDPROCESS, (int)getpid(), 				   PROCESSOR_CLASS_ANY);	if (status != OK) {	    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, ap_server_conf,			"processor unbind failed %d", status);	}#endif	RAISE_SIGSTOP(MAKE_CHILD);	/* Disable the restart signal handlers and enable the just_die stuff.	 * Note that since restart() just notes that a restart has been	 * requested there's no race condition here.	 */	apr_signal(SIGHUP, just_die);	apr_signal(SIGWINCH, just_die);	apr_signal(SIGTERM, just_die);	child_main(slot);    }    ap_scoreboard_image->parent[slot].pid = pid;#ifdef SCOREBOARD_FILE    lseek(scoreboard_fd, XtOffsetOf(scoreboard, parent[slot]), 0);    force_write(scoreboard_fd, &ap_scoreboard_image->parent[slot],		sizeof(parent_score));#endif    return 0;}/* start up a bunch of children */static void startup_children(int number_to_start){    int i;    for (i = 0; number_to_start && i < ap_daemons_limit; ++i) {	if (ap_scoreboard_image->servers[i][0].status != SERVER_DEAD) {	    continue;	}	if (make_child(ap_server_conf, i) < 0) {	    break;	}	--number_to_start;    }}/* * idle_spawn_rate is the number of children that will be spawned on the * next maintenance cycle if there aren't enough idle servers.  It is * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by * without the need to spawn. */static int idle_spawn_rate = 1;#ifndef MAX_SPAWN_RATE#define MAX_SPAWN_RATE	(32)#endifstatic int hold_off_on_exponential_spawning;static void perform_idle_server_maintenance(void){    int i;    int to_kill;    int idle_count;    short_score *ss;    int free_length;    int free_slots[MAX_SPAWN_RATE];    int last_non_dead;    int total_non_dead;    /* initialize the free_list */    free_length = 0;    to_kill = -1;    idle_count = 0;    last_non_dead = -1;    total_non_dead = 0;    ap_sync_scoreboard_image();    for (i = 0; i < ap_daemons_limit; ++i) {	int status;	if (i >= ap_max_daemons_limit && free_length == idle_spawn_rate)	    break;	ss = &ap_scoreboard_image->servers[i][0];	status = ss->status;	if (status == SERVER_DEAD) {	    /* try to keep children numbers as low as possible */	    if (free_length < idle_spawn_rate) {		free_slots[free_length] = i;		++free_length;	    }	}	else {	    /* We consider a starting server as idle because we started it	     * at least a cycle ago, and if it still hasn't finished starting	     * then we're just going to swamp things worse by forking more.	     * So we hopefully won't need to fork more if we count it.	     * This depends on the ordering of SERVER_READY and SERVER_STARTING.	     */	    if (status <= SERVER_READY) {		++ idle_count;		/* always kill the highest numbered child if we have to...		 * no really well thought out reason ... other than observing		 * the server behaviour under linux where lower numbered children		 * tend to service more hits (and hence are more likely to have		 * their data in cpu caches).		 */		to_kill = i;	    }	    ++total_non_dead;	    last_non_dead = i;	}    }    ap_max_daemons_limit = last_non_dead + 1;    if (idle_count > ap_daemons_max_free) {	/* kill off one child... we use SIGWINCH because that'll cause it to	 * shut down gracefully, in case it happened to pick up a request	 * while we were counting	 */	kill(ap_scoreboard_image->parent[to_kill].pid, SIGWINCH);	idle_spawn_rate = 1;    }    else if (idle_count < ap_daemons_min_free) {	/* terminate the free list */	if (free_length == 0) {	    /* only report this condition once */	    static int reported = 0;	    if (!reported) {		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, ap_server_conf,			    "server reached MaxClients setting, consider"

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -