[comp.sys.apollo] SET_SBRK_SIZE

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.
-------