Book: Understanding the Linux Kernel (3rd Edition)
I couldn’t believe it when someone yesterday told me that I haven’t posted a review about the classic ULK (commonly used abbreviation for “Understanding the Linux Kernel”). I have a hardcopy of this book since the day when sin left Athens and gave me his copy of this book. I had previously read it in eBook but since then I have read it numerous times. Here is a more detailed review that I hope you will find useful.
Title: Understanding the Linux kernel
Authors: Daniel P. Bovet and Marco Cesati
Chapter 1. Introduction
This is not such a technical chapter since it deals with UNIX-like operating systems in general. Here you can learn about general operating system design issues including hardware dependencies as well as some Linux specific information like kernel versioning, kernel architecture, etc.
Chapter 2. Memory Addressing
Probably one of the most popular chapters of this book. It discusses in a good amount of detail hardware segmentation even though it’s not that popular nowadays. Then authors provide a detailed overview of Linux kernel’s segmentation as well as hardware paging. The next topic is the Physical Address Extension (PAE) mechanism and 64-bit architecture concepts. Clearly, the following sections are dedicated to physical memory topics which ends with Translation Lookaside Buffers (TLB) handling.
Chapter 3. Processes
After describing the the process descriptor it dives into the details of process descriptor handling and management in Linux kernel. Next, we have various details regarding relationships among processes which leads to processes’ organization in queues. The next topic is resource limits and context switch. The chapter continues to the exact steps for creating and destroying processes and kernel threads.
Chapter 4. Interrupts and Exceptions
Beginning with the explaining the role and importance of interrupts, the book gives a brief overview of interrupts and exceptions and then dives into Advanced Programmable Interrupt Controller (APIC) to end up in Linux kernel’s Interrupt Descriptor Table (IDT). The chapter continues with various topics around interrupts and exceptions including traps, system gates, exception handling, etc. The next sections discuss in detail the internals of Linux kernel interrupt handling.
Chapter 5. Kernel Synchronization
The first part of this chapter is dedicated to kernel preemption and results in understanding the importance of synchronization. In this chapter, the reader can learn about per-CPU variables, atomic operations, various locking mechanisms, memory barriers, Read-Copy Update (RCU), etc. It’s a very interesting writing with numerous useful sections.
Chapter 6. Timing Measurements
Since timing can have serious impact on various situations, this is another extremely important subject. Here the authors describe various timing related mechanisms including Real-Time Clock (RTC), Time-Stamp Counter (TSC), Programmable Interval Timer (PIT), High Precision Event Timers (HPET), ACPI Power Management Timer, etc. In addition, there is a detailed section for the Linux kernel’s architecture (for both SMP and non-SMP) for the discussed topic. In this chapter, you can also find information regarding Non-Maskable Interrupts (NMI) watchdogs, various functions and system calls for timing operations, POSIX timers, etc.
Chapter 7. Process Scheduling
From basic understanding of scheduling policies, authors move to more advanced concepts such as process preemption, scheduling algorithms, real-time scheduling, etc. A great chapter that gives a nice insight of the scheduling process (which however, has changed since 2005). In the following sections you can find information regarding multi-processor systems’ balancing as well as system calls scheduling.
Chapter 8: Memory Management
Once again, a chapter that everyone I know have read quite a few times. Here, starting from basic information on subjects such as page descriptors, Non-Uniform Memory Access (NUMA), memory zones, etc. the authors give some great details of the Linux kernel’s Buddy system algorithm. Next, there is another excellent section regarding per-cpu page frame cache which leads to the zone allocator. Finally, the chapter starts describing the memory management and more specifically, the SLAB allocator and memory pools. At last, we can find information about non-contiguous memory areas.
Chapter 9: Process Address Space
Back to the processes, this chapter discusses how the kernel manages the processes’ address space(s) using memory descriptors. After explaining the basic components such as memory regions, data structures, access rights and handling, it starts with the actual content of this topic. There is a great section about page fault exception handler and ends up with more advanced concepts including demand paging, copy-on-write (COW), etc.
Chapter 10: System Calls
In this chapter, we can find the essentials for better understanding of this topic in the first sections. This means everything from POSIX APIs, entering/exiting a system call, issuing a system call, etc. Next, there is an interesting section about sysenter and consequently, the vsyscall page. In the following parts of this chapter we have parameter passing and verification, accessing user space from kernel space, fix-up code and exception tables, and finally, kernel wrapper routines for the system call sub-system.
Chapter 11: Signals
Firstly, there is an introductory part which discusses the importance of signals in an operating system. Then, there are a few theoretical basics like how a signal is being delivered, POSIX signals, equivalent data structures, etc. Next, authors give an in depth view of the generation, delivery and handling of such signals.
Chapter 12: The Virtual Filesystem
Once again, the initial sections describe the layered model of Linux kernel’s filesystem support through Virtual Filesystem (VFS). Next, there is an overview of VFS related data structures along with all the associated objects. The last sections are about filesystem management using the aforementioned components.
Chapter 13: I/O Architecture and Device Drivers
Based on the previous chapter, this one moves to a lower level on filesystems which is the input/output of the device drivers. After the expected introductory sections, it begins with I/O ports system to later discuss the sysfs filesystem, device driver model, device files and more general information on device drivers. The more advanced sections include I/O shared memory and Direct Memory Access (DMA).
Chapter 14: Block Device Drivers
From the information of chapter 13 we can start identifying the details of block devices in this, next chapter of this book. From design concepts to more practical issues this chapter goes through the block layer and its structures to move to the I/O operations performed and the equivalent scheduler.
Chapter 15: The Page Cache
From the previous chapter it is logical to move to the page cache. Of course, the first sections describe the building components of this sub-system which includes objects, radix trees, etc. and ends up with the actual page cache handling. Similar information is also included for the block device buffer pages and dirty pages.
Chapter 16: Accessing Files
There are a couple of different ways for file accessing and this chapter attempts to describe each one of them in detail. From various reading, writing operations and memory mapping to direct I/O transfers, this chapter is a great resource for such information.
Chapter 17: Page Frame Reclaiming
Starting with the page frame reclaiming algorithm (PFRA), it moves to its design and reverse mapping issues. The last parts are about its Linux kernel implementation using least recently used (LRU) lists, out of memory (OOM) killer, swap token and swapping.
Chapter 18: The Ext2 and Ext3 Filesystems
Since these were the most common filesystems by the time of the book’s writing, authors included detailed information about both of them. Like in the previous chapters, everything from data structures to model. design and operation is discussed in detail for both ext2 and ext3 journaling filesystems.
Chapter 19: Process Communication
All of the communication types like named pipes, semaphores, messages, shared memory and sockets are discussed in this chapter from the kernel’s point of view. It’s a very detailed chapter that contains very useful information on the subject.
Chapter 20: Program Execution
After identifying the executable context, authors start with the process credentials and capabilities which are analyzed in detail later in this chapter. Next, the topic is libraries, program segments and process memory regions layout. Finally, the last sections are about execution formats such as ELF, and execution domains.
On the upside this is the first book I know of with such complete documentation of Linux kernel’s internals with amazingly detailed figures to demonstrate authors’ writings. However, since 2005 when the 3rd edition was published, there have been countless new features and updates in Linux kernel. This means that great amount of the provided content is outdated. In addition, the writing style isn’t the best you can find in technical books. It’s not of those easy to read, pleasant tech. books. Nevertheless, it’s a great introduction to Linux kernel internals and I’m definitely suggesting it to anyone willing to study this subject.