The corresponding information is hard to find, but Mac OS seems to
*intentionally* provide these APIs (ulimit on the shell, resource.setrlimit in
Python) but ignore them. There is an article on this on lists.apple.com, which
is currently only available on the Wayback Machine:
https://web.archive.org/web/20150908044241/https://lists.apple.com/archives/unix-porting/2005/Jun/msg00115.html
In case this will no longer be available in the future, here is the most
relevant part:
======================================================================================
The setrlimit() administrative limits are not enforced except for certain ones
which are enforcible in BSD code. Those which would require enforcement in Mach
code (VM-based enforcement or scheduler- based enforcement) are not currently
enforced at all.
These administrative limits were never intended to save the system from fragile
code; the way to do that is to make the code less fragile. They historically
date back to the days when CPU time and memory usage were billable resources,
and process accounting and system accounting were used on old timeshare machines
that were purchased by groups, such as several University departments, because
there was no way for a single University department to be able to afford its own
PDP-8. Today, they are basically knobs that have to be there so old code can be
ported, but which don't actually have to do anything, for the most part.
The limits are considered administrative, as they are not intended to permit the
creation of security policy dependencies, nor are they intended to mitigate
problems related to application memory leaks.
The following is the current(0) MacOS X resource limit support table:
------------- --------- ---------
Limit name Standard? Enforced?
------------- --------- ---------
RLIMIT_CPU YES NO
RLIMIT_FSIZE YES YES(1)
RLIMIT_DATA YES NO
RLIMIT_STACK YES YES(1)
RLIMIT_CORE YES YES
RLIMIT_AS YES NO
RLIMIT_RSS NO(2) NO
RLIMIT_MEMLOCK NO NO
RLIMIT_NPROC NO YES(3)
RLIMIT_NOFILE YES YES
------------- --------- ---------
(0) This table is subject to change.
(1) Not entirely correctly enforced; specifically, boundary conditions are
clipped before or after the boundary, rather than exactly at it.
(2) RLIMIT_RSS is a MACOS X-specific alias for RLIMIT_AS.
(3) Whether this limit is the upper bounds depends on current sysctl settings on
the machine.
See also:
http://www.opengroup.org/onlinepubs/009695399/functions/ setrlimit.html
To determine whether or not a limit is enforced by an OS, rather than using the
return code of setrlimit(), you must first use getrlimit() and compare the
result to RLIM_INFINITY; if it's RLIM_INFINITY, then you should not set a limit
(you can, but you will confuse child processes which call getrlimit() themselves
to check for enforcement, if you do).
--
FWIW, the reason your test program bogs down the system is because it starts
swapping, so you end up limited to disk speed; the sample program you give is a
degenerate case.
If you actually *need* to kill a process when its resource usage gets to a
certain point, and you can only make minor modifications to the process, one way
to do this would be to start a watchdog thread that sleeps wakes up
periodically, and calls kill(getpid(), SIGTERM); if any of the fields from
getrusage(RUSAGE_SELF, &rusage); end up geting "too large".
If you need an external watchdog, you'll have to get creative with something like:
char cmd[256];
int rss;
FILE *fp;
int rss_max_allowable = XXX; /* some upper bound */
int pid_to_watch = YYY; /* pid that leaks memory */
...
for(;;) {
sleep(300); /* wake every 5 minutes */
...
sprintf(cmd, "ps -p %d -o rss | tail -1", pid_to_watch);
if ((fp = popen(cmd)) != NULL) {
if (fscanf(fp, "%d", &rss) == 1) {
if (rcss > rss_max_allowable) {
kill(pid_to_watch, SIGHUP); /* "reset" process; may use other signal or SIGKILL */
}
}
pclose(fp);
}
}
...
Obviously, you can use this with anything ps can find out about a process, or
any combination of things it can find out. Using the signal system this way lets
you be a lot more flexible than the process simply crashing when it hits the
limit. For example, maybe you could add a SIGHUP handler that reset the process
instead of killing it (this would work with e.g. bind or sendmail, which both
use dying children to do memory garbage collection), etc..
Hope that helps...
-- Terry
|