Oberon || Library || Module Index || Search Engine || Definition || Module
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;
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.
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.
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__________________________________________|
_____________________________________________________________________ |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_____________________|
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;
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;
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;
Following errors are detected by SysIPC itself:
The event conferror is passed to Assertions.Raise during initialisation time in case of configuration errors.
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
Oberon || Library || Module Index || Search Engine || Definition || Module