[comp.lang.c] shared memory

bagpiper@oxy.edu (Michael Paul Hunter) (01/31/89)

I am currently writing a multi-user game on a Prime under Primix.  I am going
to be using a shared segment (a fixed location "array").  I am interested how
similar things are done on real machines so that I can code my game in a way
that will be most portable.

				     Mike

ps. please reply to me directly if at all possible...
    (particularly in comp.unix.questions as I don't read that group...)

Michael Hunter	       UUCP  : ....{rutgers, ames}!cit-vax!oxy!bagpiper
Box 241 	       ARPA  : bagpiper@oxy.edu
Occidental College
Los Angeles, CA 90041	 I also hack for MicroCosm, Inc.

andrew@gestetner.oz (Andrew Hunt) (09/21/90)

I am having problems with UNIX shared memory system functions
(shmget, shmctl, shmop) - our system is SUN O/S 4.0.x.  
I am trying to attach some data to a shared memory identifier.

	assign global_start_address
	assign global_size

	global_shmid = shmget(IPC_PRIVATE,global_size,0777|IPC_CREAT);

	shmat(global_shmid,global_start_address,SHM_RDONLY);

The shmget is always successful but the shmat fails.  I have tried 
to assign the global_start_address to many different sizes and 
addresses.  The addresses I have tried have been from malloc'ed memory,
stack space, initialised data space, uninitialised data space....
I have experimented with changing permissions and have changed most of
the parameters but the only thing that has worked is to set

	global_start_address = (char *)0;

which allow the system to find some space for me.

Does anyone know what areas of memory can be legally attached to 
shared memory?

Thanks,
	Andrew Hunt

PS: if it helps we are trying to set up the global data space of
several UNIX processes to use common memory so that we can simulate
the shared memory of an imbedded controller.

mark@DRD.Com (Mark Lawrence) (09/22/90)

andrew@gestetner.oz (Andrew Hunt) wrote:
} I am having problems with UNIX shared memory system functions
} (shmget, shmctl, shmop) - our system is SUN O/S 4.0.x.  
} I am trying to attach some data to a shared memory identifier.
} 
} 	assign global_start_address
} 	assign global_size
} 
} 	global_shmid = shmget(IPC_PRIVATE,global_size,0777|IPC_CREAT);
} 
} 	shmat(global_shmid,global_start_address,SHM_RDONLY);

shmat returns the address.  You can't tell it where you want it.  Then 
dereference the members of interest in each process using the Psh pointer.

An example:

#include <sys/types.h>
#include <sys/shm.h>
#include <memory.h>
/*
    +-----------------------------------------------------------------
    | declarations of scope process
*/
extern int      errno;      /* system call err status */
extern const char *strerror(int);
typedef struct {
    int             foo;
    float           bar;
    short           gex;
    double          snark;
}               SHMEM;
#define SHMEM_k          0x4444 /* arbitrary key */
#define SHMEM_PERMISSION  0666  /* rw-rw-rw  */
SHMEM   *Psh;
/*
    +-----------------------------------------------------------------
    | declarations of scope file
*/
static int      shmid;
/*
    +-----------------------------------------------------------------
    | executable code begins here
*/
static void
CrynDie(char *who, char *what)
{
    fprintf(stderr,
         "\n\n%s: Fatal error from %s, %s", who, what, strerror(errno));
    exit(1);
}
 /*
  * The CreateShMem function is to be called only by one process at system
  * startup.  It will create shared memory for the system.  That process must
  * then AttachShMem.  All other processes must use the AttachShMem function
  * only which will get and attach shared memory.
  */
void
CreateShMem(char *caller)
{
    SHMEM          *pshmem;

    if ((shmid = shmget(SHMEM_k, sizeof(SHMEM),
                IPC_CREAT | SHMEM_PERMISSION)) == -1)
        CrynDie(caller, "Shared memory creation failed -- fatal error");

 /* initialize the shared memory block to all NULLs  */
    if ((pshmem = (SHMEM *) shmat(shmid, 0, 0)) == (SHMEM *) - 1)
        CrynDie(caller, "attach failed");
    memset((char *) pshmem, '\0', sizeof(SHMEM));
    shmdt((char *) pshmem);
}

void
AttachShMem(char *caller)
{
    if ((shmid = shmget(SHMEM_k, sizeof(SHMEM), SHMEM_PERMISSION)) == -1)
        CrynDie(caller, "Get shared memory id failed");
    if ((Psh = (SHMEM *) shmat(shmid, 0, 0)) == (SHMEM *) - 1)
        CrynDie(caller, "attach failed");
}

-- 
mark@DRD.Com uunet!apctrc!drd!mark$B!J%^!<%/!!!&%m!<%l%s%9!K(B

cpcahil@virtech.uucp (Conor P. Cahill) (09/22/90)

In article <566@gestetner.oz> andrew@gestetner.oz (Andrew Hunt) writes:
>Does anyone know what areas of memory can be legally attached to 
>shared memory?

This is a very OS dependent portion of the code.  What I usually do
is attach a segment at the default address and compare the value of the
pointer to the value of a malloc'd pointer (yes I know that comparing
pointers to unrelated objects is not always defined).  If the two 
are too close, I detach the segment and re-attache it at a much 
higher address (like 1MB higher).


-- 
Conor P. Cahill            (703)430-9247        Virtual Technologies, Inc.,
uunet!virtech!cpcahil                           46030 Manekin Plaza, Suite 160
                                                Sterling, VA 22170 

gt0178a@prism.gatech.EDU (BURNS,JIM) (09/22/90)

in article <1990Sep21.183326.13116@DRD.Com>, mark@DRD.Com (Mark Lawrence) says:

> shmat returns the address.  You can't tell it where you want it.  Then 

Machine dependent.

> /*
>     +-----------------------------------------------------------------
>     | declarations of scope process
> */
> extern int      errno;      /* system call err status */
> extern const char *strerror(int);

I had more compile troubles w/this last line then any other in the program.
I had to substitute:

extern char *sys_errlist[];

and for:

    fprintf(stderr,
         "\n%s: Fatal error from %s, %s\n", who, what, strerror(errno));*/

in CrynDie, I had to substitute:

    fprintf(stderr,
         "\n%s: Fatal error from %s, %s\n", who, what, sys_errlist[errno]);

to get it to compile and load on Dynix, SunOS 4.0.3, A/UX 1.1, & Ultrix
4.0.

>   * The CreateShMem function is to be called only by one process at system
>   * startup.  It will create shared memory for the system.  That process must
>   * then AttachShMem.  All other processes must use the AttachShMem function
>   * only which will get and attach shared memory.
   [...]
>     if ((shmid = shmget(SHMEM_k, sizeof(SHMEM),
>                 IPC_CREAT | SHMEM_PERMISSION)) == -1)
   [...]
>     memset((char *) pshmem, '\0', sizeof(SHMEM));

Just to avoid misunderstandings, these instructions are correct NOT
because of the way he's calling shmget(2), but simply because he's
initializing the shm seg. (Of course, he's also immediately doing a
shmdt(). ) It would only be an error to call shmget(2) w/IPC_CREAT or'd
in w/SHMEM_PERMISSION in the 2nd process if it ALSO had IPC_EXCL or'd in
as well, as the man pages on the above systems state.

I mention this because some systems guarantee that the first shmat() will
zero out the shm seg. From the HP 9000/800 HP-UX Real Time Programmers
Manual:

[example program omitted]

"Note that the fields of the tallytab entries that were not written in
subroutine write_and_read_shm() have values of 0. This is because the
first shmat called (by any process) on that shared memory identifier will
zero out all of the contents of shared memory."

I have not seen this documented in the man pages of the other above
systems, altho' a simple:

main() {
   CreateShMem("main");   /* I commented out the memset in here */
   AttachShMem("main");
   printf("%d %f %d %f\n",Psh->foo,Psh->bar,Psh->gex,Psh->snark);
   printf("%d\n",shmctl(shmid,IPC_RMID,0));
   printf("main exiting\n");
   exit(0);
}

added to the end of the posted subroutines shows all zeroes on A/UX and
Ultrix. Anybody know if this is standard?
-- 
BURNS,JIM
Georgia Institute of Technology, Box 30178, Atlanta Georgia, 30332
uucp:	  ...!{decvax,hplabs,ncar,purdue,rutgers}!gatech!prism!gt0178a
Internet: gt0178a@prism.gatech.edu

josef@nixpbe.UUCP (Moellers) (09/24/90)

In <566@gestetner.oz> andrew@gestetner.oz (Andrew Hunt) writes:

>I am having problems with UNIX shared memory system functions
>(shmget, shmctl, shmop) - our system is SUN O/S 4.0.x.  
>I am trying to attach some data to a shared memory identifier.

>	assign global_start_address
>	assign global_size

>	global_shmid = shmget(IPC_PRIVATE,global_size,0777|IPC_CREAT);

>	shmat(global_shmid,global_start_address,SHM_RDONLY);

>The shmget is always successful but the shmat fails.  I have tried 
>to assign the global_start_address to many different sizes and 
>addresses.  The addresses I have tried have been from malloc'ed memory,
>stack space, initialised data space, uninitialised data space....
>I have experimented with changing permissions and have changed most of
>the parameters but the only thing that has worked is to set

>	global_start_address = (char *)0;

>which allow the system to find some space for me.

>Does anyone know what areas of memory can be legally attached to 
>shared memory?

The system will ALLWAYS find space for You. It's YOUR task to tell it
			     ^^^^^
where to put it.
^^^^^
The address where to put it ("global_start_address") must not result in
part or all of the shared memory segment overlaying part or all of Your
allocated virtual address space.
So:
	break <= global_start_address < SP - global_size
Try something like global_start_address = 0x80000000, which, on a 32 bit
system, should put the shared memory segment in the middle of Your 4GB
virtual address space.

--
| Josef Moellers		|	c/o Nixdorf Computer AG	|
|  USA: mollers.pad@nixdorf.com	|	Abt. PXD-S14		|
| !USA: mollers.pad@nixdorf.de	|	Heinz-Nixdorf-Ring	|
| Phone: (+49) 5251 104662	|	D-4790 Paderborn	|

mark@DRD.Com (Mark Lawrence) (09/24/90)

} in article <1990Sep21.183326.13116@DRD.Com>, mark@DRD.Com (Mark Lawrence) says:
} > extern const char *strerror(int);

gt0178a@prism.gatech.EDU (BURNS,JIM) wrote:
} I had more compile troubles w/this last line then any other in the program.
} I had to substitute:
} 
} extern char *sys_errlist[];

The following thanks to Chris Torek:
/*
 * >From: chris@mimsy.UUCP (Chris Torek)
 * 
 * In article <349@auspex.UUCP> guy@auspex.UUCP (Guy Harris) writes: *Grumble
 * bitch* please use "sys_errlist[]" next time, thank you....  It might even
 * make it easier to figure out what's wrong.
 * 
 * Actually, use strerror(): This should be in your dpANS-conformant
 * C library (what, you have no dpANS-conformant *compiler*? :-) )
 * 
 */
#include <ANSIPrototypes.h>
const char     *strerror(int err)
{
	extern const char *sys_errlist[];
	extern const int sys_nerr;
	static char     expand[40];

	if ((unsigned) err < sys_nerr)
		return (sys_errlist[err]);
	(void) sprintf(expand, "unknown error code %d", err);
	return (expand);
}
-- 
mark@DRD.Com uunet!apctrc!drd!mark$B!J%^!<%/!!!&%m!<%l%s%9!K(B

karl@haddock.ima.isc.com (Karl Heuer) (09/25/90)

In article <1990Sep24.135530.405@DRD.Com> mark@DRD.Com (Mark Lawrence) writes:
>[In response to a complaint about code using `strerror()']
>The following thanks to Chris Torek:
>const char     *strerror(int err) { ... }

The prototype is wrong: strerror() returns an unqualified `char *' according
to the ANS.  The characters are *not* constant: out-of-range error values are
stored in a buffer that gets overwritten by subsequent calls.  (Acceptable
behavior, but incompatible with the declaration.)

Karl W. Z. Heuer (karl@kelp.ima.isc.com or ima!kelp!karl), The Walking Lint

dab@myrias.com (Danny Boulet) (09/26/90)

In article <josef.654158001@peun11> josef@nixpbe.UUCP (Moellers) writes:
>In <566@gestetner.oz> andrew@gestetner.oz (Andrew Hunt) writes:
>
>>I am having problems with UNIX shared memory system functions
>>(shmget, shmctl, shmop) - our system is SUN O/S 4.0.x.  
>>I am trying to attach some data to a shared memory identifier.
>
>>	assign global_start_address
>>	assign global_size
>
>>	global_shmid = shmget(IPC_PRIVATE,global_size,0777|IPC_CREAT);
>
>>	shmat(global_shmid,global_start_address,SHM_RDONLY);
>
>>The shmget is always successful but the shmat fails.  I have tried 
>>to assign the global_start_address to many different sizes and 
>>addresses.  The addresses I have tried have been from malloc'ed memory,
>>stack space, initialised data space, uninitialised data space....
>>I have experimented with changing permissions and have changed most of
>>the parameters but the only thing that has worked is to set
>
>>	global_start_address = (char *)0;
>
>>which allow the system to find some space for me.
>
>>Does anyone know what areas of memory can be legally attached to 
>>shared memory?
>
>The system will ALLWAYS find space for You. It's YOUR task to tell it
>			     ^^^^^
>where to put it.
>^^^^^
>The address where to put it ("global_start_address") must not result in
>part or all of the shared memory segment overlaying part or all of Your
>allocated virtual address space.
>So:
>	break <= global_start_address < SP - global_size
>Try something like global_start_address = 0x80000000, which, on a 32 bit
>system, should put the shared memory segment in the middle of Your 4GB
>virtual address space.

Here's another suggestion:  in the systems that I have experience with
their SysV shared memory facilities (Convergent Technologies Miniframe,
Pyramid, Sun 3's and 386's running SCO Xenix), the system wouldn't allow
me to place shared memory just anywhere.  There was only a limited part
of the address space in which shared memory could appear.  If you specified
an address outside of this limited range then the shmat call would fail.

Now for my suggestion:  make a call to shmat using the form that allows
the system to pick where the memory will appear.  Once you know where
the system would like to put the memory, change your program to always
ask for it to be placed at that location.  This allows your program
to know in advance where the memory will land although it means that you
have to repeat this experiment when you try to port the code to some other
system.

Now for the catch:  my experience with SCO Xenix (version 2.3) is quite
limited but I did discover that the system would not allow me to specify
where the memory should appear.  It insisted on being able to chose the
location itself.  I wasn't able to find a work around for this (not too
suprising since the manuals listed this as one of the deviations from SVR3).

Note that my experience is with SCO Xenix (not SCO UNIX).  The SCO UNIX
may or may not be more flexible.  One of the problems is that the Intel 80386
MMU only allows sharing of segments that are multiples of 4M in size and
located on 4M boundaries (actually, you can share less than 4M but everything
within any given 4M block is either shared or unshared).  One of the
consequences of this is that any implementation of shared memory on this
architecture needs to keep this in mind.  Since every MMU that you're
every likely to encounter has some alignment requirements and such, you
need to be careful about what assumptions you make if you want your code
to be portable.

If your software really needs to be able to insist on where the memory
lands then you might have a problem (depending on whether your system
allows you to say where it goes).

>
>--
>| Josef Moellers		|	c/o Nixdorf Computer AG	|
>|  USA: mollers.pad@nixdorf.com	|	Abt. PXD-S14		|
>| !USA: mollers.pad@nixdorf.de	|	Heinz-Nixdorf-Ring	|
>| Phone: (+49) 5251 104662	|	D-4790 Paderborn	|

gt0178a@prism.gatech.EDU (BURNS,JIM) (09/26/90)

in article <13991@hydra.gatech.EDU>, gt0178a@prism.gatech.EDU (BURNS,JIM) says:
[deleted]

In my above reply, for some reason, it got marked as Followup:
comp.lang.c, which is NOT what I intended. I don't normally read that
group, but I suscribed to it just to find out if my question at the end of
that post got answered, and it didn't. So one more time:

HP-UX documents it; it tests out on A/UX 1.1 and Ultrix 4.0 : the first
call to shmat() (by any process) automatically zeroes out the shm seg, so
explicit initialization is NOT necessary. Is this standard? Which one?

-- 
BURNS,JIM
Georgia Institute of Technology, Box 30178, Atlanta Georgia, 30332
uucp:	  ...!{decvax,hplabs,ncar,purdue,rutgers}!gatech!prism!gt0178a
Internet: gt0178a@prism.gatech.edu

reidc@eliot.UUCP (Reid Carson/Development) (09/27/90)

   Let me just add a few observations about shared memory, from our experiences
in porting to a number of different systems.

   It's generally easiest to let the point of attachment be selected by the
system, unless you have some reason to do otherwise.  Unfortunately, one
such reason is that if you need to malloc more than 20 or 30K after attaching
the shared memory, the malloc may fail, since the address at which the segment
gets mapped effectively limits your process's maximum break point (and can also
limit the size of your stack).

   This varies from system to system.  Under SunOS, the segment maps quite high
in your address space, so there's no problem.  Under Ultrix 3.0 (on our system,
anyway), the segment is mapped about 33K above the current break point, which
is fine unless you need to malloc more than that, or your stack gets pretty
big, both of which are true for us.

   Our solution is to run a small test program in our Configure script when
starting the port.  The test program attaches a segment, checks the address
it maps at, and defines a symbol in the generated config.h to indicate whether
a shared memory segment maps sufficiently high or not.  Thus the real program
can know whether to let the segment map at the default address, or attach it
up near the maximum break value.

   I should also mention that if you need to select the address yourself, you
should put the shmat() in a loop, decrementing the address each time through,
since some systems have limitations on how high you may attach a segment.
You may need to decrease the address by several million on a system like a
Silicon Graphics (if my memory serves me correctly).

cpcahil@virtech.uucp (Conor P. Cahill) (09/27/90)

In article <14140@hydra.gatech.EDU> gt0178a@prism.gatech.EDU (BURNS,JIM) writes:
>HP-UX documents it; it tests out on A/UX 1.1 and Ultrix 4.0 : the first
>call to shmat() (by any process) automatically zeroes out the shm seg, so
>explicit initialization is NOT necessary. Is this standard? Which one?

This is a basic rule of unix: 

	All memory given to a program by the kernel must be cleared to 
	zero.  This applies to new sbrk()'d areas (not always to areas that
	you previously had and then released and then got back) uninitialized
	static data areas, and shared memory segments. It even applies to stack
	the first time it is used.

This is a security measure (makes sure you can't see someone elses data).

Note that it also applies to disk "memory" (if you seek past the end of a
file, write a byte, seek back to the skipped portion and read, you will get
all nulls).

-- 
Conor P. Cahill            (703)430-9247        Virtual Technologies, Inc.,
uunet!virtech!cpcahil                           46030 Manekin Plaza, Suite 160
                                                Sterling, VA 22170