Understanding the Linux Kernel, 3rd Edition by Daniel P. Bovet & Marco Cesati The unconfirmed error reports are from readers. They have not yet been approved or disproved by the author or editor and represent solely the opinion of the reader. Here's a key to the markup: [page-number]: serious technical mistake {page-number}: minor technical mistake : important language/formatting problem (page-number): language change or minor formatting problem ?page-number?: reader question or request for clarification This page was updated April 10, 2008. UNCONFIRMED errors and comments from readers: (17) 4th paragraph from bottom, 2nd line; "a separate file descriptor to each file," should be "a separate file descriptor to each process," At least, I can't make any sense of the sentence as it is; we are speaking about "open the same file", i.e. one file; "each file" doesn't make sense in this context. {25} 3rd paragraph from bottom, 4nd line; "if its new value is greater than or equal to 0" should be "if its new value is less than or equal to 0 (50) footnote, line 1; "Recent Intel Pentium 4 processors sport an NX (No eXecute) flag... " should this be? "Recent Intel Pentium 4 processors support an NX (No eXecute) flag " (59) 1st paragraph from bottom; the "PMD_SHIFT" in "(2PMD_SHIFT)" should be the index of the "2" {60} 1st paragraph 3rd line from top; "entry of the Page Global Directory" should be "entry of the Page Upper Directory" {60} 1st Paragraph (PUD_SHIFT); The previous block (PMD_SHIFT) states "The PMD_SIZE macro computes the size of the area mapped by a single entry of the Page Middle Directory", the following block (PGDIR_SHIFT) states "The PGDIR_SIZE macro computes the size of the area mapped by a single entry of the Page Global Directory". Surely PUD_SIZE would compute the size of the area mapped by a single entry of the Page Upper Directory rather than the Page Global Directory? {60} 1st Paragraph (PUD_SHIFT); The previous section (PMD_SHIFT) describes PMD_MASK as masking the bits of the Offset and Table fields. The next section (PGDIR_SHIFT) describes the PGDIR_MASK as masking the bits of the Offset, Table, Middle Air and Upper Air fields. Should PUD_MASK not mask the bits of the Offset, Table and Middle Air fields? Also should "Air" read "Dir"? {60} 2nd paragraph; "On the 80 x 86 processors, PUD_SHIFT is always equal to PMD_SHIFT and PUD_SIZE is equal to 4 MB or 2 MB." should be "On the 80 x 86 processors, PUD_SHIFT is always equal to PGDIR_SHIFT and PUD_SIZE is equal to 4 MB or 1 GB." (63) Table 2-7; "Table 2-7. Macros acting on Page Table entries" should be "Table 2-7. Macros acting on page table entries" (63) Second entry in table 2-7 (Description column); ... address of a memory descriptor cw (see chapter 9). I think the cw is additional (probably left over markup). [67] 4th paragraph; The paragraph: "Figure 2-13 shows how the first 3 MB of RAM are filled by Linux. We have assumed that the kernel requires less than 3 MB of RAM." should read EITHER: "Figure 2-13 shows how the first 3 MB of RAM are filled by Linux. We have assumed that the kernel requires less than 2 MB of RAM." OR: "Figure 2-13 shows how the first 4 MB of RAM are filled by Linux. We have assumed that the kernel requires less than 3 MB of RAM." REASON: Page 67, paragraph 5 makes clear that the representative kernel is being loaded at 0x00100000, not 0x00000000: "The symbol _text, which corresponds to physical address 0x00100000, denotes the address of the first byte of kernel code." Therefore, the maximum kernel size shown in the diagram must be at least 1 MB smaller than the address space shown, as it is not loaded until after the first megabyte. Either the representative kernel must be under 2 MB to fit into the diagram's 3 MB space, or the diagram must be expanded to 4 MB to fit the under-3 MB kernel. The latter is preferable, and is probably what the authors originally intended, because it corresponds to page 66, paragraph 1: "A typical configuration yields a kernel that can be loaded in less than 3 MB of RAM." Please also note that, in fixing this error, the diagram in question (Figure 2-13, p. 68) must also be fixed to address the erratum currently cited at: http://www.oreilly.com/catalog/understandlk/errata/understandlk.confirmed because the diagram fails to reflect the above-cited quote about the kernel loading at 0x00100000 by placing the _text symbol directly AT 0x00100000 instead of somewhere above it. (69) Last sentence of the first paragraph; The text said it will elaborate a problem in detail in "Linear Addresses of Noncontiguous Memory Areas." But the refered section talks nothing about the problem. The right section should be "Allocating a Noncontiguous Memory Area." {73} 3rd paragraph last line from top; "linear address X maps physical address X-PAGE_OFFSET" should be "linear address X maps physical address X-PAGE_OFFSET-PHYS_OFFSET" {92} Last paragraph; "...for systems having 512MB of RAM, each hash table is store in four page frames and includes 2,048 entries." Since the return value of fls() is 1 based, while bit-position is 0-based, fls(512 *4) returns 12, although the last-bit-set is bit 11. So if megabytes = 512, then: pidhash_shift = max(4, fls(megabytes * 4)); ==> pidhash_shift = 12. pidhash_shift = min(12, pidhash_shift); ==> pidhash_shift = 12 pidhash_size = 1 << pidhash_shift; ==> pidhash_size = 1 << 12 = 4096. So the questioned statement should be "...for systems having 512MB of RAM, each hash table is store in four page frames and includes 4,096 entries." {92} last paragraph; If the system has 512MB of RAM, each hash table should have 4096 entries. That is because the "bit position" returned by fls() is 1-based. Therefore, for 512MB, the fls(512 * 4) returns 12, not 11. So final pidhash_shift in this example is 12, which leads to that pidhash_size = 1 << pidhash_shift = 1 << 12 = 4096. To make the rest of the description of the example correct, the system should be having 256MB memory. Suggested change of the last sentence: "for example, for systems having 256MB of RAM, each hash table..." {94} Figure 3-5; Left side of PID hash table, "1466" should be "1465" Right side of PID hash table, "PID 199" should be "PID 2890" {106} 3rd paragraph; --that is, the prev local variable allocated on the Kernel Mode stack of A. should be --that is, the prev local variable allocated on the Kernel Mode stack of C. (113--160) Entire pages; This book is missing what looks like a 48 page signature! The information is completely missing and content jumps from page 112 to page 160. It's not simply a pagination issue; there's clearly a gap in material. {122} Point 25:a; in "a.Initializes tsk->tgid to tsk->current->tgid" "tsk->current->tgid" should be "current->tgid" [142] 21st line from top; "CPL is lower than the DPL" should be "DPL is lower than the CPL" {215} 5th paragraph,last line; "the value one if the flag is set." should be "the value zero if the flag is set." [215] 2nd paragraph, 7th-8th line; "if it is equal to zero the function terminates" should be "if it is greater than zero the function terminates" (228) Time Stamp Couner 1st patagraph,2nd line; "80x86 microprocessors sport a " should be "80x86 microprocessors support a " {260} 3rd paragraph; The identifier "page_alloc" should probably be "alloc_page" in line 4 of the code example ("The main cycle of alloc_are_pte() is:") and in the 3rd paragraph ("Each page frame is allocated through..."). [265] 2nd line from bottom; "from 1(highest priority) to 99(lowest priority)" should be "from 0(highest priority) to 99(lowest priority)" {296} 4th paragraph: description of the _count field; The page_count() function does not return the value of the _count field increased by one, it returns exactly the the value of _count. I've checked with the following kernel versions (everything's in include/linux/mm.h): 2.6.0: page_count() is defined as a macro: #define page_count(p) atomic_read(&(p)->count) (the _count field is called count in that version) 2.6.18: static inline int page_count(struct page *page) { if (unlikely(PageCompound(page))) page = (struct page *)page_private(page); return atomic_read(&page->_count); } 2.6.21.1: the page struct is now defined in mm_types.h, but the page_count() function is the same as in 2.6.18 {302} mid-page; Present (Incorrect): For instance, if the ZONE_NORMAL zone is eight times bigger than ZONE_DMA, seven-eighths of the page frames will be taken from ZONE_NORMAL and one-eighth from ZONE_DMA. It must be: For instance, if the ZONE_NORMAL zone is eight times bigger than ZONE_DMA, eight-ninth of the page frames will be taken from ZONE_NORMAL and one-ninth from ZONE_DMA. e.g. If there are 9 pages to be reserved, 8 pages will be taken from ZONE_NORMAL and 1 page from ZONE_DMA. (337) kmem_cache_alloc() code segment ; First parameter of function is kmem_cache_t * cachep. Inside code segment, pointer now has an underscore. ac = cache_p->array[smp_processor_id()]; {337} 8th Line of code extract; Line 8 of code extract reads: ac = cache_p->array[smp_processor_id()]; I believe this should read: ac = cachep->array[smp_processor_id()]; As described in point 1 of the following description. (340) the paragraph after the section "General Purpose Objects"; As stated earlier in the section "The Buddy System Algorithm," should be : As stated earlier in the section "General and Specific Cahces," [402] last line of 2nd paragraph from bottom; "also loads the Segment Selector of the kernel data segment in ds and es" should be "also loads the Segment Selector of the user data segment in ds and es" (516) 3th line of the 6th paragraph from bottom; "F_GETBLK for the former function" may be "F_GETLK for the former function" {571} six line from bottom ; bio->bd_contains must change following sentence. from "bio->bd_contains" to "bio->bi_dev->bd_contains" {650} comment; "generic_file_aio_write_nolock()" should be "__generic_file_aio_write_nolock()" (669) 9th line from bottom; "on a file with O_DIRECT" should be "on a file being opened with O_DIRECT" {732} 5th line from bottom; "Invokes __add_to_page_cache(), passing" is incorrect. It should rather read: "Invokes __add_to_swap_cache(), passing" {735} End of point 4; The sentence: "if the page is already in the swap cache, it jumps to step 6" should read as: "if the page is already in the swap cache, it jumps to step 8" {747} figure 18-2; e_name_len should not inlucde Descr.'s length, it should be name's length. see code.(2.6.11 xattr.h) #define EXT2_XATTR_PAD_BITS 2 #define EXT2_XATTR_PAD (1<e_name_len)) ) #define EXT2_XATTR_SIZE(size) \ (((size) + EXT2_XATTR_ROUND) & ~EXT2_XATTR_ROUND) {756} bottom paragraph; dont' think 128 characters is meaningful to describe an inode, 128 bytes is more the norm. characters is usually not used to describe binary structures. {811} 2nd paragraph; "When the process forked by the shell executes such a program, its euid and fsuid fields are set to 0--to the PID of the superuser." should be: "When the process forked by the shell executes such a program, its euid and fsuid fields are set to 0--to the UID of the superuser (the file owner)." [820] program listing; The example program does not do what is intended. It will print out the memory map of the cat program, not of the program itself. I suggest this replacement: #include #include #include int main() { FILE *f; int c; brk((void *)0x8051000); f = fopen("/proc/self/maps", "r"); while (c = getc(f), c != EOF) putchar(c); fclose(f); return 0; } {838} first line; "from 0x00098000 to 0x000969ff" should read "from 0x00096800 to 0x000969ff" (840) 1st paragraph; The top sentence "or to physical address 0x00001000" should be "0x0001000", as the low address in RAM for small kernel image is at 0x00010000 (see p838, 6th paragraph) {903} "W" index; "__wait_on_bit_bit" should be "__wait_on_bit_lock"