Processes & /proc
ps, top, and what a process really is.
A program sitting on disk does nothing. It is like a recipe printed in a cookbook: instructions on a page, no cooking happening. A process is what you get when someone actually makes the recipe. A real cook stands at a real burner, using real pans, following the steps one at a time. The file /usr/bin/nginx can sit on disk for years, untouched. The nginx process is alive right now, holding memory, using processor time, and it belongs to a specific user.
When you launch a program, the kernel (the core part of the operating system that talks directly to the hardware) brings that recipe to life. It gives the new process a PID (process ID, a plain number you use to refer to it), writes down which user owns it, and remembers which process asked for it. After that, the kernel keeps tabs on everything the process does: how much memory it holds, which files it has open, whether it is running or asleep. Working out which process owns what, what each one is touching, and why one is wedged is a big part of running Linux day to day.
Every Process Carries An ID Card
Four facts pin down any process. Its PID, the number the kernel stamped on it. Its owner, the user it runs as (identified by a UID, a user ID number), which decides everything it is allowed to touch. Its parent, the process that started it, recorded as the PPID (parent process ID). And its state: running, sleeping, or finished but not yet cleaned up. PID 1 is the one to know by name. It is the first process the kernel starts at boot, the init system (the program in charge of launching and supervising everything else, which on modern Linux is systemd). Every other process is a descendant of PID 1, the way every folder on the machine lives somewhere under the top folder. That family tree matters more than it looks, and you will use it to investigate trouble.
ps: The Snapshot
ps (process status) prints a still photograph of what is running the moment you ask. The workhorse form is ps aux: every process on the box, its owner, how hard it is working the processor and memory, and the command behind it.
Read that left to right. USER is the owner. PID is the number. %CPU and %MEM are how much of the processor (the CPU, or central processing unit, the chip that does the actual computing) and the RAM (random access memory, the fast working memory) each process is using. VSZ and RSS are two ways of measuring that memory. VSZ (virtual size) is the whole address space the process has mapped, most of which is not really sitting in RAM. RSS (resident set size) is the real physical memory it is holding right now, and it is usually the number you care about. STAT is the state: S means sleeping, R means actively running on the processor. (You will also see a second letter, like the trailing s in Ss, which flags extras such as a process being the leader of its session.) The last column is the command that started it. PID 1 running /sbin/init is systemd, the ancestor of everything else.
A flat list hides the family relationships. Add --forest and ps draws the tree with indentation, so you can watch one process give birth to the next.
Read it top to bottom. systemd started the SSH service (secure shell, the encrypted way you log in to a remote machine), which accepted alice's login, which started her shell, which ran the ps command she typed. Every process has a parent, and the chain leads all the way back to PID 1.
When you already know the name and only want the PIDs, pgrep (process grep) beats scanning a wall of text. The -a flag prints the full command line beside each PID, not only the number.
top: The Live View
ps freezes one instant. top keeps a live view that redraws every few seconds, sorted so the hungriest process sits on top. It is the first thing you open when a machine feels slow, because it answers one question fast: what is eating this box right now?
The header lines summarize the whole machine: load, task counts, processor use, memory. Below that is one row per process, busiest first. Press M to sort by memory, P to go back to sorting by processor, and q to quit. If htop is installed it is the friendlier version, with colored bars and arrow-key scrolling. Right now the top row is the whole story: a process named kinsing, owned by www-data, burning a full core. www-data is the low-privilege user the web server runs as, and it has no business running a busy program that came out of nowhere. Time to open it up.
/proc: The Process Turned Inside Out
Linux tries to make everything look like a file, and live processes are no exception. The kernel keeps a special directory called /proc, and inside it every running process has a folder named after its PID. These are not real files on any disk. They are live windows the kernel opens onto a process, built the instant you read them, showing its exact state. You open them with the same cat and ls you already use for ordinary files. For our suspicious process at PID 3187, four of these files answer almost everything.
First question: what is actually running? /proc/<pid>/exe is a link that points at the real binary on disk, no matter what name the process gave itself. Reading files under another user's process usually needs root, so these run with sudo (run one command as the superuser).
Two red flags in one line. The binary lives in /tmp, a world-writable scratch folder where nothing legitimate should be running from. And the (deleted) tag means the file was unlinked from disk while the program keeps running from memory, a standard trick for hiding malware from anyone who later goes looking for the file. A normal service points at something like /usr/sbin/nginx, with no (deleted) beside it.
Next: how was it started, and with which arguments? /proc/<pid>/cmdline holds the exact command line.
That tr in the command is doing real work. The kernel stores the arguments separated by null bytes (invisible zero characters), not spaces, so cat on its own would smash them together into one blob. tr '\0' ' ' swaps each null for a space so a human can read it. Here the program was launched pointing at a config file, also parked in /tmp.
Now: what is it touching? Every open file, pipe, and network connection a process holds is listed under /proc/<pid>/fd as numbered links. fd stands for file descriptor, the small integer handle a program uses for anything it has opened.
Numbers 0, 1, and 2 are always standard input, output, and error. What matters here are the entries pointing at socket:[...]: those are open network connections. A quiet program with live sockets is talking to something across the network, which for a miner is usually a mining pool or a command server telling it what to do. fd 3 is open read-only against that /tmp config file, which is why its link shows lr-x rather than lrwx.
Last: who owns it, and who started it? /proc/<pid>/status is a plain readout of the process's identity, and grep pulls out the lines that matter.
PPid is 980, the parent process ID, which top already showed us is the nginx worker. Uid is 33, printed four times over (the real, effective, saved, and filesystem user IDs), and 33 is www-data on Debian and Ubuntu. So the web server itself spawned this miner. That is the whole break-in in two numbers: someone found a hole in the web application, and the web server, running as www-data, was made to download and run a miner.
The Owner Column Is A Security Column
A process can do exactly what its owner can do, no more and no less. That one rule is why the USER column is the first thing a defender reads. It tells you the blast radius. A bug in a program running as www-data can wreck what www-data can reach, which is bad but bounded. The same bug in a program running as root can rewrite the entire system, because root can touch everything. This is why services run as their own dedicated low-privilege users instead of as root, and why a web server running as root is treated as a serious mistake, not a shortcut.
Our miner is stuck at www-data for now, which is exactly why the attacker left it wired to the web server. Their next move is to climb from www-data to root, and reading other processes' secrets is one common ladder. Confirming the parent takes one command.
That closes the loop: the nginx worker (www-data) is the parent, so the entry point was the web application, not SSH or a stolen login. Killing PID 3187 stops the miner for a minute, but if you do not fix the hole in the web app, the same program comes back under a new PID within the hour. Contain it in the right order: capture the evidence from /proc first, then kill the process, then patch the way in.
The habit that makes all of this fast is a boring one. On a machine you trust, run ps aux and top once while everything is healthy, and actually read them. Learn what normal looks like: which users own what, which programs are always there. The day something is wrong, the odd line, the wrong owner, the binary running out of /tmp, stops being a needle in a haystack and becomes the one row that does not belong.
Try this
Work through “The Owner Column Is A Security Column” yourself on a sandbox you can throw away, following the commands above in order. Then break one step deliberately and re-run, so you have seen the failure before it finds you.
Takeaway
The trap worth remembering here: pkill matches more than you mean. Check that on your own systems before you need to, because it is cheaper to find on a quiet afternoon than during an incident.