You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modperl-cvs@perl.apache.org by pg...@apache.org on 2007/03/26 16:54:41 UTC

svn commit: r522534 - /perl/modperl/branches/1.x/lib/Apache/SizeLimit.pm

Author: pgollucci
Date: Mon Mar 26 07:54:40 2007
New Revision: 522534

URL: http://svn.apache.org/viewvc?view=rev&rev=522534
Log:
copied unchanged from https://svn.apache.org/repos/asf/perl/Apache-SizeLimit/tags/0_9/lib/Apache/SizeLimit.pm

Modified:
    perl/modperl/branches/1.x/lib/Apache/SizeLimit.pm

Modified: perl/modperl/branches/1.x/lib/Apache/SizeLimit.pm
URL: http://svn.apache.org/viewvc/perl/modperl/branches/1.x/lib/Apache/SizeLimit.pm?view=diff&rev=522534&r1=522533&r2=522534
==============================================================================
--- perl/modperl/branches/1.x/lib/Apache/SizeLimit.pm (original)
+++ perl/modperl/branches/1.x/lib/Apache/SizeLimit.pm Mon Mar 26 07:54:40 2007
@@ -1,338 +1,688 @@
+# Copyright 2001-2006 The Apache Software Foundation or its licensors, as
+# applicable.
+#
+# Licensed 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.
+
 package Apache::SizeLimit;
 
-=head1 NAME
+use Apache::Constants qw(DECLINED OK);
+use Config;
+use strict;
+use vars qw(
+    $VERSION
+    $REQUEST_COUNT
+    $START_TIME
+    $USE_SMAPS
+);
 
-Apache::SizeLimit - Because size does matter.
+$VERSION = '0.9';
 
-=head1 SYNOPSIS
+__PACKAGE__->set_check_interval(1);
 
-This module allows you to kill off Apache httpd processes if they grow too
-large.  You can choose to set up the process size limiter to check the
-process size on every request:
-
-    # in your startup.pl:
-    use Apache::SizeLimit;
-    # sizes are in KB
-    $Apache::SizeLimit::MAX_PROCESS_SIZE  = 10000; # 10MB
-    $Apache::SizeLimit::MIN_SHARE_SIZE    = 1000;  # 1MB
-    $Apache::SizeLimit::MAX_UNSHARED_SIZE = 12000; # 12MB
+$REQUEST_COUNT          = 1;
+$USE_SMAPS              = 1;
 
-    # in your httpd.conf:
-    PerlCleanupHandler Apache::SizeLimit
+use constant IS_WIN32 => $Config{'osname'} eq 'MSWin32' ? 1 : 0;
 
-Or you can just check those requests that are likely to get big, such as
-CGI requests.  This way of checking is also easier for those who are mostly
-just running CGI.pm/Registry scripts:
-
-    # in your CGI:
-    use Apache::SizeLimit;
-    &Apache::SizeLimit::setmax(10000);	        # Max size in KB
-    &Apache::SizeLimit::setmin(1000);	        # Min share in KB
-    &Apache::SizeLimit::setmax_unshared(12000); # Max unshared size in KB
 
-Since checking the process size can take a few system calls on some
-platforms (e.g. linux), you may want to only check the process size every
-N times.  To do so, put this in your startup.pl or CGI:
+use vars qw( $MAX_PROCESS_SIZE );
+sub set_max_process_size {
+    my $class = shift;
 
-    $Apache::SizeLimit::CHECK_EVERY_N_REQUESTS = 2;
+    $MAX_PROCESS_SIZE = shift;
+}
 
-This will only check the process size every other time the process size
-checker is called.
+use vars qw( $MAX_UNSHARED_SIZE );
+sub set_max_unshared_size {
+    my $class = shift;
 
-=head1 DESCRIPTION
+    $MAX_UNSHARED_SIZE = shift;
+}
 
-This module is highly platform dependent, please read the CAVEATS section.
+use vars qw( $MIN_SHARE_SIZE );
+sub set_min_shared_size {
+    my $class = shift;
 
-This module was written in response to questions on the mod_perl mailing
-list on how to tell the httpd process to exit if it gets too big.
+    $MIN_SHARE_SIZE = shift;
+}
 
-Actually there are two big reasons your httpd children will grow.  First,
-it could have a bug that causes the process to increase in size
-dramatically, until your system starts swapping.  Second, your process just
-does stuff that requires a lot of memory, and the more different kinds of
-requests your server handles, the larger the httpd processes grow over
-time.
-
-This module will not really help you with the first problem.  For that you
-should probably look into Apache::Resource or some other means of setting a
-limit on the data size of your program.  BSD-ish systems have setrlimit()
-which will croak your memory gobbling processes.  However it is a little
-violent, terminating your process in mid-request.
-
-This module attempts to solve the second situation where your process
-slowly grows over time.  The idea is to check the memory usage after every
-request, and if it exceeds a threshold, exit gracefully.
-
-By using this module, you should be able to discontinue using the Apache
-configuration directive B<MaxRequestsPerChild>, although for some folks,
-using both in combination does the job.  Personally, I just use the
-technique shown in this module and set my MaxRequestsPerChild value to
-6000.
+use vars qw( $CHECK_EVERY_N_REQUESTS );
+sub set_check_interval {
+    my $class = shift;
 
-=head1 SHARED MEMORY OPTIONS
+    $CHECK_EVERY_N_REQUESTS = shift;
+}
 
-In addition to simply checking the total size of a process, this
-module can factor in how much of the memory used by the process is
-actually being shared by copy-on-write.  If you don't understand how
-memory is shared in this way, take a look at the mod_perl Guide at
-http://perl.apache.org/guide/.
+sub handler ($$) {
+    my $class = shift;
+    my $r = shift || Apache->request;
 
-You can take advantage of the shared memory information by setting a
-minimum shared size and/or a maximum unshared size.  Experience on one
-heavily trafficked mod_perl site showed that setting maximum unshared
-size and leaving the others unset is the most effective policy.  This
-is because it only kills off processes that are truly using too much
-physical RAM, allowing most processes to live longer and reducing the
-process churn rate.
+    return DECLINED unless $r->is_main();
 
-=head1 CAVEATS
+    # we want to operate in a cleanup handler
+    if ( $r->current_callback eq 'PerlCleanupHandler' ) {
+        return $class->_exit_if_too_big($r);
+    }
+    else {
+        $class->add_cleanup_handler($r);
+    }
 
-This module is platform dependent, since finding the size of a process
-is pretty different from OS to OS, and some platforms may not be
-supported.  In particular, the limits on minimum shared memory and
-maximum shared memory are currently only supported on Linux and BSD.
-If you can contribute support for another OS, please do.
+    return DECLINED;
+}
 
-Currently supported OSes:
+sub add_cleanup_handler {
+    my $class = shift;
+    my $r = shift || Apache->request;
 
-=over 4
+    return unless $r;
+    return if $r->pnotes('size_limit_cleanup');
 
-=item linux
+    # This used to use $r->post_connection but there's no good way to
+    # test it, since apparently it does not push a handler onto the
+    # PerlCleanupHandler phase. That means that there's no way to use
+    # $r->get_handlers() to check the results of calling this method.
+    $r->push_handlers( 'PerlCleanupHandler',
+                       sub { $class->_exit_if_too_big() } );
+    $r->pnotes( size_limit_cleanup => 1 );
+}
 
-For linux we read the process size out of /proc/self/status.  This is
-a little slow, but usually not too bad. If you are worried about
-performance, try only setting up the the exit handler inside CGIs
-(with the C<setmax> function), and see if the CHECK_EVERY_N_REQUESTS
-option is of benefit.
+sub _exit_if_too_big {
+    my $class = shift;
+    my $r = shift;
 
-=item solaris 2.6 and above
+    return DECLINED
+        if ( $CHECK_EVERY_N_REQUESTS
+             && ( $REQUEST_COUNT++ % $CHECK_EVERY_N_REQUESTS ) );
 
-For solaris we simply retrieve the size of /proc/self/as, which
-contains the address-space image of the process, and convert to KB.
-Shared memory calculations are not supported.
+    $START_TIME ||= time;
 
-NOTE: This is only known to work for solaris 2.6 and above. Evidently
-the /proc filesystem has changed between 2.5.1 and 2.6. Can anyone
-confirm or deny?
+    if ( $class->_limits_are_exceeded() ) {
+        my ( $size, $share, $unshared ) = $class->_check_size();
+
+        if ( IS_WIN32 || $class->_platform_getppid() > 1 ) {
+            # this is a child httpd
+            my $e   = time - $START_TIME;
+            my $msg = "httpd process too big, exiting at SIZE=$size KB";
+            $msg .= " SHARE=$share KB UNSHARED=$unshared" if ($share);
+            $msg .= " REQUESTS=$REQUEST_COUNT  LIFETIME=$e seconds";
+            $class->_error_log($msg);
+
+            if (IS_WIN32) {
+                # child_terminate() is disabled in win32 Apache
+                CORE::exit(-2);
+            }
+            else {
+                $r->child_terminate();
+            }
+        }
+        else {
+            # this is the main httpd, whose parent is init?
+            my $msg = "main process too big, SIZE=$size KB ";
+            $msg .= " SHARE=$share KB" if ($share);
+            $class->_error_log($msg);
+        }
+    }
+    return OK;
+}
 
-=item *bsd*
+# REVIEW - Why doesn't this use $r->warn or some other
+# Apache/Apache::Log API?
+sub _error_log {
+    my $class = shift;
 
-Uses BSD::Resource::getrusage() to determine process size.  This is pretty
-efficient (a lot more efficient than reading it from the /proc fs anyway).
+    print STDERR "[", scalar( localtime(time) ),
+        "] ($$) Apache::SizeLimit @_\n";
+}
 
-=item AIX?
+sub _limits_are_exceeded {
+    my $class = shift;
 
-Uses BSD::Resource::getrusage() to determine process size.  Not sure if the
-shared memory calculations will work or not.  AIX users?
+    my ( $size, $share, $unshared ) = $class->_check_size();
 
-=item Win32
+    return 1 if $MAX_PROCESS_SIZE  && $size > $MAX_PROCESS_SIZE;
 
-Uses Win32::API to access process memory information.  Win32::API can be 
-installed under ActiveState perl using the supplied ppm utility.
+    return 0 unless $share;
 
-=back
+    return 1 if $MIN_SHARE_SIZE    && $share < $MIN_SHARE_SIZE;
 
-If your platform is not supported, and if you can tell me how to check for
-the size of a process under your OS (in KB), then I will add it to the list.
-The more portable/efficient the solution, the better, of course.
+    return 1 if $MAX_UNSHARED_SIZE && $unshared > $MAX_UNSHARED_SIZE;
 
-=head1 TODO
+    return 0;
+}
 
-Possibly provide a perl make/install so that the SizeLimit.pm is created at
-build time with only the code you need on your platform.
+sub _check_size {
+    my ( $size, $share ) = _platform_check_size();
 
-If Apache was started in non-forking mode, should hitting the size limit
-cause the process to exit?
+    return ( $size, $share, $size - $share );
+}
 
-=cut
+sub _load {
+    my $mod  = shift;
 
-use Apache::Constants qw(:common);
-use Config;
-use strict;
-use vars qw($VERSION $HOW_BIG_IS_IT $MAX_PROCESS_SIZE
-	    $REQUEST_COUNT $CHECK_EVERY_N_REQUESTS
-	    $MIN_SHARE_SIZE $MAX_UNSHARED_SIZE $START_TIME $WIN32);
-
-$VERSION = '0.03';
-$CHECK_EVERY_N_REQUESTS = 1;
-$REQUEST_COUNT = 1;
-$MAX_PROCESS_SIZE  = 0;
-$MIN_SHARE_SIZE    = 0;
-$MAX_UNSHARED_SIZE = 0;
+    eval "require $mod"
+        or die "You must install $mod for Apache::SizeLimit to work on your platform.";
+}
 
 BEGIN {
-    $WIN32 = 0;
-    # decide at compile time how to check for a process' memory size.
-    if (($Config{'osname'} eq 'solaris') &&
-	 ($Config{'osvers'} >= 2.6)) {
-	$HOW_BIG_IS_IT = \&solaris_2_6_size_check;
-    } elsif ($Config{'osname'} eq 'linux') {
-	$HOW_BIG_IS_IT = \&linux_size_check;
-    } elsif ($Config{'osname'} =~ /(bsd|aix|darwin)/i) {
-	# will getrusage work on all BSDs?  I should hope so.
-	if (eval("require BSD::Resource;")) {
-	    $HOW_BIG_IS_IT = \&bsd_size_check;
-	} else {
-	    die "you must install BSD::Resource for Apache::SizeLimit to work on your platform.";
-	}
-    } elsif ($Config{'osname'} eq 'MSWin32') {
-        $WIN32 = 1;
-        if (eval("require Win32::API")) {
-            $HOW_BIG_IS_IT = \&win32_size_check;
-        } else {
-            die "you must install Win32::API for Apache::SizeLimit to work on your platform.";
+    if (   $Config{'osname'} eq 'solaris'
+        && $Config{'osvers'} >= 2.6 ) {
+        *_platform_check_size   = \&_solaris_2_6_size_check;
+        *_platform_getppid = \&_perl_getppid;
+    }
+    elsif ( $Config{'osname'} eq 'linux' ) {
+        _load('Linux::Pid');
+
+        *_platform_getppid = \&_linux_getppid;
+
+        if ( eval { require Linux::Smaps } && Linux::Smaps->new($$) ) {
+            *_platform_check_size = \&_linux_smaps_size_check;
+        }
+        else {
+            $USE_SMAPS = 0;
+            *_platform_check_size = \&_linux_size_check;
         }
-    } else {
-	die "Apache::SizeLimit not implemented on your platform.";
     }
+    elsif ( $Config{'osname'} =~ /(?:bsd|aix)/i ) {
+        # on OSX, getrusage() is returning 0 for proc & shared size.
+        _load('BSD::Resource');
+
+        *_platform_check_size   = \&_bsd_size_check;
+        *_platform_getppid = \&_perl_getppid;
+    }
+    elsif (IS_WIN32) {
+        _load('Win32::API');
+
+        *_platform_check_size   = \&_win32_size_check;
+        *_platform_getppid = \&_perl_getppid;
+    }
+    else {
+        die "Apache::SizeLimit is not implemented on your platform.";
+    }
+}
+
+sub _linux_smaps_size_check {
+    my $class = shift;
+
+    return $class->_linux_size_check() unless $USE_SMAPS;
+
+    my $s = Linux::Smaps->new($$)->all;
+    return ($s->size, $s->shared_clean + $s->shared_dirty);
 }
 
-# return process size (in KB)
-sub linux_size_check {
-    my ($size, $resident, $share) = (0,0,0);
-    local(*FH);
-    if (open(FH, "</proc/self/statm")) {
-	($size, $resident, $share) = split(/\s/, scalar <FH>);
-	close(FH);
-    } else {
-	&error_log("Fatal Error: couldn't access /proc/self/status");
+sub _linux_size_check {
+    my $class = shift;
+
+    my ( $size, $share ) = ( 0, 0 );
+
+    if ( open my $fh, '<', '/proc/self/statm' ) {
+        ( $size, $share ) = ( split /\s/, scalar <$fh> )[0,2];
+        close $fh;
     }
+    else {
+        $class->_error_log("Fatal Error: couldn't access /proc/self/status");
+    }
+
     # linux on intel x86 has 4KB page size...
-    return($size*4, $share*4);
+    return ( $size * 4, $share * 4 );
 }
 
-sub solaris_2_6_size_check {
-    my $size = -s "/proc/self/as" or
-	&error_log("Fatal Error: /proc/self/as doesn't exist or is empty");
-    $size = int($size/1024); # to get it into kb
-    return($size, 0); # return 0 for share, to avoid undef warnings
+sub _solaris_2_6_size_check {
+    my $class = shift;
+
+    my $size = -s "/proc/self/as"
+        or $class->_error_log("Fatal Error: /proc/self/as doesn't exist or is empty");
+    $size = int( $size / 1024 );
+
+    # return 0 for share, to avoid undef warnings
+    return ( $size, 0 );
 }
 
-sub bsd_size_check {
-    return (&BSD::Resource::getrusage())[2,3];
+# rss is in KB but ixrss is in BYTES.
+# This is true on at least FreeBSD, OpenBSD, & NetBSD - Phil Gollucci
+sub _bsd_size_check {
+    my @results = BSD::Resource::getrusage();
+    my $max_rss   = $results[2];
+    my $max_ixrss = int ( $results[3] / 1024 );
+
+    return ( $max_rss, $max_ixrss );
 }
 
-sub win32_size_check {
+sub _win32_size_check {
+    my $class = shift;
+
     # get handle on current process
-    my $GetCurrentProcess = new Win32::API('kernel32', 
-                                           'GetCurrentProcess', 
-                                           [], 
-                                           'I');
-    my $hProcess = $GetCurrentProcess->Call();
+    my $get_current_process = Win32::API->new(
+        'kernel32',
+        'get_current_process',
+        [],
+        'I'
+    );
+    my $proc = $get_current_process->Call();
 
-    
     # memory usage is bundled up in ProcessMemoryCounters structure
     # populated by GetProcessMemoryInfo() win32 call
-    my $DWORD = 'B32';  # 32 bits
-    my $SIZE_T = 'I';   # unsigned integer
+    my $DWORD  = 'B32';    # 32 bits
+    my $SIZE_T = 'I';      # unsigned integer
 
     # build a buffer structure to populate
     my $pmem_struct = "$DWORD" x 2 . "$SIZE_T" x 8;
-    my $pProcessMemoryCounters = pack($pmem_struct, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-    
+    my $mem_counters
+        = pack( $pmem_struct, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
+
     # GetProcessMemoryInfo is in "psapi.dll"
-    my $GetProcessMemoryInfo = new Win32::API('psapi', 
-                                              'GetProcessMemoryInfo', 
-                                              ['I', 'P', 'I'], 
-                                              'I');
-
-    my $bool = $GetProcessMemoryInfo->Call($hProcess, 
-                                           $pProcessMemoryCounters, 
-                                           length($pProcessMemoryCounters));
+    my $get_process_memory_info = new Win32::API(
+        'psapi',
+        'GetProcessMemoryInfo',
+        [ 'I', 'P', 'I' ],
+        'I'
+    );
+
+    my $bool = $get_process_memory_info->Call(
+        $proc,
+        $mem_counters,
+        length $mem_counters,
+    );
 
     # unpack ProcessMemoryCounters structure
-    my ($cb, 
-        $PageFaultCount, 
-        $PeakWorkingSetSize,
-        $WorkingSetSize,
-        $QuotaPeakPagedPoolUsage,
-        $QuotaPagedPoolUsage,
-        $QuotaPeakNonPagedPoolUsage,
-        $QuotaNonPagedPoolUsage,
-        $PagefileUsage,
-        $PeakPagefileUsage) = unpack($pmem_struct, $pProcessMemoryCounters);
+    my $peak_working_set_size =
+        ( unpack( $pmem_struct, $mem_counters ) )[2];
 
     # only care about peak working set size
-    my $size = int($PeakWorkingSetSize / 1024);
+    my $size = int( $peak_working_set_size / 1024 );
 
-    return ($size, 0);
+    return ( $size, 0 );
 }
 
+sub _perl_getppid { return getppid }
+sub _linux_getppid { return Linux::Pid::getppid() }
 
-sub exit_if_too_big {
-    my $r = shift;
-    return DECLINED if ($CHECK_EVERY_N_REQUESTS &&
-	($REQUEST_COUNT++ % $CHECK_EVERY_N_REQUESTS));
+{
+    # Deprecated APIs
 
-    $START_TIME ||= time;
+    sub setmax {
+        my $class = __PACKAGE__;
 
-    my($size, $share) = &$HOW_BIG_IS_IT();
+        $class->set_max_process_size(shift);
 
-    if (($MAX_PROCESS_SIZE && $size > $MAX_PROCESS_SIZE)
-			   ||
-	($MIN_SHARE_SIZE && $share < $MIN_SHARE_SIZE)
-			   ||
-	($MAX_UNSHARED_SIZE && ($size - $share) > $MAX_UNSHARED_SIZE)) {
-
-	    # wake up! time to die.
-	    if ($WIN32 || (getppid > 1)) {	# this is a child httpd
-		my $e = time - $START_TIME;
-		my $msg = "httpd process too big, exiting at SIZE=$size KB ";
-		$msg .= " SHARE=$share KB " if ($share);
-                $msg .= " REQUESTS=$REQUEST_COUNT  LIFETIME=$e seconds";
-		error_log($msg);
-
-		if ($WIN32) {
-		    CORE::exit(-2); # child_terminate() is disabled in win32 Apache
-		} else {
-		    $r->child_terminate();
-		}
-
-	    } else {	# this is the main httpd, whose parent is init?
-		my $msg = "main process too big, SIZE=$size KB ";
-		$msg .= " SHARE=$share KB" if ($share);
-		error_log($msg);
-	    }
+        $class->add_cleanup_handler();
     }
-    return OK;
-}
 
-# setmax can be called from within a CGI/Registry script to tell the httpd
-# to exit if the CGI causes the process to grow too big.
-sub setmax {
-    $MAX_PROCESS_SIZE = shift;
-    Apache->request->post_connection(\&exit_if_too_big);
-}
+    sub setmin {
+        my $class = __PACKAGE__;
 
-sub setmin {
-    $MIN_SHARE_SIZE = shift;
-    Apache->request->post_connection(\&exit_if_too_big);
-}
+        $class->set_min_shared_size(shift);
 
-sub setmax_unshared {
-    $MAX_UNSHARED_SIZE = shift;
-    Apache->request->post_connection(\&exit_if_too_big);
-}
+        $class->add_cleanup_handler();
+    }
 
-sub handler {
-    my $r = shift || Apache->request;
-    if ($r->is_main()) {
-        # we want to operate in a cleanup handler
-        if ($r->current_callback eq 'PerlCleanupHandler') {
-	    exit_if_too_big($r);
-        } else {
-	    $r->post_connection(\&exit_if_too_big);
-        }
+    sub setmax_unshared {
+        my $class = __PACKAGE__;
+
+        $class->set_max_unshared_size(shift);
+
+        $class->add_cleanup_handler();
     }
-    return(DECLINED);
 }
 
-sub error_log {
-    print STDERR "[", scalar(localtime(time)), "] ($$) Apache::SizeLimit @_\n";
-}
 
 1;
 
+
+__END__
+
+=head1 NAME
+
+Apache::SizeLimit - Because size does matter.
+
+=head1 SYNOPSIS
+
+    <Perl>
+     Apache::SizeLimit->set_max_process_size(150_000);   # Max size in KB
+     Apache::SizeLimit->set_min_shared_size(10_000);     # Min share in KB
+     Apache::SizeLimit->set_max_unshared_size(120_000);  # Max unshared size in KB
+    </Perl>
+
+    PerlCleanupHandler Apache::SizeLimit
+
+=head1 DESCRIPTION
+
+******************************** NOIICE *******************
+
+    This version is only for httpd 1.x and mod_perl 1.x 
+    series.
+
+    Future versions of this module may support both.
+
+    Currently, Apache2::SizeLimit is bundled with 
+    mod_perl 2.x for that series.
+    
+******************************** NOTICE *******************
+
+This module allows you to kill off Apache httpd processes if they grow
+too large. You can make the decision to kill a process based on its
+overall size, by setting a minimum limit on shared memory, or a
+maximum on unshared memory.
+
+You can set limits for each of these sizes, and if any limit is
+exceeded, the process will be killed.
+
+You can also limit the frequency that these sizes are checked so that
+this module only checks every N requests.
+
+This module is highly platform dependent, please read the
+L<PER-PLATFORM BEHAVIOR> section for details. It is possible that this
+module simply does not support your platform.
+
+=head1 API
+
+You can set set the size limits from a Perl module or script loaded by
+Apache by calling the appropriate class method on C<Apache::SizeLimit>:
+
+=over 4
+
+=item * Apache::SizeLimit->set_max_process_size($size)
+
+This sets the maximum size of the process, including both shared and
+unshared memory.
+
+=item * Apache::SizeLimit->set_max_unshared_size($size)
+
+This sets the maximum amount of I<unshared> memory the process can
+use.
+
+=item * Apache::SizeLimit->set_min_shared_size($size)
+
+This sets the minimum amount of shared memory the process must have.
+
+=back
+
+The two methods related to shared memory size are effectively a no-op
+if the module cannot determine the shared memory size for your
+platform. See L<PER-PLATFORM BEHAVIOR> for more details.
+
+=head2 Running the handler()
+
+There are several ways to make this module actually run the code to
+kill a process.
+
+The simplest is to make C<Apache::SizeLimit> a C<PerlCleanupHandler>
+in your Apache config:
+
+    PerlCleanupHandler Apache::SizeLimit
+
+This will ensure that C<< Apache::SizeLimit->handler() >> is run
+for all requests.
+
+If you want to combine this module with a cleanup handler of your own,
+make sure that C<Apache::SizeLimit> is the last handler run:
+
+    PerlCleanupHandler  Apache::SizeLimit My::CleanupHandler
+
+Remember, mod_perl will run stacked handlers from right to left, as
+they're defined in your configuration.
+
+If you have some cleanup code you need to run, but stacked handlers
+aren't appropriate for your setup, you can also explicitly call the
+C<< Apache::SizeLimit->handler() >> function from your own cleanup
+handler:
+
+    package My::CleanupHandler
+
+    sub handler {
+        my $r = shift;
+
+        # Causes File::Temp to remove any temp dirs created during the
+        # request
+        File::Temp::cleanup();
+
+        return Apache::SizeLimit->handler($r);
+    }
+
+=over 4
+
+=item * Apache::SizeLimit->add_cleanup_handler($r)
+
+You can call this method inside a request to run
+C<Apache::SizeLimit>'s C<handler()> method for just that request. It's
+safe to call this method repeatedly -- the cleanup will only be run
+once per request.
+
+=back
+
+=head2 Checking Every N Requests
+
+Since checking the process size can take a few system calls on some
+platforms (e.g. linux), you may not want to check the process size for
+every request.
+
+=over 4
+
+=item * Apache::SizeLimit->set_check_interval($interval)
+
+Calling this causes C<Apache::SizeLimit> to only check the process
+size every C<$interval> requests. If you want this to affect all
+processes, make sure to call this during server startup.
+
+=back
+
+=head1 SHARED MEMORY OPTIONS
+
+In addition to simply checking the total size of a process, this
+module can factor in how much of the memory used by the process is
+actually being shared by copy-on-write. If you don't understand how
+memory is shared in this way, take a look at the mod_perl docs at
+http://perl.apache.org/docs/.
+
+You can take advantage of the shared memory information by setting a
+minimum shared size and/or a maximum unshared size. Experience on one
+heavily trafficked mod_perl site showed that setting maximum unshared
+size and leaving the others unset is the most effective policy. This
+is because it only kills off processes that are truly using too much
+physical RAM, allowing most processes to live longer and reducing the
+process churn rate.
+
+=head1 PER-PLATFORM BEHAVIOR
+
+This module is highly platform dependent, since finding the size of a
+process is different for each OS, and some platforms may not be
+supported. In particular, the limits on minimum shared memory and
+maximum shared memory are currently only supported on Linux and BSD.
+If you can contribute support for another OS, patches are very
+welcome.
+
+Currently supported OSes:
+
+=head2 linux
+
+For linux we read the process size out of F</proc/self/statm>. If you
+are worried about performance, you can consider using C<<
+Apache::SizeLimit->set_check_interval() >> to reduce how often this
+read happens.
+
+As of linux 2.6, F</proc/self/statm> does not report the amount of
+memory shared by the copy-on-write mechanism as shared memory. This
+means that decisions made based on shared memory as reported by that
+interface are inherently wrong.
+
+However, as of the 2.6.14 release of the kernel, there is
+F</proc/self/smaps> entry for each process. F</proc/self/smaps>
+reports various sizes for each memory segment of a process and allows
+us to count the amount of shared memory correctly.
+
+If C<Apache::SizeLimit> detects a kernel that supports
+F</proc/self/smaps> and the C<Linux::Smaps> module is installed it
+will use that module instead of F</proc/self/statm>.
+
+Reading F</proc/self/smaps> is expensive compared to
+F</proc/self/statm>. It must look at each page table entry of a
+process.  Further, on multiprocessor systems the access is
+synchronized with spinlocks. Again, you might consider using C<<
+Apache::SizeLimit->set_check_interval() >>.
+
+=head3 Copy-on-write and Shared Memory
+
+The following example shows the effect of copy-on-write:
+
+  <Perl>
+    require Apache::SizeLimit;
+    package X;
+    use strict;
+    use Apache::Constants qw(OK);
+
+    my $x = "a" x (1024*1024);
+
+    sub handler {
+      my $r = shift;
+      my ($size, $shared) = $Apache::SizeLimit->_check_size();
+      $x =~ tr/a/b/;
+      my ($size2, $shared2) = $Apache::SizeLimit->_check_size();
+      $r->content_type('text/plain');
+      $r->print("1: size=$size shared=$shared\n");
+      $r->print("2: size=$size2 shared=$shared2\n");
+      return OK;
+    }
+  </Perl>
+
+  <Location /X>
+    SetHandler modperl
+    PerlResponseHandler X
+  </Location>
+
+The parent Apache process allocates memory for the string in
+C<$x>. The C<tr>-command then overwrites all "a" with "b" if the
+handler is called with an argument. This write is done in place, thus,
+the process size doesn't change. Only C<$x> is not shared anymore by
+means of copy-on-write between the parent and the child.
+
+If F</proc/self/smaps> is available curl shows:
+
+  r2@s93:~/work/mp2> curl http://localhost:8181/X
+  1: size=13452 shared=7456
+  2: size=13452 shared=6432
+
+Shared memory has lost 1024 kB. The process' overall size remains unchanged.
+
+Without F</proc/self/smaps> it says:
+
+  r2@s93:~/work/mp2> curl http://localhost:8181/X
+  1: size=13052 shared=3628
+  2: size=13052 shared=3636
+
+One can see the kernel lies about the shared memory. It simply doesn't
+count copy-on-write pages as shared.
+
+=head2 solaris 2.6 and above
+
+For solaris we simply retrieve the size of F</proc/self/as>, which
+contains the address-space image of the process, and convert to KB.
+Shared memory calculations are not supported.
+
+NOTE: This is only known to work for solaris 2.6 and above. Evidently
+the F</proc> filesystem has changed between 2.5.1 and 2.6. Can anyone
+confirm or deny?
+
+=head2 BSD (and OSX)
+
+Uses C<BSD::Resource::getrusage()> to determine process size.  This is
+pretty efficient (a lot more efficient than reading it from the
+F</proc> fs anyway).
+
+According to recent tests on OSX (July, 2006), C<BSD::Resource> simply
+reports zero for process and shared size on that platform, so OSX is
+not supported by C<Apache::SizeLimit>.
+
+=head2 AIX?
+
+Uses C<BSD::Resource::getrusage()> to determine process size.  Not
+sure if the shared memory calculations will work or not.  AIX users?
+
+=head2 Win32
+
+Uses C<Win32::API> to access process memory information.
+C<Win32::API> can be installed under ActiveState perl using the
+supplied ppm utility.
+
+=head2 Everything Else
+
+If your platform is not supported, then please send a patch to check
+the process size. The more portable/efficient/correct the solution the
+better, of course.
+
+=head1 ABOUT THIS MODULE
+
+This module was written in response to questions on the mod_perl
+mailing list on how to tell the httpd process to exit if it gets too
+big.
+
+Actually, there are two big reasons your httpd children will grow.
+First, your code could have a bug that causes the process to increase
+in size very quickly. Second, you could just be doing operations that
+require a lot of memory for each request. Since Perl does not give
+memory back to the system after using it, the process size can grow
+quite large.
+
+This module will not really help you with the first problem. For that
+you should probably look into C<Apache::Resource> or some other means
+of setting a limit on the data size of your program.  BSD-ish systems
+have C<setrlimit()>, which will kill your memory gobbling processes.
+However, it is a little violent, terminating your process in
+mid-request.
+
+This module attempts to solve the second situation, where your process
+slowly grows over time. It checks memory usage after every request,
+and if it exceeds a threshold, exits gracefully.
+
+By using this module, you should be able to discontinue using the
+Apache configuration directive B<MaxRequestsPerChild>, although for
+some folks, using both in combination does the job.
+
+=head1 DEPRECATED APIS
+
+Previous versions of this module documented three globals for defining
+memory size limits:
+
+=over 4
+
+=item * $Apache::SizeLimit::MAX_PROCESS_SIZE
+
+=item * $Apache::SizeLimit::MIN_SHARE_SIZE
+
+=item * $Apache::SizeLimit::MAX_UNSHARED_SIZE
+
+=item * $Apache::SizeLimit::CHECK_EVERY_N_REQUESTS
+
+=item * $Apache::SizeLimit::USE_SMAPS
+
+=back
+
+Direct use of these globals is deprecated, but will continue to work
+for the foreseeable future.
+
+It also documented three functions for use from registry scripts:
+
+=over 4
+
+=item * Apache::SizeLimit::setmax()
+
+=item * Apache::SizeLimit::setmin()
+
+=item * Apache::SizeLimit::setmax_unshared()
+
+=back
+
+Besides setting the appropriate limit, these functions I<also> add a
+cleanup handler to the current request.
+
 =head1 AUTHOR
 
 Doug Bagley <do...@bagley.org>, channeling Procrustes.
@@ -344,5 +694,8 @@
 
 Matt Phillips <mp...@virage.com> and Mohamed Hendawi
 <mh...@virage.com>: Win32 support
+
+Dave Rolsky <au...@urth.org>, maintenance and fixes outside of
+mod_perl tree (0.9+).
 
 =cut