Interprocess Communication
   - A process is an instance of a program that is being executed by the OS.
   - In UNIX, the *only* way to create a process is with the fork()
     system call.
          -  The operating system creates the "init" process (that's
             PID 1).  The init process reads /etc/inittab to figure
             out what process to run next.
          -  Two important processes are "ttymon" that monitors terminals
             and "inetd" that starts networking programs.
   - Every process has a process ID (a PID) and a parent process ID
There are several ways that two prcessses can communicate:
    If the processes are on the same machine:
    1. A process can send a "signal" to another process (the OS can
       send a signal as well).
       - A signal is a notification to a process that an event has
       - Signals are "asynchronous".  That means a process does not know
         when they will occur.
       - Every signal has a signal number.
         Some examples:
             SIGINT   --  OS sends to a process when interrupt key is
                          hit (for example, control-C).
             SIGUSR1  --  User defined signal.
       - Every signal has a "default action".  For example, the kernel will
         send you a SIGSEGV if you access a bad memory location.  The default
         action for that is to dump core and exit.
       - You can change the default action.
       - For example:
            To send a signal:
            int kill(int pid, int signal_number)
            i.e.,  kill(1234, SIGUSR1);
            To handle a signal:
            void (*sigset (int sig, void (*disp)(int)))(int);
               void doit(int);
               sigset(SIGUSR1, doit);
       - More on this later.........
     2.  Process can use file and record locking
         - advisory and mandatory
         - applications use lockf() and fcntl().
     3.  Process can use "semaphore" files
         -  process A does an action, then creates file_1 when done.
         -  process B checks if file_1 exists
     4. Process can use pipes.
        - Example:
                int filedes[2];
                if (pipe(filedes) < 0) {
                   printf("can't create pipe\n");
         pipe() creates an I/O mechanism called a pipe and  returns
         two  file  descriptors,  fildes[0] and fildes[1].  The files
         associated with fildes[0] and fildes[1] are both  opened  for
         reading  and  writing.
         A read from fildes[0] accesses the data written to fildes[1]
         on  a  first-in-first-out  (FIFO)  basis  and  a  read  from
         fildes[1] accesses the data written to fildes[0] also  on  a
         FIFO basis.
         - General actions:
               - process creates a pipe;
               - fork;
               - parent closes filedes[0];
               - child closes filedes[1];
               - parent writes to filedes[1];
               - child reads from filedes[0];
     5. Process can use "Named Pipes"
        - A named pipes exists in the file system
                $ mknod /tmp/FIFO p
        - Process A can open and write;
        - Process B can open and read;
     6. Processes can use message queues, semaphores, and shared memory.
        - A message queue is memory in the kernel that lets
          process read and write to queues.
           - a process creates a message queue with the msgget() routine.
           - other processes can place data on the queue and extrace data from
             the queue by using msgsnd() and msgrcv().
        - A shared memory segment is common space in the kernel that
          several processes can read and write to.
           - use shmget() get create a shared memory segment, access
             it by mapping the memory to local variables with the
             shmat() routine.
           - semaphores allow synchronization.
    If the processes are on the different machines:
        o Server routines:
            -- socket( )
                  o Creates a transport endpoint
                  o Specify datagram or virtual circuit
            -- bind( )
                  o Allows server to attach itself to an address
            -- listen( )
                  o Marks a transport endpoint as ``listen state''
            -- accept( )
                  o Returns an accepted connection
            -- read( )
                  o Reads data from transport endpoint
            -- write( )
                  o Writes data to transport endpoint
        o Client routines:
            -- socket( )
                  o Creates a transport endpoint
                  o Specify datagram or virtual circuit
            -- connect( )
                  o Binds to a return port
                  o Issues a connection request to server
            -- read( )
                  o Reads data from transport endpoint
            -- write( )
                  o Writes data to transport endpoint
                  socket( )
                   bind( )
                     |                                 Client
                  listen( )                            socket( )
                     |                                     |
                     |                                     |
                     |                                     |
                  accept( )                            connect( )
                     |                                     |
                      <----------------------------------  |
                     |                                     |
                      <------------------------------  write( )
                   read( )                                 |
                     |                                     |
                     |                                     |
                     |                                     |
                     |                                     |
                   write( ) --------------------------->
                                                        read( )
        o We will implement the query protocol using the socket
        o The connection-oriented version uses TCP/IP
        o The connectionless version uses UDP/IP
        o Parts of the code (i.e., name lookup routines) will be
          different for other transport providers
          #include <stdio.h>
          #include <sys/types.h>
          #include <sys/socket.h>
          #include <netinet/in.h>
          #define SERV_PORT  5134
          #define MAXNAME    1024
               int socket_fd;       /* file descriptor into transport */
               int recfd;           /* file descriptor to accept on   */
               int length;          /* length of address structure    */
               struct sockaddr_in myaddr; /* address of this service  */
               struct sockaddr_in client_addr; /* address of client   */
               signal(SIGCHLD, SIG_IGN);
               /* Get a socket into TCP/IP */
               if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
                    perror("socket failed");
                *  Set up our address
               bzero((char *)&myaddr, sizeof(myaddr));
               myaddr.sin_family = AF_INET;
               myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
               myaddr.sin_port = htons(SERV_PORT);
        o The sockaddr structure is defined as follows:
          struct sockaddr {
              u_short sa_family;
              char    sa_data[14];
        o sa_family specifies the address family
        o sa_data holds the address
        o Applications cast transport specific addresses into this
        o The sockaddr_in structure holds internet addresses, and is
          defined as follows:
          struct sockaddr_in {
              short   sin_family;
              u_short sin_port;
              struct  in_addr sin_addr;
              char    sin_zero[8];
        o sin_port is the port number
        o sin_addr is the host IP address (32 bits)
        o sin_zeros fills in the remaining 8 bytes of the address
          allocated in the sockaddr structure
                *  Bind to the address the service will be offered
                *  over.
               if (bind(socket_fd, (struct sockaddr *)&myaddr,
                                      sizeof(myaddr)) < 0) {
                    perror("bind failed");
                *  Set the socket up for listening, with a queue
                *  length of 1
               if (listen(socket_fd, 1) < 0) {
                    perror("listen failed");