Oberon || Library || Module Index || Search Engine || Definition || Module


Ulm's Oberon Library:
SysIPC


NAME

SysIPC - System V interprocess communication

SYNOPSIS

CONST private = 0;              (* private key *)


(* mode bits *) CONST alloc = { 16 }; (* entry currently allocated *) CONST creat = { 22 }; (* create entry if key doesn't exist *) CONST excl = { 21 }; (* fail if key exists *) CONST nowait = { 20 }; (* error if request must wait *)

(* control commands *) CONST rmid = 0; (* remove identifier *) CONST set = 1; (* set options *) CONST stat = 2; (* get options *)

(* permission definitions *) CONST read = { 23 }; (* read permission for owner *) CONST write = { 24 }; (* write permission for owner *)

(* shared memory: operation flags *) CONST readwrite = {}; (* attach read/write *) CONST rdonly = { 19 }; (* attach read-only (else read-write) *) CONST rnd = { 18 }; (* round attach address to shmlba *)

(* messages: operation flags *) CONST noerror = { 19 }; (* no error if big message *)

(* semaphore operation flags *) CONST undo = { 19 }; (* set up adjust on exit entry *)

TYPE Key = LONGINT; (* corresponds to key_t from sys/types.h *) TYPE Size = SysTypes.Count; TYPE Modes = SET; TYPE Identifier = LONGINT; TYPE Address = SysTypes.Address; TYPE ProcessId = SysProcess.ProcessId; TYPE Time = SysTypes.Time; TYPE MessageType = LONGINT; TYPE SemVal = INTEGER;

(* common IPC access structure *) TYPE Access = RECORD uid: INTEGER; (* owner's user id *) gid: INTEGER; (* owner's group id *) cuid: INTEGER; (* creator's user id *) cgid: INTEGER; (* creator's group id *) mode: Modes; (* access modes *) seq: INTEGER; (* slot usage sequence number *) key: Key; END;

TYPE SharedMemRec = (* struct shmid_ds *) RECORD perm: Access; (* operation permission struct *) segsz: Size; (* segment size in bytes *) lpid: ProcessId; (* pid of last shmop *) cpid: ProcessId; (* pid of creator *) nattach: INTEGER; (* current # attached *) atime: Time; (* last shmat time *) dtime: Time; (* last shmdt time *) ctime: Time; (* last change time *) END;

TYPE QueueRec = (* struct msqid_ds *) RECORD perm: Access; (* operation permission struct *) first: Address; (* ptr to first message on q *) last: Address; (* ptr to last message on q *) cbytes: INTEGER; (* current # bytes on q *) qnum: INTEGER; (* # of messages on q *) qbytes: INTEGER; (* max # of bytes on q *) lspid: ProcessId; (* pid of last msgsnd *) lrpid: ProcessId; (* pid of last msgrcv *) stime: Time; (* last msgsnd time *) rtime: Time; (* last msgrcv time *) ctime: Time; (* last change time *) END;

TYPE SemaphoreRec = (* struct semid_ds *) RECORD perm: Access; (* operation permission struct *) base: Address; (* ptr to first semaphore in set *) nsems: INTEGER; (* # of semaphores in set *) otime: Time; (* last semop time *) ctime: Time; (* last change time *) END;

TYPE SemOperation = (* struct sembuf *) RECORD num: INTEGER; (* semaphore # *) op: INTEGER; (* semaphore operation *) flags: SET; (* operation flags *) END;

VAR conferror: Events.EventType;

CONST noSharedSegment = 0; CONST errorcodes = 1; TYPE ErrorCode = SHORTINT; TYPE ErrorEvent = POINTER TO ErrorEventRec; TYPE ErrorEventRec = RECORD (Events.EventRec) errorcode: ErrorCode; END; VAR errormsg: ARRAY errorcodes OF Events.Message; VAR error: Events.EventType;

PROCEDURE ShmGet(VAR shmid: Identifier; key: Key; size: Size; shmflg: Modes; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE ShmCtl(shmid: Identifier; cmd: INTEGER; VAR buf: SharedMemRec; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE ShmRemove(shmid: Identifier; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE ShmAttach(shmid: Identifier; VAR shmaddr: Address; shmflg: Modes; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE ShmDetach(shmaddr: Address; errors: RelatedEvents.Object) : BOOLEAN;

PROCEDURE MsgGet(VAR msqid: Identifier; key: Key; msgflg: Modes; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE MsgCtl(msqid: Identifier; cmd: INTEGER; VAR buf: QueueRec; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE MsgRemove(msgqid: Identifier; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE MsgSend(msgqid: Identifier; msg: ARRAY OF BYTE; msgsz: Size; msgflg: Modes; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE MsgReceive(msgqid: Identifier; VAR msg: ARRAY OF BYTE; msgtype: MessageType; msgflg: Modes; errors: RelatedEvents.Object) : BOOLEAN;

PROCEDURE SemGet(VAR semid: Identifier; key: Key; nsems: INTEGER; semflg: Modes; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE SemStat(semid: Identifier; VAR buf: SemaphoreRec; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE SemSet(semid: Identifier; buf: SemaphoreRec; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE SemRemove(semid: Identifier; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE SemGetNCnt(semid: Identifier; semnum: INTEGER; VAR ncnt: INTEGER; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE SemGetPid(semid: Identifier; semnum: INTEGER; VAR pid: ProcessId; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE SemGetVal(semid: Identifier; semnum: INTEGER; VAR semval: SemVal; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE SemGetAll(semid: Identifier; VAR semvals: ARRAY OF SemVal; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE SemGetZCnt(semid: Identifier; semnum: INTEGER; VAR zcnt: INTEGER; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE SemSetVal(semid: Identifier; semnum: INTEGER; semval: SemVal; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE SemSetAll(semid: Identifier; semvals: ARRAY OF SemVal; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE SemOp(semid: Identifier; sops: ARRAY OF SemOperation; nsops: INTEGER; errors: RelatedEvents.Object) : BOOLEAN;

DESCRIPTION

SysIPC interfaces the System V interprocess communication facilities.

Shared memory segments, message queues, and semaphore arrays are identified by a key and an identifier. The key depends on the application, i.e. if two independent processes wants to share the same ipc facility then they use the same key. The identifier is determined by the operating system and is only valid while the associated facility exists. ShmGet, MsgGet, and SemGet convert a key to an identifier.

SysIPC.private may be used as key to get an identifier which does not depend on a special key. This is convenient if the identifier is shared via process inheritence or other sorts of communications (e.g. file i/o).

Conversion of a key to an identifier either tries to create an ipc facility or to get the identifier of an existing ipc facility. This is controlled in detail by the flg parameter of type Modes. Beside usual protection modes (read/write for owner, group, and other) following flags are included:

__________________________________________________________________
|creat	 Create	new ipc	facility if the	key does not  exist.   If|
|	 creat	is  not	 given	then  the conversion returns with|
|	 SysErrors.noent if there is no	such key.		 |
|excl____Causes_the_conversion_to_fail_if_the_key_already_exists.|

Further flags are of internal use or are needed for other operations only (e.g. nowait for message queue and semaphore operations).

Control commands (ShmCtl, MsgCtl, and SemCtl) allow to get or to modify the current status. ShmRemove, MsgRemove, and SemRemove remove ipc facilities. Ipc facilities may be modified or removed by the creator or the owner of the ipc facility. The owner is initially the creator of the ipc facility and may be modified.

Ipc facilities are not released if the number of references equals 0. They must be removed explicitely.

The number of ipc facilities is very limited and depends on following configuration parameters:

___________________________________________________
|SHMMNI	  maximal number of shared memory segments|
|MSGMNI	  maximal number of messages queues	  |
|SEMMNI___maximal_number_of_semaphore_arrays______|

There are various other limits which apply to the different sorts of ipc facilities.

Shared Memory

A shared memory segments is a piece of memory which can be shared between several processes. A segment is always of the same size but not necessarily at the same position in the virtual address space. Multiple maps of the same segment into the address space of one process are possible. Segments are inherited thru SysProcess.Fork but not thru SysProcess.Exec.

ShmGet creates a segment or converts a given key. The size of the segment is given by size. If size does not match the size of an already existing segment (i.e. the given size is greater than the size of the segment) SysErrors.inval is returned as error. The size must range in [SHMMIN..SHMMAX].

ShmAttach maps a shared memory segment into the virtual address space of the calling process and ShmDetach detaches it. Shared memory segments must not overlap any valid regions of the address space. The original system call allows either to give a valid address or NIL. In the last case the address is chosen by the operating system and returned to the calling process. In some implementations the operation system choses addresses just behind the break (see brk(2)) which is a very bad choice because no dynamic storage is available afterwards. On the other side, giving own addresses is extremely unportable. The decision where to attach shared memory segments has been given into the responsibility of ShmAttach which relies on Memory to allow portable usage of shared memory. Thus, ShmAttach returns shmaddr as an out-parameter.

The access protection of the shared memory segment depends on the general protection and the modes given to ShmAttach. Following flags are available:

_____________________________________________________________________
|readwrite   attach the	segment	with read/write	permission (default)|
|rdonly	     attach the	segment	read-only			    |
|rnd	     round attach address to SHMLBA; this flag does not	make|
|	     sense  because  the  attach  address  is  determined by|
|____________ShmAttach._____________________________________________|

ShmDetach takes shmaddr as argument. The identifier is not sufficient because the same shared memory segment may be mapped multiple times into the address space of the same process. Shared memory segments are detached automatically on SysProcess.Exec and SysProcess.Exit.

Shared memory segments may be created, attached, and immediately after setup removed. This does not disturb the segments currently attached but causes the segment to vanish automatically if the number of attached segments equals 0. Valid operations after removal are ShmDetach and ShmCtl with cmd set to stat. The key is changed to private so the old key can be reused without waiting for the last detach.

Configuration parameters related to shared memory:

________________________________________________________________________
|SHMMAX	  maximal size of a shared memory segment in bytes	       |
|SHMMIN	  minimal size of a shared memory segment in bytes	       |
|SHMMNI	  maximal number of shared memory identifiers		       |
|SHMSEG	  maximal number of attached shared memory segments per	process|
|SHMALL___maximal_total_shared_memory_system_wide_(in_clicks)__________|

Some implementations have an additional parameter named SHMBRK which specifies the gap in clicks used between data and shared memory if the mapping of shared memory is left up to the operating system. As mentioned above this implementation does not rely on this parameter.

Message Queues

A message consists of a type (MessageType) and an arbitrary number of bytes. Records of different types and sizes may be put into one message queue.

MsgGet creates a message queue or converts a given key to an identifier. Messages are appended to the queue by call of MsgSend. The message msg consists of a leading type field (of type MessageType) and additional information. The size of the information in bytes is given by msgsz and defaults to the size of msg minus the size of the message type. The default is taken if msgsz equals 0.

If there is currently not enough space to allocate the message in kernel memory the process is either blocked or the system call returns with SysErrors.again. The last case applies if nowait is given as flag to msgflg.

Messages are retrieved by MsgReceive. The maximal accepted message size is determined by the size of msg. The acceptable message types are specified by msgtype:

__________________________________________________________________
|msgtype > 0   the given message type must be matched		 |
|msgtype = 0   all message types are accepted			 |
|msgtype < 0   requests	the message with the lowest message  type|
|______________which_is_less_than_ABS(msgtype)_to_be_returned____|

If there is no appropiate message the process is either blocked or the system call returns with SysErrors.nomsg (if nowait is set in msgflg).

In normal case MsgReceive would return with E2BIG if the message does not fit into msg. Setting noerror in msgflg causes the message to be truncated in this case.

Processes blocked due to MsgSend or MsgReceive may be interrupted. In this case SysErrors.intr is returned.

If the removal of the message queue identifier causes wakeup of blocked processes then SysErrors.idrm is returned.

Beside setting owner and protection it is possible to change the maximal number of bytes which may be used up by a given queue. The field qbytes in QueueRec is initially set to MSGMNB and may be decreased by the owner or creator and increased by the super-user.

Configuration parameters related to message queues:

_____________________________________________________________
|MSGMAP	  size of the memory  control  map  used  to  manage|
|	  message segments				    |
|MSGMAX	  maximum size in bytes	of a message		    |
|MSGMNB	  see above					    |
|MSGMNI	  maximal number of message queue identifiers	    |
|MSGSSZ	  size of a message segment:  messages are stored in|
|	  kernel  memory  in a sequence	of message segments.|
|	  The larger  the  segments  are,  the	greater	 the|
|	  chance  of  having  wasted  memory at	the end	of a|
|	  message.					    |
|MSGSEG	  number of message segments; the maximal size	used|
|	  up by	all messages is	given by MSGSEG	* MSGSSZ    |
|MSGTQL	  maximal number of messages of	all  message  queues|
|	  systemwide,  and  thus,  the number of outstanding|
|_________messages__________________________________________|

Semaphores

Semaphores are organized in arrays of integer semaphores. A set of operations on one semaphore array is executed atomically. Semaphores are small integers (a configurable subset of 2-byte-integers is taken). Semaphore values are set initially to 0. Typically, positive semaphore values indicate a free ressource, and negative semaphore values indicate a locked ressource. Semaphore operations are given as small integers which are to be added to the semaphore value:

_____________________________________________________________________
|semaphore_operation|__effect_______________________________________|
|> 0		    |  releases	 the  ressource:  if  the  semaphore|
|		    |  value   becomes	 greater   than	  zero	then|
|		    |  processes waiting for a	free  ressource	 are|
|		    |  waked up.				    |
|< 0		    |  locks the ressource: if the  semaphore  value|
|		    |  gets  negative  after  adding  the  semaphore|
|		    |  operation to it then the	process	 is  blocked|
|		    |  until the semaphore value becomes positive.  |
|= 0		    |  causes the process to be	 blocked  until	 the|
|		    |  semaphore value becomes zero.		    |
|___________________|_______________________________________________|

Processes are not blocked if nowait is member of the operation flags. Blocked processes may be interrupted. In this case SysErrors.intr is returned.

SemGet converts a key into a semaphore identifier and potentially creates a semaphore array of length nsems. If the semaphore array already exists then nsems must be either zero or must not exceed the length of the semaphore array.

SemOp tries to execute a list of semaphore operations given by sops atomically. The number of semaphore operations equals by default the length of the open array parameter (nsops = 0) or may be specified (nsops > 0 & nsops <= LEN(sops)). Semaphore operations are given as records of type SemOperation:

____________________________________________________________
|num	 is taken as index  to	the  semaphore	array;	the|
|	 semaphore array is indexed from 0 to nsems - 1.   |
|op	 semaphore operation:  a small integer which is	 to|
|	 be added to the semaphore value		   |
|flags	 nowait	and undo may  be  given	 as  flags;  nowait|
|	 requests  the	process	 not  to be blocked on this|
|	 operation and undo  causes  the  operation  to	 be|
|________undone_on_process_exit.___________________________|

SemStat returns the current semaphore array status and SemSet allows to change owner and protection. SemRemove removes the given semaphore. All processes which are blocked due to semaphore operations are awakened. Their calls return with SysErrors.rmid.

Various procedures allow to examine or to modify semaphores. SemGetAll returns the semaphore values of all semaphores in the array. SemGetVal returns the semaphore value of the semaphore indexed by semnum. SemSetAll sets the semaphore values of all semaphores. SemSetVal sets the semaphore value of the semaphore indexed by semnum. Processes which are blocked due to semaphore operations either awaits the semaphore value to become zero or to become greater or equal than the absolute value of their semaphore operation. SemGetZcnt returns the number of processes sleeping for the given semaphore value to become zero. SemGetNCnt returns the number of processes waiting for the given semaphore value to become positive. Further, SemGetPid returns the process id of the last process operating on the given semaphore.

Configuration parameters related to semaphores:

_____________________________________________________________
|SEMMAP	  specifies the	size of	the memory control map	used|
|	  to manage semaphores.				    |
|SEMMNI	  maximal number of semaphore identifiers	    |
|SEMMNS	  maximal number of semaphores in the system	    |
|SEMMNU	  maximal number of undo structures in the system   |
|SEMMSL	  maximal length of a semaphore	array		    |
|SEMOPM	  maximal number of operations per SemOp call	    |
|SEMUME	  maximal number of undo structures per	process	    |
|SEMUSZ	  size in bytes	of undo	structure		    |
|SEMVMX	  maximum value	of a semaphore			    |
|SEMAEM___maximul_value_of_exit_adjusts_____________________|

EXAMPLES

Shared Memory

Following example illustrates sharing of a record between related processes:
TYPE
   SharedRecPtr = POINTER TO SharedRec;
   SharedRec =
      RECORD
         (* be sure that the size of SharedRec exceeds SHMMIN *)
      END;

PROCEDURE Setup(VAR sharedp: SharedRecPtr;
                VAR pid: SysProcess.ProcessId) : BOOLEAN;
   (* create a shared memory region and fork;
      if successful then sharedp points to the shared memory region
      containing a record of type SharedRec and
      pid contains the result of SysProcess.Fork
   *)

   VAR
      shmid: SysIPC.Identifier; (* shared memory identifier *)
      shmaddr: SysIPC.Address;  (* address of attached segment *)
      forkok: BOOLEAN;          (* result of SysProcess.Fork *)

BEGIN
   IF ~SysIPC.ShmGet(shmid, SysIPC.private, SYSTEM.SIZE(SharedRec),
                     SysIPC.read + SysIPC.write, NIL) THEN
      RETURN FALSE
   END;
   IF ~SysIPC.ShmAttach(shmid, shmaddr, SysIPC.readwrite, NIL) THEN
      IF ~SysIPC.ShmRemove(shmid, NIL) THEN END;
      RETURN FALSE
   END;
   forkok := SysProcess.Fork(pid, NIL);
   (* if SysProcess.Fork succeeds then the attached segment
      is inherited to the child process
   *)
   IF ~forkok THEN
      IF ~SysIPC.ShmDetach(shmdaddr, NIL) THEN END;
   END;
   IF ~forkok OR (pid # 0) THEN
      (* cleanup identifier in each case;
         if SysProcess.Fork was successful cleanup is done
         by the parent process
      *)
      IF ~SysIPC.ShmRemove(shmid, NIL) THEN END;
   END;
   sharedp := SYSTEM.VAL(SharedRecPtr, shmaddr);
   RETURN forkok
END Setup;

Message Queues

The example following illustrates the use of message between unrelated processes. One process serves as demon which waits for messages sent by clients.

Common definitions:

CONST
   messageType = 1;
   key = "demo";
      (* keys are per convention short strings (1-4 characters) *)
TYPE
   Message =
      RECORD
         type: SysIPC.MessageType;
         (* further components *)
      END;

PROCEDURE ConvertKey(key: ARRAY OF CHAR) : SysIPC.Key;
   VAR
      val: SysIPC.Key;
      index: INTEGER;
BEGIN
   val := 0;
   index := 0;
   WHILE (index < SYSTEM.SIZE(SysIPC.Key)) & (index < LEN(key)) &
         (key[index] # 0X) DO
      val := val * 100H + ORD(key[index]);
      INC(index);
   END;
   RETURN val
END ConvertKey;

The demon:

VAR
   msqid: SysIPC.Identifier;

PROCEDURE Cleanup(event: Event.Events);
BEGIN
   IF ~SysIPC.MsgRemove(msqid, NIL) THEN END;
END Cleanup;

PROCEDURE IdentifierRemoved(event: Event.Events);
   (* called if identifier has been removed externally *)
BEGIN
   SysProcess.Exit(0);
END IdentifierRemoved;

PROCEDURE Demon;
   VAR
      msg: Message;
BEGIN
   IF ~SysIPC.MsgGet(msqid, ConvertKey(key),
                  (* protection: rw--w--w- *)
                  SysStat.uread + SysStat.write, NIL) THEN
      SysProcess.Exit(1);
   END;
   Events.Handler(SysEvents.termination, Cleanup);
   Events.Handler(SysErrors.syserror[SysErrors.idrm], IdentifierRemoved);
   LOOP
      IF SysIPC.MsgReceive(msqid, msg, messageType, {}, NIL) THEN
         (* evaluate the message *)
      END;
   END;
END Demon;

The client:

PROCEDURE SendMessage(msg: Message) : BOOLEAN;
   VAR
      msgqid: SysIPC.Identifier;
BEGIN
   RETURN SysIPC.MsgGet(msqid, ConvertKey(key), NIL) &
          SysIPC.MsgSend(msqid, msg, 0, {}, NIL)
END SendMessage;

Semaphores

Following example shows how to realize Dijkstras P and V operations:
TYPE
   Semaphore = SysIPC.Identifier;

   (* semaphore value

      > 1  more than one ressource is available; the semaphore value
           equals the number of free ressources
      = 1  free ressource
      = 0  ressource is locked but nobody waits for it
      < 0  ressource is locked and the absolute value of
           the semaphore value equals the number of processes
           waiting for the ressource to become free
   *)

PROCEDURE Init(VAR s: Semaphore; key: SysIPC.Key) : BOOLEAN;
BEGIN
   IF ~SysIPC.SemGet(s, key, 1, SysIPC.read + SysIPC.write, NIL) THEN
      RETURN FALSE
   END;
   IF ~SysIPC.SemSetVal(s, 0, 1, NIL) THEN
      IF ~SysIPC.SemRemove(s, NIL) THEN END;
      RETURN FALSE
   END;
   RETURN TRUE
END Init;

PROCEDURE Remove(s: Semaphore) : BOOLEAN;
BEGIN
   RETURN SysIPC.SemRemove(s, NIL)
END Remove;

PROCEDURE P(s: Semaphore) : BOOLEAN;
   VAR
      sops: ARRAY 1 OF SysIPC.SemOperation;
BEGIN
   sops[0].num := 0;
   sops[0].op := -1;
   sops[0].flags := SysIPC.undo;
   RETURN SysIPC.SemOp(s, sops, 0, NIL)
END P;

PROCEDURE V(s: Semaphore) : BOOLEAN;
   VAR
      sops: ARRAY 1 OF SysIPC.SemOperation;
BEGIN
   sops[0].num := 0;
   sops[0].op := 1;
   sops[0].flags := {};
   RETURN SysIPC.SemOp(s, sops, 0, NIL)
END V;

DIAGNOSTICS

System call failures lead to events of SysErrors. The errors parameter is passed to SysErrors.Raise. All routines return FALSE in error case.

Following errors are detected by SysIPC itself:

noSharedSegment
is raised by ShmDetach if the given address does not belong to a shared memory segment.

The event conferror is passed to Assertions.Raise during initialisation time in case of configuration errors.

SEE ALSO

ipcs(1)   		prints status information of current ipc facilities
ipcrm(1)   	removes	ipc facilities
shmget(2)	ShmGet
shmctl(2)	ShmCtl and ShmRemove
shmat(2)	ShmAttach and ShmDetach
msgget(2)	MsgGet
msgctl(2)	MsgCtl and MsgRemove
msgsend(2)	MsgSend	and MsgReceive
semget(2)	SemGet
semctl(2)	SemCtl and SemRemove
semop(2)	SemOp and all other semaphore operations
Assertions   	error handling in case of configuration	errors
Memory   	address	space management
SysErrors   	handling of failed system calls
SysProcess   	system calls related to	process	management
SysStat   	protection modes

Edited by: borchert, last change: 1993/06/13, revision: 1.6, converted to HTML: 1997/04/28

Oberon || Library || Module Index || Search Engine || Definition || Module