Linux Basics #7 - Processes

Last Edited: 1/6/2025

This blog post introduces process utilization in Linux.

DevOps

What is Process?

People tend to use "process" and "program" interchangeably, but they are clearly distinguished in computer science. A program is compiled code with a specific set of instructions, and a process is a running instance of a program, which includes a data section such as static variables, stack, and heap. When we write compiled code like C/C++, the compiled executable is the program in binary, which can be executed by the allocated resources like CPU, memory, disk, etc., to become a process.

In the case of interpreted languages like Python, the program is the interpreter in an executable, which can be executed to interpret the script written by the programmer, typically stored in the heap, with the allocated resources to become a process. This is why we compile the code with compilers like gcc and g++ and only specify the location of the executable file in C/C++, while we specify the executable and the script name, like python <script_name>.py or node <script_name>.js, for Python and Node.js.

Processes in Linux

In Linux, we can see the processes that are currently running with the ps command. The process has a unique ID or PID, which is running under the controlling terminal (TTY). The CPU usage time and the command are displayed in the TIME and CMD columns.

> ps
PID TTY          TIME CMD
195 pts/1    00:00:00 bash
271 pts/1    00:00:00 ps
 
> ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   4300  3428 pts/0    Ss+  Jan02   0:00 /bin/bash
root         8  0.0  0.0   4296  3652 pts/1    Ss   Jan02   0:00 bash
root       194  0.0  0.0   5380  2432 pts/1    S    Jan02   0:00 su - new
new        195  0.0  0.0   5240  4400 pts/1    S    Jan02   0:00 -bash
new        272  0.0  0.0   8020  3380 pts/1    R+   17:19   0:00 ps aux

We can visualize more information about the processes, including those by other users, with ps aux. The USER column shows the effective user, %CPU and %MEM correspond to CPU time and physical memory usage in percentage, and VSZ and RSS are virtual memory usage and physical memory used by the process in bytes. The STAT column indicates the process state, which we will discuss later.

Process Creation

In Unix-based operating systems, including Linux, a process is always created using the fork system call, which duplicates the current process and creates a parent and child process with different PIDs. It might sound redundant, but it is a simpler and more flexible approach than initializing a new process from scratch, as the resources for the parent process are already set up, which the child process can decide to utilize or execute a new program in a new memory space.

> ps l
F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
4  1001   195   194  20   0   5240  4396 do_wai S    pts/1      0:00 -bash
0  1001   270   195  20   0   8052  3644 -      R+   pts/1      0:00 ps l

When we instruct the shell process to run a new program, the fork system call makes the shell process the parent process and creates a new child process with the parent process ID (PPID) of the shell process, which executes the user-specified program. Then, the parent process can wait for the exit status of the child process to resume executing its program or continue execution concurrently. You can check the parent process ID with ps l, which displays the PPID column.

The above shows that the command ps l is executed as the child process of the -bash with PID 195. The only exception to this process creation using the fork system call is the init process, which is the first process created directly by the operating system with the PID of 1. The kernel has the system code embedded to create this process, which becomes the mother of all processes.

Process Termination

Usually, the parent process runs the wait system call to wait for the exit of the child process, which involves running the exit system call that frees up the resources used by the child process. However, there are cases where the process struggles to terminate. One such case is when the parent process dies before the child process finishes its execution. In such a case, the child process becomes an orphan process, which is taken care of by the init. The init process will run the wait system call on behalf of the parent for the orphans to die.

Another case is when the parent process fails to run the wait system call before the child process exits. Such a child process is called a zombie process, whose process remains in the process table while its memory is freed up for other processes. Since the child process has technically already died, we cannot kill the process manually via a signal (which we will discuss later). When the parent process later runs wait, the zombie disappears, which is called "reaping." If the parent process does not run wait, the init process eventually runs it on its behalf. Zombies are tricky because we cannot kill them, and they do not disappear from the process table unless the parent or init calls wait, which can prevent other processes from running.

Process States

The STAT column of the table from ps aux and ps l commands shows the process state. R is when the process is running or runnable and waiting for CPU to finish it. S is when the process is asleep and waiting for an event to complete. D is when the process is in uninterruptible sleep and cannot be killed with a signal. T is a stopped process, and Z is the zombie process we discussed. You will see suffixes like s and +, which mean the process is the session leader and is in the foreground process group.

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 by job control signal
               t    stopped by debugger during the tracing
               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

The above is the result of man ps, which you can refer to when interpreting the STAT column. The session is a collection of process groups or jobs (which we will discuss later) with a unique session ID (SID) whose value is the same as the PID of the session leader. The session is set up for isolating the processes and jobs of different users and daemon processes that run continuously in the background, detached from user control.

Jobs

The process groups or jobs are set up so that the processes can be run either by blocking the terminal window in the foreground until completion or without blocking the terminal window in the background. In Linux, we can add & at the end of the command to run the process as a background job, or we can stop the process with Ctrl+Z and use the bg command to move the currently running process to a background job. To view background jobs, we can use the jobs command.

> sleep 1000 &
> sleep 1001 &
> sleep 1002
^Z
[3]+    Stopped     sleep 1002
 
> bg
[3]+    sleep 1002 &
 
> jobs
[1]   Running                 sleep 1000 &
[2]-  Running                 sleep 1001 &
[3]+  Running                 sleep 1002 &

The above shows the usage of the commands related to jobs. The + in the job list indicates the most recent process added to the background job, and the - shows the second most recent process added to the background job. We can move the process in the background to the foreground with fg %<job_number> or %+ and %- for the most recent and previous process added to the background.

Signals

When you make a mistake in the command, you might want to interrupt the process by pressing Ctrl+C, which is the shortcut for sending the SIGINT signal to interrupt the process. Signals are like enums and have corresponding numbers. For SIGINT, the value is 2. There are other signals used to communicate with processes when certain events occur.

  • SIGHUP or HUP or 1: Hangup
  • SIGINT or INT or 2: Interrupt
  • SIGKILL or KILL or 9: Kill
  • SIGSEGV or SEGV or 11: Segmentation fault
  • SIGTERM or TERM or 15: Software termination
  • SIGSTOP or STOP: Stop

The above is a list of signals you and processes may use. A process can decide how to handle most signals, so which one to use is often a matter of convention. SIGHUP usually sends the process to the background process group and is sent every time the user disconnects from the current terminal. It can also be sent manually. However, SIGINT interrupts the process without sending it to the background, which you can observe using the jobs command. The SIGKILL signal is the only signal that a process cannot block, and it never fails to terminate the process.

> kill -<SIGNAL> <PID>

The above shows how to send signals to processes with the kill command. Without specifying a signal, it sends the SIGTERM signal by default, but you can specify the number associated with the desired signal to send it to the process with the specified PID.

Process Details

There are other ways of viewing and tracking the details of processes. For example, you can use ps m to observe threads in the processes. Threads are lightweight and share the same memory, making them easier to communicate with each other and faster to switch between within a process. You can also utilize the fact that Linux treats everything as a file and look into the file associated with storing the details of a process with cat /proc/<PID>/status.

The top command tracks processes and resource usage every 10 seconds, making it a very useful tool. It can track all the information you can observe with ps and also statistics like load average (the number of processes waiting for CPU time over 1 min, 5 mins, and 15 mins), CPU usage by different types of processes, memory usage for various purposes, and niceness levels that indicate the priority of the processes. The niceness level can be set initially with nice -n 5 <command> or reassigned using renice 10 -p <PID>.

Conclusion

In this article, we covered the definition of a process, how it gets created and terminated, and how we can track and configure processes. I would highly recommend checking other resources as well, especially those linked at the bottom, to understand the fundamental concepts in greater depth. Also, consider exploring useful continuous process monitoring tools like sar.

Resources