Every running program on Linux is a process. Understanding how to view, control, and kill processes is essential for any server work.
Processes vs Threads
A process is an independent program with its own memory space. A thread is a lightweight unit of execution inside a process that shares memory with other threads in the same process.
When we run node server.js, that’s one process. If Node spawns worker threads internally, those are threads within that process.
Process States
A zombie process is one that finished but its parent hasn’t read its exit status yet. They’re harmless in small numbers but can pile up if the parent is buggy.
Viewing Processes
ps aux # list ALL processes with details
ps aux | grep nginx # find a specific process
top # real-time process monitor (press q to quit)
htop # better version of top (install: apt install htop)
pgrep -f "node server" # get PID of a process by name
In ps aux output, the columns we care about most: USER, PID, %CPU, %MEM, and COMMAND.
Killing Processes
Every process has a PID (process ID). We use kill to send signals to a process.
kill 1234 # send SIGTERM (graceful shutdown — "please stop")
kill -9 1234 # send SIGKILL (force kill — "stop NOW, no cleanup")
kill -HUP 1234 # send SIGHUP (reload config without restart)
killall nginx # kill all processes named "nginx"
pkill -f "node app" # kill processes matching a pattern
The key difference: SIGTERM (15) lets the process clean up (close connections, save state). SIGKILL (9) is instant death — the process can’t catch or ignore it. Always try SIGTERM first.
Foreground and Background
./long_task.sh # runs in foreground (blocks terminal)
./long_task.sh & # runs in background (terminal stays usable)
jobs # list background jobs in current shell
fg %1 # bring job 1 back to foreground
bg %1 # resume a stopped job in background
# Ctrl+Z # pause (stop) a foreground process
nohup keeps a process running even after we close the terminal.
nohup ./deploy.sh & # runs in background, survives logout
nohup ./deploy.sh > deploy.log 2>&1 & # with custom log file
systemd — The Service Manager
Modern Linux uses systemd to manage services (daemons). Think of it as the boss that starts, stops, and babysits our services.
systemctl start nginx # start a service
systemctl stop nginx # stop a service
systemctl restart nginx # stop + start
systemctl reload nginx # reload config without downtime
systemctl status nginx # check if it's running + recent logs
systemctl enable nginx # start automatically on boot
systemctl disable nginx # don't start on boot
systemctl list-units --type=service # list all services
Checking Logs with journalctl
systemd captures logs from all services. journalctl is how we read them.
journalctl -u nginx # logs for a specific service
journalctl -u nginx --since "1 hour ago" # recent logs only
journalctl -u nginx -f # follow logs in real-time (like tail -f)
journalctl -u nginx --no-pager # dump all without paging
In simple language, processes are just programs doing their thing. We can watch them (ps, top), talk to them (kill signals), and let systemd babysit them so they stay running.