Processes

Overview

Terminology
Process States

There are several states in which a process can be. They are mutually-exclusive, so a process can only be in exactly one of these states at any given time:

State Transitions

  1. (Admitted) The process has been created and is now being put into the ready/runnable queue.
  2. (Dispatched) The OS has selected a process to run and is executing it on the CPU.
  3. (Timeout) The process has used up its allotted time slice and is put back in the queue for later execution.
  4. (Need I/O or event to happen) The process has requested I/O or has requested to wait until a future event.
  5. (I/O done or event occurred) The I/O has completed or event has occurred that the process was waiting on.
  6. (Ending) The process has completed its task or the system has terminated the process.

Utilities like psps, top, htop (on Linux, with GUIs ksysguard, gnome-system-monitor) and Task ManagerTask Manager and Process Explorer (on Windows) can give you lots of detailed information about all of the processes on a computer.

From the ps man page on Linux:

PROCESS STATE CODES

 Here are the different values that the s, stat and state output specifiers (header "STAT" or "S") will 
 display to describe the state of a process.
       D    Uninterruptible sleep (usually IO)
       R    Running or runnable (on run queue)
       S    Interruptible sleep (waiting for an event to complete)
       T    Stopped, either by a job control signal or because it is being traced.
       W    paging (not valid since the 2.6.xx kernel)
       X    dead (should never be seen)
       Z    Defunct ("zombie") process, terminated but not reaped by its parent.

       For BSD formats and when the stat keyword is used, additional characters may be displayed:
       <    high-priority (not nice to other users)
       N    low-priority (nice to other users)
       L    has pages locked into memory (for real-time and custom IO)
       s    is a session leader
       l    is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
       +    is in the foreground process group


Process Control Block (PCB)

Each process has a block of memory (typically a C struct) that contains all of the relevant information about a process. The PCB can contain a massive amount of information. Some of the info includes:

Linux task_struct

The PCBs are kept on a linked-list structure that represents a queue for various devices (CPU, disk, etc.)

Operating System Concepts - 8th Edition Silberschatz, Galvin, Gagne ©2009  

Process Scheduling

Types of Scheduling Context Switching

Process Creation

Process Creation The POSIX fork Function

DO NOT use the technique above (WNOHANG) for any assignments in the class unless specifically instructed to do so. Doing so will cause you to lose significant points. I'm only showing this technique so that, in the future (outside of this class), you may find that you need this capability.

The exec Function

Win32 Process Creation

CreateProcess Example (CreateProcess.cpp)
#include <iostream>
#include <windows.h>

int main(void) 
{
  STARTUPINFO start_info;
  PROCESS _INFORMATION proc_info;
  
  DWORD pid = GetCurrentProcessId();
  std::cout << "parent pid = " << pid << std::endl;

    // allocate memory and set to 0
  ZeroMemory(&start_info, sizeof(STARTUPINFO));
  ZeroMemory(&proc_info, sizeof(PROCESS_INFORMATION));
  
  std::cout << "creating child process" << std::endl;
  const char *program = "c:\\windows\\system32\\notepad.exe";
  BOOL err = CreateProcess(program,     // program to run
                           0,           // command line
                           0,           // security attributes
                           0,           // thread attributes
                           FALSE,       // don't inherit handles
                           0,           // creation flags (none)
                           0,           // use parent's environment
                           0,           // use parent's directory
                           &start_info, // start up info
                           &proc_info   // process info
                          );
  
  if (!err)
  {
    std::cout << "Error creating process" << std::endl;
    return -1;
  }

  std::cout << "waiting for child to terminate" << std::endl;
  WaitForSingleObject(proc_info.hProcess, INFINITE);
  std::cout << "parent terminating" << std::endl;

  CloseHandle(proc_info.hProcess);
  CloseHandle(proc_info.hThread);

  return 0;
}
Creating Multiple Processes Example
#include <stdio.h>
#include <windows.h>

int main(void) 
{
  const int COUNT = 2;

  HANDLE proc[COUNT], thread[COUNT];
  const char *programs[] = {"c:\\windows\\system32\\notepad.exe",
                             "c:\\windows\\system32\\mspaint.exe",
                            };

  for (int i = 0; i < COUNT; ++i) 
  {
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory(&si, sizeof(si));
    ZeroMemory(&pi, sizeof(pi));

    CreateProcess(programs[i], 0, 0, 0, FALSE, 0, 0, 0, &si, &pi);

    proc[i] = pi.hProcess;
    thread[i] = pi.hThread;
  }

  WaitForMultipleObjects(COUNT, proc, TRUE, INFINITE);

  for (int i = 0; i < COUNT; ++i) 
  {
    printf("Process: %i, Thread: %i ended.\n", proc[i], thread[i]);
    CloseHandle(proc[i]);
    CloseHandle(thread[i]);
  }
  return 0;
}

Process Termination

Voluntary exits: Involuntary exits:

Interprocess Communication (IPC)

Shared memory Message queues

POSIX pipes


Self check: Programming Problem 3.18 from the suggested textbook.

"Design a program using ordinary pipes in which one process sends a string message to a second process, and the second process reverses the case of each character in the message and sends it back to the first process. For example, if the first process sends the message Hi There, the second process will return hI tHERE. This will require using two pipes, one for sending the original message from the first to the second process, and the other for sending the modified message from the second back to the first process."

#include <stdio.h>    /* printf, fgets            */
#include <stdlib.h>   /* exit                     */
#include <string.h>   /* strlen                   */
#include <ctype.h>    /* isalpha, toupper         */
#include <unistd.h>   /* pipe, read, write, close */
#include <sys/wait.h> /* wait                     */

void revcase(char *buffer)
{
  int i;
  int len = strlen(buffer);
  for (i = 0; i < len; i++)
  {
    if (isupper(buffer[i]))
      buffer[i] = tolower(buffer[i]);
    else if (islower(buffer[i]))
      buffer[i] = toupper(buffer[i]);
  }
}

int main(void) 
{
  int pid;

  /* setup stuff */
  
  pid = fork();
  
  if (pid == 0) /* child */
  {
  
    /* DO STUFF */  
  
    exit(0); 
  }
  else /* parent */
  {
    /* DO STUFF */
      
    wait(NULL);  
  }
  
  return 0;
}