Error Sheet for Third Edition

Last updated 2/26/10

The errata listed on this page lists errors that cause the meaning of the passage to be incorrect or misleading. There is a companion list of all errors I know about, including typos, if you wish to see those as well. If you see one that is not on either page, I would certainly appreciate knowing about it -- send a message to Gary.Nutt@colorado.edu.

You can find more supplementary materials — lecture notes, examples, short explanations, exercises, and lab exercises — here.


Page xi, Line 15: Change "... The approach has the added benefit on not requringing ..." to "... The approach has the added benefit of not requringing ..."
(Thanks to Prof. James Evans, Lawrence University)

Page 9, Line 31: Change "seek(device, 236);" to "seek(device, track); //e.g., track=236"

Page 9, Line 32: Change "out(device, 9);" to "out(device, sector); //e.g., sector=9"

Page 9, Line 36: Change "... such as 236 in the load" to "... such as track=236 in the seek"

Page 9, Line 37: Change "... such as 9 in the out" to "... such as sector=9 in the out"

Page 37, Line 43: Change "... but it is orders of magnitude faster. ..." to "... but it is about five times faster. ..."
(Thanks to Prof. James Evans, Lawrence University)

Page 22, Line 17: Change "A job can use the process only ..." to "A job can use the processor only ..."
(Thanks to Prof. James Evans, Lawrence University)

Page 60, Line 34: Change "childPID = fork();" to "theChild = fork();"
and "childPID" in line 34 should be changed to "theChild".
(Thanks to Andre Deutz, Leiden University)

Page 63, parseCommand() function: Here is a very useful note from David Shaffer (Asst. Prof., Westminster College) regarding bugs in the code, and in the problem specification. I agree with his assessment. Thanks also to Wason Lewlomprisarn for his comments regarding this problem.
On page 63 the function parseCommand erroneously allocates memory and, while that doesn't prevent it from executing correctly, it presents conceptual confusion to students. Especially as they try to free the allocated memory before their next call to that function. Here's the problem: you allocate memory with malloc in several places but then immediately overwrite the pointer returned by malloc with the return result of strsep. If you correct this problem, though, then your use of the sizeof operator near the end of the function seems to fail (I'm not sure why sizeof(some char *) would not always produce 4 but I'm no expert on sizeof). Here's my corrected version which simply fills the argv array with pointers into the cLine string as returned by strsep. The only allocated memory is in cmd->name and it should be free()-ed as soon as it is no longer needed (for Simple Shell lab that is usually right before you call parseCommand again)...typed from memory so pardon any newly introduced errors ;-)
int parseCommand(char *cLine, struct command_t *cmd) {
  int argc;
  char *clPtr;  /* my preference for how to treat strsep arg */
  clPtr = cLine;
  argc = 0;
  while((cmd->argv[argc++] = (char *)strsep(&clPtr, WHITESPACE)) != NULL)
      ;
  cmd->argc = argc-1;
  cmd->name = (char *)malloc(strlen(cmd->argv[0])+sizeof(char));
  strcpy(cmd->name, cmd->argv[0]);
  return 1;
}

Now, personally I think your goal might have been to copy the substrings returned by strsep into malloc-ed memory. I obviously didn't try to reproduce your intent, just cleared the memory leak. I would encourage students to eventually call free(cmd->name) in the docs of parseCommand.

Finally, your definition of WHITESPACE to include the period character is troublesome since you give several examples with period in your commands: "a.out foo 100" or "gcc main.c" both fail with your parser. I don't see the usefulness of comma or period in WHITESPACE. Am I missing something?



Page 87, Line 4: Add "#include <process.h>"
(Thanks to Prof. Tom Anastasio, Salisbury Univ.)

Page 123, Line 36: Change "cuted in 0.0004 μs, or 0.4 ns."" to "cuted in 0.0005 μs, or 0.5 ns.""
(Thanks to Chris Johnson, University of Colorado)

Page 124, Figure 4.6: Change label in Command box from "write" to "read"
(Thanks to Wang-ting Lin, University of Colorado, and Prof. James Evans, Lawrence University)

Page 127, Line 6: Change "The SCSI (small computer serial interface) is an" to "The SCSI (small computer system interface) is an"
(Thanks to Gary Schubert)

Page 131, Line 38: Change "... An interrupt request flag," to "... An interrupt request flag (or register),"
(Thanks to Andre Deutz, Leiden University)

Page 132, Replace Figure 4.11 with this (text-only)version:
Software Algorithm:
// Wait for device to become idle
while((busy == 1) || (done == 1))
    wait();
// busy == 0 and done == 0
// Start the device (Will set busy to 1)
    . . .
while(busy == 1)
    wait();  // Wait for op to finish
// busy is 0 and done is 1
// Now clean up after I/O operation
    . . . 
    done = 0;
// Device I/O complete

Hardware Algorithm:
// Request for I/O op
while((busy == 0) && (done == 1))
    wait();
// Do the I/O operation
busy = 1;
. . .
busy = 0;
done = 1;
You can see an image of the graphic figure here.
(Thanks to Prof. Bo Sun, Lamar University, and many others for requesting an improvement in the figure)

Page 133, Caption for Figure 4.14: Change the reference to Figure 4.11 to Figure 4.12.
(Thanks to Andre Deutz, Leiden University)

Page 133, Caption for Figure 4.14: Change "...busy flag changes to 0," to "...done flag changes to 1,"
(Thanks to Prof. James Evans, Lawrence University)

Page 134, Line 6: Change the index, i to j.
(Thanks to Andre Deutz, Leiden University)

Page 134, Line 8: Change the reference to Figure 4.12 to Figure 4.14, and change the index, i to j.
(Thanks to Andre Deutz, Leiden University)

Page 134, Line 13: Change "ation to be completed. ..." to "ation to be completed, and the second device interrupt to be ignored. It is usually possible (but more complex) to rewrite the Interrupt Handler to process all of the interrupts.
(Thanks to Andre Deutz, Leiden University)

Page 136, Figure 4.17: The device and controller are shown connected to both the Address and Data Buses, but one line should be connected to the Address Bus and the other to the Data Bus.
(Thanks to Yi Dai, Macau University of Science and Technology)


Page 138, Line 2 (below Figure 4.19): Change "...bootstrap loader of the form shown in step(1) in Figure 4.20 ..." to "...bootstrap loader identified in step(1) of Figure 4.19, and of the form shown in Figure 4.20 ..."

Page 146, Section on "Shared Memory Multiprocessors": Mark Kampe (UCLA professor) adds the following comments regarding developments in SMP machines in the last ten years:
... you say that no manufacturer has been able to build bigger than 20-way SMPs. It wouldn't surprise me if your own home directory at CU was on a 64 or 128-way SPARC. The old 1985 common bus Symmetric Multi-Processor architecture indeed reached a point where memory and other shared resource contention eliminated the marginal benefits for large numbers of processors ... but that is old news. By the mid 90s it was recognized that Non-Uniform Memory Architectures (where all processors can reference all memory, but some memory is faster for some processors) could dramatically reduce the cost and increase scalability of SMP systems. Many large servers are now "domained" in ways that make it extremely difficult to decide how many systems you have. The OS work to support these architectures is horrendous, but those may be the paths du jour to huge servers. ...


Page 146, Section on "Distributed Memory Multiprocessors": Mark Kampe (UCLA professor) adds the following comments regarding developments in SMP machines in the last ten years:
... Indeed many memory interconnects work by exchanging message over a very fast network (much more scalable than a big memory bus), but the systems I have worked with make it look like mirrored memory (though perhaps without coherent cache) by the time software sees it. ...


Page 188, Exercise 1: The reference to "Section 5.1" should be to "Section 5.2".
(Thanks to Prof. Gary Hasman, Grand View College)

Page 190, Additional information about Part B:: Here is some sample output for Part B. Instead of using the generic block format shown on line 29, this sample has a form that is a little more intuitive (and a heck of a lot easier to read). One of the tricky things here is that DOS disks use little endian word formats; the second tricky part is that it uses 12-bit fields packed into bytes. In the FAT dumps, this sample unpacks the bits so that they are actually block addresses (this interpretation of the bytes in the FAT is explained in detail in the . Windows lab exercise supplement for Chapter 11.

Page 194, Attacking the Problem: If you are solving this problem using Unix, you will find it helpful to use this implementation of DeviceIoControl() to read the disk geometry.

Page 195, Line 13: Change "DeviceIoCall()" to "DeviceIoControl()".

Page 196, Lines 9 & 11: Change "segmentDump()" to "sectorDump()".
(Thanks to Wang-ting Lin, University of Colorado)

Page 238, delete line 6: "// Enable child 1 signal handlers"
(Thanks to Michael Russell, University of Colorado)

Page 238, line 38: Change "// Enable child 1 signal handlers" to "// Enable child 2 signal handlers"
(Thanks to Erin Rowland, University of Colorado)

Page 239, First line of Exercise 1: Change "1. Explain why all processes in a thread block ..." t0 "1. Explain why all threads in a process block ..."
(Thanks to Dwayne Towell, Abilene Christian University)

Page 258, Line 9 below Figure 7.6 caption: Change "InterruptCount = K}" to "InterruptCount = K;"
(Thanks to William Franklin, General Dynamics/University of Maryland)

Page 276, Line 7: Change "... then the OS can transition the blocked thread to the running state." to "... then the OS can transition the blocked thread to the ready state."
(Thanks to Chris Johnson, University of Colorado)

Page 282, Line 2: Add the following statement to the problem. "Use a time quantum of 15, as in Problem 6." to the running state."
(Thanks to Gary Hasman, Grand View College)

Page 300, Line 11: Change "/* Let interrupt occur */" to "/* Let interrupt occur */ }" (add a closing brace to the exit() function).
(Thanks to William Franklin, University of Maryland University College)

Page 310, Line 3 (part of figure description): Change "attempt to read x after proc_B has written it. ..." to "attempt to read x after proc_A has written it. ..."
(Thanks to Nick Crawford)

Page 311, Line 12: Change "... In Figure 5.11, we" to "... In Figure 5.12, we"
(Thanks to Sergejs Melderis, Salisbury State University)

Page 312, Figure 8.18, change Line 9 in the producer() code from: "here = obtain(empty);" to "here = obtain(emptyPool);"
and change Line line 8 in the consumer() code from: "here = obtain(full);" to "here = obtain(fullPool);"
(Thanks to Paul Kavanagh, UCLA)

Page 319, Line 14: Change "... s.hold as being TRUE." to "... s.hold as being FALSE."
(Thanks to Andre Deutz, Leiden University)

Page 334, Lines 20 and 25: Change "pthread mutex_unlock(bal_mutex);" to "pthread_mutex_unlock(bal_mutex);"
(Thanks to William Franklin, University of Maryland University College)

Page 338, Line 31: Change "} else {" to "}"
(Thanks to Wang-ting Lin, University of Colorado and Prof. James Evans, Lawrence University)

Page 339, Figure 9.4: Here is a note from Tim O'Neill (University of Akron) regarding a problem/assumption with the enqueue() function.
... Specifically the code
	P_sim (PID callingThread, semaphore R, semaphore S) {
	...
	if (R.val==0) {
		r_Num++;
		enqueue(callingThread, R_wait);
		V(mutex);
		goto L1;
	} else {
	...
To reach this point I must be unable to acquire the R semaphore and am enqueued waiting its availability. If enqueue() is a blocking call I never release mutex, so another thread that could release me will block at the beginning of V_sim() and the system grinds to a halt. On the other hand, if enqueue() is not blocking, I could potentially place myself on the R_wait queue multiple times unnecessarily. On the other (third?) hand, if I alter the code so that V(mutex) precedes the blocking enqueue() call, I no longer have exclusive access to the R_wait queue and have a potential race condition.

Prof. O'Neill is correct. The enqueue() needs to atomically release the mutex when it blocks, then resume at L1. This means that the V(mutex) should be removed since it is absorbed into the enqueue() function.


Page 357, Lines 29-33: Regarding the sentence that begins "Therefore, some systems block ...": This sentence contradicts the definitions of async/sync send that are given in the preceding paragraphs. The problem is that some designers, especially for IPC that will be used over a network, sometimes use the definition of weak synchronous send for their asynchronous send, and the definition of strong synchronous send as the definition for their synchronous send. They do this because the sender sometimes has little knowledge about whether or not the receiving process exists at the moment of the send. This means that async sends fail more often than one would like in this environment, so the designers just dispense with the strict definition. (Thanks to Chris Johnson, University of Colorado, for asking for clarification about this.)

Page 360, Line 17: (Exercise 9.5) Change "POSIX" to "System V".

Page 366, Line 20:
Change "int errno /*For nonblocking read flag */;" to
"int errno;
int on = 1; /*For nonblocking read flag */;
"
(Thanks to Sebastian de la Chica, University of Colorado)

Page 367, Line -1:
Change "ZeroMemory(&pStartInfo, sizeof(STARTUPINFO);" to "ZeroMemory(&srcStartInfo, sizeof(STARTUPINFO);"
(Thanks to Mark Eret, University of Colorado)

Page 368
Line 3: Change "srcStartInfo.hStdOutput = pfWritePipe;" to "srcStartInfo.hStdOutput = writePipe;"
Line 11Change "ZeroMemory(&cStartInfo, sizeof(STARTUPINFO);" to "ZeroMemory(&sinkStartInfo, sizeof(STARTUPINFO);"
Line 14: Change "srcStartInfo.hStdOutput = pfWritePipe;" to "sinkStartInfo.hStdOutput = readPipe;"
(Thanks to Mark Eret, University of Colorado)

Page 400, Lines 30-31: Replace the bulleted lines by "- All request edges for the process can be satisfied."
(Thanks to Andre Deutz, Leiden University)

Page 408, Subsection on "Recovery": Mark Kampe (UCLA professor) adds the following comments regarding more robust (less cavalier) recovery in contempory systems. His remarks reflect the way that an OS can be made to do a better job of recovery (beside the rollback approach). It is also clear that OS designers tradeoff this kind of functionality against performance.
Health monitoring identifies the components that are not currently meeting their requirements. Failure mode analysis networks consider the union of all reports and suggest a course of action. Configured restart groups determine which processes should be killed and restarted, how, and in what order. If recovery does not happen within a prescribed time period (or number of attempts), higher level dependency networks describe a sequence of graduated escalations (warm restarts, cold restarts, restarting 1st and 2nd order clients, rebooting individual systems, and clusters of systems, ...)


Page 415, Line 24: Change "... (for example, terrabytes) ...) to "... (for example, terabytes) ...)
(Thanks to Prof. James Evans, Lawrence University)

Page 427, Line 33: Change "sbrk() system call ..." to "brk() system call (via the sbrk() libary call) ..."
(Thanks to "Vivek B," Government Engineering College, Thissur, India)

Page 430, Figure 11.13: The block at the bottom of the figure that is labeled "Process 1" should be labeled "Process 2".

Page 433, Line 1: Change this line to read: "When p6 releases its memory, then n5+n6 units of memory can be combined with the unallocated"
(Thanks to Chris Johnson, University of Colorado)

Page 455, last line of footnote: Change this line to read: "bitwise OR operator ("|") and assigned to shmflg.
(Thanks to Mark Eret, University of Colorado)

Page 457, Line 17 Change "#include " to "#include "
(Thanks to Kim Yoo-na, Kyoungpook National University, South Korea)

Page 459, Line 6: Change this line to read: "/* Create N work processes, setup IPC using shared memory */"
(Thanks to Dave Brown)

Page 460, first line following Section 12.1 header: The line reads "Although virtual memory did not appear in commercial systems until the 1980s,". However, IBM VM/370 used virtual memory in its OS as early as 1970.
(Thanks to D. J. Foreman, Ph.D, SUNY-Binghamton)

Page 483, Line 10: Change "... A plot of the fault rate" to "... A plot of the fault rate versus the memory allocation"
(Thanks to Prof. James Evans, Lawrence University)

Page 484, Lines 12-17: Here are some additional remarks regarding the relationship between the working set size, window size (w), and the page frame allocation. The working set represents the unique pages that are currently being used by the process. The window set is the number of page referernces that are being considered to determine the members of the working set. The page frame allocation is the number of page frames in the primary memory that are allocated to the process. So w would ordinarily be much larger than the working set size or the page frame allocation. The page frame allocation will always be at least as large as the working set size, but not much larger. See this web site for more explanation.
(Thanks to Prof. Mark Kampe, UCLA)

Page 486, Table 12.10: The asterisk on the 3 in the rightmost column should be deleted, i.e., only page 7 is loaded in that column.
(Thanks to Scott Davis, Univ of Iowa)

Page 506, Line 11: (Exercise 12.4) Change the 32-entry page middle directory to be a 64-entry. Then have bits 0:9 - 10-bit offset, bits 10:17 - 8-bit pte, bits 18:23 - 6-bit pmd, and bits 24:31 - 8-bit pgd.

Page 508, Line 1 of the caption for Figure 12.22: Change "The Sink reads a byte stream ..." to "The Source reads a byte stream ..."
(Thanks to Wang-ting Lin, University of Colorado)

Page 518, Line 45: Change "record-stream translation, it is a ..." to "record-block translation, it is a ..."
(Thanks to Wang-ting Lin, University of Colorado)

Page 559, Line 16: (Exercise 13.4) Change "O_FILE_APPEND_DATA" to "FILE_APPEND_DATA".

Page 582, Line 7: Change "... = ith one-time password" to "... = (i+1)th one-time password"
(Thanks to Jim Cook, Univ of Maryland University College)

Page 584, Line 43:
and
Page 585, Line 1: Change "... TSL ..." to "... TLS ..."
(Thanks to Josh Rosenbluh, Grinnell College)

Page 609, Lines 14-15: Change the two references to "K_e" to "K_p"
(Thanks to Prof. James Evans, Lawrence University)

Page 635, Lines 12-15: The description of IP addresses is incorrect. Here is the real description from [Stevens, 1994]:
Class A
 0 1    7 8                                         31
+-+------+-------------------------------------------+
|0|  net |                host                       |
+-+------+-------------------------------------------+

Class B
 0  2                   15 16                       31
+--+----------------------+--------------------------+
|10|        net           |        host              |
+--+----------------------+--------------------------+

Class C
 0   3                                  23 24       31
+---+-------------------------------------+----------+
|110|               net                   |   host   |
+---+-------------------------------------+----------+

Class D
 0    4                                             31
+----+-----------------------------------------------+
|1110|               Multicast group ID              |
+----+-----------------------------------------------+

Class E (experimental - not described in the text)
 0     5                                            31
+-----+----------------------------------------------+
|11110|           (Reserved for future use)          |
+-----+----------------------------------------------+


Page 635, Paragraph 2: There is a mistake in the explanation of Class B addresses, which causes a number of small errors. Here is a restatement of the entire paragraph:

The type of an IP address is determined by the setting of the most significant bits in the address: Class A addresses have the most significant bit set to 0; they are 10 in Class B addresses; Class C addresses use 110; and Class D addresses have the tag field set to 1110 [Stevens, 1994]. For example, suppose we had a 32-bit IP address 0x807BEA0C. When we are thinking about IP addresses, we usually write the 32-bit address using the dotted decimal notation: We convert the hexadecimal representation to dotted decimal notation by first separating the 32 bits into 4 bytes: 0x 80 7B EA 0C. Next, we convert each of the bytes into a decimal number, so 80(base 16) = 128(base 10), 7B(base 16) = 123(base 10), EA(base 16) = 234(base 10), and 0C(base 16) = 12(base 10). Finally, we write these four decimal numbers to represent the 32-bit number as 128.123.234.12. When we see a dotted decimal IP address where the first number is in the range 128-191, we have a Class B address: The 2 most significant bits are the tag, the next 14 most significant bits are the net number and the 16 least significant bits are the host number. So, after stripping out the tag field from the two most significant bits, we see that the net number for this IP address is 0.123 in dotted decimal notation (0x007B in hexadecimal notation), or 123(base 10). The host number is the two least significant bytes, or 3756(base 10).
(Thanks to Yuzo Yamamoto, Dalarna University, Sweden)


Page 662, last Line on the page: Change "... Computers could suddenly exchange information ten thousand ..." to "... Computers could suddenly exchange information a thousand ..."
(Thanks to Prof. James Evans, Lawrence University)

Page 695, Line 7: Change "... and to transfer it to the disk controller ..." to "... and the time to transfer the data in the block to the disk controller ..."
(Thanks to Prof. James Evans, Lawrence University)

Page 771, Line 2. (Exercise 18.1) Oops. This is the same as Exercise 2b in Chapter 17. Delete it.

Page 771, Line 5: (Exercise 18.2) Change "Section 18.2" to "Lab Exercise 11.1."

Page 771, Line 12. (Exercise 18.6) Oops. This is the same as Exercise 5 in Chapter 14. Delete it.

Page 778, Line 27. Change "the OS to a different OS ..." to "the OS to a different architecture ..."
(Thanks to Alex Snoeren)