# Linux from my Eyes

## The Filesystem That Doesn't Exist

Every Linux user knows `/proc`. But almost nobody knows what it actually is. It's not a filesystem. There are no files on any disk. Everything inside it is generated on the fly by the kernel the moment you read it. It's theater a stage where the kernel performs its internal state for you, formatted as files.

This matters because it means reading `/proc/meminfo` is not like reading a log file. It is the act of asking the kernel a live question. The answer is constructed at the exact moment your `cat` command touches the file descriptor.

Imagine you walk into a library. You see thousands of books on the shelves. Normally, you’d pick one up, and the words inside would be printed in ink, unchanging since the day the book was made. That is a Standard Filesystem (like your hard drive).

But Linux has a ghost library called `/proc`. When you open a book in `/proc`, the pages are blank until your eyes touch the paper. The moment you look, a librarian (the Kernel) sprints behind the page and scribbles down exactly what is happening in the building at that microsecond. When you close the book, the ink vanishes.

![](https://cdn.hashnode.com/uploads/covers/6950d1ca85602739c40abd67/53fad349-e5ac-4704-bcfe-531b802a2065.png align="center")

Notice the sizes: every file is **0 bytes**. That's the tell. Real files have sizes. These are kernel function pointers dressed as files. `/proc/self` is particularly mind-bending it's a symlink that points to the PID of whoever reads it. Every process that reads `/proc/self` sees itself. It's a mirror.

## Your Process Is a Directory

Every running process gets its own directory in `/proc/[PID]/`. Inside that directory is a complete portrait of the process its open files, its memory map, its environment variables, its current working directory, what executable it is. This is not metadata. This is the process, seen from the outside.

### **What we found in PID 1**

PID 1 is the init process the ancestor of all other processes. In a traditional Linux system, this is `systemd` or `init`. Here, it revealed something different:

### Command

*   `/proc/1/`: This is the directory for the **Init process**, the mother of all processes. It is the first thing the kernel starts after booting.
    
*   `cmdline`: This "file" contains the exact command used to start that process.
    
*   `tr '\0' ' '`: Kernel files in `/proc` often use "null bytes" (`\0`) to separate arguments. This part of your command simply replaces those invisible separators with spaces so you can read them.
    

![](https://cdn.hashnode.com/uploads/covers/6950d1ca85602739c40abd67/36cfb1bc-aa96-4521-b48f-df9dcea5e4da.png align="center")

The output `/usr/lib/systemd/systemd` tells us that our system is managed by systemd.

In the illusion of the filesystem, this file exists to tell you exactly how the system was brought to life. The `--deserialize=79` flag is a piece of internal state a memory systemd kept of its previous self if it was re-executed without a full reboot.

## Network Knobs Hidden in /proc/sys/net

Sysadmins tune Linux networking by writing numbers to files. Security hardening, performance tuning, DDoS mitigation all of it lives in /proc/sys/net/ipv4/ and friends. Most documentation tells you what to set. Almost none explains what you can read to understand the system's current posture.

![](https://cdn.hashnode.com/uploads/covers/6950d1ca85602739c40abd67/f0225c5f-3d8c-4163-a409-f7a50bfadcee.png align="center")

## Everything is a File in Linux

You're running a Node.js or Python server and under load it crashes with `EMFILE: too many open files`. This is one of the most common errors developers hit and one of the least understood. Here's the full picture.

Every open file, socket, pipe, or database connection consumes a **file descriptor** (FD). Linux enforces limits on how many a process can hold at once. There are two limits stacked on top of each other:

| **Limit** | **Where it lives** | **Default** | **Scope** |
| --- | --- | --- | --- |
| Soft limit | `ulimit -n` | 1,024 | Per process — the active ceiling. Can be raised to hard limit by user. |
| Hard limit | `ulimit -Hn` | 4,096 | Per process — the ceiling on the soft limit. Only root can raise. |
| System limit | `/proc/sys/fs/file-max` | ~400,000 | Entire OS — total FDs across all processes combined. |

The default soft limit of **1,024**. A modern web server handling 500 concurrent connections is already using half its budget just for sockets, before counting open config files, logs, and library handles.

**Address Already in Use**

You start your dev server and get `EADDRINUSE: address already in use :::3000`. Something is already listening on that port. Here's how to find and handle it.

![](https://cdn.hashnode.com/uploads/covers/6950d1ca85602739c40abd67/526ecc23-1d54-4516-a3f0-5a0de38d065c.png align="center")

Sometimes there's no process on the port but you still can't bind. This happens because the port is in **TIME\_WAIT** state the kernel is holding it for 60 seconds after a recent connection closed, to ensure stale packets don't confuse a new connection.

![](https://cdn.hashnode.com/uploads/covers/6950d1ca85602739c40abd67/4c12f0fe-69be-4d12-87c4-c91193a953c4.png align="center")

## **Zombie Processes**

A zombie process is not a running process. It's a **dead process whose exit code hasn't been collected yet**. The kernel keeps a tiny tombstone entry in the process table until the parent calls `wait()` to read the exit status. Until then: zombie.

![](https://cdn.hashnode.com/uploads/covers/6950d1ca85602739c40abd67/a4f45c2b-31ef-4db6-826d-7bba11284c09.png align="center")

| **Scenario** | **Why zombies appear** | **Fix** |
| --- | --- | --- |
| Python `subprocess` | You call `Popen()` but never call `.wait()` or `.communicate()` | Always call `proc.wait()` or use [`subprocess.run`](http://subprocess.run)`()` |
| Node.js `child_process` | Spawned process exits but `'exit'` event never handled | Listen to the `'exit'` or `'close'` event |
| Docker containers | Your app is PID 1 and spawns children without a proper init | Use `--init` flag or `tini` as PID 1 |
| Shell scripts | Background jobs `&` whose parent script exits without `wait` | Call `wait` at end of script or use `wait $!` per job |

## `/etc/hosts`

`/etc/hosts` is checked *before* DNS. This is defined in `/etc/nsswitch.conf` as `hosts: files dns` "files" (hosts) first, "dns" second. This makes `/etc/hosts` an instant, zero-latency DNS override with no servers involved.

| **Entry to add** | **What it does for you** |
| --- | --- |
| `127.0.0.1 myapp.local` | Access your local dev server at [`http://myapp.local:3000`](http://myapp.local:3000) in any browser — useful when cookies or CORS require a real-looking hostname. |
| `127.0.0.1 api.myapp.local` | Fake a subdomain for your local API. No DNS server. No Nginx. Just edit hosts and curl works immediately. |
| `0.0.0.0` [`ads.tracker.com`](http://ads.tracker.com) | Block a domain entirely by routing it to 0.0.0.0 (not routable, fails fast). More effective than 127.0.0.1 because 127.0.0.1 might have something listening. |
| `192.168.1.50 staging` | Give your staging server a short name. Now you can `ssh staging`, `curl staging:8080`, etc. |
| `127.0.0.1` [`prod-db.company.com`](http://prod-db.company.com) | Intercept production DNS for testing. Point the production hostname to localhost temporarily. Useful for testing without modifying app configs. |

## **Quick Reference Cheatsheet**

![](https://cdn.hashnode.com/uploads/covers/6950d1ca85602739c40abd67/3b53bac8-3e39-4ae4-9fa1-171d1cb04081.png align="center")

* * *

*Thanks for reading! I'm* ***Satpalsinh Rana****, a Software Engineer from Ahmedabad building real-time systems with Angular, React, Node.js and Electron.*

*   🌐 Portfolio: [satpal.cloud](https://www.satpal.cloud)
    
*   💼 LinkedIn: [satpalsinhrana](https://www.linkedin.com/in/satpalsinhrana/)
    
*   ⚡ GitHub: [Satpal777](https://github.com/Satpal777)
    

*Got questions or feedback? My inbox is always open.*
