<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Linux on</title><link>/tags/linux/</link><description>Recent content in Linux on</description><generator>Hugo</generator><language>en</language><lastBuildDate>Tue, 13 Aug 2024 00:00:00 +0000</lastBuildDate><atom:link href="/tags/linux/index.xml" rel="self" type="application/rss+xml"/><item><title>Lesson 8: Memory-Mapped IO — How Databases Read Files</title><link>/post/fundamentals/linux-mmap/</link><pubDate>Tue, 13 Aug 2024 00:00:00 +0000</pubDate><guid>/post/fundamentals/linux-mmap/</guid><description>&lt;p&gt;I was reading about how RocksDB works one evening and kept seeing references to &lt;code&gt;mmap&lt;/code&gt; for reads. I had heard of memory-mapped files but thought of them as a niche optimization. Then I realized: SQLite uses mmap. WiredTiger (MongoDB&amp;rsquo;s storage engine) uses mmap. LMDB is built almost entirely around mmap. Even some Postgres configurations use mmap for WAL. Understanding mmap explains a lot about how high-performance storage works, why some databases are so fast for random reads, and why memory and I/O are so deeply intertwined at the OS level.&lt;/p&gt;</description></item><item><title>Lesson 7: Containers from Scratch — Namespaces, Cgroups, What Docker Does</title><link>/post/fundamentals/linux-containers/</link><pubDate>Mon, 29 Jul 2024 00:00:00 +0000</pubDate><guid>/post/fundamentals/linux-containers/</guid><description>&lt;p&gt;I spent the first two years of my career using Docker without understanding what it was actually doing. I thought containers were a &amp;ldquo;lightweight VM&amp;rdquo; — some kind of virtualization. Then I read Liz Rice&amp;rsquo;s &amp;ldquo;Containers from Scratch&amp;rdquo; talk and wrote a 100-line container runtime in Go. Containers are not virtual machines. They are just Linux processes with restricted views of the system, implemented using two kernel features: namespaces and cgroups. Once you see how simple the underlying mechanism is, you understand exactly what Docker adds and why containers behave the way they do.&lt;/p&gt;</description></item><item><title>Lesson 6: Signals — SIGTERM vs SIGKILL and Graceful Shutdown</title><link>/post/fundamentals/linux-signals/</link><pubDate>Sun, 14 Jul 2024 00:00:00 +0000</pubDate><guid>/post/fundamentals/linux-signals/</guid><description>&lt;p&gt;The first time I deployed a service to Kubernetes and watched it restart, I noticed that requests in flight were sometimes failing with connection resets. The pod was receiving 502 responses for a few seconds before it disappeared. I had heard of &amp;ldquo;graceful shutdown&amp;rdquo; but hadn&amp;rsquo;t implemented it. Kubernetes sends &lt;code&gt;SIGTERM&lt;/code&gt; before killing a process, giving it time to finish in-flight requests — but my service was either ignoring the signal or exiting immediately, cutting off connections mid-request. Learning how signals work at the OS level, and then implementing correct signal handling in Go, fixed the issue permanently.&lt;/p&gt;</description></item><item><title>Lesson 5: Epoll and IO Multiplexing — How Go''s Netpoller Works</title><link>/post/fundamentals/linux-epoll/</link><pubDate>Thu, 27 Jun 2024 00:00:00 +0000</pubDate><guid>/post/fundamentals/linux-epoll/</guid><description>&lt;p&gt;I used to wonder how a Go HTTP server could handle 100,000 concurrent connections with only 8 OS threads. If each connection required a dedicated thread, you would need 100,000 threads — which would require roughly 800 GB of stack space and would spend all their time in the kernel scheduler. The answer is epoll: a Linux kernel interface that lets a single thread wait on thousands of file descriptors simultaneously and be notified only when one is ready for I/O. Go&amp;rsquo;s runtime uses epoll internally as the foundation of its network I/O model. This lesson explains how.&lt;/p&gt;</description></item><item><title>Lesson 4: TCP/IP Stack — SYN Floods, TIME_WAIT, Connection Tuning</title><link>/post/fundamentals/linux-tcp-ip/</link><pubDate>Tue, 11 Jun 2024 00:00:00 +0000</pubDate><guid>/post/fundamentals/linux-tcp-ip/</guid><description>&lt;p&gt;A load test I ran against a Go HTTP server one afternoon produced a strange result: throughput leveled off at about 30,000 requests per second and couldn&amp;rsquo;t go higher, even though CPU was at 30%. Running &lt;code&gt;netstat -an | grep TIME_WAIT | wc -l&lt;/code&gt; showed over 28,000 connections in &lt;code&gt;TIME_WAIT&lt;/code&gt; state. The kernel was running out of local port numbers. Understanding TCP connection states — and how to tune Linux&amp;rsquo;s TCP stack — unblocked the test and taught me more about networking than any course had. This lesson covers the TCP mechanics that actually matter for backend engineers running high-throughput services.&lt;/p&gt;</description></item><item><title>Lesson 3: File Descriptors — Why Too Many Open Files Kills Your Server</title><link>/post/fundamentals/linux-file-descriptors/</link><pubDate>Mon, 27 May 2024 00:00:00 +0000</pubDate><guid>/post/fundamentals/linux-file-descriptors/</guid><description>&lt;p&gt;I got a 3 AM page for a service that was returning connection errors to every client. The application logs said &lt;code&gt;dial tcp: lookup ...: too many open files&lt;/code&gt;. We hadn&amp;rsquo;t changed anything. Load was normal. But over 48 hours, something had been slowly accumulating open file descriptors and not closing them, and we hit the per-process limit. Restarting the service fixed the immediate problem; understanding why it happened required me to actually learn what file descriptors are and how the kernel manages them.&lt;/p&gt;</description></item><item><title>Lesson 2: Virtual Memory — 1GB RSS but Only 50MB is Real</title><link>/post/fundamentals/linux-virtual-memory/</link><pubDate>Sun, 12 May 2024 00:00:00 +0000</pubDate><guid>/post/fundamentals/linux-virtual-memory/</guid><description>&lt;p&gt;Early in my career I deployed a Go service and checked &lt;code&gt;top&lt;/code&gt;. The &lt;code&gt;VIRT&lt;/code&gt; column showed 1.2 GB. I nearly had a heart attack — our server had 4 GB of RAM and I thought the service was consuming nearly a third of it. A senior engineer laughed and told me to look at &lt;code&gt;RES&lt;/code&gt; instead: 52 MB. I had no idea what the difference was. Understanding virtual memory is fundamental to reading memory metrics correctly, debugging out-of-memory kills, and understanding how processes interact with the kernel.&lt;/p&gt;</description></item><item><title>Lesson 1: Processes and Threads — What Goroutines Map To</title><link>/post/fundamentals/linux-processes/</link><pubDate>Thu, 25 Apr 2024 00:00:00 +0000</pubDate><guid>/post/fundamentals/linux-processes/</guid><description>&lt;p&gt;When I first started using Go seriously, I accepted goroutines as &amp;ldquo;lightweight threads&amp;rdquo; without really understanding what that meant. The Go runtime creates them, schedules them, and I launch them with &lt;code&gt;go&lt;/code&gt;. Then I got curious: what does the OS actually see? When I run 10,000 goroutines, does the kernel manage 10,000 things? The answer is no — and understanding why requires understanding the difference between processes, kernel threads, and userspace threads. It also explains why goroutines scale so much better than Java threads or Python threads.&lt;/p&gt;</description></item></channel></rss>