dgaudet 97/08/18 00:19:39
Modified: htdocs/manual new_features_1_3.html src CHANGES PORTING src/core conf.h http_core.c http_protocol.c http_protocol.h Log: Added mmap() support for static files going through default_handler. Revision Changes Path 1.18 +1 -1 apachen/htdocs/manual/new_features_1_3.html Index: new_features_1_3.html =================================================================== RCS file: /export/home/cvs/apachen/htdocs/manual/new_features_1_3.html,v retrieving revision 1.17 retrieving revision 1.18 diff -u -r1.17 -r1.18 --- new_features_1_3.html 1997/08/18 07:17:19 1.17 +++ new_features_1_3.html 1997/08/18 07:19:30 1.18 @@ -193,7 +193,7 @@ to issue multiple writes with a single system call. They also avoid copying memory into buffers as much as possible. The result is less CPU time spent on transferring large files. - <li><i>XXX Incomplete:</i> Static requests are served using + <li>Static requests are served using <code>mmap</code>, which means bytes are only copied from the disk buffer to the network buffer directly by the kernel. The program never copies bytes around, which reduces CPU time. 1.404 +7 -0 apachen/src/CHANGES Index: CHANGES =================================================================== RCS file: /export/home/cvs/apachen/src/CHANGES,v retrieving revision 1.403 retrieving revision 1.404 diff -u -r1.403 -r1.404 --- CHANGES 1997/08/18 07:17:21 1.403 +++ CHANGES 1997/08/18 07:19:31 1.404 @@ -1,5 +1,12 @@ Changes with Apache 1.3a2 + *) Various architectures now define USE_MMAP_FILES which causes + the server to use mmap() for static files. There are two + compile-time tunables MMAP_THRESHOLD (minimum number of bytes + required to use mmap(), default is 0), and MMAP_SEGMENT_SIZE (maximum + number of bytes written in one cycle from a single mmap()d object, + default 32768). [Dean Gaudet] + *) API: Added post_read_request API phase which is run right after reading the request from a client, or right after an internal redirect. It is useful for modules setting environment variables that depend only on 1.10 +5 -1 apachen/src/PORTING Index: PORTING =================================================================== RCS file: /export/home/cvs/apachen/src/PORTING,v retrieving revision 1.9 retrieving revision 1.10 diff -u -r1.9 -r1.10 --- PORTING 1997/08/09 00:55:53 1.9 +++ PORTING 1997/08/18 07:19:32 1.10 @@ -171,7 +171,8 @@ HAVE_MMAP: Define if the OS supports the BSD mmap() call. This is used by various OSs to allow the scoreboard file to be held in shared mmapped-memory - instead of a real file. + instead of a real file. Note that this is only used to determine + if mmap should be used for shared memory. HAVE_SHMGET: Define if the OS has the SysV-based shmget() family of shared-memory @@ -213,6 +214,9 @@ USE_LONGJMP: use the longjmp() call instead of siglongjmp() (as well as setjmp() instead of sigsetjmp()) + + USE_MMAP_FILES: + Enable the use of mmap() for sending static files. -- 1.125 +12 -0 apachen/src/core/conf.h Index: conf.h =================================================================== RCS file: /export/home/cvs/apachen/src/core/conf.h,v retrieving revision 1.124 retrieving revision 1.125 diff -u -r1.124 -r1.125 --- conf.h 1997/08/17 11:14:34 1.124 +++ conf.h 1997/08/18 07:19:34 1.125 @@ -85,6 +85,7 @@ char *mktemp(char *template); #define JMP_BUF sigjmp_buf #define HAVE_MMAP +#define USE_MMAP_FILES #include <sys/time.h> #define NEED_STRERROR typedef int rlim_t; @@ -105,6 +106,7 @@ #define USE_PTHREAD_SERIALIZED_ACCEPT #define NEED_UNION_SEMUN #define HAVE_MMAP +#define USE_MMAP_FILES #define HAVE_CRYPT_H int gethostname(char *name, int namelen); @@ -118,6 +120,7 @@ /*#define USE_FCNTL_SERIALIZED_ACCEPT*/ #define USE_SYSVSEM_SERIALIZED_ACCEPT #define HAVE_SHMGET +#define USE_MMAP_FILES #define HAVE_CRYPT_H #define NO_LONG_DOUBLE #define HAVE_BSTRING_H @@ -158,6 +161,7 @@ #define JMP_BUF sigjmp_buf #ifndef __ps2__ #define HAVE_MMAP +#define USE_MMAP_FILES #define DEFAULT_GROUP "nobody" #endif #define DEFAULT_USER "nobody" @@ -183,6 +187,7 @@ #undef NO_SETSID #define JMP_BUF sigjmp_buf #define HAVE_MMAP +#define USE_MMAP_FILES #define HAVE_CRYPT_H #define NO_LONG_DOUBLE @@ -192,6 +197,7 @@ #undef NO_SETSID #define JMP_BUF sigjmp_buf #define HAVE_MMAP +#define USE_MMAP_FILES #define HAVE_CRYPT_H #define NO_LONG_DOUBLE typedef int rlim_t; @@ -271,6 +277,7 @@ #define NET_SIZE_T size_t #endif #define HAVE_SHMGET +#define USE_MMAP_FILES #define HAVE_SYS_RESOURCE_H typedef int rlim_t; /* flock is faster ... but hasn't been tested on 1.x systems */ @@ -302,6 +309,7 @@ #define HAVE_SYS_SELECT_H #define USE_FCNTL_SERIALIZED_ACCEPT #define HAVE_MMAP +#define USE_MMAP_FILES #define HAVE_SYS_RESOURCE_H #define SecureWare @@ -366,6 +374,7 @@ #define JMP_BUF sigjmp_buf #define HAVE_RESOURCE #define HAVE_MMAP +#define USE_MMAP_FILES #define HAVE_SHMGET #define HAVE_CRYPT_H #define HAVE_SYS_SELECT_H @@ -421,6 +430,7 @@ #undef NO_SETSID #define JMP_BUF sigjmp_buf #define HAVE_MMAP +#define USE_MMAP_FILES #define DEFAULT_USER "nobody" #define DEFAULT_GROUP "nogroup" #if defined(__bsdi__) || \ @@ -469,6 +479,7 @@ #define JMP_BUF sigjmp_buf #define USE_FCNTL_SERIALIZED_ACCEPT #define HAVE_MMAP +#define USE_MMAP_FILES #define HAVE_CRYPT_H #elif defined(__EMX__) @@ -510,6 +521,7 @@ #undef NO_SETSID #undef NEED_STRDUP #define HAVE_MMAP +#define USE_MMAP_FILES #define NO_TIMEZONE #include <stdio.h> 1.112 +104 -13 apachen/src/core/http_core.c Index: http_core.c =================================================================== RCS file: /export/home/cvs/apachen/src/core/http_core.c,v retrieving revision 1.111 retrieving revision 1.112 diff -u -r1.111 -r1.112 --- http_core.c 1997/08/17 20:21:36 1.111 +++ http_core.c 1997/08/18 07:19:34 1.112 @@ -64,6 +64,28 @@ #include "scoreboard.h" #include "fnmatch.h" +#ifdef USE_MMAP_FILES +#include <unistd.h> +#include <sys/mman.h> + +/* mmap support for static files based on ideas from John Heidemann's + * patch against 1.0.5. See + * <http://www.isi.edu/~johnh/SOFTWARE/APACHE/index.html>. + */ + +/* Files have to be at least this big before they're mmap()d. This is to + * deal with systems where the expense of doing an mmap() and an munmap() + * outweighs the benefit for small files. + */ +#ifndef MMAP_THRESHOLD +#ifdef SUNOS4 +#define MMAP_THRESHOLD (8*1024) +#else +#define MMAP_THRESHOLD 0 +#endif +#endif +#endif + /* Server core module... This module provides support for really basic * server operations, including options and commands which control the * operation of other modules. Consider this the bureaucracy module. @@ -1500,6 +1522,20 @@ int do_nothing (request_rec *r) { return OK; } +#ifdef USE_MMAP_FILES +struct mmap { + void *mm; + size_t length; +}; + +static void mmap_cleanup (void *mmv) +{ + struct mmap *mmd = mmv; + + munmap(mmd->mm, mmd->length); +} +#endif + /* * Default handler for MIME types without other handlers. Only GET * and OPTIONS at this point... anyone who wants to write a generic @@ -1514,6 +1550,9 @@ (core_dir_config *)get_module_config(r->per_dir_config, &core_module); int rangestatus, errstatus; FILE *f; +#ifdef USE_MMAP_FILES + caddr_t mm; +#endif /* This handler has no use for a request body (yet), but we still * need to read and discard it if the client sent one. @@ -1555,24 +1594,76 @@ || (errstatus = set_content_length (r, r->finfo.st_size))) return errstatus; - if (d->content_md5 & 1) { - table_set (r->headers_out, "Content-MD5", md5digest(r->pool, f)); +#ifdef USE_MMAP_FILES + block_alarms(); + if (r->finfo.st_size >= MMAP_THRESHOLD) { + /* we need to protect ourselves in case we die while we've got the + * file mmapped */ + mm = mmap (NULL, r->finfo.st_size, PROT_READ, MAP_PRIVATE, + fileno(f), 0); + } else { + mm = (caddr_t)-1; } - rangestatus = set_byterange(r); - send_http_header (r); - - if (!r->header_only) { - if (!rangestatus) - send_fd (f, r); - else { - long offset, length; - while (each_byterange(r, &offset, &length)) { - fseek(f, offset, SEEK_SET); - send_fd_length(f, r, length); + if (mm == (caddr_t)-1) { + unblock_alarms(); + + log_unixerr ("mmap_handler", r->filename, "mmap failed", r->server); +#endif + + if (d->content_md5 & 1) { + table_set (r->headers_out, "Content-MD5", md5digest(r->pool, f)); + } + + rangestatus = set_byterange(r); + send_http_header (r); + + if (!r->header_only) { + if (!rangestatus) + send_fd (f, r); + else { + long offset, length; + while (each_byterange(r, &offset, &length)) { + fseek(f, offset, SEEK_SET); + send_fd_length(f, r, length); + } + } + } + +#ifdef USE_MMAP_FILES + } else { + struct mmap *mmd; + + mmd = palloc (r->pool, sizeof (*mmd)); + mmd->mm = mm; + mmd->length = r->finfo.st_size; + register_cleanup (r->pool, (void *)mmd, mmap_cleanup, mmap_cleanup); + unblock_alarms(); + + if (d->content_md5 & 1) { + MD5_CTX context; + + MD5Init(&context); + MD5Update(&context, (void *)mm, r->finfo.st_size); + table_set (r->headers_out, "Content-MD5", + md5contextTo64(r->pool, &context)); + } + + rangestatus = set_byterange(r); + send_http_header (r); + + if (!r->header_only) { + if (!rangestatus) + send_mmap (mm, r, 0, r->finfo.st_size); + else { + long offset, length; + while (each_byterange(r, &offset, &length)) { + send_mmap(mm, r, offset, length); + } } } } +#endif pfclose(r->pool, f); return OK; 1.155 +62 -0 apachen/src/core/http_protocol.c Index: http_protocol.c =================================================================== RCS file: /export/home/cvs/apachen/src/core/http_protocol.c,v retrieving revision 1.154 retrieving revision 1.155 diff -u -r1.154 -r1.155 --- http_protocol.c 1997/08/18 07:17:26 1.154 +++ http_protocol.c 1997/08/18 07:19:36 1.155 @@ -1762,6 +1762,68 @@ return total_bytes_sent; } + + +/* The code writes MMAP_SEGMENT_SIZE bytes at a time. This is due to Apache's + * timeout model, which is a timeout per-write rather than a time for the + * entire transaction to complete. Essentially this should be small enough + * so that in one Timeout period, your slowest clients should be reasonably + * able to receive this many bytes. + */ +#ifndef MMAP_SEGMENT_SIZE +#define MMAP_SEGMENT_SIZE 32768 +#endif + +/* send data from an in-memory buffer */ +API_EXPORT(size_t) send_mmap(void * mm, request_rec *r, size_t offset, + size_t length) +{ + size_t total_bytes_sent = 0; + int n, w; + + if (length == 0) return 0; + + soft_timeout("send mmap", r); + + length += offset; + while (!r->connection->aborted && offset < length) { + if (length - offset > MMAP_SEGMENT_SIZE) { + n = MMAP_SEGMENT_SIZE; + } else { + n = length - offset; + } + + while (n && !r->connection->aborted) { + w = bwrite(r->connection->client, (char *)mm + offset, n); + if (w > 0) { + reset_timeout(r); /* reset timeout after successful write */ + total_bytes_sent += w; + n -= w; + offset += w; + } + else if (w < 0) { + if (r->connection->aborted) + break; + else if (errno == EAGAIN) + continue; + else { + log_unixerr("send mmap lost connection to", + get_remote_host(r->connection, + r->per_dir_config, REMOTE_NAME), + NULL, r->server); + bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + break; + } + } + } + } + + kill_timeout(r); + SET_BYTES_SENT(r); + return total_bytes_sent; +} + API_EXPORT(int) rputc (int c, request_rec *r) { if (r->connection->aborted) return EOF; 1.26 +3 -0 apachen/src/core/http_protocol.h Index: http_protocol.h =================================================================== RCS file: /export/home/cvs/apachen/src/core/http_protocol.h,v retrieving revision 1.25 retrieving revision 1.26 diff -u -r1.25 -r1.26 --- http_protocol.h 1997/07/24 04:23:59 1.25 +++ http_protocol.h 1997/08/18 07:19:36 1.26 @@ -113,6 +113,9 @@ API_EXPORT(long) send_fb(BUFF *f, request_rec *r); API_EXPORT(long) send_fb_length(BUFF *f, request_rec *r, long length); + +API_EXPORT(size_t) send_mmap(void *mm, request_rec *r, size_t offset, + size_t length); /* Hmmm... could macrofy these for now, and maybe forever, though the * definitions of the macros would get a whole lot hairier.