roger@mips.UUCP (Roger March) (09/16/87)
Argh! I hate it when I have to drag one of my programs to an
Apollo and "set_sbrk_size()" is right at the top of my list of
vomit inducers. I mean, give me a break, the default heap size
is 256Kb, go above that and its segment fault city. RETCH! Not
only do I have about zero programs which can live in such cramped
quarters, but I've had UNIX utilities ("sort" I think) which I
believe crashed due to running out of heap space. What is it
about Domain-IX/AEGIS which keeps "sbrk()" from just allocating
some more turf when it runs out of heap space up to the virtual
memory limit?
Enough (justiified) flaming, onto an interesting side effect
of "set_sbrk_size()". My usual technique for getting a man sized
heap space is to put something like this in "main()":
#define MB * 1024 * 1024
main()
{
static int vm[] = { 7MB, 48MB, 120MB };
int i;
/*
* Reset the heap size until we can allocate.
*/
for (i = sizeof(vm) / sizeof(int); i--;){
set_sbrk_size(vm[i]);
if (sbrk(0) >= 0)
break;
}
... /* real stuff follows */
Which pretty much sets the heap at the virtual limit for the
machine the program is run on. This has worked fine for a long
time. But now I want my program to "fork()". Yeah, real tough
code like:
if (pid = fork()){
... /* more real stuff */
and the "fork()" fails. Good old "perror()" says "I/O ERROR";
pretty informative huh. And I was a real nice guy and the heap
is virtually empty at the point I "fork()", so it doesn't seem
possible it barfed because it didn't have space to replicate
the data areas.
Its appears to be "set_sbrk_size()" related in that if I just
hardwire the heap space to:
set_sbrk_size(4MB);
everything works just fine. Does anyone have clues to why
"set_sbrk_size()" breaks "fork()"? What is it about Apollo's
memory management that spawned an abortion like "set_sbrk_size()"?
And if I'm doing something stupid which makes this problem go
away let me know and you can even call me nasty things (oh yeah,
don't stop!).
--
-your friendly neighborhood Rogue Monster roger@mips.com
UUCP: {decvax,ucbvax,ihnp4,hplabs,sun,ames,prls}!decwrl!mips!roger
USPS: MIPS Computer Systems, 930 Arques, Sunnyvale, CA 94086, (408) 991-0220
peterson@utah-cs.UUCP (John W Peterson) (09/16/87)
> > And if I'm doing something stupid which makes this problem go > away let me know and you can even call me nasty things (oh yeah, > don't stop!). > On recent releases (certainly SR9.5 and beyond) you're "doing something stupid" if you use set_sbrk_size in the first place. It's currently a no-op, provided only for backward compatibility (Yes, they really do fix things that people complain about). If you are running an old release, you might check the man page. I recall there was an environment variable you could set (SBRK_SIZE or somesuch) instead of hacking up your application's code. Cheers, jp
rees@apollo.UUCP (Jim Rees) (09/16/87)
What is it about Domain-IX/AEGIS which keeps "sbrk()" from just allocating some more turf when it runs out of heap space up to the virtual memory limit? The semantics of sbrk() don't fit into a modern computer address space. Sbrk() was invented back in the days when you had some limited amount of real, physical memory. You started allocating from the end of your fixed-size data region, and stopped when you ran out. Now consider a system in which the data region is not fixed size. In fact, you can have any number of things ("objects") mapped at arbitrary places in your address space. You can dynamically map new ones, extend the size of or unmap old ones, shrink them, relocate them, etc. Now, how would you implement a call (like malloc()) that returns some heap storage for you to use? You would just pick some hole in the address space that's big enough, map a temporary object there, and return a pointer to it. Unfortunately, we couldn't do that. There are lots of programs out there that don't just use malloc() to allocate space, they make assumptions about the internal workings of malloc(). I claim that this is a Bad Thing. They assume, for example, that they can continue to use free()d space until the next malloc(). So, we have to reserve some pool of storage just for malloc(), and not let any other clients of the mapping system (like the file I/O system) use that space. Now consider sbrk(). Note that it doesn't just return storage space; it makes certain guarantees about that space. For example, it guarantees that the next time you call sbrk(), you will get a piece of storage contiguous with the last piece you got. And when you free some storage, then allocate more, you get back the same piece you just freed. Programs depend on this behavior. We aren't at liberty to change those programs, because some of them were written by customers. This means that any space you will ever want to allocate with sbrk() has to be pre-allocated at the time the program is started (not the actual storage, but at least the address space). In AT&T and Berkeley Unix, this is no problem, because address space is allocated statically at the time your program is loaded. The text, data, and bss sizes are all fixed. Nothing more ever gets mapped in once the program is running. But in the Domain system, things are much more dynamic. Things are popping in and out of your address space all the time. So we need some way to reserve space for sbrk(). Hence, set_sbrk_size(). One way to fix this would be to give up the advantages of the dynamic address space allocation. This would mean, for example, no type managers in user space. No NFS without putting it in the kernel. No user defined types. Just like Unix. Another way to fix it, and the approach we took, is to re-arrange the address space. Put text at the bottom, then data, then bss, just like unix. Put the stack at the top and let it grow down towards bss. Go through various gyrations to try to keep sbrk() separate from the other users of the address space. This reduces the generality of the system, but it may be worth it if it eliminates the dreaded set_sbrk_size() (we hate it as much as you do). I'm not sure whether you really care about the implementation details. The bottom line is that set_sbrk_size has gone away (actually, it's a no-op) in sr9.5. But now I want my program to "fork()". Yeah, real tough code like: if (pid = fork()){ ... /* more real stuff */ and the "fork()" fails. Good old "perror()" says "I/O ERROR"; pretty informative huh. And I was a real nice guy and the heap is virtually empty at the point I "fork()", so it doesn't seem possible it barfed because it didn't have space to replicate the data areas. Remember that the semantics of sbrk() demand that you pre-allocate the space, regardless of whether you ever intend to use it. -------