Interprocess communication (IPC) on OS/400 is made up of five services divided into the two categories of identifier-based services and pointer-based services. The identifier-based IPC services consist of message queues, semaphore sets, and shared memory. The pointer-based services consist of unnamed and named semaphores. The basic purpose of these services is to provide OS/400 processes and threads with a way to communicate with each other through a set of standard APIs. These functions are based on the definitions in the X/Open single UNIX specification (formerly Spec 1170).
Topics covered include:
Although each IPC service provides a specific type of interprocess communication, the three identifier-based services share many similarities. Each service defines a mechanism through which its communications take place. For message queues, that mechanism is a message queue; for semaphore sets, it is a semaphore set; and for shared memory, it is a shared memory segment. These mechanisms are identified by a unique positive integer called, respectively, a message queue identifier (msqid), a semaphore set identifier (semid), and a shared memory identifier (shmid).
Note: In this chapter, the term thread is used extensively. This does not mean that IPC objects can be used only between threads within one process, but rather that authorization checks and waits occur for the calling thread within a process.
Associated with each identifier is a data structure that contains state information for the IPC mechanism, as well as ownership and permissions information. The ownership and permissions information is defined in a structure in the <sys/ipc.h> header file as follows:
typedef struct ipc_perm {
uid_t uid; /* Owner's user ID */
gid_t gid; /* Owner's group ID */
uid_t cuid; /* Creator's user ID */
gid_t cgid; /* Creator's group ID */
mode_t mode; /* Access modes */
} ipc_perm_t;
This structure is similar to a file permissions structure, and is initialized by the thread that creates the IPC mechanism. It is then checked by all subsequent IPC operations to determine if the requesting thread has the required permissions to perform the operation.
To get an identifier, a thread must either create a new IPC mechanism or access an existing mechanism. This is done through the msgget(), semget(), and shmget() functions. Each get operation takes as input a key parameter and returns an identifier. Each get operation also takes a flag parameter. This flag parameter contains the IPC permissions for the mechanism as well as bits that determine whether or not a new mechanism is created. The rules for whether a new mechanism is created or an existing one is referred to are as follows:
When a message queue, semaphore set, or shared memory segment is created, the thread that creates it determines how it can be accessed. The thread does this by passing mode information in the low-order 9 bits of the flag parameter on the msgget(), semget(), or shmget() function call. This information is used to initialize the mode field in the ipc_perm structure. The values of the bits are given below in hexadecimal notation:
Subsequent IPC operations do a permission test on the calling thread before allowing the thread to perform the requested operation. This permission test is done in one of three forms:
Message queues provide a form of message passing in which any process (given that it has the necessary permissions) can read a message from or write a message to any IPC message queue on the system. There are no requirements that a process be waiting to receive a message from a queue before another process sends one, or that a message exist on the queue before a process requests to receive one.
Every message on a queue has the following attributes:
A thread gets a message queue identifier by calling the msgget() function. Depending on the key and msgflg parameters passed in, either a new message queue is created or an existing message queue is accessed. When a new message queue is created, a data structure is also created to contain information about the message queue. This structure is defined in the <sys/msg.h> header file as follows:
typedef struct msqid_ds {
struct ipc_perm msg_perm; /* Operation permission struct */
msgqnum_t msg_qnum; /* # msgs currently on queue */
msglen_t msg_qbytes; /* Max # bytes allowed on queue*/
pid_t msg_lspid; /* Process ID of last msgsnd() */
pid_t msg_lrpid; /* Process ID of last msgrcv() */
time_t msg_stime; /* Time of last msgsnd() */
time_t msg_rtime; /* Time of last msgrcv() */
time_t msg_ctime; /* Time of last change */
} msqid_ds_t;
A thread puts a message on a message queue by calling the msgsnd() function. The following parameters are passed in:
A thread gets a message from a message queue by calling the msgrcv() function. The following parameters are passed in:
A thread removes a message queue ID by calling the msgctl() function. The thread also can use the msgctl() function to change the data structure values associated with the message queue ID or to retrieve the data structure values associated with the message queue ID. The following parameters are passed in:
For details on the message queue functions, see the following:
| Function | See . . . |
|---|---|
| ftok() | ftok()--Generate IPC Key from File Name |
| msgctl() | msgctl()-Perform Message Control Operations |
| msgget() | msgget()-Get Message Queue |
| msgrcv() | msgrcv()-Receive Message Operation |
| msgsnd() | msgsnd()-Send Message Operation |
IPC message queues differ from the message queue definition in the X/Open single UNIX specification (formerly Spec 1170) in the following ways:
A semaphore is a synchronization mechanism similar to a mutex or a machine interface (MI) lock. It can be used to control access to shared resources, or used to notify other threads of the availability of resources. It differs from a mutex in the following ways:
Thus, a semaphore can be used as a resource counter or as a lock.
A process gets a semaphore set identifier by calling the semget() function. Depending on the key and semflg parameters passed in, either a new semaphore set is created or an existing semaphore set is accessed. When a new semaphore set is created, a data structure is also created to contain information about the semaphore set. This structure is defined in the <sys/sem.h> header file as follows:
typedef struct semid_ds {
struct ipc_perm sem_perm; /* Permissions structure */
unsigned short sem_nsems; /* Number of sems in set */
time_t sem_otime; /* Last sem op time */
time_t sem_ctime; /* Last change time */
} semtablentry_t;
A thread performs operations on one or more of the semaphores in a set by calling the semop() function. The following parameters are passed in:
The sembuf structure is defined in the <sys/sem.h> header file as follows:
struct sembuf {
unsigned short sem_num; /* Semaphore number */
short sem_op; /* Semaphore operation */
short sem_flg; /* Operation flags */
};
The operation performed on a semaphore is specified by the sem_op field, which can be positive, negative, or zero:
The sem_flg value specifies whether or not the thread is willing to wait, and also whether or not the thread wants the system to keep a semaphore adjustment value for the semaphore.
Semaphore waits are visible from the Work with Active Jobs display. A thread waiting on a semaphore in a semaphore set appears to be in a semaphore wait state (SEMW) on the Work with Threads display (requested using the WRKJOB command and taking option 20). Displaying the call stack of the thread shows the semop() function near the bottom of the stack.
A thread removes a semaphore set ID by calling the semctl() function. The thread also can use the semctl() function to change the data structure values associated with the semaphore set ID or to retrieve the data structure values associated with the semaphore set ID. The following parameters are passed in:
In addition, the semctl() function can perform various other control operations on a specific semaphore within a set, or on an entire semaphore set:
For details on the semaphore
set
functions, see the following:
| Function | See . . . |
|---|---|
| ftok() | ftok()--Generate IPC Key from File Name |
| semctl() | semctl()-Perform Semaphore Control Operations |
| semget() | semget()-Get Semaphore Set with Key |
| semop() | semop()-Perform Semaphore Operations on Semaphore Set |
IPC semaphore sets differ from the semaphore definition in the X/Open single UNIX specification (formerly Spec 1170) in the following ways:
IPC handles semaphores at the thread level. An IPC semaphore:
Processes and threads can communicate directly with one another by sharing parts of their memory space and then reading and writing the data stored in the shared memory. Synchronization of shared memory is the responsibility of the application program. Semaphores and mutexes provide ways to synchronize shared memory use across processes and threads.
A thread gets a shared memory identifier by calling the shmget() function. Depending on the key and shmflg parameters passed in, either a new shared memory segment is created or an existing shared memory segment is accessed. The size of the shared memory segment is specified by the size parameter. When a new shared memory segment is created, a data structure is also created to contain information about the shared memory segment. This structure is defined in the <sys/shm.h> header file as follows:
typedef struct shmid_ds {
struct ipc_perm shm_perm; /* Operation permission struct*/
int shm_segsz; /* Segment size */
pid_t shm_lpid; /* Process id of last shmop */
pid_t shm_cpid; /* Process id of creator */
int shm_nattch; /* Current # attached */
time_t shm_atime; /* Last shmat time */
time_t shm_dtime; /* Last shmdt time */
time_t shm_ctime; /* Last change time */
} shmtablentry_t;
A process gets addressability to the shared memory segment by attaching to it using the shmat() function. The following parameters are passed in:
A process detaches a shared memory segment by calling the shmdt() function. The only parameter passed in is the shared memory segment address. The process implicitly detaches from the shared memory when the process ends.
A thread removes a shared memory ID by calling the shmctl() function. The thread also can use the shmctl() function to change the data structure values associated with the shared memory ID or to retrieve the data structure values associated with the shared memory ID. The following parameters are passed in:
For details on shared memory functions, see the following:
| Function | See . . . |
|---|---|
| ftok() | ftok()--Generate IPC Key from File Name |
| shmat() | shmat()-Attach Shared Memory Segment to Current Process |
| shmctl() | shmctl()-Perform Shared Memory Control Operations |
| shmdt() | shmdt()-Detach Shared Memory Segment from Calling Process |
| shmget() | shmget()-Get ID of Shared Memory Segment with Key |
The IPC shared memory segments are created as teraspace= shared memory segments or as nonteraspace shared memory segments. A teraspace shared memory segment is accessed by adding the shared memory segment to a process's teraspace. A teraspace is a space that has a much larger capacity than other OS/400 spaces and is addressable from only one process. A nonteraspace shared memory segment creates shared memory using OS/400 space objects.
A teraspace shared memory segment is created if SHM_TS_NP is specified on the shmflag parameter of shmget() or if a shared memory segment is created from a program that was compiled using the TERASPACE(*YES *TSIFC) option of CRTBNDC or CRTCMOD. The following capabilities and restrictions apply for teraspace shared memory segments.
The nonteraspace IPC shared memory differs from the shared memory definition in the X/Open single UNIX specification (formerly Spec 1170) in the following ways:
The named and unnamed semaphores on OS/400 differ from the other IPC mechanisms in this chapter in that they do not have an IPC identifier associated with them. Instead, pointers to the semaphore are used to operate on the semaphore. Before using a semaphore, a process must obtain a pointer to the semaphore. Unlike a semaphore set, a named or unnamed semaphore only refers to a single semaphore. A semaphore contains a value, a maximum value, and a title.
There are two types of semaphores: named and unnamed semaphores. Once a semaphore is created and a pointer to the semaphore is obtained, the same operations are used to manipulate the values of both types of semaphores. Like the semaphores in a semaphore set, a named or unnamed semaphore has a non-zero value. A semaphore can be used as a resource counter or as a lock. A thread decrements a semaphore to obtain one or more associated resources, and increments the semaphore to release the resource. A semaphore also has a maximum value associated with it. An attempt to increment the value of a semaphore above its maximum value results in an error.
Besides a value, named and unnamed semaphores contain a maximum value and a title. The maximum value sets the highest value that the semaphore value may obtain. The title is a null-terminated string with a maximum size of 16 characters that are associated with the semaphore and may be used to contain debugging information. The titles associated with named and unnamed semaphores may be obtained by using the QP0ZOLIP() API.
A process obtains a pointer to a named semaphore by calling the sem_open() or sem_open_np() functions. These functions find the semaphore associated with a name. The name is a character string, interpreted in the CCSID of the job. The name may be structured so that it looks like a pathname. This name, however, has no relationship to any file system. If the semaphore exists and the process has permission to use the semaphore, then the system allocates memory for the semaphore and returns a pointer to the caller. If the semaphore does not exist, it will be created if the appropriate flags are set. When a new named semaphore is created, the permissions of the semaphore are set using the information provided by the mode parameter. These permissions are the same as those used by the identifier- based IPC services. The sem_open_np() function permits the caller to set the maximum value and title of a semaphore when creating a named semaphore. When a process is finished using a named semaphore, it should call sem_close() to close the semaphore. The semaphore is also explicitly closed when a process terminates. When a named semaphore will no longer be needed, it can be removed from the system using sem_unlink().
A process obtains a pointer to an unnamed semaphore calling the sem_init() or sem_init_np() functions. These functions initialize a semaphore at the specified memory location. The sem_init_np() function permits the caller to set the maximum value and title of a unnamed semaphore when it is created. When a process is finished using an unnamed semaphore, it should call sem_destroy() to destroy the semaphore and release system resources associated with that semaphore.
A process decrements by one the value of a semaphore using the sem_wait() and sem_wait_np() functions. If the value of the semaphore is currently zero, then the thread is blocked until the value of the semaphore is incremented or until the time specified on the sem_wait_np() has expired. The sem_trywait() call may be used to decrement the value of the semaphore if it is greater than zero. If the current value of the semaphore is zero, then sem_trywait() will return an error. The sem_post()and sem_post_np()functions are used to increment the value of a semaphore. After the value of the semaphore is incremented, it may be decremented immediately by threads that have blocked trying to decrement the semaphore.
Named and unnamed semaphore waits are visible from the Work with Active Jobs display. A thread waiting on a named or unnamed semaphore will be in a semaphore wait state (SEMW).
The sem_getvalue()function returns the value of the semaphore if the value is greater than or equal to zero. If there are threads waiting on the semaphore, sem_getvalue() returns a negative number whose absolute value is the number of threads waiting on the semaphore.
For details on the semaphore functions, see the following.
| Function | See . . . |
|---|---|
| sem_close() | sem_close()-Close Named Semaphore |
| sem_destroy() | sem_destroy()-Destroy Unnamed Semaphore |
| sem_getvalue() | sem_getvalue()-Get Semaphore Value |
| sem_init() | sem_init()-Initialize Unnamed Semaphore |
| sem_init_np() | sem_init_np()-Initialize Unnamed Semaphore with Maximum Value |
| sem_open() | sem_open()-Open Named Semaphore |
| sem_open_np() | sem_open_np()-Open Named Semaphore with Maximum Value |
| sem_post() | sem_post()-Post to Semaphore |
| sem_post_np() | sem_post_np()-Post Value to Semaphore |
| sem_trywait() | sem_trywait()-Try to Decrement Semaphore |
| sem_unlink() | sem_unlink()-Unlink Named Semaphore |
| sem_wait() | sem_wait()-Wait for Semaphore |
| sem_wait_np() | sem_wait_np()-Wait for Semaphore with Timeout |
Figure 1-1 lists the IPC functions and what they do.
Figure 1-1. Interprocess Communication Functions
| Function | Description |
|---|---|
| ftok() | Generate IPC Key from File Name |
| msgctl() | Perform message control operations |
| msgget() | Get message queue |
| msgrcv() | Receive message operation |
| msgsnd() | Send message operation |
| QP0ZDIPC | Delete interprocess communication objects |
| QP0ZOLIP | Open list of interprocess communication objects |
| QP0ZOLSM | Open list of semaphores |
| QP0ZRIPC | Retrieve an interprocess communication object |
| sem_close() | Close named semaphore |
| sem_destroy() | Destroy unnamed semaphore |
| sem_getvalue() | Get semaphore value |
| sem_init() | Initialize unnamed semaphore |
| sem_init_np() | Initialize unnamed semaphore with maximum value |
| sem_open() | Open named semaphore |
| sem_open_np() | Open named semaphore with maximum value |
| sem_post() | Post to semaphore |
| sem_post_np() | Post value to semaphore |
| sem_trywait() | Try to decrement semaphore |
| sem_unlink() | Unlink named semaphore |
| sem_wait() | Wait for semaphore |
| sem_wait_np() | Wait for semaphore with timeout |
| semctl() | Perform semaphore control operations |
| semget() | Get semaphore set with key |
| semop() | Perform semaphore operations on semaphore set |
| shmat() | Attach shared memory segment to current process |
| shmctl() | Perform shared memory control operations |
| shmdt() | Detach shared memory segment from current process |
| shmget() | Get ID of shared memory segment with key |
Note: These functions use header (include) files from the library QSYSINC, which is optionally installable. Make sure QSYSINC is installed on your system before using any of the functions. See for the file and member name of each header file.
The descriptions of the interprocess communication APIs are organized in alphabetical order as follows:
| Top | UNIX-Type APIs | APIs by category |
| [Information Center Home Page | Feedback ] | [Legal | AS/400 Glossary] |