You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Dean Gaudet <dg...@arctic.org> on 1998/02/09 09:12:38 UTC

75 requests in 20 syscalls

Believe it or not, this is a trace from a slightly modified 1.3 in which I
made 75 pipelined requests: 

accept(15, {sin_family=AF_INET, sin_port=htons(9565), sin_addr=inet_addr("127.0.0.1")}, [16]) = 3
getsockname(3, {sin_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("127.0.0.1")}, [16]) = 0
setsockopt(3, IPPROTO_TCP1, [1], 4)     = 0
read(3, "GET /icons/back.gif HTTP/1.1\nho"..., 4096) = 3544
writev(3, [{"HTTP/1.1 200 OK\r\nDate: Mon, 09"..., 2068}, {"GIF89a\3\1 \0\367\0\0\377\377\377"..., 2326}], 2) = 4394
writev(3, [{"HTTP/1.1 200 OK\r\nDate: Mon, 09"..., 3914}, {"GIF89a\24\0\26\0\302\0\0\377\377"..., 247}], 2) = 4161
write(3, "HTTP/1.1 200 OK\r\nDate: Mon, 09"..., 4096) = 4096
write(3, "st-Modified: Sat, 16 Aug 1997 23"..., 4096) = 4096
writev(3, [{"gth: 249\r\nAccept-Ranges: bytes"..., 1421}, {"GIF89a\340\1\f\2\204\0\0\377\377"..., 11977}], 2) = 13398
writev(3, [{"HTTP/1.1 200 OK\r\nDate: Mon, 09"..., 4089}, {"GIF89a\24\0\26\0\302\0\0\377\377"..., 237}], 2) = 4326
write(17, "127.0.0.1 - - [09/Feb/1998:00:00"..., 4038) = 4038
writev(3, [{"HTTP/1.1 200 OK\r\nDate: Mon, 09"..., 4073}, {"GIF89a\24\0\26\0\241\0\0\377\377"..., 185}], 2) = 4258
writev(3, [{"HTTP/1.1 200 OK\r\nDate: Mon, 09"..., 3883}, {"GIF89a\24\0\26\0\302\0\0\377\377"..., 248}], 2) = 4131
writev(3, [{"HTTP/1.1 200 OK\r\nDate: Mon, 09"..., 3965}, {"GIF89a\24\0\26\0\241\0\0\377\377"..., 164}], 2) = 4129
oldselect(4, [3], NULL, NULL, {0, 0})   = 1 (in [3], left {0, 0})
read(3, "rld2.gif HTTP/1.1\nhost: localho"..., 4096) = 35
oldselect(4, [3], NULL, NULL, {0, 0})   = 0 (Timeout)
write(3, "HTTP/1.1 200 OK\r\nDate: Mon, 09"..., 1854) = 1854
read(3, "", 4096)                       = 0
close(3)                                = 0

That's the *ENTIRE* sequence... an average of .27 syscalls per request.
Isn't it a beauty?

To do this you need:

- the patch that follows

- mod_mmap_static, all files mmap()d

- Configuration:
    EXTRA_CFLAGS=-DDYNAMIC_MODULE_LIMIT=0 -DSINGLE_LISTEN_UNSERIALIZED_ACCEPT -DBUFFERED_LOGS -DSHARED_TIME
    Rule STATUS=no

- my "usual" tweaks to Options FollowSymLinks and AllowOverride none

I can't seem to get usr1 restarts to work correctly with this though... I
think it's a linux kernel bug, but I can't reproduce it in a small test
program yet.  It'd be cool if someone can try this on freebsd/solaris...
after sending a USR1 to the parent if the children stick around then
you've got the same problem I'm running into.

The SHARED_TIME stuff is an obvious optimization to get rid of all the
time(0) calls.

The other part of this is a new usr1 handler for the child.  SIGUSR1 is
tricky because we need it to interrupt some syscalls at some points
in the code, we need it to be ignored at other points, and we need
it to kill off the child at other points.  It's easy to do all but
the selective interrupt.  To do the selective interrupt I actually
try to raise(SIGUSR2).  Now, the bug I'm seeing is that even though
my SIGUSR1 handler has SIGUSR2 blocked, the linux kernel is raising
SIGUSR2 immediately.  (An alternate solution is for all of the Apache
code to be EINTR safe, and I don't trust libc or many of the modules
to do this right.)

Dean

Index: include/http_main.h
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/include/http_main.h,v
retrieving revision 1.24
diff -u -r1.24 http_main.h
--- http_main.h	1998/01/21 19:17:38	1.24
+++ http_main.h	1998/02/09 08:03:19
@@ -104,6 +104,13 @@
 unsigned int set_callback_and_alarm(void (*fn) (int), int x);
 int check_alarm(void);
 
+enum usr1_state {
+    usr1_ignored,
+    usr1_interrupts,
+    usr1_just_die
+};
+API_EXPORT(void) set_usr1_state(enum usr1_state);
+
 #ifndef NO_OTHER_CHILD
 /*
  * register an other_child -- a child which the main loop keeps track of
Index: include/httpd.h
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/include/httpd.h,v
retrieving revision 1.183
diff -u -r1.183 httpd.h
--- httpd.h	1998/02/08 20:46:18	1.183
+++ httpd.h	1998/02/09 08:03:21
@@ -929,6 +929,14 @@
 #define OPTIMIZE_TIMEOUTS
 #endif
 
+#ifdef SHARED_TIME
+#ifndef OPTIMIZE_TIMEOUTS
+# error "you need OPTIMIZE_TIMEOUTS for SHARED_TIME"
+#endif
+extern time_t shared_time(time_t *);
+#define time(x) shared_time(x)
+#endif
+
 /* A set of flags which indicate places where the server should raise(SIGSTOP).
  * This is useful for debugging, because you can then attach to that process
  * with gdb and continue.  This is important in cases where one_process
Index: include/scoreboard.h
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/include/scoreboard.h,v
retrieving revision 1.36
diff -u -r1.36 scoreboard.h
--- scoreboard.h	1998/01/21 19:17:44	1.36
+++ scoreboard.h	1998/02/09 08:03:22
@@ -131,6 +131,9 @@
 typedef struct {
     int exit_generation;	/* Set by the main process if a graceful
 				   restart is required */
+#ifdef SHARED_TIME
+    time_t right_now;		/* the current time */
+#endif
 } global_score;
 
 /* stuff which the parent generally writes and the children rarely read */
Index: main/http_config.c
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/main/http_config.c,v
retrieving revision 1.96
diff -u -r1.96 http_config.c
--- http_config.c	1998/02/06 18:19:56	1.96
+++ http_config.c	1998/02/09 08:03:23
@@ -76,6 +76,7 @@
 #include "http_conf_globals.h"	/* Sigh... */
 #include "http_vhost.h"
 #include "explain.h"
+#include "http_main.h"
 
 DEF_Explain
 
@@ -1360,9 +1361,7 @@
 #ifdef SIGHUP
     signal(SIGHUP, SIG_IGN);
 #endif
-#ifdef SIGUSR1
-    signal(SIGUSR1, SIG_IGN);
-#endif
+    set_usr1_state(usr1_ignored);
 
     for (m = top_module; m; m = m->next)
 	if (m->child_exit)
Index: main/http_main.c
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/main/http_main.c,v
retrieving revision 1.286
diff -u -r1.286 http_main.c
--- http_main.c	1998/02/09 01:09:39	1.286
+++ http_main.c	1998/02/09 08:03:28
@@ -1052,6 +1052,20 @@
     timeout_name = NULL;
 }
 
+#ifdef SHARED_TIME
+#undef time
+time_t shared_time(time_t *t)
+{
+    time_t res;
+
+    if (child_timeouts) {
+	return time(0);
+    }
+    res = scoreboard_image->global.right_now;
+    if (t) *t = res;
+    return res;
+}
+#endif
 
 /*
  * More machine-dependent networking gooo... on some systems,
@@ -2124,15 +2138,35 @@
     }
 }
 
-static int volatile usr1_just_die = 1;
+static enum usr1_state usr1_cur_state;
 static int volatile deferred_die;
 
 static void usr1_handler(int sig)
 {
-    if (usr1_just_die) {
+    switch (usr1_cur_state) {
+    case usr1_ignored:
+	break;
+
+    case usr1_interrupts:
+	deferred_die = 1;
+	kill(my_pid, SIGUSR2);
+	break;
+
+    case usr1_just_die:
+	deferred_die = 1;
 	just_die(sig);
+	break;
     }
-    deferred_die = 1;
+}
+
+static void usr2_handler(int sig)
+{
+    /* we're only here to cause an EINTR */
+}
+
+API_EXPORT(void) set_usr1_state(enum usr1_state s)
+{
+    usr1_cur_state = s;
 }
 
 /* volatile just in case */
@@ -2970,6 +3004,19 @@
 #endif
     signal(SIGPIPE, timeout);
     signal(SIGALRM, alrm_handler);
+    usr1_cur_state = usr1_just_die;
+    {
+	struct sigaction sa;
+
+	sa.sa_handler = usr1_handler;
+	sa.sa_flags = SA_RESTART;
+	sigfillset(&sa.sa_mask);
+	if (sigaction(SIGUSR1, &sa, NULL) == -1) {
+	    aplog_error(APLOG_MARK, APLOG_WARNING, NULL,
+		"unable to sigaction(SIGUSR1)");
+	}
+    }
+    signal(SIGUSR2, usr2_handler);
 
 #ifdef __EMX__
 /* Stop Ctrl-C/Ctrl-Break signals going to child processes */
@@ -2987,8 +3034,7 @@
 	 * we can exit cleanly.  Since we're between connections right
 	 * now it's the right time to exit, but we might be blocked in a
 	 * system call when the graceful restart request is made. */
-	usr1_just_die = 1;
-	signal(SIGUSR1, usr1_handler);
+	usr1_cur_state = usr1_just_die;
 
 	/*
 	 * (Re)initialize this child to a pre-connection state.
@@ -3053,7 +3099,7 @@
 	     * defer the exit
 	     */
 	    deferred_die = 0;
-	    usr1_just_die = 0;
+	    usr1_cur_state = usr1_interrupts;
 	    for (;;) {
 		clen = sizeof(sa_client);
 		csd = accept(sd, &sa_client, &clen);
@@ -3081,7 +3127,7 @@
 	    }
 
 	    /* go around again, safe to die */
-	    usr1_just_die = 1;
+	    usr1_cur_state = usr1_just_die;
 	    if (deferred_die) {
 		/* ok maybe not, see ya later */
 		clean_child_exit(0);
@@ -3100,7 +3146,7 @@
 	/* We've got a socket, let's at least process one request off the
 	 * socket before we accept a graceful restart request.
 	 */
-	signal(SIGUSR1, SIG_IGN);
+	usr1_cur_state = usr1_ignored;
 
 	note_cleanups_for_fd(ptrans, csd);
 
@@ -3155,10 +3201,6 @@
 
 	while ((r = read_request(current_conn)) != NULL) {
 
-	    /* read_request_line has already done a
-	     * signal (SIGUSR1, SIG_IGN);
-	     */
-
 	    (void) update_child_status(my_child_num, SERVER_BUSY_WRITE, r);
 
 	    process_request(r);
@@ -3193,8 +3235,7 @@
 	     * connections to close before receiving a response because
 	     * of network latencies and server timeouts.
 	     */
-	    usr1_just_die = 1;
-	    signal(SIGUSR1, usr1_handler);
+	    usr1_cur_state = usr1_just_die;
 	}
 
 	/*
@@ -3333,6 +3374,10 @@
     idle_count = 0;
     last_non_dead = -1;
     total_non_dead = 0;
+
+#ifdef SHARED_TIME
+    scoreboard_image->global.right_now = now;
+#endif
 
     sync_scoreboard_image();
     for (i = 0; i < daemons_limit; ++i) {
Index: main/http_protocol.c
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/main/http_protocol.c,v
retrieving revision 1.188
diff -u -r1.188 http_protocol.c
--- http_protocol.c	1998/02/09 01:09:40	1.188
+++ http_protocol.c	1998/02/09 08:03:31
@@ -713,9 +713,7 @@
         }
     }
     /* we've probably got something to do, ignore graceful restart requests */
-#ifdef SIGUSR1
-    signal(SIGUSR1, SIG_IGN);
-#endif                          /* SIGUSR1 */
+    set_usr1_state(usr1_ignored);
     bsetflag(conn->client, B_SAFEREAD, 0);
     if (len == (HUGE_STRING_LEN - 1)) {
         aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,