C SC 340 Lecture 3: Processes

[ previous | schedule | next ]

What is a process?

Each process has identity

Every process is given an identifier when created, typically an integer value known as its Process I.D. (PID).

Each process has state

Scheduling and running Processes

Process creation

Process termination

Simple non-robust Unix example

#include <stdio.h>
#include <unistd.h>

int main() {
   if (fork()) {
      printf("I am parent, my PID is %d.\n",getpid());
      wait();
      printf("My child is terminated, I am leaving too.\n");
   } else {
      printf("I am child, my PID is %d.\n",getpid());
   }
   exit(0);
}
Sample output:
I am parent, my PID is 1265.
I am child, my PID is 1266.
My child is terminated, I am leaving too.

Concurrent Cooperating processes

With concurrency, multiple processes exist at the same time. If they communicate with each other or depend on each other, they are called cooperating processes.

Such communication of data or information is called interprocess communication (IPC). IPC mechanisms fall into two categories: shared memory and message passing

The classic example of cooperating processes is the producer-consumer problem. This is nicely illustrated by the classic I Love Lucy chocolate factory scene.

The producer and consumer cooperate through the shared buffer. There are variations on the problem for unbounded (infinite) buffer, bounded (finite) buffer, and zero capacity buffer.

Message-passing IPC facilities are simple in concept: send(toID, message) and receive(fromID, message) are the only calls required. They are varied and possibly complex in implementation.

Processes communicating via IPC may communicate directly, using their partner's process IDs as the toID and fromID. Alternatively, they may communicate indirectly, using a port or mailbox as the toID and fromID. In this case, both are the same ID and it must be accessible to both parties. Ports and mailboxes are akin to buffers because they are an intermediary. Communication is not limited to two parties: anyone who has access to the same shared ID can participate. Kind of like IM or chat.

The send and receive operations can be implemented differently to achieve different degrees of synchronization between sender and receiver.

Unix support for producer/consumer: pipe

The Unix pipe() system call creates a bounded buffer (size is system-dependent) through which two processes may interact, one as producer and the other as consumer. The producer will block if the pipe is full and the consumer will block if the pipe is empty.

To make the pipe shareable, it must be created before a fork(), then after the fork the parent and child processes both have access to the pipe and communicate by reading/writing to it.

Here is an example showing the setup of a pipe then after the fork the parent sends the child some characters through the pipe and the child reads and prints them. The pipe-related code is shown in boldface.

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

#define BUFFER_CAPACITY 6  

char *packets[] = { "Hello there" , " from me." };

int main() {

   int channels[2], readChannel, writeChannel, pipeCreateFailed;
   pid_t processID;

   pipeCreateFailed = pipe(channels);

   if (pipeCreateFailed) {
      printf("pipe failed.\n");
      exit(-1);
   } else {
      readChannel = channels[0];
      writeChannel = channels[1];
   }

   processID = fork(); 

   if (processID < 0) {            /*  fork failure  */

      printf("fork failed.\n");
      exit(-1);

   } else if (processID > 0) {     /*  parent code  */

      int numPackets, i;
      close(readChannel);
      numPackets = sizeof(packets)/sizeof(char *);
      for (i=0; i<numPackets; i++) {
         write(writeChannel, packets[i], strlen(packets[i]));
      }
      close(writeChannel); 
      wait();
      printf("parent terminates\n");

   } else {                  /*  child code  */

      char buffer[BUFFER_CAPACITY+1];
      int bytes;
      close(writeChannel);
      while ( (bytes = read(readChannel, buffer, BUFFER_CAPACITY)) != 0 ) {
         buffer[bytes] = '\0';
         printf("%d chars :%s: received by child\n", bytes, buffer);
      }
      close(readChannel); 
      printf("child terminates\n");

   }

   exit(0);
}
 

This code requires a little explanation:

Internet Client-Server communication: sockets

Sockets are a low-level IPC (InterProcess Communication) mechanism through which client and server processes may communicate with each other across the Internet. Both may reside on the same machine or on different machines.

This is an asymmetric arrangement. Before communication can occur, the server process must be running and listening for client requests on a specific port. In order to make a request, the client must first know the IP address and port number associated with the server process. It makes the request, rendezvous occurs and communication proceeds according to the protocol defined by their respective programs. The server continues to run after finished with the client.

Here is a simple sockets-based application for an upper-case server. Client sends a string and the server converts the string to all upper case then sends the result back. The example code is shown in Java because it is somewhat cleaner than the equivalent C code. All input and output is done through stream objects that are attached to the socket.

The Client
import java.io.*; 
import java.net.*;
 
class TCPClient { 
    static final String SERVER = "PeteSanderson.otterbein.edu";
    static final int PORT = 6789;
    public static void main(String argv[]) throws Exception { 
        String sentence; 
        String modifiedSentence; 

        BufferedReader inFromUser = 
                         new BufferedReader(new InputStreamReader(System.in)); 

        Socket clientSocket = new Socket(SERVER, PORT); 

        DataOutputStream outToServer = 
                        new DataOutputStream(clientSocket.getOutputStream()); 
        BufferedReader inFromServer = 
                        new BufferedReader(new
                        InputStreamReader(clientSocket.getInputStream())); 
        sentence = inFromUser.readLine(); 
        outToServer.writeBytes(sentence + '\n'); 
        modifiedSentence = inFromServer.readLine(); 
        System.out.println("FROM SERVER: " + modifiedSentence); 
        clientSocket.close(); 
    } 
} 
   
The Server
import java.io.*; 
import java.net.*; 

class TCPServer { 
    static final int PORT = 6789;
    public static void main(String argv[]) throws Exception {
       String clientSentence; 
       String capitalizedSentence; 

       ServerSocket welcomeSocket = new ServerSocket(PORT); 
  
       while(true) { 
           Socket connectionSocket = welcomeSocket.accept(); 
           BufferedReader inFromClient = 
                          new BufferedReader(new
                          InputStreamReader(connectionSocket.getInputStream())); 
           DataOutputStream  outToClient = 
                          new DataOutputStream(connectionSocket.getOutputStream()); 
           clientSentence = inFromClient.readLine(); 
           capitalizedSentence = clientSentence.toUpperCase() + '\n'; 
           outToClient.writeBytes(capitalizedSentence); 
        } 
    } 
} 
   
   


[ C SC 340 | Peter Sanderson | Math Sciences server  | Math Sciences home page | Otterbein ]

Last updated:
Peter Sanderson (PSanderson@otterbein.edu)