Wired Memory Utilities
Wired Memory Utilities
- Subject: Wired Memory Utilities
- From: Herbie Robinson <email@hidden>
- Date: Mon, 11 Mar 2002 02:44:32 -0500
I spent the evening researching the system calls on OS X for wiring
memory and found three facilities:
vm_wire
mlock/munlock
task_wire
vm_wire and mlock have already been mentioned in the archives of this
list. I found task_wire in headers.
vm_wire is a MACH kernel primitive. If can be called from a user
program, but only works if you are root. My take on it is that this
would be a very bad call to use, because it is a primitive that
doesn't stop you from running the system out of memory (i.e.,
crashing it). It also only works if you are root.
mlock/munlock are POSIX memory mapping calls that do about the same
thing. They end up call vm_wire, but they do some sanity checks
first. One of the sanity checks in mlock appears to limit the total
amount of memory that can be wired in the system. If returns EAGAIN
if this is hit. That would imply that one should sleep a bit and try
again for a least a bit when one gets that error. If has another
check that appears to be checking some sort of per process limit on
allocated wired memory: That check returns ENOMEM.
There are actually some ifdefs controlling the limit checks in mlock.
Hopefully they are being compiled in. One of the variants requires
that the caller be superuser (root) and it works from user mode; so,
that check is definitely compiled out. Hopefully, the OS guys have
the correct build flags.
At any rate, mlock and munlock to appear to be the best choice for
wiring memory and they appear to work. Oh yeah, there is a bug in
mlock (it calls vm_wire with the wrong protection flags). There is
an avoidance in the sample code below.
The other interesting one I found was task_wire. This MACH routine
claims to operate a switch that makes all memory allocated by the
MACH task (essentially the process) wired. I don't know if it
actually does anything, but it doesn't return any error codes if you
do it in user mode. Assuming it works, this would be a good thing to
do before loading any plug-ins that should be wired and before
starting any threads that should be wired (threads would want to run
up a little stack to make sure that gets nailed down before turning
the flag off again). There doesn't appear to be any way to unwire
the memory this allocates (other than process termination); so, it
wouldn't be a good thing to allocate large buffers with (presumably
we should be good citizens and unwire large buffers when real time
operations are suspended).
****************************************************************************************************
Sample code that does all the above calls follows.
****************************************************************************************************
/*
* testmain.cpp
* t2server
*
* Created by Herbie Robinson on Sun Mar 10 2002.
* Copyright (c) 2001 Herbert W. Robinson.
* Permission granted to copy freely.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include <mach/vm_map.h>
#include <mach/mach_traps.h>
int trick_optimizer = 0;
#define framesizer 1000
typedef struct
{
char x[framesizer];
} filler_t;
filler_t g_filler;
static void
use_some_stack( int num_bytes )
{
filler_t filler;
num_bytes -= framesizer;
if (num_bytes <= 0)
return;
filler = g_filler;
trick_optimizer += 5;
if (trick_optimizer >= framesizer)
trick_optimizer = 0;
filler.x[trick_optimizer] = num_bytes;
g_filler = filler;
use_some_stack(num_bytes);
}
int
main(int argc, char *argv[])
{
kern_return_t code;
vm_map_t me = mach_task_self();
host_priv_t my_host = mach_host_self();
char *p = NULL;
vm_size_t ps;
natural_t numProcessors;
processor_info_array_t pit;
mach_msg_type_number_t infoCnt;
// vm_statistics_data_t stats;
int err;
long int pgsize = getpagesize();
printf("Page size %x.\n", vm_page_size);
printf("Page size from getpagesize() = %lx.\n", pgsize);
printf("Task Port = %x.\n", me);
printf("Host Port = %x.\n", my_host);
code = host_page_size(my_host, &ps);
if (code == KERN_INVALID_HOST)
printf("host_page_size returned KERN_INVALID_HOST.\n");
else if (code)
printf("host_page_size error code = %d.\n", code);
else
printf("host_page_size(...) is %x.\n", ps);
code = host_processor_info(my_host, PROCESSOR_BASIC_INFO,
&numProcessors, &pit,
&infoCnt);
if (code == KERN_INVALID_HOST)
printf("vm_wire returned KERN_INVALID_HOST.\n");
else if (code)
printf("host_processor_info error code = %d.\n", code);
else
{
processor_basic_info_t p = (processor_basic_info_t) pit;
printf("host_processor_info(...) succeeded, Num
Processors = %d.\n", numProcessors);
if (numProcessors*PROCESSOR_BASIC_INFO_COUNT != infoCnt)
printf("The info count returned should be
%ld, but is %d.\n",
numProcessors*PROCESSOR_BASIC_INFO_COUNT, infoCnt);
printf("# type sub-type slot attributes.\n");
for(unsigned int j = 0; j < numProcessors; j++)
{
printf("%d M M %s%s.\n", j,
p->cpu_type,
p->cpu_subtype, p->slot_num,
(p->running) ?
"running " : "stopped ",
(p->is_master) ?
"master" : "");
}
}
code = task_wire(me, true);
if (code == KERN_INVALID_HOST)
printf("vm_wire returned KERN_INVALID_HOST.\n");
else if (code)
printf("task_wire error code = %d.\n", code);
else
printf("task_wire(..., true) succeeded.\n");
use_some_stack(ps * 20);
code = task_wire(me, false);
if (code == KERN_INVALID_HOST)
printf("vm_wire returned KERN_INVALID_HOST.\n");
else if (code)
printf("task_wire error code = %d.\n", code);
else
printf("task_wire(..., false) succeeded.\n");
/*
code = ::vm_statistics(me, &stats);
if (code == EPERM)
printf("vm_statistics not allowed.\n");
else if (code)
printf("vm_statistics error code = %d.\n", code);
else
printf("vm_statistics (..., ...) succeeded.\n");
*/
p = (char *) malloc(ps*2);
p += ps;
p = (char *)(((unsigned int) p) & ~(ps-1));
code = vm_wire(my_host, me, (vm_address_t) p, ps,
VM_PROT_READ | VM_PROT_WRITE);
if (code == KERN_PROTECTION_FAILURE)
printf("vm_wire returned KERN_PROTECTION_FAILURE.\n");
else if (code == KERN_INVALID_HOST)
printf("vm_wire returned KERN_INVALID_HOST (must be root).\n");
else if (code)
printf("vm_wire error code = %d.\n", code);
else
printf("vm_wire (..., <<all>>) succeeded.\n");
*p = '1';
code = vm_wire(my_host, me, (vm_address_t) p, ps, VM_PROT_NONE);
if (code == KERN_PROTECTION_FAILURE)
printf("vm_wire returned KERN_PROTECTION_FAILURE.\n");
else if (code == KERN_INVALID_HOST)
printf("vm_wire returned KERN_INVALID_HOST (must be root).\n");
else if (code)
printf("vm_wire error code = %d.\n", code);
else
printf("vm_wire (..., VM_PROT_NONE) succeeded.\n");
*p = '2';
err = mlock( p, ps );
if (err && errno != EPERM)
{
/* check to see if mlock needs the memory permissions set */
mprotect( p, ps, PROT_EXEC | PROT_WRITE | PROT_READ);
err = mlock( p, ps );
}
if (err)
{
if (errno == EPERM)
printf("mlock failed with EPERM (returned
from suser()).\n");
else if (errno == EINVAL)
printf("mlock failed with EINVAL (address
wraparound).\n");
else if (errno == EAGAIN)
printf("mlock failed with EAGAIN (too many
pages wired system wide).\n");
else if (errno == ENOMEM)
printf("mlock failed with ENOMEM (limit
exceeded or vm_wire error).\n");
else
printf("mlock failed with %d (returned from
suser()).\n", errno);
}
else
{
printf("mlock succeeded.\n");
}
*p = '3';
err = munlock( p, ps );
if (err)
{
if (errno == EPERM)
printf("munlock failed with EPERM (returned
from suser()).\n");
else if (errno == EINVAL)
printf("munlock failed with EINVAL (address
wraparound).\n");
else if (errno == ENOMEM)
printf("munlock failed with ENOMEM (vm_wire
error).\n");
else
printf("munlock failed with %d (returned from
suser()).\n", errno);
}
else
{
printf("munlock succeeded.\n");
}
*p = '3';
return 0;
}
--
-*****************************************
**
http://www.curbside-recording.com/ **
******************************************
_______________________________________________
coreaudio-api mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/coreaudio-api
Do not post admin requests to the list. They will be ignored.