Interprocess Communication APIs

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:

Identifier Based Services

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:

Bit
Meaning
X'0100'
Read by user
X'0080'
Write by user
X'0020'
Read by group
X'0010'
Write by group
X'0004'
Read by others
X'0002'
Write by others

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

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

Message Queue Differences and Restrictions

IPC message queues differ from the message queue definition in the X/Open single UNIX specification (formerly Spec 1170) in the following ways:

Semaphore Sets

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

Semaphore Set Differences and Restrictions

IPC semaphore sets differ from the semaphore definition in the X/Open single UNIX specification (formerly Spec 1170) in the following ways:

Shared Memory

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

Shared Memory Differences and Restrictions

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:

Pointer Based Services

Named and Unnamed Semaphores

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

Interprocess Communication APIs--Summary

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]