6 Process Management

Process Management

Objective

  • Introduce the concept of a process in an operating system.
  • Show a dynamic real-time view of the running operating system.
  • Set and adjust process priority using nice and renice.
  • Send signals to processes using kill.
  • Understand the fork system call and use it to create processes.
  • Differentiate between parent and child processes.
  • Use getpid and getppid system calls for process identification.

exit htop jobs kill nice pgrep ping ps renice sleep top xargs 0 NULL fork() getpid() getppid() perror()

Environment

  • Almost everything in Unix is either a file or a process.
  • In computing, a process is an instance of a computer program that is being executed. It contains the program code and its current activity.
  • A process is identified by a unique PID (process identifier). A process could be made up of multiple threads, that are lightweight processes, of execution that execute instructions concurrently.
  • A file is a collection of data. They are created by users using text editors, running compilers, and other means.
    • The output of one utility or program can be sent to a file, or as input to another utility or program.
    • Many utilities may take their input from files other than the keyboard.

Command-Line Execution

  1. The shell looks for a command in the following order:
    1. alias
    2. built-in
    3. function
    4. search path
  2. If the command is located, the shell creates a new process to execute the command.
  3. The shell waits while the command executes.
  4. The shell wakes up when the command is done executing and re-issues the prompt.

Processes

  • A process is an instance of an executing program.
  • Processes are created by a parent process in two steps:
    1. The parent process clones itself (fork), and
    2. The child process replaces the parent program (exec).
  • Each process has a unique number assigned to it (PID).
  • Every process has two parts:
    1. program (image), and
    2. environment:
      • inherited from parent.
      • includes standard files for input, output, and error.

Listing Processes

Use ps to see the status of your processes and their PID’s. The options are:

OptionDescription
-eEvery process on the system
-fFull listing of process details
-lLong listing of process details
-u usernameProcesses belonging to a certain user

Example 1

> ps
    PID TTY          TIME CMD
   3147 pts/0    00:00:00 bash
   3511 pts/0    00:00:00 ps

The abbreviations in the above example output are:

  • PID is the identification number for the process.
  • TTY is the terminal console to which the process belongs.
  • TIME is the total CPU time used by the process.
  • CMD is the command line being executed.

You can use the manual to know more about the ps command options: man ps.

Exercise 1

Run the following commands:

ps
sleep 15
ps -e
ps -f
sleep 20
ps -e
ps -u root
ps -ef | more

System Initialization

init process: The first process to run in your system. init is short for initialization. Typically, it has a PID of 1 but this may vary based on the implementation. This process is responsible for starting services when booting, stopping them when shutting down, and supervising them while the system is running. There are many implementations of the init (opens in a new tab) process. Examples of that are systemd (opens in a new tab), SysV, Upstart, OpenRC, runit, and many others.

Realtime View

The top command provides a dynamic real-time view of the processes of an operating system. The top command can display system summary information as well as a list of all processes currently being managed by the kernel, sorted by CPU usage.

The command-line syntax for top consists of top -option, where the common options are:

OptionDescription
-pMonitor only processes with specified process identifiers. This option can provide a comma delimited list with up to 20 PID’s.
-uMonitor only processes with an effective user name matching the one given.
-iInclude processes that are idle or zombie.
-HAll individual threads will be displayed.
-SEach process is listed with the CPU time that it and its children have used.

The output of the top command contains the following fields:

FieldDescription
PIDProcess identifier
PPIDParent process identifier
USERProcess owner username
TTYName of controlling terminal
PRProcess priority
NINice value (adjustable priority). A negative value means high priority, whereas a positive value means low priority.
%CPUThe elapsed CPU time since the last screen update expressed as a percentage of total CPU time.
TIMEThe total CPU time used by the process since it started
TIME+Same as TIME, but with more accuracy to a hundredth of a second.
%MEMA process’s currently used share of available physical memory.
VIRTThe total amount of virtual memory used by the process, in kB. VIRT = SWAP + RES
SWAPThe swapped out portion of a process’s total virtual memory, in kB.
RESThe non-swapped (resident) physical memory a process has used, in kB.
SHRThe amount of memory shared by the process and other processes, in kB.
nDRTThe number of dirty pages that have been modified since were last written to the disk.
nFLTThe number of major page faults that have occurred for process. A page fault occurs when a process attempts to read from or write to page that is not currently present in its address space.
SThe status of the process can be one of:
D = uninterruptible sleep
R = running
S = sleeping
T = traced or stopped
Z = zombie
COMMANDDisplay the command line used to start a task or the name of the associated program.

Note: You can also install and use the htop command, which provides an interactive dynamic view of the processes.

Process Priority

The kernel uses separate priority ranges for normal and real-time tasks. For normal tasks a priority range (or nice value) of -20 to +19 is used. Lower nice corresponds to higher priority. In other words the task is being less nicer to other tasks in the system. For real-time tasks a priority range of 0 to 99 is used. In this case a higher number indicates higher priority. RT is short for real-time priority.

Typically, PR = 20 + NI, where PR is the priority and NI is the nice value. Internally, the kernel uses a single range (0139) that maps each of the above two ranges. In this range lower value means higher priority. The user-space NI values, -2019, are mapped to 100139.

The Completely Fair Scheduler (CFS) is a process scheduler which handles CPU resource allocation for executing processes, and aims to maximize overall CPU utilization while also maximizing interactive performance. The Earliest Eligible Virtual Deadline First ( EEVDF) is a real-time scheduler that is based on the Earliest Deadline First (EDF) algorithm. (CFS) replaced by the EEVDF scheduler in Linux 6.6 and later.

The nice command is used to run a given process with its scheduling priority adjusted. The renice command is used to alter or change the scheduling priority of a running process.

You can run a program (process) with its default priority adjusted by using the nice command followed by the priority number then the program name, for example:

nice sleep 5m &

This will run the sleep process with adjusted priority. Since the priority is not specified in this example, it will be increased by 10 (the process will run with less priority) from the default value (usually 0).

The priority can be explicitly incremented or decremented using, for example:

nice -n 15 sleep 5m &
nice -n -10 sleep 5m &

To set a process to have a different priority, first find the PID number using the ps or pgrep commands, then use the renice command:

renice 5 -p 3314

This will set the process, with PID 3314, to a priority of -20. Note that only the root user can specify negative priority values.

Process Control

You can kill a process by using the kill command and the PID number, for example:

kill 1721

You can also suspend, resume, and restart processes by sending them various signals as in the following examples:

Command                  Description
kill -STOP 1721Stops (suspends) process 1721 by sending the STOP signal to the process. This process will still be on the task list.
kill -CONT 1721Continues process 1721 causing it to resume. The CONT signal is sent to the process.
kill -TERM 1721Terminates process 1721 by sending the TERM signal to the process. This process will no longer show up on the task list if it is actually terminated. The terminated process cannot be continued.
kill -HUP 1721Stops then restarts process 1721. This is usually done when a process is not working properly or the configuration files for that process have been changed. This command sends the HUP signal to the process which means hang-up.

Try the kill options with the following command:

ping 127.0.0.1 > output &

You can use pgrep to find the PID of a process by its name, for example: pgrep ping. The output of this command can be used with the kill command, for example: pgrep ping | xargs kill. The xargs command is used to build and execute command lines from standard input.

The pkill command, which sends signals to processes based on names and other attributes, can also be used: pkill ping.

Signals

Standard signals range from 1 to 31, the default signal is 15 (SIGTERM). Signal number 0, called the null signal, is generally not used but the kill command uses it as a special case: No signal is sent but it can be used (rather unreliably) to check if the process still exists.

In your terminal, ^C sends a SIGINT (2) to interrupt a process, ^D sends a SIGTSTP (20) to suspend a process, and ^\ sends a SIGQUIT (3) to quit a process. All the supported signals can be listed using kill -l.

There are some processes that are protected and can catch or ignore signals, therefore to ensure a guaranteed kill we use the signal number 9 (SIGKILL). To illustrate a simple sure kill command, try the following:

kill -9 terminal_PID # a sure kill, your terminal will be closed

Note: You cannot kill a process of another user.

Background Processes

  • The shell normally waits for its child process to terminate before returning to prompt.
  • Long running jobs that do not require user input can be run in the background.
  • A process may be in the foreground, in the background, or be suspended.
    • foreground process: The shell does not return the prompt until the current process has finished.
    • background process: The shell returns immediately to the prompt while the process is running in the background. Useful for long processes so that we can continue to execute other tasks.
    • suspended process: The process is stopped by ^Z while executing.
  • To background a process, type an & at the end of the command line:
    • The shell returns a job id and a process id.
    • The output and error is still directed to your terminal.
    • Use redirection to send stdout and stderr to files.
  • Use jobs to list foreground, background processes, and their job number.
  • Background and foreground processes can be terminated with kill PID.
  • Stubborn processes that will not die gracefully can be killed using kill -9 PID.

Example 2

example-02.sh
sleep 10
sleep 400 &
ps -e
find / -name core > list 2> /dev/null &
jobs

Exercise 2

  1. List your current processes using ps.

  2. Start a new shell using sh.

  3. List your current processes again. Notice the new process you created, then exit from sh using exit.

  4. List your current processes again. The sh shell process no longer exits.

  5. To view more detailed information about a process, use the -f (full details) option, as in, ps -f.

  6. To list all the processes that are currently running on the system, use ps -ef | more.

  7. Execute the following commands to start a number of shell processes:

    sh
    bash
    sh
  8. Execute ps -f. Follow the PID and PPID relationship to determine the processes and the order in which they were started.

  9. Type exit or ^D until you have returned to your login shell;use ps to check.

  10. Return to your home directory and execute sleep 10. The shell process will normally wait or sleep until its child has completed its task and has terminated.

  11. Execute sleep 10 &. The shell responds with a job and a process id.

  12. Execute ps to list your processes.

  13. Execute jobs to list all of your background processes.

  14. Execute the following commands and note their PID numbers:

    sleep 1000 &
    find / -name core > ~/mylist 2> /dev/null &
  15. List your processes using ps.

  16. Identify the PID of the find process and terminate it using kill PID.

  17. Identify the PID of your login shell process and terminate it using kill PID.

  18. Try to forcefully kill using kill -9 PID. What happens?

Process Creation

fork

  • A system call that creates a new process under the operating system.

  • Process Control Block (PCB): a data structure that represents one process.

  • When n fork calls are executed in sequence, we end up with 2n processes. This can be visualized using a full binary tree for three fork() calls, resulting in 23 = 8 processes, as follows:

    This sequence of n fork() calls can also be represented using the following self-similar tree:

Differences

  • Different PID’s for parent and child.
  • In the parent, fork() returns the PID of the child process if a child process is created. In the child, it always returns 0.
  • Separate copies of all data, including variables with their current values and the stack.
  • Separate program counters (PC) indicating where to execute next; originally both have the same value but they are thereafter separate.
  • After fork(), the two processes do not share their variables.

Implementation

  • fork() is a system call that creates a new PCB.
  • It copies most information from the current process’s PCB into the next free location in the process table.
  • The parent and child processes will now both be ready to execute.
  • One can be left/placed in the RUNNING state and the other in the READY state.
  • Henceforth, both will take turns in using the processor, along with all the other processes.
  • Which one is chosen to run first, depends on the scheduler of the operating system.
  • A call to fork() returns:
    • The pid of the new child process to the parent process; this is equivalent to telling the parent the name of its child.
    • 0 to the child process.
    • A negative number (-1) if there is an error, that is, fork() failed because a new process could not be created.

Example 3

Compile and run the following program:

example-03.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h> /* defines fork() and pid_t */
 
int main(void) {
  printf("Just one process so far\n");
  printf("Forking...\n");
 
  pid_t pid;
  pid = fork();
 
  if (pid < 0) {
    printf("fork failed\n");
  }
 
  if (pid == 0) {
    printf("I'm the child\n");
  }
 
  if (pid > 0) {
    printf("I'm the parent with child ID = %d\n", pid);
  }
 
  return EXIT_SUCCESS;
}

Exercise 3

Write a program to demonstrate that the program variables in a parent process and a child process have the same initial values but are independent of each other, that is, the child receives a copy of the variables.

Example 4

Compile and run the following program, which demonstrates the use of the getpid() and getppid() system calls:

example-04.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
 
int main(void) {
  printf("Original process ID = %d\n", getpid());
  printf("Forking...\n");
 
  pid_t pid = fork();
 
  if (pid != 0) {
    printf("I am the parent process with PID = %d and my child's PID = %d\n",
           getpid(), pid);
  } else {
    /* pid is zero, so child process is running this part */
    printf("I am the child process with PID = %d and PPID = %d\n", getpid(),
           getppid());
    sleep(5); /* allows the parent to execute first */
  }
 
  /* both processes are running this part */
  printf("PID = %d terminated\n", getpid());
  return EXIT_SUCCESS;
}

The getpid() system call returns the process’s identifier and the getppid() system call returns the parent process’s identifier.

Exercise 4

Write a C program that will fork a new process. The parent side has to calculate and print the average of all positive numbers of an array composed of 6 elements entered by the user, while the child process has to calculate and print the product of all negative numbers of the same array. Compile and run your program.

Resources