Received: (from majordom@localhost) by hyperreal.org (8.8.5/8.8.5) id TAA00344; Sat, 16 Aug 1997 19:49:30 -0700 (PDT) Received: from twinlark.arctic.org (twinlark.arctic.org [204.62.130.91]) by hyperreal.org (8.8.5/8.8.5) with SMTP id TAA00335 for ; Sat, 16 Aug 1997 19:49:27 -0700 (PDT) Received: (qmail 72 invoked from network); 17 Aug 1997 02:46:28 -0000 Received: from benchlark.arctic.org (206.221.201.235) by twinlark.arctic.org with SMTP; 17 Aug 1997 02:46:28 -0000 Date: Sat, 16 Aug 1997 19:49:44 -0700 (PDT) From: Dean Gaudet To: new-httpd@apache.org Subject: [PATCH] USE_MMAP_FILES Message-ID: MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Sender: new-httpd-owner@apache.org Precedence: bulk Reply-To: new-httpd@apache.org Here is mmap integrated into the core. Pretty much everything that defined HAVE_MMAP I also defined USE_MMAP_FILES. I also added USE_MMAP_FILES for Linux 2.x and IRIX. Tested on Linux 2.0.30 and IRIX 5.3. Dean Index: htdocs/manual/new_features_1_3.html =================================================================== RCS file: /export/home/cvs/apachen/htdocs/manual/new_features_1_3.html,v retrieving revision 1.14 diff -u -r1.14 new_features_1_3.html --- new_features_1_3.html 1997/08/11 10:04:43 1.14 +++ new_features_1_3.html 1997/08/17 02:34:58 @@ -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. -
  • XXX Incomplete: Static requests are served using +
  • Static requests are served using mmap, 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. Index: src/CHANGES =================================================================== RCS file: /export/home/cvs/apachen/src/CHANGES,v retrieving revision 1.398 diff -u -r1.398 CHANGES --- CHANGES 1997/08/17 02:31:47 1.398 +++ CHANGES 1997/08/17 02:35:08 @@ -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] + *) Configure changes: TestLib replaced by TestCompile, which has some additional capability (such as doing a sanity check of the compiler and flags selected); the version of Solaris is now Index: src/PORTING =================================================================== RCS file: /export/home/cvs/apachen/src/PORTING,v retrieving revision 1.9 diff -u -r1.9 PORTING --- PORTING 1997/08/09 00:55:53 1.9 +++ PORTING 1997/08/17 02:35:08 @@ -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. -- Index: src/core/conf.h =================================================================== RCS file: /export/home/cvs/apachen/src/core/conf.h,v retrieving revision 1.123 diff -u -r1.123 conf.h --- conf.h 1997/08/15 18:12:09 1.123 +++ conf.h 1997/08/17 02:35:10 @@ -85,6 +85,7 @@ char *mktemp(char *template); #define JMP_BUF sigjmp_buf #define HAVE_MMAP +#define USE_MMAP_FILES #include #define NEED_STRERROR typedef int rlim_t; @@ -102,6 +103,7 @@ #define JMP_BUF sigjmp_buf #define USE_FCNTL_SERIALIZED_ACCEPT #define HAVE_MMAP +#define USE_MMAP_FILES #define HAVE_CRYPT_H int gethostname(char *name, int namelen); @@ -114,6 +116,7 @@ #define JMP_BUF sigjmp_buf #define USE_FCNTL_SERIALIZED_ACCEPT #define HAVE_SHMGET +#define USE_MMAP_FILES #define HAVE_CRYPT_H #define NO_LONG_DOUBLE #define HAVE_BSTRING_H @@ -154,6 +157,7 @@ #define JMP_BUF sigjmp_buf #ifndef __ps2__ #define HAVE_MMAP +#define USE_MMAP_FILES #define DEFAULT_GROUP "nobody" #endif #define DEFAULT_USER "nobody" @@ -179,6 +183,7 @@ #undef NO_SETSID #define JMP_BUF sigjmp_buf #define HAVE_MMAP +#define USE_MMAP_FILES #define HAVE_CRYPT_H #define NO_LONG_DOUBLE @@ -188,6 +193,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; @@ -267,6 +273,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 */ @@ -298,6 +305,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 @@ -362,6 +370,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 @@ -417,6 +426,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__) || \ @@ -465,6 +475,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__) @@ -506,6 +517,7 @@ #undef NO_SETSID #undef NEED_STRDUP #define HAVE_MMAP +#define USE_MMAP_FILES #define NO_TIMEZONE #include Index: src/core/http_core.c =================================================================== RCS file: /export/home/cvs/apachen/src/core/http_core.c,v retrieving revision 1.109 diff -u -r1.109 http_core.c --- http_core.c 1997/08/06 20:21:24 1.109 +++ http_core.c 1997/08/17 02:35:15 @@ -64,6 +64,28 @@ #include "scoreboard.h" #include "fnmatch.h" +#ifdef USE_MMAP_FILES +#include +#include + +/* mmap support for static files based on ideas from John Heidemann's + * patch against 1.0.5. See + * . + */ + +/* 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. @@ -1484,6 +1506,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 @@ -1498,6 +1534,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. @@ -1539,24 +1578,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; Index: src/core/http_protocol.c =================================================================== RCS file: /export/home/cvs/apachen/src/core/http_protocol.c,v retrieving revision 1.152 diff -u -r1.152 http_protocol.c --- http_protocol.c 1997/08/15 17:59:50 1.152 +++ http_protocol.c 1997/08/17 02:35:20 @@ -1754,6 +1754,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; Index: src/core/http_protocol.h =================================================================== RCS file: /export/home/cvs/apachen/src/core/http_protocol.h,v retrieving revision 1.25 diff -u -r1.25 http_protocol.h --- http_protocol.h 1997/07/24 04:23:59 1.25 +++ http_protocol.h 1997/08/17 02:35:21 @@ -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.