A process is a currently executing program (or command). A program is a series of instructions that tell the computer what to do. When we run a program, those instructions are copied into memory and space is allocated for variables and other stuff required to manage its execution. This running instance of a program is called a process.
The top command can show running processes in the terminal in real-time.
myuser@hostname:~$ top
...
In linux, multiple users running multiple processes, at the same time and on the same system. The CPU manages all these processes simultaneously, according to the below state diagram:
A process can be in one of several states at any given time, indicating its current status or activity. These states are important to understand for managing and troubleshooting system performance.
Here are a short description of each state:
A new process is created because an existing one makes an exact copy of itself (forking). This implies a tree structure of processes:
myuser@hostname:~$ pstree
systemd─┬─ModemManager───2*[{ModemManager}]
├─NetworkManager───2*[{NetworkManager}]
├─2*[SACSrv───3*[{SACSrv}]]
├─accounts-daemon───2*[{accounts-daemon}]
├─acpid
├─atd
├─avahi-daemon───avahi-daemon
.
.
.
This child process has the same environment as its parent, only different process id (PID). When a process ends normally (it is not killed or otherwise unexpectedly interrupted), the program returns its exit code (a number) to the parent. Only exit status of 0 means success.
A process has a series of characteristics, which can be viewed with the ps
command:
myuser@hostname:~$ ps -aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 172752 9440 ? Ss 13 Feb 7:21 /sbin/init splash
root 2 0.0 0.0 0 0 ? S 13 Feb 0:00 [kthreadd]
The ps
command can display a lot of information about processes, including their PID, state (STAT column), CPU and memory usage, the user owning this process, and the command that initiated the process.
Processes are communicating with each other and with the kernel using signals. There are multiple signals that you can send to a process. Use the kill
command to send a signal to a process.
Signal Name | Signal Number | Meaning |
---|---|---|
SIGTERM | 15 | Terminate the process in orderly way |
SIGINT | 2 | Interrupt the process. The process can ignore this signal |
SIGKILL | 9 | Interrupt the process. The process can’t ignore this signal |
SIGHUP | 1 | The parent process was terminated |
Use man 7 signal
for a comprehensive list of linux signals.
Let’s kill a process by send it a KILL signal:
myuser@hostname:~$ sleep 600
The above command initiates a process that just sleeps 600 seconds, and ends.
In another terminal, use the ps
to get the PID of the process running the sleep
command:
myuser@hostname:~$ ps -a
PID TTY TIME CMD
46214 pts/1 00:00:00 sleep
46445 pts/3 00:00:00 ps
...
myuser@hostname:~$ kill -9 46214
Observe how the process running the sleep
command is terminated.
In bash, you can use keyboard shortcuts to send signals to process:
Shortcut | Signal | Meaning |
---|---|---|
CTRL+C | SIGINT | Terminate the process in orderly way |
CTRL+Z | SIGSTOP | Suspend the process in the background |
We now demonstrate a very important concept called Graceful Termination.
Graceful termination refers to the process of shutting down a program (a process) in a way that allows it to complete its current tasks and close down all processes in a safe and controlled manner. This ensures that no data is lost, and all system resources are properly released.
In our shared repo, under graceful_term_simulate/server.py
you are given a dummy Python “Server”. Run this server by:
# You might try 'python' instead 'python3' command
myuser@hostname:~$ python3 graceful_term_simulate/server.py
Server is running, PID=47098
Obviously, we can send SIGKILL (9) to the server and kill it aggressively. But we want the server to be terminated gracefully. To do so, we will first send SIGINT, which indicates to the server “you are going to be terminated soon, so take some grace period to terminate yourself gracefully”.
The server is finishing the process clients requests, closes the connection to the database, and performs some cleanup tasks. Finally, the server terminates itself, while everything is healthy.
In Linux, services are background processes that run continuously and provide specific functions to the operating system or other applications. Here are some common Linux services:
ssh
: A secure remote login protocol that allows users to log in to a remote computer securely.cron
: A service that executes scheduled tasks or commands at specified intervals.ufw
: An easy-to-use interface for configuring and managing firewall rules on a Linux system.apache
: A web server that serves HTML pages and other files over the internet.The systemctl
command is used to manage services in your system:
myuser@hostname:~$ sudo systemctl status ufw
● ufw.service - Uncomplicated firewall
Loaded: loaded (/lib/systemd/system/ufw.service; enabled; vendor preset: enabled)
Active: active (exited) since Sun 2022-01-01 07:25:58 UTC; 2h 16min ago
Docs: man:ufw(8)
Main PID: 338 (code=exited, status=0/SUCCESS)
CPU: 1ms
Jan 01 07:25:58 hostname systemd[1]: Starting Uncomplicated firewall...
Jan 01 07:25:58 hostname systemd[1]: Finished Uncomplicated firewall.
myuser@hostname:~$ sudo systemctl stop ufw
myuser@hostname:~$ sudo systemctl start ufw
myuser@hostname:~$ sudo systemctl restart <service name>
Enable a service to start automatically at boot time by:
sudo systemctl enable <service name>
As can be seen in the output of pstree
, systemd is the first process in many Linux distributions, which is a system and service manager that provides a way to manage and control system services. Systemd reads unit files, which are configuration files used by systemd to define system services.
Unit files can be found in the /etc/systemd/system
directory:
myuser@hostname:~$ ls /etc/systemd/system/*.service
/etc/systemd/system/sshd.service
/etc/systemd/system/mysql.service
/etc/systemd/system/jenkins.service
...
List all your system services:
myuser@hostname:~$ systemctl list-units --type=service
UNIT LOAD ACTIVE SUB DESCRIPTION
accounts-daemon.service loaded active running Accounts Service
acpid.service loaded active running ACPI event daemon
alsa-restore.service loaded active exited Save/Restore Sound Card
....
In the above output, UNIT represents the unit name, LOAD indicates that the unit’s configuration has been read by systemd, ACTIVE is the state of the unit.
Enter the interactive self-check page
Resource locking is a technique used in computer systems to prevent multiple processes (or users) from simultaneously accessing a shared resource such as a file, database, or piece of memory. The general idea behind resource locking is to ensure that only one person can access the resource at any given time, to prevent race conditions and other synchronization issues that could lead to data corruption or inconsistencies.
As a real life example, in an airline online check-in system, resource locking may be used to prevent multiple users from simultaneously booking the same seat. Only one user should be able to book a certain seat in the aircraft at any given time, to prevent conflicts and ensure data consistency.
Throughout the course, we will encounter resource locking in many cases.
Generally speaking, in Linux systems, two different processes cannot write to the same file concurrently (exactly at the same moment).
Each process needs to obtain an exclusive write lock for the file.
That implies that all the other processes who are willing to write to this file will have to wait while one process is writing data into it.
The more I/O intensive processes you have, the longer the wait time.
In this question we will create processes which are competing on the same resource (same file), and see how some of them are changing their state from Running to Waiting.
Create a file under ~/write_to_file_sequentially.sh
contains the following code, and give it executable (x
) permissions:
#!/bin/bash
for i in $(seq 1000); do
echo "hello world" > overloaded_file
done
This script writes the string “hello world” to a file called “overloaded_file”. It does so 1000 times sequentially, and so will never be competing for access to the file (why?).
Now, because we need multiple processes competing over the same resource, create the following script as well:
#!/bin/bash
for i in $(seq 1000); do
./write_to_file_sequentially.sh &
done
Name it ~/multi_process_file_writing.sh
, make it executable. Make sure you understand what this script does.
Run the following commands, in 3 different terminal sessions:
./multi_process_file_writing.sh
.top
command in terminal 2. Observe the multi_process_file_writing
process in top’s view.In the last terminal you will try to catch processes of your program in different states.
In the ps
man page, read what each process state code means.
ps -aux
multiple times until you’ll see indications that some processes are waiting (sleeping due to IO operation), and some others are running.
While you’ll find many processes in a waiting state (D
), it may be hard to catch a process in a running state (R
)… Use the grep
command to filter only relevant lines.systemd-resolved
service running?ufw
service active in your system? Is it running? If not, what is the exit code of the process that runs the service?systemd-timesyncd
service? Restart the service, what is the PID? What is this service responsible for?ssh
. Send SIGKILL
to the process ID of this service. What happened to the service? Check the status, what is the status, why?Follow Pratham Patel’s great tutorial
“Interactive processes” are processes that are initialized and controlled through a Linux terminal session. These processes can run in the foreground, occupying the terminal. Alternatively, they can run in the background. The terminal can accept new commands while the program is running.
sleep 600
which initiates a process that “sleeps” 10 minutes and ends.CTRL+Z
.fg
.&
operator: sleep 600 &
.CTRL+C
.