Interprocess Communication
A process can be of two types:
   • Independent process.
   • Co-operating process.
• An independent process is not affected by the execution of other processes
  while a co-operating process can be affected by other executing processes.
• Inter-process communication (IPC) is a mechanism that allows processes to
  communicate with each other and synchronize their actions.
• An Interprocess communication (IPC) can happen between the processes
  within the same host or between the processes on different hosts.
• The communication between these processes can be seen as a method of
  co-operation between them.
• Within same hosts – pipes, FIFO, message queues, semaphores, and
  shared memory.
• Different hosts – sockets and streams.
Pipes
         Write                                            Read
                                     Pipe
         fd[1]                                            fd[0]
A pipe is created by calling the pipe function.
 Two file descriptors are returned through the fd argument: fd[0] is open
 for reading, and fd[1] is open for writing. The output of fd[1] is the
 input for fd[0].
Pipes have two limitations.
1. They have been half duplex (i.e., data flows in only one direction)
   (unidirectional). Some systems now provide full-duplex pipes, but for
   maximum portability, we should never assume that this is the case.
2. Pipes can be used only between processes that have a common
   ancestor. Normally, a pipe is created by a process, that process calls
   fork, and the pipe is used between the parent and the child.
• In Single Process
• The process that calls pipe then calls fork, creating an IPC channel
  from the parent to the child, or vice versa.
• When one of the pipe is closed:
   • Read – Write end is closed --- returns 0 //nothing more to read
   • Write – Read end is closed --- signal called SIGPIPE will be generated.
                             Returns -1 and error EPIPE
Program 2
popen and pclose Functions
• These two functions handle all the work that: creating a pipe, forking a child,
  closing the unused ends of the pipe, executing a shell to run the command, and
  waiting for the command to terminate.
• The function popen does a fork and exec to execute the cmdstring and
  returns a standard I/O file pointer. If type is "r", the file pointer is connected to
  the standard output of cmdstring. If type is "w", the file pointer is connected to
  the standard input of cmdstring.
Coprocesses
• Filter – A program that reads from standard input or writes to standard
  output.
• Filters are normally connected linearly in shell pipelines.
• A filter becomes a coprocess when the same program generates the filter’s
  input and reads the filter’s output.
• A coprocess normally runs in the background from a shell, and its standard
  input and standard output are connected to another program using a pipe.
FIFOs
• FIFOs are sometimes called named pipes.
• Unnamed pipes can be used only between related processes when a common
  ancestor has created the pipe.
• With FIFOs, however, unrelated processes can exchange data.
• Recall that a FIFO is a type of file.
• One of the encodings of the st_mode member of the stat structure
  indicates that a file is a FIFO.
• We can test for this with the S_ISFIFO macro.
Creating a FIFO is similar to creating a file. Indeed, the pathname for a FIFO
exists in the file system.
• Once we have used mkfifo or mkfifoat to create a FIFO, we open it using
  open.
• Indeed, the normal file I/O functions (e.g., close, read, write, unlink) all
  work with FIFOs.
There are two uses for FIFOs.
1. FIFOs are used by shell commands to pass data from one shell pipeline to
   another without creating intermediate temporary files.
2. FIFOs are used as rendezvous points in client–server applications to pass
   data between the clients and the servers.
Example - Using FIFOs to Duplicate Output Streams
• FIFOs can be used to duplicate an output stream in a series of shell commands.
• This prevents writing the data to an intermediate disk file (similar to using
  pipes to avoid intermediate disk files).
• But whereas pipes can be used only for linear connections between processes,
  a FIFO has a name, so it can be used for nonlinear connections.
Consider a procedure that needs to process a filtered input stream twice.
We create the FIFO and then start prog3 in the background, reading from the
FIFO. We then start prog1 and use tee to send its input to both the FIFO and
prog2.
Example — Client–Server Communication Using a
FIFO
• Another use for FIFOs is to send data between a client and a server. If we
  have a server that is contacted by numerous clients, each client can write its
  request to a well-known FIFO that the server creates.
• The problem in using FIFOs for this type of client–server communication is
  how to send replies back from the server to each client.
• A single FIFO can’t be used, as the clients would never know when to read
  their response versus responses for other clients.
• One solution is for each client to send its process ID with the request.
• The server then creates a unique FIFO for each client, using a pathname based
  on the client’s process ID.
• This arrangement works, although it is impossible for the server to tell whether
  a client crashes. A client crash leaves the client-specific FIFO in the file
  system.
• The server also must catch SIGPIPE, since it’s possible for a client to send a
  request and terminate before reading the response, leaving the client-specific
  FIFO with one writer (the server) and no reader.
                       Message Queues
Process A          1    2   3    4   5    6   7   8   9   10             Process B
• A message queue is a linked list of messages stored within the kernel and
  identified by a message queue identifier.
• We’ll call the message queue just a queue and its identifier a queue ID.
• A message queue is an inter-process communication (IPC) mechanism that
  allows processes to exchange data in the form of messages between two
  processes.
• It allows processes to communicate asynchronously by sending messages to
  each other where the messages are stored in a queue, waiting to be processed,
  and are deleted after being processed.
• The message queue is a buffer that is used in non-shared memory environments,
  where tasks communicate by passing messages to each other rather than by
  accessing shared variables. Tasks share a common buffer pool.
• The message queue is an unbounded FIFO queue that is protected from
  concurrent access by different threads.
Functions involved in message queues
• A new queue is created or an existing queue opened by msgget.
• New messages are added to the end of a queue by msgsnd.
• Every message has a positive long integer type field, a non-negative length, and
  the actual data bytes (corresponding to the length), all of which are specified to
  msgsnd when the message is added to a queue.
• Messages are fetched from a queue by msgrcv. We don’t have to fetch the
  messages in a first-in, first-out order. Instead, we can fetch messages based on
  their type field.
• The final function is msgctl, which is the control function.
Each queue has the following msqid_ds structure associated with it:
    This structure defines the current status of the queue.
  msgget()
The first function normally called is msgget to either open an existing queue or
create a new queue.
On success, msgget returns the non-negative queue ID. This value is then used
with the other three message queue functions.
When a new queue is created, the following members of the msqid_ds structure
are initialized.
 • The ipc_perm structure is initialized.
 • The mode member of this structure is set to the corresponding permission bits
   of flag.
 • msg_qnum, msg_lspid, msg_lrpid, msg_stime, and msg_rtime
   are all set to 0.
 • msg_ctime is set to the current time.
 • msg_qbytes is set to the system limit.
msgctl()
The msgctl function performs various operations on a queue.
The cmd argument specifies the command to be performed on the queue
specified by msqid.
• IPC_STAT - Fetch the msqid_ds structure for this queue, storing it in the
  structure pointed to by buf.
• IPC_SET - Copy the following fields from the structure pointed to by buf to the
  msqid_ds structure associated with this queue: msg_perm.uid,
  msg_perm.gid, msg_perm.mode, and msg_qbytes.
• IPC_RMID - Remove the message queue from the system and any data still on
  the queue. This removal is immediate.
 msgsnd()
 Data is placed onto a message queue by calling msgsnd.
• Messages are always placed at the end of the queue.
• The ptr argument points to a long integer that contains the positive integer
  message type, and it is immediately followed by the message data. (There is
  no message data if nbytes is 0.)
• If the largest message we send is 512 bytes, we can define the following
  structure:
The ptr argument is then a pointer to a mymesg structure. The message type
can be used by the receiver to fetch messages in an order other than first in,
first out.
• If flag value of IPC_NOWAIT is specified. - If the message queue is full
  (either the total number of messages on the queue equals the system limit, or
  the total number of bytes on the queue equals the system limit), specifying
  IPC_NOWAIT causes msgsnd to return immediately with an error of
  EAGAIN.
• If IPC_NOWAIT is not specified, we are blocked until there is room for the
  message, the queue is removed from the system, or a signal is caught and the
  signal handler returns. In the second case, an error of EIDRM is returned
  (‘‘identifier removed’’); in the last case, the error returned is EINTR.
msgrcv()
Messages are retrieved from a queue by msgrcv.
• As with msgsnd, the ptr argument points to a long integer (where the
  message type of the returned message is stored) followed by a data buffer for
  the actual message data.
• nbytes specifies the size of the data buffer. If the returned message is larger
  than nbytes and the MSG_NOERROR bit in flag is set, the message is
  truncated.
• If the message is too big and this flag value is not specified, an error of
  E2BIG is returned instead (and the message stays on the queue).
The type argument lets us specify which message we want.
• A nonzero type is used to read the messages in an order other than first in,
  first out.
• For example, the type could be a priority value if the application assigns
  priorities to the messages.
• Another use of this field is to contain the process ID of the client if a single
  message queue is being used by multiple clients and a single server (as long
  as a process ID fits in a long integer).
Semaphores
• A semaphore isn’t a form of IPC similar to the others that we’ve described
  (pipes, FIFOs, and message queues).
• A semaphore is a counter used to provide access to a shared data object for
  multiple processes.
To obtain a shared resource, a process needs to do the following:
1. Test the semaphore that controls the resource.
2. If the value of the semaphore is positive, the process can use the resource. In
   this case, the process decrements the semaphore value by 1, indicating that
   it has used one unit of the resource.
3. Otherwise, if the value of the semaphore is 0, the process goes to sleep until
   the semaphore value is greater than 0. When the process wakes up, it returns
   to step 1.
When a process is done with a shared resource that is controlled by a
semaphore, the semaphore value is incremented by 1. If any other processes are
asleep, waiting for the semaphore, they are awakened.
• Testing of a semaphore and its increment or decrement must be an atomic
  operation.
• Semaphores are normally implemented inside the kernel.
• A common form of semaphore is called a binary semaphore. It controls a
  single resource, and its value is initialized to 1.
The kernel maintains a semid_ds structure for each semaphore set:
semget()
semget is a function to obtain a semaphore ID.
When a new set is created, the following members of the semid_ds structure
are initialized.
• The ipc_perm structure is initialized.
• The mode member of this structure is set to the corresponding permission
  bits of flag.
• sem_otime is set to 0.
• sem_ctime is set to the current time.
• sem_nsems is set to nsems.
• The number of semaphores in the set is nsems.
• If a new set is being created (typically by the server), we must specify nsems.
  If we are referencing an existing set (a client), we can specify nsems as 0.
semctl()
The semctl function is the catchall for various semaphore operations.
semop()
The function semop atomically performs an array of operations on a
semaphore set.
Shared Memory
• Shared memory allows two or more processes to share a given region of
  memory.
• This is the fastest form of IPC, because the data does not need to be copied
  between the client and the server.
• The only trick in using shared memory is synchronizing access to a given
  region among multiple processes.
• If the server is placing data into a shared memory region, the client shouldn’t
  try to access the data until the server is done.
• Often, semaphores are used to synchronize shared memory access.
To use shared memory, we have to perform two basic steps:
1. Request a memory segment that can be shared between processes to the
   operating system.
2. Associate a part of that memory or the whole memory with the address
   space of the calling process.
The kernel maintains a structure with at least the following members for each
shared memory segment:
  shmget()
The first function called is usually shmget, to obtain a shared memory
identifier.
 shmctl()
The shmctl function is the catchall for various shared memory operations
shmat()
Once a shared memory segment has been created, a process attaches it
to its address space by calling shmat.
shmdt()
When we’re done with a shared memory segment, we call shmdt to detach
it.
Example 1 - Output
  Identifiers and Keys
• Each IPC structure (message queue, semaphore, or shared memory segment)
  in the kernel is referred to by a non-negative integer identifier.
• To send a message to or fetch a message from a message queue, for example,
  all we need know is the identifier for the queue.
• Unlike file descriptors, IPC identifiers are not small integers.
• Indeed, when a given IPC structure is created and then removed, the
  identifier associated with that structure continually increases until it reaches
  the maximum positive value for an integer, and then wraps around to 0.
• Whenever an IPC structure is being created (by calling msgget, semget,
  or shmget), a key must be specified.
• The data type of this key is the primitive system data type key_t, which
  is often defined as a long integer in the header <sys/types.h>.
• This key is converted into an identifier by the kernel.
ftok()
The only service provided by ftok is a way of generating a key from a
pathname and project ID.
• The path argument must refer to an existing file.
• Only the lower 8 bits of id are used when generating the key.
Permission Structure
• This structure defines the permissions and owner and includes at least
  the following members:
• All the fields are initialized when the IPC structure is created.
• At a later time, we can modify the uid, gid, and mode fields by calling
  msgctl, semctl, or shmctl.
• To change these values, the calling process must be either the creator of the
  IPC structure or the superuser.
• Changing these fields is similar to calling chown or chmod for a file.