| Processes The Basics | 
| 
                      Prof. David Bernstein | 
| Computer Science Department | 
| bernstdh@jmu.edu | 
         
      
main():
      -a all processes associated with terminals-A all processes-l generates a long listing 
            #include <stdio.h>
#include <stdlib.h>
// Define an initialized constant with file scope
const int constant_data = 11;
// Define an initialized variable with file scope
int initialized_data = 22;
// Tentatively define a variable with global (i.e., file) scope 
// which is in the Block Started by Symbol (BSS) section (a.k.a., the
// unitialized data block)
int bss_data;
// Remember, argc and argv are copies of the actual parameters
// (since parameters are passed by value)
int main(int argc, char* argv[])
{
  int    local_variable = 99;
  int*   heap_firstcall;
  int*   heap_secondcall;
  // Note that memory allocators often use blocks of different sizes,
  // the smallest of which is often 16 or 32 bytes. So, even though an
  // int requires less space, malloc() will allocate more (i.e., the
  // amount of the smallest block)
  heap_firstcall   = (int*)(malloc(sizeof(int)));
  heap_secondcall  = (int*)(malloc(sizeof(int)));
  // The %p specifier prints a pointer as a hex character sequence 
  printf("Stack        %p\n", &heap_secondcall); // Note the &
  printf(".            %p\n", &heap_firstcall);  // Note the &
  printf(".            %p\n", &local_variable);
  printf(".            %p\n", &argc);
  printf(".            %p\n", &argv);
  printf("\n");
  printf(".            %p\n", heap_secondcall);  // Note the lack of an &
  printf("Heap:        %p\n", heap_firstcall);   // Note the lack of an &
  printf("BSS:         %p\n", &bss_data);
  printf("Initialized: %p\n", &initialized_data);
  printf("Constants:   %p\n", &constant_data);
  printf("Code:        %p\n", &main);
  // Cleanup
  free(heap_firstcall);
  free(heap_secondcall);
  // Done
  return 0;
}
        
         
                           name=value
                        
                        export 
                        name
                        export 
                        name=value
                        setenv 
                        name 
                        value
                        printenv
                     unset (or
              unsetenv in the C shell)int setenv(const char *name, const char* value, int overwrite)
               | name | A pointer to the name of the environment variable | 
| value | A pointer to the value of the environment variable | 
| ovewrite | 0 not to overwrite an existing variable; nonzero to always set/reset | 
| Return | 0 on success; -1 on error | 
  Note: There is also a putenv()
               
                   function but you must be much more
  careful when using it because it does not duplicate the string it
  just points to it. Hence, the string must not be an automatic
  variable (i.e., a character on the stack) or an array that might change.
                function but you must be much more
  careful when using it because it does not duplicate the string it
  just points to it. Hence, the string must not be an automatic
  variable (i.e., a character on the stack) or an array that might change.
  
pid_t fork(void)
               | Return | Parent: PID of the child or -1. Child: 0 | 
  Note: fork() is called from the parent process but creates 
  a duplicate child process (containing the same instructions). So, there
  is a return in both the parent and the child.
  
fork())fork():
       
             
            main()
         void exit(int status)
               | status | 0 for a successful termination; positive otherwise | 
| Return | Does not return | 
  Note: exit() calls exit handlers (in reverse order of 
  registration), flushes the standard files, and calls _exit().
  
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
  const char* fname;
  FILE *input_file;
  int   arguments, grads, total;
  pid_t pid;
  pid = fork(); // The assignment will be performed in the parent and the child
  switch (pid)
    {
    case -1:
      exit(1);
    case 0:
      fname = "college_1990.dat";
      break;
    default:
      fname = "college_2000.dat";
      break;
    }
  total = 0;
  input_file = fopen(fname, "r");
  while (!feof(input_file))
    {
      arguments = fscanf(input_file, "%d\n", &grads);
      if (arguments == 1) total += grads;
    }
  fclose(input_file);
  
  printf("Total College Grads in %s: %d\n", fname, total);
  
  
  exit(0);
}
        
         #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
  const char* fname;
  FILE *input_file;
  float commute, total_commute;
  int   arguments, grads, n, total_grads;
  pid_t pid;
  pid = fork(); // The assignment will be performed in the parent and the child
  switch (pid)
    {
    case -1:
      exit(1);
    case 0:
      fname = "college_2000.dat";
      total_grads = 0;
      input_file = fopen(fname, "r");
      while (!feof(input_file))
        {
          arguments = fscanf(input_file, "%d\n", &grads);
          if (arguments == 1) total_grads += grads;
        }
      
      fclose(input_file);
      printf("Total College Grads in %s: %d\n", fname, total_grads);
      break;
    default:
      fname = "commute_2000.dat";
      total_commute = 0.0;
      n     = 0;
      input_file = fopen(fname, "r");
      while (!feof(input_file))
        {
          arguments = fscanf(input_file, "%f\n", &commute);
          if (arguments == 1)
            {
              total_commute += commute;
              ++n;
            }
        }
      fclose(input_file);
      printf("Average Commute in %s: %f\n", fname, total_commute/n);
      break;
    }
  
  exit(0);
}
        
         #include "stats_lib.h"
#include <stdio.h>
float
mean(const char *fname)
{
  FILE   *input_file;
  float  datum, total;
  int    arguments, n;
  total = 0.0;
  n     = 0;
  input_file = fopen(fname, "r");
  while (!feof(input_file))
    {
      arguments = fscanf(input_file, "%f\n", &datum);
      if (arguments == 1)
        {
          total += datum;
          ++n;
        }
    }
  fclose(input_file);
  return total / (double)n;
}
int
total(const char *fname)
{
  FILE *input_file;
  int   arguments, datum, total;
  total = 0;
  input_file = fopen(fname, "r");
  while (!feof(input_file))
    {
      arguments = fscanf(input_file, "%d\n", &datum);
      if (arguments == 1) total += datum;
    }
  fclose(input_file);
  return total;
}
        
         #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "stats_lib.h"
int
main(int argc, char *argv[])
{
  const char* fname;
  pid_t pid;
  pid = fork(); // The assignment will be performed in the parent and the child
  switch (pid)
    {
    case -1:
      exit(1);
    case 0:
      fname = "college_1990.dat";
      printf("Total College Grads in %s: %d\n", fname, total(fname));
      break;
    default:
      fname = "college_2000.dat";
      printf("Total College Grads in %s: %d\n", fname, total(fname));
      break;
    }
  exit(0);
}
        
         #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "stats_lib.h"
int
main(int argc, char *argv[])
{
  const char* fname;
  pid_t pid;
  pid = fork(); // The assignment will be performed in the parent and the child
  switch (pid)
    {
    case -1:
      exit(1);
    case 0:
      fname = "college_2000.dat";
      printf("Total College Grads in %s: %d\n", fname, total(fname));
      break;
    default:
      fname = "commute_2000.dat";
      printf("Average Commute in %s: %f\n", fname, mean(fname));
      break;
    }
  exit(0);
}
        
         #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "stats_lib.h"
int
main(int argc, char *argv[])
{
  const char* fname;
  pid_t pid, qid;
  pid = fork(); // fork once
  switch (pid)
    {
    case -1:
      exit(1);
    case 0:
      fname = "college_1990.dat";
      printf("Total College Grads in %s: %d\n", fname, total(fname));
      break;
    default:
      fname = "commute_1990.dat";
      printf("Average Commute in %s: %f\n", fname, mean(fname));
      break;
    }
  qid = fork(); // fork again
  switch (qid)
    {
    case -1:
      exit(1);
    case 0:
      fname = "college_2000.dat";
      printf("Total College Grads in %s: %d\n", fname, total(fname));
      break;
    default:
      fname = "commute_2000.dat";
      printf("Average Commute in %s: %f\n", fname, mean(fname));
      break;
    }
  exit(0);
}
        
         fork() there are two.
              Then, both the parent and child call fork()
              resulting in two more.
              commute_2000.dat
                     main() functions)
              exist for the tasks we want to perform in 
              the differentexec familyint execve(const char *path, char *const argv[], char *const envp[]);
               | path | The path name of the new program | 
| argv | The NULL-terminated (so the length can be determined) array of arguments to be passed to the new program | 
| envp | The NULL-terminated (so the length can be determined) array of name-value pairs specifying the environment list | 
| Return | Does not return on success; returns -1 on error | 
Note: The process ID remains the same and the file descriptors are left open.
errno
         | ENOENT | The file doesn't exist | 
| ENOEXEC | The file isn't in a recognizable format | 
| ETXTBSY | The file is open for writing | 
| E2BIG | The argument list and/or environment space are too big | 
execve()
         #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "stats_lib.h"
int
main(int argc, char *argv[])
{
  const char* fname;
  
  fname = argv[1];
  printf("Total College Grads in %s: %d\n", fname, total(fname));
  exit(0);
}
        
            
               
            
            #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "stats_lib.h"
int
main(int argc, char *argv[])
{
  const char* fname;
  fname = argv[1];
  printf("Average Commute in %s: %f\n", fname, mean(fname));
  exit(0);
}
        
         execve() (cont.)#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// Note: There is no reason to #include the headers for grads and
//       commute because neither the compiler nor linker uses them.
int
main(int argc, char *argv[])
{
  char *argvec[3];
  char *envvec[] = {NULL};
  pid_t pid;
  argvec[2] = NULL;
  pid = fork(); // The assignment will be performed in the parent and the child
  switch (pid)
    {
    case -1:
      exit(1);
    case 0:
      argvec[0] = (char*)"college";
      argvec[1] = (char*)"college_2000.dat";
      break;
    default:
      argvec[0] = (char*)"commute";
      argvec[1] = (char*)"commute_2000.dat";
      break;
    }
  execve(argvec[0], argvec, envvec);
}
        
         #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
  char *argvec[3];
  char *envvec[] = {NULL};
  pid_t pid;
  argvec[2] = NULL;
  pid = fork();  
  switch (pid)
    {
    case -1:
      exit(1);
    case 0:
      argvec[0] = (char*)"college";
      argvec[1] = (char*)"college_2000.dat";
      break;
    default:
      argvec[0] = (char*)"commute";
      argvec[1] = (char*)"commute_2000.dat";
      break;
    }
  execve(argvec[0], argvec, envvec);
  pid = fork();  
  switch (pid)
    {
    case -1:
      exit(1);
    case 0:
      argvec[0] = (char*)"college";
      argvec[1] = (char*)"college_1990.dat";
      break;
    default:
      argvec[0] = (char*)"commute";
      argvec[1] = (char*)"commute_1990.dat";
      break;
    }
  execve(argvec[0], argvec, envvec);
  
  exit(0);
}
        
         fork() there are two.
              Then the text/code segment of both processes is changed
              by execve() so the second call
              to fork() is never executed.
              #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
  char *argvec[3];
  char *envvec[] = {NULL};
  pid_t pid;
  argvec[2] = NULL;
  pid = fork();
  if      (pid == -1) exit(1);
  else if (pid ==  0) // First child
    {
      argvec[0] = (char*)"commute";
      argvec[1] = (char*)"commute_1990.dat";
      execve(argvec[0], argvec, envvec);
    }
  pid = fork();
  if      (pid == -1) exit(1);
  else if (pid ==  0) // Second child
    {
      argvec[0] = (char*)"commute";
      argvec[1] = (char*)"commute_2000.dat";
      execve(argvec[0], argvec, envvec);
    }
  pid = fork();
  if      (pid == -1) exit(1);
  else if (pid ==  0) // Third child
    {
      argvec[0] = (char*)"college";
      argvec[1] = (char*)"college_1990.dat";
      execve(argvec[0], argvec, envvec);
    }
  // Parent
  argvec[0] = (char*)"college";
  argvec[1] = (char*)"college_2000.dat";
  execve(argvec[0], argvec, envvec);
  
  exit(0);
}
        
         fork() creates a child
              exec() Familyexecve()
              are inconvenientexec functions with slightly
              different signatrues (e.g., execle(),
              execlp(), execvp(),
              execv(), and execl())waitpid()
                     pid_t waitpid(pid_t pid, int *status, int options);
               | pid | The process to wait for (see below) | 
| status | An outbound parameter used to indicate how the child terminated | 
| options | A bit mask | 
| Return | The PID of the child or 0; -1 on error | 
  Note: If pid is greater than 0 then this call returns
  when the specific process ID terminates. If pid is 0
  then this call returns when any child in the same process as the
  caller group terminates. If pid is less than -1 then
  this call returns when any child in the group of the absolute value
  of the PID terminates. If pid is -1 then it returns
  when any child terminates.
  
| WNOHANG | Return immediately (with a value of 0) if no member of the wait set has already terminated. | 
| WUNTRACED | Suspend execution of the calling process until a running member of the wait set is terminated or stopped. | 
| WCONTINUED | Suspend execution of the calling process until a cunning member of the wait set is terminated or a stopped member has been resumed. | 
pid_t wait(int *status);
               | status | An outbound parameter used to indicate how the child terminated | 
| Return | The PID of the child or 0; -1 on error | 
  Note: Calling wait(&status) is equivalent to calling
  waitpid(-1, &status; 0).
  
| WIFEXITED(status) | Returns true if the child exited normally. | 
| WIFSIGNALED(status) | Returns true if the child was 
    killed by a signal (and WTERMSIG(status)returns the 
    signal number). | 
| WIFSTOPPED(status) | Returns true if the child 
    that caused the return is stopped (and WSTOPSIG(status)then returns the signal number). | 
| WIFCONTINUED(status) | Returns true if the child that caused the return was resumed. | 
init - the ancestor of all processes)wait() (and has most of its resources returned
              to the system but the process ID and termination status
              are maintained)wait()/waitpid()
                     wait()/waitpid()
                     init)wait()/waitpid() to
              ensure that they don't create long-lived zombies
              (this is known as reaping)#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define N 5
int
main(void)
{
  pid_t   pid;
  printf("\nWhile the parent is sleeping, execute the following command");
  printf("(and note the <defunct> indicator).\n");
  printf("ps -l --ppid %ld\n\n", (long)getpid());
  
  for (int i=0; i<N; i++)
    {
      pid = fork();
      if (pid == 0) // This is a child process
        {
          printf("I'm child %d with a PID of %ld\n", i, (long)getpid());
          exit(0); // Terminate (and become a zombie)
        }
    }
  
  // Executed in the parent only
  sleep(20);
  printf("\nAfter the parent terminates, execute the command again.\n");
  printf("ps -l --ppid %ld\n\n", (long)getpid());
  exit(0);
}
        
         #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define N 5
int
main(void)
{
  pid_t   pid;
  printf("\nWhile the parent is sleeping, execute the command:\n");
  printf("ps --ppid %ld\n\n", (long)getpid());
  
  pid = fork();
  if (pid == 0) // This is a child process
    {
      // When the parent terminates, the child becomes an orphan
      // and is inherited by init (which has a PID of 1)
      printf("\nThe child has a PID of %ld\n", (long)getpid());
      printf("After the parent terminates, execute the command:\n");
      printf("ps -l --pid %ld\n", (long)getpid()); 
      sleep(30);
      // When the child also terminates it becomes a zombie
      // and will be reaped by init
      printf("\nThe child has now terminated.\n");
      printf("Execute the command again:\n");
      printf("ps -l --pid %ld\n", (long)getpid()); 
      exit(0);
    }
  // Executed in the parent only
  sleep(20);
  printf("\nThe parent has now terminated.\n");
  exit(0);
}
        
         #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define N 5
int
main(void)
{
  pid_t   pid;
  printf("While the parent is sleeping, execute the following command.\n");
  printf("ps -l --ppid %ld\n\n", (long)getpid());
  
  for (int i=0; i<N; i++)
    {
      pid = fork();
      if (pid == 0) // This is a child process
        {
          printf("I'm child %d with a PID of %ld\n", i, (long)getpid());
          sleep(1);
          exit(0); // Terminate (and become a zombie)
        }
    }
  // Executed in the parent only
  
  // Reap a single child ("at random")
  //
  // -1 indicates the wait set is all child processes
  //  0 indicates execution should be suspended until one element terminates
  // This call returns immediately if the child is a zombie, otherwise
  // it waits.
  // 
  int   status;
  pid_t reaped;
  reaped = waitpid(-1, &status, 0); // Equivalent to wait(&status)
  printf("Reaped child with PID %ld\n", (long)reaped);
  sleep(20);
  exit(0);
}
        
         #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define N 5
int
main(void)
{
  pid_t   pid;
  printf("While the parent is sleeping, execute the command:\n");
  printf("ps -l --ppid %ld\n\n", (long)getpid());
  
  for (int i=0; i<N; i++)
    {
      pid = fork();
      if (pid == 0) // This is a child process
        {
          printf("I'm child %d with a PID of %ld\n", i, (long)getpid());
          sleep(1);
          exit(0); // Terminate (and become a zombie)
        }
    }
  // Executed in the parent only
  
  // Reap all children ("in random order")
  //
  int   status;
  pid_t reaped;
  while ((reaped = waitpid(-1, &status, 0)) > 0)
    {
      printf("Reaped child with PID %ld\n", (long)reaped);
    }
  sleep(20);
  exit(0);
}