Errata for The Linux Programming Interface

This page lists errata for The Linux Programming Interface. (For information about changes to the kernel-user-space and glibc APIs since TLPI was published, see the API changes page. For information about changes to the source code examples associated with book, see the source code CHANGES page.)

In general, if you make an error report I'll list your name against the report, and even link to a home page or similar if you like. If you don't want your name to appear against the error report, please let me know at the time that you make the report.

You may also want to look at this frequently asked question.

Order errata list by: page number, print run, report date, or category

Last updated: 2024-10-15; last error report: 2024-09-13


This version of the errata page lists fixes according to the print run of the book in which they first appeared.

Below, the page number column uses the following coding for errors:

Color code Explanation
Code fix A significant fix to a piece of code
Minor code fix A minor fix to a piece of code; generally the problem and its solution should be obvious on an attentive reading of the text, or there is a syntax error in a code snippet that would be detected during compilation
Technical fix A significant fix to a technical point explained in the text
Minor technical fix A minor fix to a technical point explained in the text (typically a correction to a minor or obvious technical error or imprecision in the text)
Clarification A significant clarification to an explanation in the text
Minor clarification A minor clarification to an explanation in the text (the overall meaning of the text isn't changed, but a minor detail is added or improved)
Update This is a note on information that is new since the book was published (normally information relating to a kernel or glibc change); there won't be a change to the text
Typo A fix to a minor typographical or grammar error, or a replacement of an (obviously) incorrect word

Use the following links to jump to:

To determine which printing of the book you have, look at the page containing the copyright notice (one of the pages right at the start of the book, before the table of contents). Near the top of the page, just below the copyright notice, is a line of numbers. The lowest ordinal number that you can see in this list indicates the print run that produced your copy of the book.

Fixes queued for the next print run

The following fixes and improvements will be applied in the next printing.

[No errata]

Notes

The following points are notes to the text, rather than fixes; there are no corresponding changes in the text of any print run.

Page Fix Reported
9

As a consequence of the (July 2011) renumbering of the Linux kernel series from 2.6.x to 3.x and then 4.x, 5.x, and (most recently) 6.x, the discussion of kernel numbering on this page is now out-of-date. However, all that has changed is that the numbering scheme has been simplified (replacing an invariant 2.6 with 3 and, subsequently, 4, 5, and 6); the kernel development model remains unchanged. As Linus Torvalds noted of the 3.0 release, there is otherwise nothing special about the release (i.e., no changes more significant than those in Linux 2.6.39 and each of the preceding 2.6.x releases).

In the bulleted list at the top of the page, each of the instances of 2.6.z could simply be replaced by 6.z and the description on this page would still be accurate for the current kernel development model.


Explanation:

(This note was last updated in December 2022.)

2011-07-29

9

The small-font note at the bottom of the page states "At the time of writing, the 2.4 stable Linux kernel is still supported…" This has now ceased to be true: in April 2012, Willy Tarreau, the Linux 2.4 maintainer, noted that the 2.4 kernel series has now reached end-of-life.

2012-04-10

16

As a follow-on to the discussion in Section 1.3.5, note that the Austin group published Technical Corrigendum Number 1 for POSIX.1-2008/SUSv4, in 2013, Technical Corrigendum Number 2 in 2016. The standard can be found online at http://www.unix.org/version4/.


Explanation:

(This note was updated on 14 October 2017.)

2014-01-17

44

In the list item "5" (toward the bottom of the page), change:

In response to the trap to location 0x80, the kernel invokes its system_call() routine (located in the assembler file arch/x86/kernel/entry.S) to handle the trap.

to:

In response to the trap to location 0x80, the kernel invokes its system_call() routine to handle the trap.

Explanation:

Over the years, the details about which source files contain various kernel functions have changed. Those details are in any case not critical to the discussion on this page, so, rather that trying to keep the text constantly up to date with the source code changes, it seems better to remove such details.

An earlier version of this erratum (reported by Leif Dyvik on 2012-04-25 and incorporated in the fourth print run) is shown below.

In the list item "5" (toward the bottom of the page), change:

arch/i386/entry.S

to:

arch/x86/kernel/entry.S

Reported by Alex Colomar.

2021-04-05

46

Because of ongoing changes in the kernel and in the GNU C library, all of the pathnames mentioned in Figure 3-1 are no longer accurate. Since such changes are likely to be ongoing in the future, I won't make further updates in this figure, and will just note that the pathnames in the upper right, lower left, and lower right boxes are all nowadays out of date.


Explanation:

An earlier erratum for this figure (dated 2012-05-25, for a change that was made in the fourth print run) is shown in the following paragraphs. By now, that erratum has itself become out of date.

In the bottom left hand of Figure 3-1, under the heading System call service routine, change:

arch/x86/kernel/process_32.c

to:

arch/x86/kernel/process.c

Erratum originally written on 2012-04-25; a small correction was made on 2012-05-25 after a note from Robert P. J. Day.

Reported by Alex Colomar.

2021-04-05

62

The description of the _BSD_SOURCE feature test macro about one third of the way down this page says that it causes BSD definitions to be favored in a few cases where standards conflict. Starting with glibc 2.19, _BSD_SOURCE no longer has this effect.

2014-01-06

62

Starting with version 2.19, glibc adds a new feature test macro, _DEFAULT_SOURCE. Defining this macro provides an effect similar to the feature test macros that are defined by default—that is, _BSD_SOURCE, _SVID_SOURCE, and _POSIX_C_SOURCE=200809. This macro can be defined to ensure that the "default" definitions are provided even when the defaults would otherwise be disabled, as happens when individual macros are explicitly defined, or the compiler is invoked in one of its "standard" modes (e.g., cc -std=c99).

2014-01-06

62

In glibc 2.20 and later, _BSD_SOURCE and _SVID_SOURCE are deprecated. They are redefined to have the same meaning as _DEFAULT_SOURCE, and thus continue to expose all of the definitions that they exposed earlier. However, the use of these macros now produces a warning, unless _DEFAULT_SOURCE is also defined.

This means that all function prototype boxes in TLPI that show _BSD_SOURCE or _SVID_SOURCE as being required should instead show _DEFAULT_SOURCE. However, that change will be left to a future edition of TLPI. In the meantime, a workaround fix to the makefiles has been added to the code in the source distribution so that now all code is compiled with _DEFAULT_SOURCE defined. Note also that starting with release 4.05 (March 2016), the Linux manual pages provide up-to-date documentation of the feature test macro requirements since glibc 2.20.

2014-03-12

72

Some further explanation may be useful for readers that try running a command similar to this command shown in the shell session near the top of the page:

$ ./copy /dev/pts/16 /dev/tty

In this case, /dev/pts/16 is a pseudoterminal device that is being used as the input file for the copy program. (You can discover the name of the pseudoterminal being employed in a terminal window by running the tty command.)

If trying this example, you may see results that are puzzling until various topics are covered later in the book. In particular, if, as is likely the case, a shell is running in the foreground of the source device, then the following points mean that some of the text that you type on the source device may not be copied to /dev/tty:

  • On the source device, the shell and the copy program are competing for input.
  • Assuming that a line-editing shell such as bash(1) is running on the source device, the shell will have placed the terminal in noncanonical mode (see TLPI section 62.6), so that input is available a character at a time. This means the shell will get some of the characters typed on the source device and copy will get some.

One way to avoid this confusing behavior is to run a command such as sleep 1000 on the source device. This will cause the shell to set the terminal to canonical mode (input available a line at a time) and place itself in the background. This will stop the shell competing for input, and will make all input typed on the source device available (a line at a time) to the copy program.


Reported by Henri van de Water.

2015-03-12

74

Since Linux 2.6.39, the open() system call supports a new file creation flag, O_PATH, that can be used to obtain a file descriptor with specific, limited uses. The returned file descriptor can be closed, duplicated, or passed via a UNIX domain socket (see Section 61.13.3); the file descriptor can't be read from or written to. O_PATH can be used to obtain file descriptors for symbolic links. O_PATH file descriptors are provided for use as the directory file descriptor argument in the *at() system calls (see Section 18.11).

2011-06-26

82

This note is also relevant for the discussion of file holes on page 83.

Linux 3.1 adds two new operations for the lseek() system call: SEEK_HOLE and SEEK_DATA. These operations can be used to determine the start and end of holes in a file. SEEK_HOLE seeks to the offset of the next file hole whose location is greater than or equal to the current file offset. SEEK_DATA seeks to the offset of the next non-hole region whose location is greater than or equal to the current file offset.

2011-07-29

83

Linux 2.6.38 added FALLOC_FL_PUNCH_HOLE, a new operation for the fallocate() system call. This operation creates a hole in a file in the byte range indicated by the system call's offset and len arguments. (The file data in the specified range is lost.) Among the file systems that already support this operation are XFS and (since Linux 3.0) ext4. Btrfs is capable of supporting the operation, and support is likely to be added in the future.

2011-06-30

100

The details described in the second paragraph (starting "However, the glibc wrapper functions for readv() and writev()) in the small-font note at the top of the page have no longer been true for quite a while. Since glibc 2.10, if the wrapper functions for readv() and writev() detect that the kernel version is 2.6.18 or later, they always only invoke the raw system calls. And since glibc 2.20, where the minimum Linux kernel version required by the library is 2.6.32, the wrapper functions always just directly invoke the system calls. For further details, see the preadv(2) manual page.

2015-01-20

137

Regarding the discussion on pages 135-137 of longjmp() clobbering variables (and especially the example shown a bit more than half way down page 137), things seem to have changed a little since the book was published. In more recent versions of gcc, the "warning: variable `nvar' might be clobbered by `longjmp' or `vfork'" warning is generated in cases where compiler optimization is disabled, and is not generated when optimization is enabled. Presumably, gcc is warning about the cases where clobbering can occur, and in more recent versions of the compiler, that is the case when optimization is disabled. I've yet to determine in which gcc version the behavior changed, or the rationale for the change.


Reported by Henri van de Water.

2015-03-24

165

About half-way down the page, there is the following piece of code in Listing 8-2 (users_groups/check_password.c):

    spwd = getspnam(username);
    if (spwd == NULL && errno == EACCES)
        fatal("no permission to read shadow password file");

    if (spwd != NULL)           /* If there is a shadow password record */
        pwd->pw_passwd = spwd->sp_pwdp;     /* Use the shadow password */

However, depending on your system configuration, getspnam() may not return the error EACCES if the user doesn't have permission to read the shadow password file. In particular, it may be that in your system's /etc/nsswitch.conf file, you see systemd listed:

shadow:     files   systemd

(It is likely that you will see this if you have a Debian-based or Arch-based distribution.)

In the case where the Name Server Switch falls back to using systemd, and systemd determines that the user does not have sufficient permission to access the shadow password file, the getspnam() call simply returns NULL without setting errno. In that case, the program will fall through to a later check that prints a message that the password is incorrect:

    authOk = strcmp(encrypted, pwd->pw_passwd) == 0;
    if (!authOk) {
        printf("Incorrect password\n");
        exit(EXIT_FAILURE);
    }

Reported by Joaquín Moreno.

2024-01-09

224

In the first paragraph of this page, I refer to /proc as a "virtual file system". It would have been worth noting two points in relation to this term:

  • Some other sources use the term "pseudofilesystem" synonymously.
  • The Virtual File System described in Section 14,5, is a term used to refer to a specific component of the Linux kernel. This usage is unrelated to the term as used when describing the /proc file system.

However, there isn't sufficient space on the page to add this information.


Reported by Bill McConnaughey.

2011-07-18

241

Since Linux 2.6.39, the new syncfs() system call can be used to flush all kernel buffers containing updated file information for a specified file system (i.e., a per-file-system sync()). The file system is specified using a file descriptor that refers to an open file in the file system.

2011-06-26

241

The discussion of the pdflush kernel thread in the two small-font paragraphs near the bottom of this page is obsolete. (It was already obsolete at the time the book went to press, when kernel 2.6.35 was the current kernel version, but I missed the change.) Although the general principles described in the text remain accurate, in Linux 2.6.32 the pdflush was replaced by the so-called per-BDI flush mechanism, described in the LWN.net article, "Flushing out pdflush".

2012-08-21

301

The example shell session at the top of this page is technically correct, but could have been better chosen. As explained earlier in Section 15.4.5, the sticky bit no longer serves any purpose for regular (executable) files, but it does serve a purpose for directories. Therefore, a better example would have been to demonstrate manipulating the sticky bit of a directory, rather than a file.


Reported by Simon Durrant.

2011-08-23

312

The first bullet point on this page notes that various filesystems require that the filesystem be mounted with the mount –o user_xattr option in order to employ user EAs. Among the listed filesystems is ext4. However, since Linux 2.6.39, this option is no longer required for ext4: user EAs are enabled by default.

2015-01-22

314

The second bullet point on this page notes that on JFS there is a 128 kB limit on the total bytes used by the names and values of all EAs on a file. More recent kernels have removed this limit, so that it is possible to create much larger sets of EAs on a file. I haven't determined what the limiting factors are now, or when the changes occurred, but on a Linux 3.0 system I was able to attach 4 MB of EAs to a file on JFS.


Reported by Junjiro Okajima.

2012-07-06

319

The second paragraph of the small-font note on this page notes that various filesystems require that the filesystem be mounted with the mount –o acl option in order to user ACLs. Among the listed filesystems is ext4. However, since Linux 2.6.39, this option is no longer required for ext4: ACLs are enabled by default.

2015-01-22

344

This note relates to the section headed Interpretation of symbolic links by system calls. Linux 3.6 added some restrictions on the operation of symbolic links; these restrictions can cause system calls that dereference symbolic links to fail with the error EACCES if they are violated. The goal of these restrictions is to prevent a class of security issues caused by time-of-check, time-of-use races when accessing symbolic links. For more information, see this LWN.net article and the documentation of /proc/sys/fs/protected_symlinks in the proc(5) man page.

2012-10-15

345

Linux 3.6 added some restrictions on the creation of hard links; these restrictions can cause link() to return with an EPERM error if they are violated. The goal of the changes is to prevent a class of security issues caused by hard-link-based time-of-check, time-of-use races, most commonly seen in world-writable directories such as /tmp. For more information, see the documentation of /proc/sys/fs/protected_hardlinks in the proc(5) man page.

2012-10-15

348

This note relates to the shell session shown at the top of the page. Depending on factors including the file system type, the size of file created, and system scheduling decisions, you may not always see the behavior shown in this example. Specifically, by the time that the second df command is executed (via system()) the blocks of the closed file may not yet have been freed, so that the output of the second df shows no change in the amount of disk space used in the file system.

As a workaround, adding a sufficiently long sleep() call immediately after the close() call in dirs_links/t_unlink.c should ensure that the file blocks have been freed; in all cases that I tested (including very large file sizes), sleep(1) was sufficient to ensure that the file space was freed by the time of the second df command. Note that calling sync() after the close() call does not suffice to eliminate this behavior. Presumably, this is because sync() flushes dirty blocks in the buffer cache, whereas the file blocks in question here are being (asynchronously) deallocated by the kernel.


Reported by Yang Yang.

2011-12-04

357

Since glibc 2.24, the readdir_r() function has been marked as deprecated, for the reasons discussed in Austin Group bug report 696, this glibc bug report, and in the readdir_r() manual page.


This erratum was updated on 15 Aug 2017 to amend the glibc version where readdir)r() was deprecated from 2.23 to 2.24.

2016-02-29

362

Because of a regression that took place in glibc 2.4, the example shell session demonstrating the use of the nftw_dir_tree.c program may (if you are using a glibc version that does not have a fix for the regression) produce different results than are shown on this page.

In particular, in more or less the middle of the page there is the following line of output:

    1 SLN  2327987       dsl

Compare this line with an identical line shown further down the page where the nftw_dir_tree.c program is employed with the –p option, which specifies the use of the FTW_PHYS flag, meaning display information about symbolic links themselves, rather than the files they refer to.

The fact that the same i-node number is shown for the dangling symbolic link, dsl, both with and without the use of the FTW_PHYS flag indicates that even when the FTW_PHYS flag is not specified, if nftw() encounters a dangling symbolic link, then it uses lstat() to obtain a stat structure describing the symbolic link and that structure is passed to the callback function.

In glibc 2.4, there was a change in behavior that appears to have been unintended. Following that change, in the case where the FTW_PHYS flag is not specified, then when a dangling symbolic link is encountered, the contents of the stat structure that is passed to the callback function are left undefined. (In practice, this typically means that the last stat structure that was previously fetched for a file will be supplied to the callback function.)

This changed behaviour of the glibc nftw() implementation differed from every other implementation that I've tested or examined (Solaris, OpenBSD, FreeBSD, musl) and also my reading of POSIX.1, which notes that in the case where FTW_NS is passed in the typeflag argument of the callback function, the contents of the stat structure are undefined, but makes no similar statement regarding the case where FTW_SLN is passed in the typeflag argument (with the implication that the contents of the stat buffer are defined). The older (pre-2.4) glibc implementation of nftw() behaved in the same way as other current implementations.

I was already aware that there was an issue regarding the difference between the current glibc nftw() implementation and the behavior of nftw() on other systems, but LaoK's bug report against my book reminded me that I had myself earlier determined that the glibc implementation used to behave the same as other nftw() implementations.

This regression led to some bug reports against glibc (Red Hat bugzilla report 1422736 and glibc bug 23501) and Austin bug report 1121 against the POSIX.1 standard. Ultimately, it was resolved that this was a glibc bug, and the regression was fixed in glibc 2.30 (and possibly backported to some earlier glibc versions).

This erratum was updated on 2020-07-02 to reflect the fact that the glibc bug has been fixed.


Reported by LaoK.

2017-06-11

365

Since Section 18.11 was written, some new AT_* flags have been added to various system calls mentioned in this section.

Linux 2.6.38 added the AT_NO_AUTOMOUNT flag for fstatat(). This flag can be used to prevent automounting of the terminal component of a pathname given to fstatat().

Linux 2.6.39 added the AT_EMPTY_PATH flag for linkat(), fchownat(), fstatat(), and name_to_handle_at(). (An explanation of the last of these system calls can be found on LWN.net.) This flag allows empty relative pathnames to be specified for these system calls, in which case the calls operate on the directory file descriptor.

2011-06-26

378

Linux 2.6.36 adds a new input flag for inotify, IN_EXCL_UNLINK, that prevents children of a watched directory from generating events for a directory after they have been unlinked from that directory. This flag would slot into Table 19-1, just below IN_DONT_FOLLOW. Further information can be found in the inotify(7) manual page.

2011-05-08

379

In the discussion of the IN_ATTRIB event in the first bullet point, it is worth noting that the "link count changed" case also includes deletion of a file. However, there isn't sufficient space on the page to add this information.


Reported by Bill McConnaughey.

2011-07-18

380

At the end of the first of the two bullet points at the bottom of this page, there is the following sentence:

An IN_IGNORED event is not generated when a watch that was established with IN_ONESHOT is automatically removed because an event was triggered.

The semantics described in that sentence were by design, The idea being that if an application employed the IN_ONESHOT flag, then it knew that on the next event the watch would be dropped, and did not need the "noise" of an event to be informed of that fact.

However, starting with kernel 2.6.36, an IN_ONESHOT event is generated in this case. This change in semantics was almost certainly an unintended consequence of code reworking that went on during the development of the fanotify API.

2014-03-28

386

At the end of Chapter 19, rather more that could have been said about the limitations of the inotify API. In the meantime, I have made some substantial revisions to the inotify(7) man page to discuss limitations and pitfalls that one should be aware of when using the API. Having read Chapter 19, it's strongly advisable to read that manual page.

2014-04-06

406

Starting with version 2.32 (August 2020), the sys_siglist variable described in Section 20.8 is no longer exported by glibc.

2020-10-10

426

Regarding Table 21-1, POSIX.1-2008 Technical Corrigendum 1 (POSIX.1-2013) adds the following functions to the list of async-signal-safe functions: fchdir(), pthread_kill(), pthread_self(), and pthread_sigmask().

POSIX.1-2008 Technical Corrigendum 2 (POSIX.1-2016), adds even more functions to the list of async-signal-safe functions, including longjmp() and siglongjmp(), as well as a host of functions that deal with strings, wide-character strings, and memory comparisons and copying. For details, see the new signal-safety(7) manual page.


Explanation:

This note was updated on 12 Nov 2016 to reflect the POSIX.1-2016 changes.

2015-09-11

429

A small-font note near the bottom of this page says

If we define the _BSD_SOURCE feature test macro when compiling a program, then (the glibc) setjmp() follows the BSD semantics.

Since the deprecation of _BSD_SOURCE in glibc 2.19, _BSD_SOURCE no longer has this effect for setjmp(). For further details, see the setjmp(3) manual page.

2017-08-15

442

In Section 21.5, in the first paragraph following the numbered list, the error EINTR is described in parentheses as "Interrupted function". This text is the SUSv3 description of this error. However, glibc associates this error with a slightly different text: "Interrupted system call".


Reported by Junjiro Okajima.

2012-01-16

444

A bullet point at the bottom of this page notes that a read from an inotify file descriptor is never restarted, regardless of the setting of the SA_RESTART flag. However, starting with Linux 3.8, the SA_RESTART flag does cause a read from an inotify file descriptor to be restarted when interrupted by a signal handler.

2014-12-11

445

Under the heading Unhandled stop signals can generate EINTR for some Linux system calls, it is noted that certain system calls can produce the EINTR error even when there is no signal handler. Among the operations listed here are reads from inotify file descriptors. However, starting with Linux 3.8, reads from inotify file descriptors no longer demonstrate this behavior.

2014-12-11

450

With respect to Table 22-1, Linux 3.0 added a new specifier, %E. This specifier is replaced by the pathname of the executable file, with slashes (/) substituted by exclamation marks (!). (The substitution is required because slashes can't form part of a filename—they delimit the filename components of a pathname.)


Reported by Junjiro Okajima.

2012-01-16

450

With respect to Table 22-1, Linux 3.7 added a new specifier, %P. This specifier is replaced by the PID of the process as seen in the initial PID namespace (whereas the existing %p specifier is replaced by the PID in the PID namespace where the process resides).

2014-03-14

450

With respect to Table 22-1, Linux 3.12 added a new specifier, %d. This specifier is replaced by the "dumpable" mode of the process (the same value as is returned by the prctl(2) PR_GET_DUMPABLE operation).

2014-03-14

458

Although I refer to sigqueue() twice on this page (and once on page 477) as a system call, strictly speaking, it is a library function (layered on top of the Linux rt_sigqueueinfo(2) system call). In September 2011, I made changes to the Linux man pages to more accurately reflect that fact, documenting the underlying system call and moving the sigqueue() manual page from Section 2 (System Calls) to Section 3 (Library Functions).

2011-09-30

468

Ideally, the following information regarding portability should have been included at the bottom of this page:

On Linux, a blocked signal whose disposition is ignore is added to the process's set of pending signals if it is generated. However, according to SUSv3, an implementation may discard the signal in this case. (SUSv3 XSH 2.4.1: "If the action associated with a blocked signal is to ignore the signal and if that signal is generated for the process, it is unspecified whether the signal is discarded immediately upon generation or remains pending.") Thus, in addition to blocking the signal, a portable application that uses sigwaitinfo() to accept a signal must ensure that the signal is not ignored, by either installing a handler for the signal or ensuring that the signal's disposition is SIG_DFL (unless the default action is to ignore the signal).

However, there isn't sufficient space on the page to include this information.


Explanation:

This note was originally composed on 2011-12-08, but then expanded on 2012-02-14.

2012-02-14

491

The second paragraph of Section 23.5 says:

On Linux, programs using this API must be compiled with the –lrt option, in order to link against the librt (realtime) library.

Starting with glibc 2.17, this is no longer true. This change was requested in glibc bug 14743, so that single-threaded programs using the clock_* functions do not end up incurring the cost of dragging in libpthread. The change affects clock_getcpuclockid(), clock_getres(), clock_gettime(), clock_nanosleep(), and clock_settime().

2012-10-29

491

Since Section 23.5 was written, some new clocks have been added to the POSIX clocks API.

Linux 2.6.39 adds CLOCK_BOOTTIME. This clock is the same as CLOCK_MONOTONIC, except that it includes time that the system has spent in suspended state. The new clock is intended for applications that want a monotonically increasing clock and also want to be aware of time the system has been suspended.

Linux 3.0 adds two clocks to the POSIX clocks API: CLOCK_BOOTTIME_ALARM and CLOCK_REALTIME_ALARM. These clocks behave identically to CLOCK_REALTIME and CLOCK_BOOTTIME, but the _ALARM suffixed clocks will wake the system if it is suspended. A new CAP_WAKE_ALARM capability governs the use of these clocks.

2011-06-26

492

Relevant to Section 23.5.2: since Linux 2.6.39, the new clock_adjtime() system call provides an interface to adjust the value of a POSIX clock (if that clock supports the interface) in the same manner as the adjtimex(2) system call.

2011-06-26

508

Since Linux 3.0, the timerfd_settime() system call supports a further flag, TFD_TIMER_CANCEL_ON_SET. If this flag is set for a CLOCK_REALTIME absolute (TFD_TIMER_ABSTIME) timer, then the timer is expired if the clock is reset.

2011-06-26

519

The first paragraph below the listing on this page starts with the following text:

Sharing of open file attributes between the parent and child processes is frequently useful. For example, if the parent and child are both writing to a file, sharing the file offset ensures that the two processes don't overwrite each other's output.

Implicit in that text is the notion that file I/O operations (e.g., read() and write()) are atomic with respect to updates to the file offset. POSIX.1-2008/SUSv4 Section XSI 2.9.7 ("Thread Interactions with Regular File Operations") is explicit on this point:

All of the following functions shall be atomic with respect to each other in the effects specified in POSIX.1-2008 when they operate on regular files or symbolic links: … read(), readv(). … write(), writev().

Among the effects specified by POSIX for various I/O functions is of course updating the current file offset.

However, a longstanding bug meant that Linux did not conform to the POSIX requirements (and was not consistent with other implementations such as FreeBSD and Solaris). In short, when performing file I/O system calls, the I/O operation plus accompanying file offset change were not performed atomically on Linux. Consequently, if two process (or threads) shared an open file description (and thus the current file offset, as described in Section 5.4 of TLPI), and performed simultaneous reads or writes, then there was a chance that the blocks of data transferred by those I/O system calls would (incorrectly) overlap with one another. This problem was fixed in Linux 3.14.

I made the problem report that led to the fix after Yongzhi Pan reported puzzling results when running my disc_SIGHUP.c program (Listing 34-4) as described on pages 713 and 714 of TLPI. Yongzhi noted that some of the expected output was not appearing and when I dug deeper, I discovered the nonatomicity issue. The reason that I had not discovered the (kernel) problem when I originally wrote the program was that the problem behavior appears much less frequently on single-CPU systems, and I happened to have tested the program on a single-CPU system as I wrote that chapter of the book. After Yongzhi's report, I was immediately (and easily) able to reproduce the behavior he reported on a multi-CPU system.


Reported by Yongzhi Pan.

2014-05-04

552

The first paragraph on this page includes the following text:

(A future corrigendum to SUSv4 is likely to add a requirement that si_pid and si_signo are zeroed in this case.)

This requirement was indeed added in the 2013 Technical Corrigendum 1 to SUSv4.


Reported by Ильдар Низамов (Ildar Nizamov)

2017-09-14

553

Section 26.2 discusses the traditional semantics for adoption of orphaned children by the init process. Linux 3.4 added the PR_SET_CHILD_SUBREAPER prctl() operation, which allows a "service manager" process to mark itself as a sort of 'sub-init', so that it adopts all orphaned processes created by the services it starts. All SIGCHLD signals will be delivered to the service manager. There is a corresponding PR_GET_CHILD_SUBREAPER prctl() operation.

More recent init frameworks employ the PR_SET_CHILD_SUBREAPER feature to create a dedicated subreaper process that becomes the parent of all orphaned children. On such frameworks, which include systemd (dependent on start-up options) and (the now obsolete) upstart, getppid() will report the new parent PID as the PID of the subreaper process (i.e., a value other than 1) when a process becomes orphaned.


Explanation:

This errata updated on 2017-11-02, after a note from Matthew Meffan.

2012-04-24

562

Exercise 26-1 asks the reader to verify that after a process becomes orphaned, its parent PID (PPID, as returned by getppid()) becomes 1. Historically, this was true. But on some modern init frameworks such as systemd, a dedicated process (with PID other than 1) takes on the role of adopting orphaned children. Thus, in code that solves the exercise, the PPID of the process will change as the child becomes orphaned, but the change with be to a value other than 1. For further details, see the erratum for page 553.

A similar qualification also applies for exercise 26-2.


Explanation:

This erratum was updated on 2019-11-19 include mention of exercise 26-2.

Reported by Matthew Meffan.

2017-11-02

571

The last paragraph on the page should have noted that:

The fexecve() function is not specified in SUSv3, but is specified in SUSv4.


Explanation:

At various other places in the book (pages 15 and 426), I noted that SUSv4 specified fexecve(). That fact should also have been mentioned in Section 27.2.4. However, there isn't sufficient space on the page to add this sentence.

2010-12-31

608

At the foot of this page, there is a brief discussion of various CLONE_NEW* that are used with containers. For more information on these flags, see this article series on namespaces that I wrote for LWN.net.

2013-03-07

615

In the entry for prctl(PR_SET_PDEATHSIG), the exec() column should say "See notes". In the fourth column, the text should say something like the following:

If a set-user-ID or set-group-ID program is being executed, the parent death signal value is cleared, otherwise it is preserved.

However, there isn't sufficient space on the page to add this information.

2012-10-27

628

In Section 29.7, I could have noted that calling pthread_detach() more than once on the same thread results in unspecified behavior.


Explanation:

It would have been desirable to note this point in the text. (It is noted in the pthread_detach() manual page that I wrote.) However, there isn't sufficient space on the page to add this sentence.

Reported by Jens Thoms Toerring.

2011-02-28

635

Section 30.1.1 discusses statically initialized mutexes, stating that PTHREAD_MUTEX_INITIALIZER can be used only for statically allocated mutexes. This follows the specification in POSIX.1-2008. However, the subsequent Austin Group Defect Report 70 noted that this restriction imposed by the standard arose from the constraints of C89, which was the standard that was current when that detail of the specification was originally written. The defect report notes that because C99 allows structure copying, PTHREAD_MUTEX_INITIALIZER can be used generally to initialize a mutex, regardless of whether it is statically allocated or not. This detail was subsequently clarified in POSIX.1-2013, which removed the restriction that PTHREAD_MUTEX_INITIALIZER can be used only with statically allocated mutexes. The use of pthread_mutex_init() is required only to initialize a mutex with nondefault attributes.

This change means that the various statements in TLPI about statically allocated mutexes either no longer hold true or need slight amendment. The affected pages are at least: 635, 639, 640, and 641.

2017-10-14

638

A word of caution about the timing statistics for mutexes, file locks, and semaphores that are presented in Section 30.1.3. These statistics were obtained on an x86-32 system. The (relative) performance of the various synchronization methods depends on the hardware architecture and the implementation of each method on that architecture. In cases where you suspect that performance of a synchronization method may be critical to the performance of your application, there is no substitute for application-specific benchmarking on the target architecture.


Reported by Stefan Puiu.

2013-07-18

643

Section 30.1.1 discusses statically initialized condition variables, stating that PTHREAD_COND_INITIALIZER can be used only for statically allocated condition variables. This follows the specification in POSIX.1-2008. However, the subsequent Austin Group Defect Report 70 noted that this restriction imposed by the standard arose from the constraints of C89, which was the standard that was current when that detail of the specification was originally written. The defect report notes that because C99 allows structure copying, PTHREAD_COND_INITIALIZER can be used generally to initialize a condition variable, regardless of whether it is statically allocated or not. This detail was subsequently clarified in POSIX.1-2013, which removed the restriction that PTHREAD_COND_INITIALIZER can be used only with statically allocated condition variables. The use of pthread_cond_init() is required only to initialize a condition variable with nondefault attributes.

This change means that the various statements in TLPI about statically allocated mutexes either no longer hold true or need slight amendment. The affected pages are at least: 643, 651, and 652.

2017-10-14

664

In version 2.32, glibc removed the symbols _sys_nerr and _sys_errlist. As a consequence, the code example shown on this page (Listing 31-1, threads/strerror.c) will no longer compile on systems that have glibc 2.32 or later.

2020-10-07

666

In version 2.32, glibc removed the symbols _sys_nerr and _sys_errlist. As a consequence, the code example shown on this page and the following page (Listing 31-3, threads/strerror_tsd.c) will no longer compile on systems that have glibc 2.32 or later.

2020-10-07

669

In version 2.32, glibc removed the symbols _sys_nerr and _sys_errlist. As a consequence, the code example shown on this page (Listing 31-4, threads/strerror_tls.c) will no longer compile on systems that have glibc 2.32 or later.

2020-10-07

679

In Listing 32-2 thread/thread_cleanup.c, calls to pthread_mutex_lock() and pthread_mutex_unlock() should surround the assignment to the global variable glob, so that the code reads:

        printf("main:    about to signal condition variable\n");
 
        s = pthread_mutex_lock(&mtx);
        if (s != 0)
            errExitEN(s, "pthread_mutex_lock");
 

        glob = 1;
 
        s = pthread_mutex_unlock(&mtx);
        if (s != 0)
            errExitEN(s, "pthread_mutex_unlock");
 

        s = pthread_cond_signal(&cond);
        if (s != 0)
            errExitEN(s, "pthread_cond_signal");

The above change has been made in the source code distribution. However, there isn't sufficient space on the page to make this change in the printed book.


Reported by Jingtian Zhang.

2014-03-09

704

The paragraph immediately preceding Section 34.3 says:

If we explicitly define the _BSD_SOURCE feature test macro when compiling a program, then glibc provides the BSD-derived versions of setpgrp() and getpgrp(), instead of the default versions.

Since the deprecation of _BSD_SOURCE in glibc 2.19, _BSD_SOURCE no longer has this effect for setpgrp() and getpgrp(), For further details, see the setpgrp(2) manual page.

2017-08-15

708

A small-font note at the bottom of the page states:

If there is no foreground process group for this terminal, tcgetpgrp() returns a value greater than 1 that doesn't match the ID of any existing process group.

The text could have said a little more about what kind of scenarios may result in such a return value.

The most likely scenario is that (all of the processes in) the foreground process terminated, and no other process group was subsequently made the foreground process group for the terminal. In this case, tcgetpgrp() will return the process group ID of the terminated process group.

In practice, such situations are rare: normally the foreground process group consists of (child) processes created by the shell, and when the shell observes that those processes have terminated, it makes itself the foreground process group for the terminal. However, if, for example, an application program creates a child process in a new process group, makes that process group the foreground process group, and then the child process terminates, then the shell will not notice that the foreground process group has disappeared and will thus not make itself the foreground group.


Reported by Jimm Chen.

2024-02-09

710

Section 34.6.1 notes that when a shell is sent a SIGHUP signal (because it is the controlling process and there was a terminal hangup), it in turns sends a SIGHUP signal to each of the process groups (jobs) that it has created. It should be noted that the latter behavior is dependent on a shell option, and on many modern systems the option is disabled by default. (In bash the option is set using the command shopt -s huponexit.)

2017-10-16

714

A longstanding kernel bug means that if you are running the disc_SIGHUP.c program on a multi-CPU system with a Linux kernel version of 3.13 or earlier, then it is likely that you won't see the results shown at the top of this page. For further details, see the erratum note for page 519.


Reported by Yongzhi Pan.

2014-05-04

734

The addition of the autogroup feature in Linux 2.6.38 caused significant changes to the semantics of the nice value. For details, see the sched(7) manual page.

2016-12-02

744

See the third bullet point at the top of the page. Starting with Linux 2.6.39, the limitation on modifying the SCHED_IDLE scheduling policy changes. An unprivileged SCHED_IDLE thread can now switch to another policy as long as its current nice value falls within the range permitted by the RLIMIT_NICE resource limit (see page 736).

2011-06-26

747

A small-font note at the foot of the page notes that the realtime round-robin timeslice is 0.1 seconds (100 milliseconds). Linux 3.9 adds the /proc/sys/kernel/sched_rr_timeslice_ms file, which can be used to view and modify the timeslice value. The value in this file is expressed in milliseconds, and the default is 100. Writing "0" to the file restores the value to the default.

2013-03-22

755

Linux 2.6.36 adds a new system call, prlimit(), that allows the caller to both set and retrieve its own resource limits (including retrieving the old limit at the same time as a new limit is set) and (with suitable permissions) perform the same task for other processes. See the manual page.

2011-05-08

755

The small-font note at the bottom of the page notes that /proc/PID/limits is readable only by the file owner. Linux 2.6.36 (the very next release after publication of TLPI) changed the permissions of this file so that it is readable by all users.

2012-11-24

760

Starting with version 2.13, glibc has implemented wrapper functions for getrlimit() and setrlimit() that use the prlimit() system call mentioned in the notes for page 755 to work around the problem described in the paragraph at the top of this page.

2011-05-08

761

Regarding the description of RLIMIT_CPU (and RLIMIT_RTTIME on page 764), it's worth mentioning that, after a report from Trevor Woerner, I added the following text to the getrlimit(2) manual page, under the heading BUGS:

Since Linux 2.6.12, if a process reaches its soft RLIMIT_CPU limit and has a handler installed for SIGXCPU, then, in addition to invoking the signal handler, the kernel increases the soft limit by one second. This behavior repeats if the process continues to consume CPU time, until the hard limit is reached, at which point the process is killed. Other implementations do not change the RLIMIT_CPU soft limit in this manner, and the Linux behavior is probably not standards conformant. The Linux-specific RLIMIT_RTTIME limit exhibits the same behavior when the soft limit is encountered.

This behavior is something of a Linux oddity. No other implementation that I know of does this (I tested Solaris 10, FreeBSD 9.0 and OpenBSD 5.1), and, as I noted in the manual page, I suspect it isn't standards conformant. The current description in TLPI about delivery of SIGXCPU when the RLIMIT_CPU and RLIMIT_RTTIME soft limits are exceeded is correct, but the underlying implementation detail that I added to the manual page may of interest to readers who, like Trevor, try digging deeply into the details of the behavior when these limits are hit by an application.


Reported by Trevor Woerner.

2012-11-23

764

The treatment of the RLIMIT_RTTIME soft limit when a SIGXCPU signal is delivered is the same as for the RLIMIT_CPU soft limit, as described in the erratum for page 761.


Reported by Trevor Woerner.

2012-11-23

768

A small-font note near the top of this page mentions pdflush as an example of a kernel thread. The example is accurate, but obsolete, since the pdflush mechanism was replaced in Linux 2.6.32. See the erratum for page 241.


Reported by Junjiro Okajima.

2012-08-21

792

The C11 standard (ratified by ISO on 8 Dec 2011), officially removes the dangerous function gets() from the language specification. From version 2.16, glibc does not expose the definition of gets() if the _ISOC11_SOURCE feature test macro is defined.

2012-01-18

793

The small-font note somewhat more than half-way down the page states that glibc does not implement strlcpy(). However, starting in version 2.38 (released in 2023), glibc does implement this function.

2023-08-05

801

Linux 2.6.38 adds a new capability, CAP_SYSLOG, used (instead of CAP_SYS_ADMIN) to govern privileged syslog(2) operations.

2011-05-08

801

Linux 3.0 adds a new CAP_WAKE_ALARM capability, which governs the use of the CLOCK_BOOTTIME_ALARM and CLOCK_REALTIME_ALARM clocks. (See also the note for page 491.)

2011-06-26

801

Linux 3.5 adds a new CAP_EPOLLWAKEUP capability, which governs the use of the epoll EPOLLWAKEUP flag. (See also the note for page 1359.)

2012-07-16

823

The example program (loginacct/dump_utmpx.c) discussed at the foot of this page provides functionality similar to the utmpdump(1) command.

2013-02-12

853

Regarding the discussion under the heading The ELF DT_RPATH and DT_RUNPATH entries, it's worth noting that some distributions enable --enable-new-dtags by default, among them openSUSE and Gentoo, In upstream binutils, and in many other distributions, this flag is disabled by default.


Reported by Trevor Woerner.

2012-12-30

853

The example in the middle of this page shows that when the --enable-new-dtags linker option is specified, then both a DT_RPATH entry and a DT_RUNPATH entry are created in the executable. The linker did this as an interim measure for the benefit of old dynamic linkers that understood DT_RPATH but not DT_RUNPATH. DT_RUNPATH support was added to the glibc dynamic linker in 1999.

14 years later, in 2013 (binutils-2.24), the static linker was changed so that when the --enable-new-dtags linker option is specified, only a DT_RUNPATH entry is produced in the executable. The presumption is that all modern systems by now have a dynamic linker that understands DT_RUNPATH.

Consequently, if repeating the example shown on this page, the output of the objdump command will be just:

$ objdump -p prog | grep PATH
  RUNPATH     /home/mtk/pdir/d1

2017-04-14

864

Pages 863 and 864 discuss the rather clumsy cast that is required when assigning the value returned by dlsym() to a function pointer:

    *(void **) (&funcp) = dlsym(handle, symbol);

Here, SUSv3 and SUSv4 were accepting the requirements of ISO C standard, which leaves the results of casting between function pointers and void * undefined. The 2013 Technical Corrigendum (TC1) to the standard improves matters, by requiring conforming implementations to allow void * to be assigned to a function pointer. As a result, such assignments can be done using more natural casts of the form:

    funcp = (int (*) (int)) dlsym(handle, symbol);

Note, however, that current compilers may still complain about this cast (e.g., gcc -pedantic may give warnings).

2014-01-02

872

The last part of Section 42.3 discusses the use of objdump –t to display information from the symbol table of an executable, in order to see version tag dependencies.

Yang Yang notes that using objdump –T (which yields the same information for the cases discussed in the text, but in a slightly different form) may be preferable. The reason is that objdump –T obtains its information from the ELF dynamic symbol table (.dynsym), which is always present in a dynamically linked binary. By contrast, objdump –t obtains its information from the standard symbol table (.symtab), which is not present if the executable has been stripped.

The blog post Inside ELF Symbol Tables by Ali Bahrami provides a nice introduction to the two types of symbol tables.


Reported by Yang Yang.

2012-01-27

874

Immediately preceding Section 42.6, there is the following sentence:

For security reasons, set-user-ID and set-group-ID programs ignore LD_PRELOAD.

This should read something like:

The dynamic linker searches for each of the names specified in LD_PRELOAD according to the rules specified in Section 46.11. For security reasons, set-user-ID and set-group-ID programs ignore pathnames containing slashes, and preload libraries located in the standard search directories only if they have the set-user-ID permission bit enabled.

The above change addresses two problems with the existing text. First of all, Section 42.5 does not clearly explain how the names in LD_PRELOAD are interpreted. Second, some of the subtleties of the handling set-user-ID and set-group-ID programs were omitted in the existing text. Although it would be nice to make this change to the text, unfortunately, there isn't sufficient space on the page to make the change.


Reported by Junjiro Okajima.

2012-08-24

926

Toward the bottom of the page, there is the following text:

The glibc ftok() algorithm is similar to that employed on other UNIX implementations, and suffers a similar limitation: there is a (very small) possibility that two different files could yield the same key value. This can occur because there is a chance that the least significant bits of an i-node number could be the same for two files on different file systems, coupled with the possibility that two different disk devices (on a system with multiple disk controllers) could have the same minor device number.

Yaakov Eisenberg points out that there is another (somewhat more likely) possibility: the least significant bits of the inode numbers of two files on the same file system are the same in their least significant bits. (However, coupled with the fact their are 256 different possible proj values, the chances of a collision when using ftok() remain small.)


Reported by Yaakov Eisenberg.

2024-01-09

1011

Regarding the discussion of IPC_RMID, Linux 3.1 added a new /proc/sys/kernel/shm_rmid_forced file that can be used to control the handling of System V shared memory segments that have no attached process. The default value in this file is 0, which provides the traditional behavior: unattached segments remain in existence and can be reattached at a later point in time by another process. If the value in shm_rmid_forced is 1, then the effect is as though an IPC_RMID operation is performed on all shared memory segments that currently exist and that are created in the future. This means that those segments will be destroyed as soon as the last process detaches from them. This can be useful to ensure that shared memory segments are counted against the resource usage and limits of at least one process, but it is nonstandard and has the potential to break applications that depend on the traditional behavior.

2013-03-22

1023

The program in Listing 49-1 (mmap/mmcat.c) does not correctly handle a zero-length input file. Given such a file, zero will be passed as the second argument of mmap(), which will fail with the error EINVAL. The fix for this problem is to add the following two lines of code before the call to mmap():

    if (sb.st_size == 0)
        exit(EXIT_SUCCESS);

However, there isn't sufficient space on the page to make this change. (The change has, however, been made in the "distribution" version of the code for the book.)


Reported by Liu Jiaming.

2013-10-22

1032

The small-font note in the first half of the page that begins "If we take no further action" contains a reference to the obsolete pdflush kernel thread. As noted in an erratum for page 241, pdflush was replaced by a different mechanism in kernel 2.6.32.

2012-08-21

1040

In Linux 2.6.36, the OOM killer underwent another rewrite. Following the rewrite, the /proc/PID/oom_adj file can still be used to influence the OOM killer, but this file is now deprecated in favor of the new /proc/PID/oom_score_adj file. In addition, the logic to select a candidate for OOM killing was simplified to take into account just the amount of memory used by the process and whether or not it is privileged. For further information, see the proc(5) man page.


Explanation:

This note was updated on 2017-11-19 to note that the logic for selecting a candidate for OOM killing was simplified in Linux 2.2.36.

2012-12-21

1041

Because it has few users, and adds significant complexity to the kernel code, the remap_file_pages() system call has been deprecated. Eventually, it will be replaced by a slower in-kernel emulation (that maintains the ABI). For further information, see the LWN.net article, The possible demise of remap_file_pages().

2014-06-12

1055

To the first small-font note, about two thirds of the way down the page, it can be noted that Linux 2.6.38 added two new flags to the madvise() system call: MADV_HUGEPAGE and MADV_NOHUGEPAGE. These flags enable and disable an attribute on the memory region that indicates that it is important that the region be backed by huge pages. (Huge pages are briefly described on page 999.)

2011-06-26

1055

To the first small-font note, about two thirds of the way down the page, it can be noted that Linux 3.4 added two new flags to the madvise() system call: MADV_DONTDUMP and MADV_DODUMP. The MADV_DONTDUMP flag specifies that an address range should not be included in core dumps. The MADV_DODUMP flag reverses the effect of MADV_DONTDUMP.

2012-04-24

1061

With respect to the discussion of portability of POSIX IPC in the last bullet point on this page, it's worth noting the following:

  • Neither NetBSD nor OpenBSD currently implements POSIX shared memory.
  • OpenBSD does not currently implement POSIX message queues. FreeBSD added support only with version 7.0 (2008), and NetBSD added support only with version 5.0 (2009).

2013-10-09

1080

The program in Listing 52-6 (pmsg/mq_notify_sig.c) does not handle the case where a message has already been placed on the queue by the time the first attempt is made to register for message notification. For an example of how that case could be handled, see mq_notify_via_signal.c.

2014-12-01

1082

The program in Listing 52-7 (pmsg/mq_notify_thread.c) does not handle the case where a message has already been placed on the queue by the time the first attempt is made to register for message notification. For an example of how that case could be handled, see mq_notify_via_thread.c.

2014-12-01

1086

In Linux 3.5, various details of the default values, limitations on modification, and interpretation with respect to privileged processes changed for the message queue limit files msg_max, msgsize_max, and queues_max. Full details can be found in the mq_overview(7) manual page.

In addition, Linux 3.5 added two new /proc files can be used to read and modify the values that are used to provide defaults when a POSIX message queue is created using an mq_open() call in which the attr argument is specified as NULL. The /proc/sys/fs/mqueue/msg_default file defines the default value used for a new queue's mq_maxmsg attribute. The default value in this file is 10. The /proc/sys/fs/mqueue/msgsize_default file defines the default value used for a new queue's mq_msgsize attribute. The default value in this file is 8192.

2013-03-22

1129

With respect to the discussion of deadlock detection in Section 55.3.1, it's worth noting that there are limits to the kernel's deadlock-detection algorithm. Most notably, circular deadlocks involving a long (> 10) chain of locks are not detected (meaning that the processes concerned will remain blocked indefinitely waiting on each other's locks). For further details, see the fcntl(2) manual page.

2014-05-20

1137

Starting with version 3.15, Linux adds a new (as yet unstandardized) lock type: an open file description (OFD) lock. OFD locks operate in a similar manner to traditional process-associated record locks (created using fcntl(2) F_SETLK), but have inheritance and release semantics that are the same as flock(2). OFD locks therefore do not suffer the defects of traditional process associated locks: they are not automatically released when a process closes any file descriptor referring to the file, and they can be used within a multithreaded application to synchronize file access between threads. For details, see the fcntl(2) man page.

2014-05-01

1140

The problems with mandatory locking have long been recognized by kernel developers, and there is a widespread belief that because of these problems this feature has few or perhaps no users. Therefore, since Linux 4.5, mandatory locking has been made an optional feature (governed by a kernel option), as a stepping stone toward eventually removing the feature completely. More information can be found in Jon Corbet's LWN.net article Optional mandatory locking. (This note was updated on 2016-04-18.)

2015-12-10

1152

Two points regarding the small-font note at the bottom of the page:

  • The first sentence notes that some Linux architectures such as Alpha and IA-64 do provide direct system calls rather than multiplexing via socketcall(). The x86-64 architecture is of course another prominent exception.
  • Since Linux 4.3, the x86-64 architecture also provides direct system calls for the sockets API. The primary motivation for this change is to facilitate the creation of seccomp() filters (for new binaries that have been compiled to employ the new system calls).

2015-12-20

1161

In the small-font note about three quarters of the way down the page that discusses MSG_TRUNC in relation to recvmsg(), it is worth noting that MSG_TRUNC can be specified in the flags argument of recv() and recvfrom(). In this case, if a datagram is larger than length, the call returns the actual size of the datagram. The caller can detect the fact that truncation has occurred because the return value exceeds length. This feature is a nonstandardLinux extension.

2013-11-04

1171

The first paragraph on this page notes that UNIX domain datagram sockets are reliable—datagrams are delivered in order and unduplicated. This is true on all of the implementations that I know of, but there is one detail that varies across implementations. On Linux, if the receiving UNIX domain datagram socket is full, then a process that sends a datagram to that socket will block. On some other implementations (e.g., FreeBSD), the sender will get an error (ENOBUFS) in this case. A portable application would handle this error by waiting and then retrying the send operation.

2013-02-05

1272

Linux 3.6 and 3.7 added code to implement TCP Fast Open, a feature that, in certain circumstances, allows a round-trip time to be saved during TCP connection set up. (In essence, TCP Fast Open allows a client that has previously authenticated itself to send data in the initial SYN segment of the three-way handshake.) For details, see my LWN.net article, TCP Fast Open: expediting web services.

2012-10-15

1284

The small-font note about one third of the way down the page states that a sendmmsg() system call is likely to be implemented in the future. This system call was added in Linux 3.0.

2011-06-26

1322

On the last line of this page, Michael Sweet's Serial Programming Guide for POSIX Operating Systems is noted as being online at http://www.easysw.com/~mike/serial/. This is no longer the case. The guide is now available as a book and ebook from lulu.com. The original HTML document can still be found online in various locations, but the license of those copies is unclear.


Reported by MingYi Liao.

2013-08-12

1350

Linux 3.6 added a new fcntl() flag, F_GETOWNER_UIDS, which can be used to retrieve the real and effective user IDs associated with a previous F_SETOWN call. (Those UIDs determine the rules for sending a signal to another process for signal-driven I/O.) The third argument of the call is of type uid_t *, and should point to a two-element array that stores the real user ID and effective user ID. This feature is intended for use by the checkpoint/restore facility and is only provided if the kernel was configured with the CONFIG_CHECKPOINT_RESTORE option.

2012-10-15

1355

One of the things I aimed to do in TLPI was come up with as many good diagrams as possible. See this LWN article for a diagram that I wish I'd thought of when I was writing the introductory piece of Section 63.4.

2012-10-18

1357

In Linux 3.7, the epoll_ctl() adds a new flag, EPOLL_CTL_DISABLE that allows multithreaded applications to safely disable monitoring of a file descriptor.

2012-10-15

1359

Linux 3.5 adds a new epoll flag, EPOLLWAKEUP. The new flag can be supplied in a call to epoll_ctl() to prevent system suspend while epoll events are available for the specified file descriptor. Use of this flag requires that the caller have the EPOLLWAKEUP capability, which was also added in Linux 3.5.

2012-07-16

1388

In the discussion on closing file descriptors referring to the pseudoterminal master near the end of this page, there is the following text:

A read() from the slave device returns end-of-file (0).

This statement was correct at the time of writing. However, since publication there appears to have been a subtle change in behavior, which could be summarized with the following text:

A read() from the slave device that is blocked at the time that the master device is closed fails with the error EIO. A read() from the slave device that is performed after the master device is closed returns end-of-file (0).

As far as I can tell, this change was a (probably unintended) consequence of kernel commit 699390354da (pty: Ignore slave pty close() if never successfully opened) that was merged in Linux 3.9.

2017-04-02

1406

The bullet point at the bottom on the page states:

If we are processing multiple command-line vectors or rescanning the same command line, then we must explicitly reset optind to 1.

This is the POSIX-specified way of doing things. However, a GNU extension also allows the possibility of setting optind to 0 to ensure that certain GNU-specific initializations are performed. Further details can be found in the getopt(3) manual page.


Reported by 맹규호.

2019-01-05

1438

The web site for Dave Butenhof's book, Programming with POSIX Threads, has gone away, and its new location is not yet confirmed. In the meantime, you can find the code for the book at http://www.informit.com/store/programming-with-posix-threads-9780201633924.

2013-11-09

Fixes and improvements in the fifteenth printing

The following fixes and improvements were applied in the fifteenth and later printings of the book.

Page Fix Reported
53

Two related changes are made on this page.

About two thirds of the way down the page, change:

However, this approach is inefficient because errno is defined in threaded programs as a macro that expands into a function call that returns a modifiable lvalue.

to:

However, this approach is inefficient because errno is defined in threaded programs as a macro that produces a modifiable lvalue via a function call that returns a pointer to a thread-specific storage area.

In the small-font note at the bottom of the page, change:

Under the POSIX threads API, errno is redefined to be a function that returns a pointer to a thread-specific storage area (see Section 31.3).

to:

Under the POSIX threads API, errno is defined as a macro that produces a modifiable lvalue by dereferencing the result of calling a function that returns a pointer to a thread-specific storage area (see Section 31.3).

Explanation:

Yaakov Eisenberg pointed out that neither of the original sentences was quite accurate. To resolve this, I reduced the level of detail in the main text, and provided the full details in the small-font note.

Reported by Yaakov Eisenberg.

2022-12-04

54

Several small changes are required to fix some formatting issues that were introduced in the thirteenth print run.

In the first paragraph, change "out-put" to "output".

In the second paragraph, change "pro-gram" to "program".

In the first line of Listing 3-3, the string "error_functions.c" is misplaced. It should appear at the end of the end of the previous line (so that the end of that line reads "lib/error_functions.c").

In Listing 3-3, the following lines:

NORETURN
static void
terminate(Boolean
useExit3) {

should read:

NORETURN
static void
terminate(Boolean useExit3)
{

Reported by Yaakov Eisenberg.

2022-12-04

129

In the last line on this page, change:

Returns 0 on success, or a nonzero on error

to:

Returns 0 on success, or nonzero on error

Reported by Yaakov Eisenberg.

2023-02-09

143

In the shell session shown at the bottom of the page, change:

$ ./free_and_sbrk 1000 10240 1 500 1000
Initial program break:           0x804a6bc
Allocating 1000*10240 bytes
Program break is now:            0x8a13000
Freeing blocks from 500 to 1000 in steps of 1
After free(), program break is:  0x852b000

to:

$ ./free_and_sbrk 1000 10240 1 501 1000
Initial program break:           0x804a6bc
Allocating 1000*10240 bytes
Program break is now:            0x8a13000
Freeing blocks from 501 to 1000 in steps of 1
After free(), program break is:  0x852b000

Explanation:

An off-by-one error in the demonstration...

Reported by Joaquín Moreno.

2024-01-09

150

In the bullet point at the top of the page, change:

The address of the allocated memory is returned in memptr.

to:

The address of the allocated memory is returned in *memptr.

Reported by Yaakov Eisenberg.

2023-02-09

155

In the last paragraph of Section 8.2, change:

Not all UNIX implementations provide this feature, and on implementations where it is provided the details of locations and APIs vary.

to:

Not all UNIX implementations provide this feature, and on implementations where it is provided the details of the file locations and APIs vary.

Explanation:

This change fixes an error that was introduced in the seventh print run.

Reported by Yaakov Eisenberg.

2023-02-09

168

In the last sentence of the first paragraph of Section 9.3, a small formatting fix to change:

(The terms set-user-ID program and set-group-ID program are sometimes abbreviated as set-UID program and set-GID program.)

to:

(The terms set-user-ID program and set-group-ID program are sometimes abbreviated as set-UID program and set-GID program.)

Reported by Yaakov Eisenberg.

2023-02-09

187

In the small-font note at the top of the page, change:

The tz_minuteswest field indicates the number of minutes that must be added to times in this zone to match UTC, with a negative value indicating that an adjustment of minutes to the east of UTC

to:

The tz_minuteswest field indicates the number of minutes that must be added to times in this zone to match UTC, with a negative value indicating an adjustment of minutes to the east of UTC

Reported by Yaakov Eisenberg.

2023-02-09

234

In the first sentence after the heading in the middle of the page, change:

The kernel performs the same number of disk accesses, regardless of whether we perform 1000 writes of a single byte or a single write of a 1000 bytes.

to:

The kernel performs the same number of disk accesses, regardless of whether we perform 1000 writes of a single byte or a single write of 1000 bytes.

Reported by Yaakov Eisenberg.

2023-02-25

248

In Listing 13-1 (filebuff/direct_read.c), change one line in the comment in the code:

    we ensure that 'buf' is aligned on a non-power-of-two multiple of

to:

    we ensure that 'buf' is aligned on an odd multiple of

Explanation:

An odd multiple is sufficient, and is what the code actually does.

Reported by Yaakov Eisenberg.

2023-02-25

253

Change the last two sentences of the second-to-last paragraph:

Tracks themselves are divided into a number of sectors, each of which consists of a series of physical blocks. Physical blocks are typically 512 bytes (or some multiple thereof) in size, and represent the smallest unit of information that the drive can read or write.

to:

Tracks themselves are divided into a number of sectors, which are typically 512 bytes (or some multiple thereof) in size, and represent the smallest unit of information that the drive can read or write.

Reported by Yaakov Eisenberg.

2023-02-25

257

In the second-to-last paragraph, change:

For even larger files, the fourteenth pointer (numbered 13 in the diagram) is a double indirect pointer—it points to blocks of pointers that in turn point to blocks of pointers that in turn point to data blocks of the file.

to:

For even larger files, the fourteenth pointer (numbered 13 in the diagram) is a double indirect pointer—it points to a block pointers that in turn point to blocks of pointers that in turn point to data blocks of the file.

Reported by Yaakov Eisenberg.

2023-02-25

258

This report applies only for the PDF version of the book, and involves two fixes to Figure 14-2.

In print run 3, a fix was made to Figure 14-2, but the fix was was accidentally reverted in print run 6. The same fix must be reapplied.

In the "Key" box in the upper right hand corner of Figure 14-2, change:

2IPB = Double IBP

to:

2IPB = Double IPB

In addition, a formatting glitch appeared in print run 6, such that the bottom edge of a box in the diagram has been lost. This will be fixed in the next PDF version.


Reported by Yaakov Eisenberg.

2023-02-25

292

In the middle of this page, change:

An unprivileged process can use chown() to change the group ID of a file that it owns (i.e., the process’s effective user ID matches the user ID of the file) to any of the groups of which they are a member.

to:

An unprivileged process can use chown() to change the group ID of a file that it owns (i.e., the process’s effective user ID matches the user ID of the file) to any of the groups of which it is a member.

Reported by Yaakov Eisenberg.

2023-04-30

294

In the fourth line of code from the top of this page, change:

            fatal("No group user (%s)", argv[1]);

to:

            fatal("No such group (%s)", argv[2]);

Reported by Yaakov Eisenberg.

2023-02-26

381

In the second bullet point at the top of the page, change:

We describe the IN_Q_OVERFLOW in Section 19.5,

to:

We describe the IN_Q_OVERFLOW event in Section 19.5,

Reported by Yaakov Eisenberg.

2023-04-06

424

in Listing 21-1 (signals/nonreentrant.c), change:

static char *str2;              /* Set from argv[2] */
static int handled = 0;         /* Counts number of calls to handler */

to:

static char *str2;                  /* Set from argv[2] */
static volatile int handled = 0;    /* Counts number of calls to handler */

Explanation:

Since the variable handled is accessed both from the main program and asynchronously from the signal handler, it should me marked as volatile in order to prevent the optimizer aliasing the variable to a register.

Reported by Joaquín Moreno.

2024-03-11

444

A bit more than one third of the way down this page, delete a sentence:

The sem_wait() and sem_timedwait() functions used to decrement a POSIX semaphore. (On some UNIX implementations, sem_wait() is restarted if the SA_RESTART flag is specified.)

to:

The sem_wait() and sem_timedwait() functions used to decrement a POSIX semaphore.

Reported by Yaakov Eisenberg.

2023-04-30

445

About half way down this page, change:

In kernels before 2.6.24, poll() also exhibited this behavior, as did sem_wait(), sem_timedwait(), futex(FUTEX_WAIT), in kernels before 2.6.22, msgrcv() and msgsnd() in kernels before 2.6.9, and nanosleep() in Linux 2.4 and earlier.

to:

In kernels before 2.6.24, poll() also exhibited this behavior, as did sem_wait(), sem_timedwait(), and futex(FUTEX_WAIT) in kernels before 2.6.22, msgrcv() and msgsnd() in kernels before 2.6.9, and nanosleep() in Linux 2.4 and earlier.

Explanation:

This small edit makes the sentence easier to parse.

Reported by Yaakov Eisenberg.

2023-04-30

466

Near the top of the page, change:

Uses sigsuspend() to unblock SIGINT and SIGQUIT ...

to:

Use sigsuspend() to unblock SIGINT and SIGQUIT ...

Reported by Yaakov Eisenberg.

2023-04-30

477

In the second paragraph on this page, change:

Note that this function is defined with different calling signatures in the System V and BSD APIs.

to:

Note that although a function with this name exists in both the System V and BSD APIs, the int argument is interpreted differently in the two APIs.

Reported by Yaakov Eisenberg.

2023-05-05

491

About two thirds of the way down the page, in the paragraph beginning "The time value", change the last sentence in the paragraph:

The clock_getres() system call returns via the argument res, a pointer to a timespec structure containing the resolution of the clock specified in clockid.

to:

The clock_getres() system call returns the resolution of the clock specified in clockid in the timespec structure pointed to by res.

Explanation:

A refinement to an earlier fix that was made in the third print run.

Reported by Yaakov Eisenberg.

2023-05-05

499

In the second paragraph of Section 23.6.3, change:

The curr_value.it_value field returns the time until next timer expiration, even if this timer was established as an absolute timer using TIMER_ABSTIME.

to:

The curr_value.it_value field returns the time until the next timer expiration, even if this timer was established as an absolute timer using TIMER_ABSTIME.

Reported by Yaakov Eisenberg.

2023-05-06

504

This is a correction for the PDF version of the book only. In the second sentence of Section 23.6.7, the text "30" should hyperlink to Chapter 30 in the PDF. However, this link is broken and needs repairing to point to the correct location.


Reported by Yaakov Eisenberg.

2023-05-19

514

In the small-font note at the top of this page, change:

generally only one of the parent and child terminate by calling exit();

to:

generally only one of the parent and child terminates by calling exit();

Reported by Yaakov Eisenberg.

2023-05-19

514

In the the first bullet point at the top of the page, fix a formatting error by changing:

Second, the termination status of the child is returned in the status argument of wait().

to:

Second, the termination status of the child is returned in the status argument of wait().

Reported by Yaakov Eisenberg.

2023-05-19

535

Fix a page reflow error that first appeared in print run 5, so that the code snippet at the bottom of the page does not split badly onto the top of page 536.


Reported by Yaakov Eisenberg.

2023-05-19

621

In the second paragraph, change:

On Linux, a thread-specific errno is achieved in a similar manner to most other UNIX implementations: errno is defined as a macro that expands into a function call returning a modifiable lvalue that is distinct for each thread.

to:

On Linux, a thread-specific errno is achieved in a similar manner to most other UNIX implementations: errno is defined as a macro that produces a modifiable lvalue by dereferencing the pointer returned by a function that returns a pointer to a thread-specific data area.

Explanation:

See also the erratum for page 53.

2022-12-20

624

In the prototype box for pthread_equal() near the bottom of the page, change:

Returns nonzero value

to:

Returns a nonzero value

Reported by Yaakov Eisenberg.

2023-06-09

644

In the third paragraph from the bottom of the page, change:

By contrast, pthread_cond_broadcast() handles the case where the waiting threads are designed to perform different tasks (in which case they probably have different predicates associated with the condition variable).

to:

By contrast, pthread_cond_broadcast() handles the case where the waiting threads are designed to perform different tasks. In this case, the threads probably have different predicates associated with the condition variable (see Section 30.2.3).

Reported by Yaakov Eisenberg.

2023-08-03

649

Near the top of Listing 30-4 (threads/thread_multijoin), change:

static pthread_cond_t threadDied = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t threadMutex = PTHREAD_MUTEX_INITIALIZER;
                /* Protects all of the following global variables */

static int totThreads = 0;      /* Total number of threads created */
static int numLive = 0;         /* Total number of threads still alive or
                                   terminated but not yet joined */

to:

static int totThreads = 0;      /* Total number of threads created */
static int numLive = 0;         /* Total number of threads still alive or
                                   terminated but not yet joined */
static pthread_cond_t threadDied = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t threadMutex = PTHREAD_MUTEX_INITIALIZER;
                /* Protects all of the following global variables */


Explanation:

The comment saying that the threadMutex mutex protects all of the following variables was not accurate. It does not (and does not need to) protect accesses to totThreads and numLive, because those variables are accessed only by the main thread. The simplest way to address this inaccuracy was to reorder the declarations. (The mutex is also not used to protect accesses to the sleepTime field of the threads array, because the timing of these accesses does not require mutex protection.)

Reported by Yaakov Eisenberg.

2023-08-05

653

In the fourth function prototype, change:

Boolean lookup(char *key, void **value)

to:

Boolean lookup(tree, char *key, void **value)

Reported by Yaakov Eisenberg.

2023-06-09

660

In the last line of this page, change:

A call to pthread_setspecific() is a request to the Pthreads implementation to say

to:

A call to pthread_setspecific() is a request to the Pthreads implementation that says

Reported by Yaakov Eisenberg.

2023-06-09

672

In the second paragraph on this page, change:

Precisely what happens to the target thread, and when it happens, depends

to:

Precisely what happens to the target thread, and when it happens, depend

Reported by Yaakov Eisenberg.

2023-06-09

691

In the second line of this page, change:

process as whole

to:

process as a whole

Reported by Yaakov Eisenberg.

2023-06-09

702

The following change applies only to the PDF version of the book. A correction was made in both the printed book and the PDF in print run 6, but that fix was somehow reverted in print run 7.

In the third line in the paragraph in the middle of this page, change:

If septgid() changes

to:

If setpgid() changes

Reported by Yaakov Eisenberg.

2023-06-09

708

In the first sentence of the paragraph just above the prototype box, change:

The tcgetpgrp() and tcsetpgrp() functions respectively retrieve and change the process group of a terminal.

to:

The tcgetpgrp() and tcsetpgrp() functions respectively retrieve and change the foreground process group of a terminal.

Reported by Yaakov Eisenberg.

2023-08-03

715

In the first sentence of the last paragraph, change:

After we typed Control-Z, the shell displays the command that has been stopped in the background.

to:

After we typed Control-Z, the shell displays the command that has been stopped and moved to the background.

Explanation:

The original wording was okay, but the revised wording makes the details a little clearer.

Reported by Yaakov Eisenberg.

2023-08-03

716

In the small-font note near the top of the page, change:

The Korn and C shells provide the command stop as a shorthand for kill –stop.

to:

The Korn and C shells provide the command stop as a shorthand for kill –STOP.

Reported by Yaakov Eisenberg.

2023-08-03

721

In the second sentence of the second paragraph, change:

We can also see that the new job is in the same session as the shell and that all of the processes are in the same process group.

to:

We can also see that the new job is in the same session as the shell and that both of its processes are in a single new process group.

Explanation:

Yaakov pointed out that the reader could misunderstand "all of the processes" to include the shell. The new wording, suggested by Yaakov, is more precise and lessens the possibility for misunderstanding.

Reported by Yaakov Eisenberg.

2023-08-03

723

In the second line from the top of the page, change:

caused one of its child to stop.

to:

caused one of its children to stop.

Reported by Yaakov Eisenberg.

2023-08-03

726

In the first sentence of the small-font note near the top of the page, change:

By definition, a session leader is in an orphaned process group.

to:

By definition, a session leader is (initially) in an orphaned process group.

Explanation:

Yaakov Eisenberg pointed out that there are scenarios whereby the process group of the session leader could subsequently become nonorphaned. For example:

  1. Process A becomes a session leader.
  2. Process A creates B, which moves to a new process group.
  3. Process B creates process C, which begins its life in the same process group as B, but then moves to the process group of process A.

Reported by Yaakov Eisenberg.

2023-08-04

730

In the last sentence of the second-to-last paragraph, change:

If the terminal TOSTOP is set,

to:

If the terminal's TOSTOP flag is set,

Reported by Yaakov Eisenberg.

2023-08-03

736

Change the initial sentences of the fourth paragraph:

Since kernel 2.6.12, Linux provides the RLIMIT_NICE resource limit, which permits unprivileged processes to raise priorities. An unprivileged process can set its own nice value to the maximum specified by the formula 20 – rlim_cur where rlim_cur is the current RLIMIT_NICE soft resource limit. As an example, if a process’s RLIMIT_NICE soft limit is 25, then its nice value can be set to –5.

to:

Since kernel 2.6.12, Linux provides the RLIMIT_NICE resource limit, which permits unprivileged processes to raise priorities. An unprivileged process can set its own nice value as low as 20 – rlim_cur where rlim_cur is the current RLIMIT_NICE soft resource limit. As an example, if a process’s RLIMIT_NICE soft limit is 25, then its nice value can be set to –5, but no lower.

Explanation:

This is a further refinement of wording that was already modified in print run 3.

Reported by Yaakov Eisenberg.

2023-08-04

762

In the text following the RLIMIT_NOFILE subheading, change:

The RLIMIT_NICE limit (Linux-specific; since Linux 2.6.12) specifies a ceiling on the nice value that may be set for this process using sched_setscheduler() and nice(). The ceiling is calculated as 20 – rlim_cur, where rlim_cur is the current RLIMIT_NICE soft resource limit.

to:

The RLIMIT_NICE limit (Linux-specific; since Linux 2.6.12) specifies a floor on the nice value that may be set for this process using setpriority() and nice(). The floor is calculated as 20 – rlim_cur, where rlim_cur is the current RLIMIT_NICE soft resource limit.

Explanation:

While applying a wording change suggested by Yaakov Eisenberg, I noticed that one of the functions mentioned in this sentence was incorrect.

Reported by Yaakov Eisenberg.

2023-08-04

762

In the last sentence of the first paragraph following the RLIMIT_NOFILE subheading, change:

In most cases, the error is EMFILE, but for dup2(fd, newfd) it is EBADF, and for fcntl(fd, F_DUPFD, newfd) with newfd is greater than or equal to the limit, it is EINVAL.

to:

In most cases, the error is EMFILE, but for dup2(fd, newfd) it is EBADF and for fcntl(fd, F_DUPFD, newfd) it is EINVAL if newfd is, in either case, greater than or equal to the limit.

Reported by Yaakov Eisenberg.

2023-08-03

772

In the last sentence of the second paragraph, change:

and restart it after (fixing the bug).

to:

and restart it (after fixing the bug).

Reported by Yaakov Eisenberg.

2023-08-03

784

In the first sentence of the third paragraph, change:

Even in cases where a set-user-ID or set-group-ID is needed,

to:

Even in cases where a set-user-ID or set-group-ID program is needed,

Reported by Yaakov Eisenberg.

2023-08-03

791

In the first line of this page, change:

from process's effective group ID

to:

from the process's effective group ID

Reported by Yaakov Eisenberg.

2023-08-05

793

At the end of the first sentence in the small-font note somewhat more than half-way down the page, change:

always appends a null character at the end of the buffer.

to:

always appends a null character after the copied string.

Reported by Yaakov Eisenberg.

2023-08-05

801

In the first line of the second column of the CAP_SYS_NICE entry, change:

Raise nice value

to:

Lower nice value, i.e., raise priority

Reported by Yaakov Eisenberg.

2023-08-13

808

In the last line of this page (in Listing 39-1, cap/check_password_caps.c), change:

static int
modifyCap(int capability, int setting)

to:

static int
modifyCap(cap_value_t capability, int setting)

2024-01-23

809

About two thirds of the way down this page (in Listing 39-1, cap/check_password_caps.c), change:

static int              /* Raise capability in caller's effective set */
raiseCap(int capability)

to:

static int              /* Raise capability in caller's effective set */
raiseCap(cap_value_t capability)

2024-01-23

812

In Table 39-2, in the entry for SECBIT_NOROOT, change:

If a process with a real or effective user ID of 0 does an exec(), or it execs a set-user-ID-root program, don’t grant it capabilities (unless the executable has file capabilities).

to:

If a process with a real or effective user ID of 0 does an exec(), or any process execs a set-user-ID-root program, don’t grant capabilities to the process (unless the executable has file capabilities).

And near the bottom of the page, change:

if (prctl(PR_SET_SECUREBITS,
          /* SECBIT_KEEP_CAPS off */
          SECBIT_NO_SETUID_FIXUP | SECBIT_NO_SETUID_FIXUP_LOCKED |
          SECBIT_NOROOT | SECBIT_NOROOT_LOCKED)
        == -1)
    errExit("prctl");

to:

if (prctl(PR_SET_SECUREBITS,
          /* SECBIT_KEEP_CAPS off */
          SECBIT_NO_SETUID_FIXUP | SECBIT_NO_SETUID_FIXUP_LOCKED |
          SECBIT_NOROOT | SECBIT_NOROOT_LOCKED) == -1)
    errExit("prctl");

Explanation:

The second part of this change is made simply to create sufficient vertical space to accommodate the first change.

Reported by Yaakov Eisenberg.

2023-08-13

824

Near the end of Listing 40-2 (loginacct/dump_utmpx.c), change:

        time_t tv_sec = ut->ut_tv.tv_sec;
        printf("%s", ctime((time_t *) &tv_sec));
    }
    endutxent();

to:

        time_t tv_sec = ut->ut_tv.tv_sec;
        printf("%s", ctime(&tv_sec));
    }
    endutxent();

Explanation:

This is a small clean-up to an earlier erratum that changed the same piece of code. The cast was needed in the earlier version of the code, but, as Yaakov Eisenberg points out, the cast is unnecessary in the modified code.

Reported by Yaakov Eisenberg.

2023-08-13

829

Make two similar changes in Listing 40-3 (loginacct/utmpx_login.c).

Near the top of the program, change:

    ut.ut_type = USER_PROCESS;         /* This is a user login */
    strncpy(ut.ut_user, argv[1], sizeof(ut.ut_user));
    if (time((time_t *) &ut.ut_tv.tv_sec) == -1)
       errExit("time");                /* Stamp with current time */
    ut.ut_pid = getpid();

to:

    ut.ut_type = USER_PROCESS;         /* This is a user login */
    strncpy(ut.ut_user, argv[1], sizeof(ut.ut_user));
    ut.ut_tv.tv_sec = time(NULL);      /* Stamp with current time */
    ut.ut_pid = getpid();

Near the bottom of the page, change:

    ut.ut_type = DEAD_PROCESS;         /* Required for logout record */
    time((time_t *) &ut.ut_tv.tv_sec);  /* Stamp with logout time */
    memset(&ut.ut_user, 0, sizeof(ut.ut_user));

to:

    ut.ut_type = DEAD_PROCESS;         /* Required for logout record */
    ut.ut_tv.tv_sec = time(NULL);      /* Stamp with logout time */
    memset(&ut.ut_user, 0, sizeof(ut.ut_user));

Explanation:

This change is required for similar reasons to the changes already made on pages 824 and 831 after 2017 reports by LaoK.

Reported by Yaakov Eisenberg.

2023-08-13

860

In the second sentence in the last paragraph on this page, change the formatting:

The library is loaded into memory only once (by the initial call), and all calls return the same handle value.

to:

The library is loaded into memory only once (by the initial call), and all calls return the same handle value.

Reported by Yaakov Eisenberg.

2023-08-27

864

In the second paragraph on this page, change:

The *(void **) syntax doesn't incur this warning because we are assigning to an address pointed to by the assignment's lvalue.

to:

The *(void **) syntax doesn't incur this warning because we are are not assigning to the cast expression itself, but rather to the result of dereferencing it.

Reported by Yaakov Eisenberg.

2024-01-09

866

In the last sentence of the first paragraph on this page, change:

An implicit dlclose() of all libraries is performed on process termination.

to:

An implicit dlclose() of all libraries is performed on normal process termination via exit().

2023-12-15

871

In the second sentence of the paragraph in the middle of this page, change:

The first of these directives says that xyz_old() is the implementation of xyz() to be used for applications linked against version tag VER_1 (i.e., program p1 in our example), and that xyz_new() is the implementation of xyz() to be used by applications linked against version tag VER_2.

to:

The first of these directives says that xyz_old() is the implementation of xyz() to be used for applications linked against version tag VER_1 (i.e., program p1 in our example), and the second says that xyz_new() is the implementation of xyz() to be used by applications linked against version tag VER_2.

Reported by Yaakov Eisenberg.

2024-01-09

875

Near the middle of this page, change:

If desired, we can assign multiple options to LD_DEBUG by separating them with commas (no spaces should appear).

to:

If desired, we can assign multiple options to LD_DEBUG by separating them with commas, colons, or (if the string is quoted) spaces.

2023-11-25

881

In the second-to-last sentence of the second bullet point, change:

Because of its limited functionality, flock() locking facility is rarely used nowadays.

to:

Because of its limited functionality, the flock() locking facility is rarely used nowadays.

Reported by Yaakov Eisenberg.

2024-01-09

885

In the first bullet point under the heading Accessibility, change:

For some IPC facilities (e.g., FIFOs and sockets)

to:

For some IPC facilities (e.g., FIFOs and UNIX domain sockets)

Reported by Yaakov Eisenberg.

2024-01-09

896

Near the end of this page (in Listing 44-2, pipes/simple_pipe.c), change:

        if (close(pfd[0]) == -1)
          errExit("close");

to:

        if (close(pfd[0]) == -1)
            errExit("close");

Explanation:

This erratum fixes a small formatting glitch that was introduced in an erratum that was added in print run 13.

Reported by Yaakov Eisenberg.

2024-01-01

907

In the second sentence of the second-to-last paragraph on this page, change:

Generally, the only sensible use of a FIFO is to have a reading process and a writing process on each end.

to:

Generally, the only sensible use of a FIFO is to have a reading process on one end and a writing process on the other end.

Explanation:

Yaakov pointed out that the original wording could be misunderstood as meaning four processes are involved (two on each end).

Reported by Yaakov Eisenberg.

2024-01-09

920

In the fourth line of Exercise 44-6, change:

and other client's requests would be indefinitely delayed.

to:

and other clients' requests would be indefinitely delayed.

Reported by Yaakov Eisenberg.

2024-01-09

923

In the first sentence of the small-font note near the top of this page, change:

In the context of System V IPC, the object doesn't carry any of the connotations associated with object-oriented programming.

to:

In the context of System V IPC, the term object doesn't carry any of the connotations associated with object-oriented programming.

Reported by Yaakov Eisenberg.

2024-01-09

927

In the second-to-last paragraph on this page, change:

Initially, the corresponding user and creator ID fields have the same values, which are taken from the effective IDs of the calling processes.

to:

Initially, the corresponding owner and creator ID fields have the same values, which are taken from the effective IDs of the calling process.

Reported by Yaakov Eisenberg.

2024-01-09

931

In Listing 45-1 (svipc/svmsg_demo_server.c), change:

            if (msgctl(msqid, IPC_RMID, NULL) == -1)
                errExit("msgget() failed to delete old queue");

to:

            if (msgctl(msqid, IPC_RMID, NULL) == -1)
                errExit("msgctl() failed to delete old queue");

Reported by Yaakov Eisenberg.

2024-01-09

935

About half-way down the page, remove this entire (one-sentence) paragraph:

Unlike the ipcs command, these files always show all objects of the corresponding type, regardless of whether read permission is available on the objects.

Explanation:

As pointed out in an earlier erratum for page 934, ipcs nowadays obtains its information from files in /proc/sysvipc and does not require read permission on the IPC objects.

Reported by Yaakov Eisenberg.

2024-01-09

944

About half-way down the page, change:

Upon successful completion, msgrcv() returns the size of the mtext field of the received message; on error, -1 is returned.

to:

Upon successful completion, msgrcv() returns the the number of bytes that were copied into the mtext field; on error, -1 is returned.

Explanation:

The reason why the difference between the old and new text matters can be understood if you read the description of the MSG_NOERROR flag that is described just above the modified sentence.

Reported by Yaakov Eisenberg.

2024-01-09

952

In the first line of this page, remove this sentence:

(The ipcs program employs these operations.)

Explanation:

As pointed out in an earlier erratum for page 934, ipcs no longer uses these ctl operations and nowadays obtains its information from files in /proc/sysvipc.

Reported by Yaakov Eisenberg.

2024-01-09

1039

In the third paragraph, change:

The default value contained in this file is 50, meaning that the kernel can overallocate up to 50% of the size of the system’s RAM, and this will be successful, as long as not all processes try to use their full allocation.

to:

The default value contained in this file is 50, meaning that the kernel limits allocations to the size of swap space plus 50% of the size of the system’s RAM.

Reported by Yaakov Eisenberg.

2023-01-30

1111

Towards the end of Listing 54-1 (pshm/pshm_create.c), change:

    /* Map shared memory object */

    addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED)
        errExit("mmap");

to:

    /* Map shared memory object */

    if (size > 0) {
        addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if (addr == MAP_FAILED)
            errExit("mmap");
    }

Explanation:

The code was originally written before a change in Linux 2.6.12 that causes mmap() to return an error if size is 0. (The kernel change was made to accord with POSIX.1, which specifies an error for this case.) The change in the code of the program is necessary so that the demonstration of the use of this program near the bottom of page 1113 (where the final command-line argument of 0 causes mmap() to be called with a size argument of 0) does not display an error.

2022-12-18

1336

In the second-to-last shell session log on this page, change:

$ ./t_select 0 0r
ready = 0
timeout after select(): 0.000

to:

$ ./t_select 0 0r
0:
ready = 0
timeout after select(): 0.000

Reported by Joaquín Moreno.

2024-03-10

1372

Towards the botton of the page (in Listing 63-9, altio/self_pipe.c), change:

            }
  
            /* Perform any actions that should be taken in response to signal */
        }
    }

to:

            }
        }
  
        /* Perform any actions that should be taken in response to signal */
    }

Explanation:

The explanation of the code on page 1371 was correct, but the comment was slightly misplaced.

Reported by Sergio García Tapia.

2024-09-13

1480

Remove the following index entry:

physical block, 253

Explanation:

This change is for alignment with the erratum on page 253.

2023-02-25

Fixes and improvements in the fourteenth printing

The following fixes and improvements were applied in the fourteenth and later printings of the book.

Page Fix Reported
40

In the first bullet point of Section 2.16, change:

Universal Coordinated Time

to:

Coordinated Universal Time

Reported by Yaakov Eisenberg.

2022-08-17

186

In the first paragraph of Section 10.1, change:

Universal Coordinated Time

to:

Coordinated Universal Time

2022-08-17

189

In the second to last paragraph on page 189, change:

The tm_sec field can be up to 60 (rather than 59) to account for the leap seconds that are occasionally applied to adjust human calendars to the astronomically exact (the so-called tropical) year.

to:

The tm_sec field can be up to 60 (rather than 59) to account for the leap seconds that are occasionally applied to account for the difference between the precise International Atomic Time (TAI) and the observed solar time, which is less precise because of factors such as geological events and the gradual slowdown in the earth's rotation.

Reported by Yaakov Eisenberg.

2022-08-22

626

In listing 29-1 (threads/simple_thread.c), change:

static void *
threadFunc(void *arg)
{
    char *s = (char *) arg;

to:

static void *
threadFunc(void *arg)
{
    char *s = arg;

Explanation:

No cast is needed when assigning a void pointer to another pointer type.

Reported by Alejandro Colomar.

2021-09-17

861

Two related changes are required on this page.

In the description of the RTLD_GLOBAL flag, change:

Symbols in this library and its dependency tree are made available for resolving references in other libraries loaded by this process and also for lookups via dlsym().

to:

Symbols in this library and its dependency tree are made available for satisfying subsequent symbol resolution operations in other shared libraries (and in the main program) and also for lookups via dlsym().

In the description of the RTLD_LOCAL flag, change:

This is the converse of RTLD_GLOBAL and the default if neither constant is specified. It specifies that symbols in this library and its dependency tree are not available to resolve references in subsequently loaded libraries.

to:

This is the converse of RTLD_GLOBAL and the default if neither constant is specified. It specifies that symbols in this library and its dependency tree are not available for satisfying subsequent symbol resolution operations in other shared libraries (and in the main program).

Explanation:

The choice of RTLD_GLOBAL versus RTLD_LOCAL affects more than the handling of subsequently loaded libraries. Rather, it affects all subsequent symbol resolution operations, including, for example, lazy symbol resolutions that are subsequently performed for (previously loaded) shared libraries and the main program.

2022-03-29

872

In the first paragraph on this page, change:

Version tag dependencies indicate the relationships between successive library versions. Semantically, the only effect of version tag dependencies on Linux is that a version node inherits global and local specifications from the version node upon which it depends.

to:

Version tag dependencies indicate the relationships between successive library versions. The dependencies thus help human readers understand the relationship between library versions. However, semantically, the dependencies have no effect on the behavior of either the static linker or the dynamic linker.

Explanation:

The symbol-versioning concept came from Solaris, and there it seems that the version tag dependencies do have some inheritance effect. However, this doesn't seem to be the case on Linux.

2022-08-31

872

Replace the entire fourth paragraph on this page:

The VER_2 version tag also specifies that the new function pqr() is to be exported by the library and bound to the VER_2 version tag. If we didn't declare pqr() in this manner, then the local specification that VER_2 version tag inherited from the VER_1 version tag would make pqr() invisible outside the library. Note also that if we omitted the local specification altogether, then the symbols xyz_old() and xyz_new() would also be exported by the library (which is typically not what we want).

with the following two paragraphs:

The VER_2 node specifies that the new function pqr() is exported as a public symbol, tagged with the version VER_2. When a symbol is marked as global in one version node, it is also implicitly marked as global for all other nodes (except those that explicitly declare it as local). Thus, xyz() with the tag VER_2 is also exported as a public symbol.

Note that if we omitted the local specification in the VER_1 node, then xyz_old() and xyz_new() would also be exported from the library as public symbols (which is typically not what we want).


Explanation:

As noted in the previous erratum on this page, there is no inheritance implied by version tag dependencies. However, global and local declarations do seem to be aggregated across version nodes.

2022-09-11

1004

Near the end of Listing 48-2 (svshm/svshm_xfr_writer.c), change:

    if (shmctl(shmid, IPC_RMID, 0) == -1)
        errExit("shmctl");

to:

    if (shmctl(shmid, IPC_RMID, NULL) == -1)
        errExit("shmctl");

2022-07-02

1113

Toward the end of Listing 54-3 (pshm/pshm_read.c), change:

    if (close(fd) == -1);             /* 'fd' is no longer needed */
                            errExit("close");

to:

    if (close(fd) == -1)              /* 'fd' is no longer needed */
        errExit("close");

Explanation:

This changes fixes a typo in the code (an excess semicolon) and fixes a white-space formatting mistake introduced in print run 13.

2021-12-17

Fixes and improvements in the thirteenth printing

The following fixes and improvements were applied in the thirteenth and later printings of the book.

Page Fix Reported
54

In Listing 3-3 (lib/error_functions.c), change:

#ifdef __GNUC__
__attribute__ ((__noreturn__))
#endif
static void
terminate(Boolean useExit3)

to:

NORETURN 
static void
terminate(Boolean useExit3)

Explanation:

The NORETURN macro that is already defined in lib/error_functions.h is exactly equivalent to the code that is replaced here.

Reported by Alejandro Colomar.

2021-05-16

61

At the top of the page (in the final part of Listing 3-6, lib/get_num.c), change:

    return (int) res;

to:

    return res;

Explanation:

The cast serves no purpose, especially given the range check in the immediately preceding lines of this function.

Reported by Alejandro Colomar.

2021-05-18

64

Near the start of Table 3-1, in the second column of the row that starts dev_t, change:

an arithmetic type

to:

integer

Explanation:

POSIX.1-2001 specified that dev_t shall be an arithmetic type. In practice, implementations have used an integer type. POSIX.1-2008 tightened the specification to say that dev_t shall be an integer type.

2020-10-18

65

Toward the end of Table 3-1, in the second column of the row that starts time_t, change:

integer or real-floating

to:

integer

Explanation:

POSIX.1-2001 specified that time_t shall be an integer or real-floating type. In practice, (all?) implementations have used an integer type. POSIX.1-2008 tightened the specification to say that time_t shall be an integer type.

2020-10-18

116

At the end of the first paragraph in the small-font note in the second half of this page, remove the following sentence:

Sometimes, the term section is used instead of segment, since section is more consistent with the terminology used in the now ubiquitous ELF specification for executable file formats.

Explanation:

That sentence wasn't correct. An ELF file can contain sections and segments, but the two terms are not synonymous.

Reported by Martin McClure.

2020-11-24

324

At the bottom of this page, change:

    chmod(pathname, 751);

to:

    chmod(pathname, 0751);

Reported by Junjiro Okajima.

2020-09-02

330

In the box in the bottom left-hand corner of Figure 17-2, change "acl_type_t" to "acl_tag_t".

(This erratum was modified slightly on 2021-08-17, after a note from Claudio Fornaro.)


Reported by Andrew McKinlay.

2020-08-11

331

In the last sentence on this page, change:

The tag_type argument has the type acl_type_t

to:

The tag_type argument has the type acl_tag_t

Reported by Andrew McKinlay.

2020-08-11

896

Toward the bottom of the page, change:

        write(STDOUT_FILENO, "\n", 1);
        if (close(pfd[0]) == -1)
            errExit("close");
        _exit(EXIT_SUCCESS);

to:

        write(STDOUT_FILENO, "\n", 1);
        if (close(pfd[0]) == -1)
            errExit("close");
        exit(EXIT_SUCCESS);

Explanation:

There was no particular reason to use _exit() rather than exit() in this program.

2021-03-26

1087

Near the end of the small-font note near the top of the page, change:

it was not deemed possible to provide

to:

it was deemed not possible to provide

Reported by David Featherstone.

2021-01-29

1113

Toward the end of Listing 54-3 (pshm/pshm_read.c), change:

    write(STDOUT_FILENO, addr, sb.st_size);
    printf("\n");

to:

    write(STDOUT_FILENO, addr, sb.st_size);
    write(STDOUT_FILENO, "\n", 1);

Explanation:

The original code was not incorrect, but it is somewhat inconsistent to mix the use of a system call and a library function on the same file (standard output).

2021-03-12

1440

In the small-font note under the bibliography entry for "Josey, A. (ed.). 2004", change:

See http://www.unix-systems.org/version3/theguide.html for details on ordering this guide.

to:

This guide can be found online at http://www.unix.org/version3/theguide.html.

2020-11-22

Fixes and improvements in the twelfth printing

The following fixes and improvements were applied in the twelfth and later printings of the book.

Page Fix Reported
71

Toward the end of Listing 4-1 (fileio/copy.c), change:

    while ((numRead = read(inputFd, buf, BUF_SIZE)) > 0)
        if (write(outputFd, buf, numRead) != numRead)
            fatal("couldn't write whole buffer");

to:

    while ((numRead = read(inputFd, buf, BUF_SIZE)) > 0)
        if (write(outputFd, buf, numRead) != numRead)
            fatal("write() returned error or partial write occurred");

Explanation:

This program is intended as an early (simple) example that demonstrates the use of the key I/O system calls. Nevertheless, the fact that two different cases—write() returning an error and a partial write—are combined in the same error diagnostic has been the source of questions from a few readers. The aim of this change to the text is to more clearly alert the reader that there are two separate cases here. If there had been sufficient space on the page, ideally I might also have included an explicit forward reference to Section 61.1, which discusses partial writes on sockets and shows a function (writen()) that restarts write() calls after a partial write.

Partial writes can also occur when writing to regular files if, for example, the disk is nearly full, or the process resource limit on file size (RLIMIT_FSIZE) has been reached. However, in such cases, restarting the write() is not useful, since it will either result in an ENOSPC error if the disk is no full, or a SIGXFSZ signal if the RLIMIT_FSIZE resource limit has been exceeded.

Reported by Hrvoje Nikšić.

2020-01-14

75

In the second and third bullet points at the top of the page, change:

  • File creation flags: These are the flags shown in the second part of Table 4-3. They control various aspects of the behavior of the open() call, as well as options for subsequent I/O operations. These flags can’t be retrieved or changed.
  • Open file status flags: These are the remaining flags in Table 4-3. They can be retrieved and modified using the fcntl() F_GETFL and F_SETFL operations (Section 5.3).

to:

  • File creation flags: These are the flags shown in the second part of Table 4-3. They control various aspects of the behavior of the open() call itself. These flags can’t be retrieved or changed.
  • Open file status flags: These are the remaining flags in Table 4-3. They affect the semantics of subsequent I/O system calls and can be retrieved and modified using the fcntl() F_GETFL and F_SETFL operations (Section 5.3).

Reported by 徐锋.

2020-06-12

84

Near the start of the main() function in Listing 4-3 (fileio/seek_io.c) change:

    int fd, ap, j;
    char *buf;
    ssize_t numRead, numWritten;

to:

    int fd, ap, j;
    unsigned char *buf;
    ssize_t numRead, numWritten;

Explanation:

See the erratum for page 85.

Reported by Philippe Brodeur.

2019-12-15

85

In Listing 4-3 (fileio/seek_io.c), about one third of the way down the page, change:

                for (j = 0; j < numRead; j++) {
                    if (argv[ap][0] == 'r')
                        printf("%c", isprint((unsigned char) buf[j]) ?
                                                buf[j] : '?');
                    else
                        printf("%02x ", (unsigned int) buf[j]);
                }

to:

                for (j = 0; j < numRead; j++) {
                   if (argv[ap][0] == 'r')
                        printf("%c", isprint(buf[j]) ?  buf[j] : '?');
                   else
                        printf("%02x ", buf[j]);
               }

Explanation:

The fundamental issue was a sign-extension problem that occurred with the "R" operation when displaying bytes whose most significant bit was set. A simple fix would have been to just change the (unsigned int) cast in the printf() to (unsigned char). However, fixing the type of buf is tidier, since it allows us to remove the casts from both printf() calls.

Reported by Philippe Brodeur, with a fix (s/byte/bit/) to the explanation on 2024-10-15 after a note from Dave Chupreev.

2019-12-15

108

In the last sentence of Section 5.11, change:

As a convenience, the names /dev/stdin, /dev/stdout, and /dev/stderr are provided as symbolic links to, respectively, /dev/fd/0, /dev/fd/1, and /dev/fd/2.

to:

As a convenience, the names /dev/stdin, /dev/stdout, and /dev/stderr are provided as symbolic links to, respectively, /proc/self/fd/0, /proc/self/fd/1, and /proc/self/fd/2 (equivalent to /dev/fd/0, /dev/fd/1, and /dev/fd/2).

Reported by Ghe Rivero.

2020-07-02

Fixes and improvements in the eleventh printing

The following fixes and improvements were applied in the eleventh and later printings of the book.

Page Fix Reported
462

Near the start of listing 22-3 (signals/catch_rtsigs.c), change:

static volatile int handlerSleepTime;
static volatile int sigCnt = 0;         /* Number of signals received */
static volatile int allDone = 0;

to:

static volatile int handlerSleepTime;
static volatile int sigCnt = 0;         /* Number of signals received */
static volatile sig_atomic_t allDone = 0;

Explanation:

As was already shown in various other programs and described in Section 21.1.3 of TLPI, a flag variable that is shared between the main program and a signal handler should be declared with the type sig_atomic_t. Note that the two other global variables that are accessed in the signal handler, sigCnt and handlerSleepTime don't need to be declared in the same way, because the logic of the code is such that the signal handler can never interrupt the main program while it is accessing those variables.

Reported by Alexander Pozdneev.

2019-11-21

483

In the main() function of Listing 23-1 (timers/real_timer.c), change:

    if (argc > 1 && strcmp(argv[1], "--help") == 0)
        usageErr("%s [secs [usecs [int-secs [int-usecs]]]]\n", argv[0]);
 
    sigCnt = 0;

to:

    if (argc > 1 && strcmp(argv[1], "--help") == 0)
        usageErr("%s [secs [usecs [int-secs [int-usecs]]]]\n", argv[0]);

Explanation:

The sigCnt variable is also initialized to zero lower down in the main() function.

Reported by Cyrus Ramavarapu.

2019-02-15

517

In the paragraph at the bottom of the page, change:

These duplicates are made in the manner of dup(), which means that corresponding descriptors in the parent and the child refer to the same open file description.

to:

The duplicated file descriptors in the child refer to the same open file descriptions as the corresponding descriptors in the parent.

Explanation:

The old text could potentially mislead the reader about the treatment of file descriptor flags during fork(), in particular, the close-on-exec flag discussed on pages 576-578. When a file descriptor is duplicated by dup(), the close-on-exec flag is cleared for the duplicated descriptor. By contrast (and as described on page 613), when a file descriptor is duplicated in the child during fork(), the value of the close-on-exec flag is made a copy of the value for the corresponding file descriptor in the parent.

Reported by Conor O'Rourke.

2019-01-09

906

In the last sentence of the second paragraph of Section 44.6, change:

If this is a problem, there is little we can do unless we can modify the source code of the program running in the child process to include calls to setbuf() of fflush().

to:

If this is a problem, there is little we can do unless we can modify the source code of the program running in the child process to include calls to setbuf() or fflush().

Reported by Kaleem Khan.

2019-02-04

1383

Near the top of the page, change:

Once we have unlocked the slave device with unlockpt(), we can open it using the traditional open() system call.

to:

Once we have unlocked the slave device with unlockpt(), we can open it by passing the name returned by ptsname() to the open() system call.

Explanation:

This adjustment to the text clarifies the sequence of steps in the handling of the pseudoterminal slave name.

Reported by Yuchao Song.

2019-06-10

Fixes and improvements in the tenth printing

The following fixes and improvements were applied in the tenth and later printings of the book.

Page Fix Reported
1112

Near the end of Listing 54-2 (psh/pshm_write.c), change:

    if (close(fd) == -1)
        errExit("close");                   /* 'fd' is no longer needed */

to:

    if (close(fd) == -1)                    /* 'fd' is no longer needed */
        errExit("close");

2018-08-26

1291

In the first paragraph of the small-font note at the top of this page, change:

On Linux, sysconf(_SC_MAX_INPUT) and sysconf(_SC_MAX_CANON) both return the value 255.

to:

On Linux, pathconf() returns the value 255 for both of these limits.

Reported by Vishnu Bharathi.

2018-08-20

Fixes and improvements in the ninth printing

The following fixes and improvements were applied in the ninth and later printings of the book.

Page Fix Reported
vi

Toward the end of this page, a typographical glitch means that the "Registered Trademark" symbol after the word "UNIX" was incorrectly rendered. The (tiny) characters after "UNIX" need to be replaced by a circled "R".


Explanation:

This typo first appeared in print run 5.

Reported by Alex Pinkney.

2018-06-21

294

In the fourth line of code from the top of this page, change:

            fatal("No group user (%s)", argv[1]);

to:

            fatal("No group user (%s)", argv[2]);

2018-06-19

318

Towards the end of Listing 16-1 (xattr/xattr_view.c), fix a casting error by changing:

                printf("value=");
                for (k = 0; k < valueLen; k++)
                    printf("%02x ", (unsigned int) value[k]);

to:

                printf("value=");
                for (k = 0; k < valueLen; k++)
                    printf("%02x ", (unsigned char) value[k]);

2018-05-03

361

In Listing 18-3 (dirs_links/nftw_dir_tree.c), change:

    switch (sbuf->st_mode & S_IFMT) {      /* Print file type */
    case S_IFREG:  printf("-"); break;
    case S_IFDIR:  printf("d"); break;
    case S_IFCHR:  printf("c"); break;
    case S_IFBLK:  printf("b"); break;
    case S_IFLNK:  printf("l"); break;
    case S_IFIFO:  printf("p"); break;
    case S_IFSOCK: printf("s"); break;
    default:       printf("?"); break;     /* Should never happen (on Linux) */
    }
 
    printf(" %s  ", (type == FTW_D)  ? "D  " : (type == FTW_DNR) ? "DNR" :
            (type == FTW_DP) ? "DP " : (type == FTW_F)   ? "F  " :
            (type == FTW_SL) ? "SL " : (type == FTW_SLN) ? "SLN" :
            (type == FTW_NS) ? "NS " : "  ");
 
    if (type != FTW_NS)
        printf("%7ld ", (long) sbuf->st_ino);
    else
        printf("        ");
 
    printf(" %*s", 4 * ftwb->level, "");        /* Indent suitably */
    printf("%s\n",  &pathname[ftwb->base]);     /* Print basename */
    return 0;                                   /* Tell nftw() to continue */
}

to:

    if (type == FTW_NS) {                  /* Could not stat() file */
        printf("?");
    } else {
        switch (sbuf->st_mode & S_IFMT) {  /* Print file type */
        case S_IFREG:  printf("-"); break;
        case S_IFDIR:  printf("d"); break;
        case S_IFCHR:  printf("c"); break;
        case S_IFBLK:  printf("b"); break;
        case S_IFLNK:  printf("l"); break;
        case S_IFIFO:  printf("p"); break;
        case S_IFSOCK: printf("s"); break;
        default:       printf("?"); break; /* Should never happen (on Linux) */
        }
    }

    printf(" %s  ", (type == FTW_D)  ? "D  " : (type == FTW_DNR) ? "DNR" :
            (type == FTW_DP) ? "DP " : (type == FTW_F)   ? "F  " :
            (type == FTW_SL) ? "SL " : (type == FTW_SLN) ? "SLN" :
            (type == FTW_NS) ? "NS " : "  ");
    if (type != FTW_NS)
        printf("%7ld ", (long) sbuf->st_ino);
    else
        printf("        ");
    printf(" %*s", 4 * ftwb->level, "");        /* Indent suitably */
    printf("%s\n",  &pathname[ftwb->base]);     /* Print basename */
    return 0;                                   /* Tell nftw() to continue */
}

Explanation:

Before inspecting sbuf->st_mode, we must check if the file was was stat()-able. (There are additional white-space changes to ensure that the listing will still fit on the printed page.)

Reported by Raam sri.

2017-11-25

586

In the last sentence of the small-font note near the top of the page, change:

In this case, if a signal was delivered to the child in the small time interval before the call to execl(), then the handler would be invoked in the child, after the signal was unblocked by sigprocmask().

to:

In this case, if a signal was delivered to the child in the small time interval before the call to execl(), then the handler would be invoked in the child. (By contrast, in the implementation shown in Listing 27-9, there is a small window of time before the execl() call where these signals will be ignored rather than being handled according to the dispositions that were in effect in the caller.)

Explanation:

The deleted text removes an inaccuracy (the SIGINT and SIGQUIT signals were not blocked). The added text clarifies a detail that might occur to curious readers.

Reported by Romain Vimont.

2018-01-08

720

Near the bottom of this page, change:

    for(;;)             /* Wait for signals */
        pause()

to:

    for (;;)            /* Wait for signals */
        pause()

2018-03-16

729

In the last paragraph of the page, change:

As can be seen, both children receive SIGCONT and SIGHUP after the parent exits.

to:

As can be seen, both children receive SIGCONT and SIGHUP after the parent exits. (The SIGCONT output appears first because the SIGCONT handler interrupted the SIGHUP handler before the latter handler had a chance to print its output.)

Explanation:

The additional sentence clarifies a detail that might otherwise puzzle the reader. See page 451 for a discussion of the fact that a signal handler might interrupt an already executing signal handler. See also the code shown on page 463, where all signals are handled with the sa.sa_mask field filled with a full set of signals, so as to prevent the handler for a signal from being interrupted by delivery of another signal:

    sigfillset(&sa.sa_mask);

Reported by Sergey Oskotskiy.

2017-11-10

812

In Table 39-2, in the entry for SECBIT_NO_SETUID_FIXUP, change:

Don't change capabilities when effective or file-system user IDs are switched between 0 and nonzero values.

to:

Don't change capabilities when user IDs are switched between 0 and nonzero values (i.e, during the transitions described in Section 39.6).

Explanation:

The SECBIT_NO_SETUID_FIXUP flag affects the behavior for credential changes beyond changes to the effective and file-system user IDs.

2017-12-02

915

In the second line of the code shown on this page, change:

        usageErr("%s [seq-len...]\n", argv[0]);

to:

        usageErr("%s [seq-len]\n", argv[0]);

Explanation:

The program takes at most one argument, so the ellipsis in the diagnostic message was incorrect.

Reported by Matthew Meffan.

2017-11-10

976

In the middle of Listing 47-5, change:

        if (errno != EEXIST) {          /* Unexpected error from semget() */
            errExit("semget 1");

to:

        if (errno != EEXIST)            /* Unexpected error from semget() */
            errExit("semget 1");

Reported by Matthew Meffan.

2017-11-19

977

Somewhat more than half way through Listing 47-6, change:

        if (errno != EEXIST) {          /* Unexpected error from semget() */
            errExit("semget");

to:

        if (errno != EEXIST)            /* Unexpected error from semget() */
            errExit("semget");

Reported by Matthew Meffan.

2017-11-19

1050

In the middle of this page, change:

mlock(*p1, len1);
mlock(*p2, len2);                /* Actually has no effect */
munlock(*p1, len1);

to:

mlock(p1, len1);
mlock(p2, len2);                 /* Actually has no effect */
munlock(p1, len1);

Reported by Matthew Meffan.

2017-12-17

1082

Near the top of Listing 52-7 (pmsg/mq_notify_thread.c), change:

#include <mqueue.h>
#include <fcntl.h>              /* For definition of O_NONBLOCK */

to:

#include <mqueue.h>
#include <signal.h>
#include <fcntl.h>              /* For definition of O_NONBLOCK */

Explanation:

Starting with glibc 2.26, <mqueue.h> no longer defines the struct sigevent type. (SUSv3 required that header file to define the type, but SUSv4 removed the requirement.) Consequently, it is necessary to include <signal.h> to get the type definition.

2017-12-05

1331

Toward the bottom of the page, change:

An exceptional condition occurs in just two circumstances on Linux (other UNIX implementations are similar):

to:

An exceptional condition occurs in only a few cases on Linux, including the following:


Explanation:

The text at the bottom of the page noted two of the reasons that an exceptional condition can occur with select(): state change on a pseudoterminal and out-of-band data on a stream socket.

I overlooked one or two other cases in the kernel that was current at the time TLPI was published, and subsequently Linux has added a few other cases. The other cases include the following:

  • When a filesystem is mounted or unmounted, a file descriptor opened on /proc/mounts will return an exceptional condition.
  • When a swap area is added or removed, a file descriptor opened on /proc/swaps will return an exceptional condition.
  • When certain attributes change in files under /sys, an exceptional condition can occur.
  • When the contents of some files in cgroup filesystems (e.g., cgroup.events) change, an exceptional condition can occur.
  • Some device drivers can return an exceptional condition for device-specific events.
  • The SO_SELECT_ERR_QUEUE socket option added in Linux 3.10 can cause a socket to return an exceptional condition.

These cases also return POLLPRI in the revents fields returned by poll().

This erratum was originally written on 2013-12-20, and subsequently modified on 2018-03-26.

2018-03-26

Fixes and improvements in the eighth printing

The following fixes and improvements were applied in the eighth and later printings of the book.

Page Fix Reported
618

In the label of the upward pointing arrow on the left-hand side of Figure 29-1, change:

increasing virtual addesses

to:

increasing virtual addresses

Reported by Giannis Tsaraias.

2016-12-26

824

Near the end of Listing 40-2 (loginacct/dump_utmpx.c), change:

        printf("%s", ctime((time_t *) &(ut->ut_tv.tv_sec)));

    }
 
    endutxent();

to:

        time_t tv_sec = ut->ut_tv.tv_sec;
        printf("%s", ctime((time_t *) &tv_sec));
    }
    endutxent();


Explanation:

LaoK noted that when sizeof(ut_tv.tv_sec) is smaller than sizeof(time_t), the cast used in the old code will produce incorrect results. On a POSIX-compliant system, this should not be an issue, since POSIX defines the ut_tv field to be of type struct timeval (and thus the ut_tv.tv_sec subfield is defined to be time_t). However, this field is declared otherwise on some systems (e.g., as a structure containing uint32_t fields on mixed 32-/64-bit systems). Therefore, it's safer to assign the ut_tv.tv_sec value to a temporary variable of the right type.

Reported by LaoK

2017-06-09

831

Near the end of Listing 40-4 (loginacct/view_lastlog.c), change:

        printf("%-8.8s %-6.6s %-20.20s %s", argv[j], llog.ll_line,
                llog.ll_host, ctime((time_t *) &llog.ll_time));

to:

        time_t ll_time = llog.ll_time;
        printf("%-8.8s %-6.6s %-20.20s %s", argv[j], llog.ll_line,
                llog.ll_host, ctime(&ll_time));


Explanation:

LaoK noted that when sizeof(llog.ll_time) is smaller than sizeof(time_t), the cast used in the old code will produce incorrect results. Although the llog.ll_time) field is shown as having the type time_t on page 831, (e.g., as a uint32_t on mixed 32-/64-bit systems). Therefore, it's safer to assign the llog.ll_time value to a temporary variable of the right type.

Reported by LaoK

2017-06-09

922

In the first line of this page, change:

first appearing in the late 1970s in Columbus UNIX.

to:

first appearing in the mid-1970s in Columbus UNIX.

Explanation:

A 1985 USENET posting by Dale DeJager, who was involved in the development of CB Unix, gives more precise dates.

2017-03-21

929

In the small-font note toward the bottom of the page, change:

It is possible to set the permissions on an IPC object so that the owner or creator can no longer use IPC_STAT to obtain the associated data structure containing the object permissions (which means that the object won’t be displayed by the ipcs(1) command described in Section 45.6), although IPC_SET can still be used to change them.

to:

It is possible to set the permissions on an IPC object so that the owner or creator can no longer use IPC_STAT to obtain the associated data structure containing the object permissions, although IPC_SET can still be used to change them.

Explanation:

See the erratum for page 934.

Reported by Dmitry Safonov.

2016-10-06

934

In the second paragraph (just below the shell session showing the output of the ipcs command), change:

On Linux, ipcs(1) displays information only about IPC objects for which we have read permission, regardless of whether we own the objects. On some UNIX implementations, ipcs shows the same behavior as on Linux. However, on other implementations, ipcs displays all objects regardless of whether read permission is granted to the user.

to:

On Linux, ipcs(1) originally used the IPC_STAT ctl operation (Section 45.3), so that it could display information only about IPC objects for which we have read permission, regardless of whether we own the objects. More recent versions of ipcs use the /proc/sysvipc interfaces (Section 45.7), and can display all objects, regardless of permissions.

Explanation:

The original text was correct at the time of publication. The behavior of ipcs(1) was changed in 2012 (in util-linux version 2.23).

Reported by Dmitry Safonov.

2016-10-06

1308

In the the last paragraph on the page change:

the left-arrow key generates the 3-character sequence consisting of Escape followed by 0D

to:

the left-arrow key generates the 3-character sequence consisting of Escape followed by [D

2017-03-22

Fixes and improvements in the seventh printing

The following fixes and improvements were applied in the seventh and later printings of the book.

Page Fix Reported
210

In the second sentence of exercise 1, change:

Assuming that the clock_t value returned by times() is an unsigned 32-bit integer

to:

Assuming that the clock_t value returned by times() is a signed 32-bit integer

Reported by Olivier Blin.

2015-09-19

281

In the last line of the third paragraph, change:

On Linux, they are exposed by <sys/types.h> if the _BSD_SOURCE macro is defined.

to:

On Linux, they are exposed by <sys/sysmacros.h>.

Explanation:

The use of <sys/types.h> is not wrong, but <sys/sysmacros.h> is the preferred method to get the definitions.

2016-04-20

284

At the start of Listing 15-1 (files/t_stat.h), change:

<#define _BSD_SOURCE     /* Get major() and minor() from <sys/types.h> */
#include <sys/types.h>
#include <sys/stat.h>

to:

#include <sys/sysmacros.h>
#include <sys/stat.h>

Explanation:

See the erratum for page 281.

2016-04-20

350

In the last sentence of the third paragraph, change:

Alternatively, we can size pathname using the PATH_MAX constant

to:

Alternatively, we can size buffer using the PATH_MAX constant

Reported by Olivier Blin.

2015-09-21

489

In the last sentence of the small-font note at the bottom of the page, change:

and the time spent in the sleep state

to:

and the time spent in the stopped state

Reported by Alexander Jaoshvili.

2016-08-03

570

Towards the end of Listing 27-4 (procexec/t_execle.c), change:

    execle(argv[1], filename, "hello world", (char *) NULL, envVec);

to:

    execle(argv[1], filename, "hello world", "goodbye", (char *) NULL, envVec);

Explanation:

This makes the program do the same thing as the program in Listing 27-1, as stated in the text on page 570.

Reported by Tobin Harding.

2016-04-01

662

In the last line of the prototype box toward the bottom of the page, change:

isassociated

to:

is associated

Reported by 맹규호.

2015-12-18

865

Toward the end of Listing 42-1 (shlibs/dynload.c), change:

    err = dlerror();
    if (err != NULL)
        fatal("dlsym: %s", err);

    /* If the address returned by dlsym() is non-NULL, try calling it
       as a function that takes no arguments */

    if (funcp == NULL)
        printf("%s is NULL\n", argv[2]);
    else
        (*funcp)();

    dlclose(libHandle);                         /* Close the library */

to:

    err = dlerror();
    if (err != NULL)
        fatal("dlsym: %s", err);

    /* Try calling the address returned by dlsym() as a function
       that takes no arguments */

    (*funcp)();

    dlclose(libHandle);                         /* Close the library */

Explanation:

The check for a NULL pointer here served no real purpose.

2016-05-13

1006

In the second-to-last line of the first paragraph in Section 48.5, change:

depending on the kernel versions

to:

depending on the kernel version

2016-03-17

1168

Make the following changes in Listing 57-3 (sockets/us_xfr_sv.c).

At the top of the listing, change:

#include "us_xfr.h"

#define BACKLOG 5

to:

#include "us_xfr.h"
#define BACKLOG 5

And change:

    /* Construct well-known address, bind server socket to it,
       and make this a listening socket */

    if (remove(SV_SOCK_PATH) == -1 && errno != ENOENT)
        errExit("remove-%s", SV_SOCK_PATH);

    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, SV_SOCK_PATH, sizeof(addr.sun_path) - 1);

to:

    /* Construct well-known address, bind server socket to it,
       and make this a listening socket */

    if (strlen(SV_SOCK_PATH) > sizeof(addr.sun_path) - 1)
        fatal("Server socket path too long: %s", SV_SOCK_PATH);
 
    if (remove(SV_SOCK_PATH) == -1 && errno != ENOENT)
        errExit("remove-%s", SV_SOCK_PATH);

    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, SV_SOCK_PATH, sizeof(addr.sun_path) - 1);

Explanation:

In the (hypothetical) case that the SV_SOCK_PATH pathname is longer than sizeof(addr.sun_path) - 1 bytes, the remove() would attempt to remove that pathname, but the bind() call might nevertheless fail because it attempts to bind to a truncated version of the pathname that still exists.

Since the server socket pathname string in this program is actually far smaller than the sun_path field, this check is not in practice necessary for this program. However, in the general case (where the pathname string size may be arbitrary), it is better to have such a check, and the program should follow this good practice. (The first, white-space change is made simply to allow enough space on the printed page to accommodate the second change.)

Reported by Waclaw Kusnierczyk.

2016-07-25

1172

In Listing 57-6 (sockets/ud_ucase_sv.c), change:

    /* Construct server socket address bind socket to it */

    if (remove(SV_SOCK_PATH) == -1 && errno != ENOENT)
        errExit("remove-%s", SV_SOCK_PATH);

    memset(&svaddr, 0, sizeof(struct sockaddr_un));
    svaddr.sun_family = AF_UNIX;
    strncpy(svaddr.sun_path, SV_SOCK_PATH, sizeof(svaddr.sun_path) - 1);

to:

    /* Construct server socket address bind socket to it */

    if (strlen(SV_SOCK_PATH) > sizeof(svaddr.sun_path) - 1)
        fatal("Server socket path too long: %s", SV_SOCK_PATH);
 
    if (remove(SV_SOCK_PATH) == -1 && errno != ENOENT)
        errExit("remove-%s", SV_SOCK_PATH);

    memset(&svaddr, 0, sizeof(struct sockaddr_un));
    svaddr.sun_family = AF_UNIX;
    strncpy(svaddr.sun_path, SV_SOCK_PATH, sizeof(svaddr.sun_path) - 1);

For details, see the erratum for page 1168.

2016-07-25

1222

In the second-to-last line in the example program (sockets/is_seqnum_sv.c), change:

    if (write(cfd, &seqNumStr, strlen(seqNumStr)) != strlen(seqNumStr))

to:

    if (write(cfd, seqNumStr, strlen(seqNumStr)) != strlen(seqNumStr))

Explanation:

The ampersand was harmless, but unnecessary.

2016-04-14

1231

In the prototype box at the bottom of the page, change:

struct hostent *gethostbyaddr(const char *addr, socklen_t len, int type);

to:

struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type);

Reported by Matthew Meffan.

2015-12-05

1381

In the prototype box shown toward the bottom of this page, change:

#define _XOPEN_SOURCE 500

to:

#define _XOPEN_SOURCE

Reported by 맹규호.

2015-11-20

1381

In the first of the two bullet points at the bottom of the page, change:

change the ownership of the slave to be the same as the effective user ID of the calling process;

to:

change the ownership of the slave to be the same as the real user ID of the calling process;

Reported by 맹규호.

2015-11-20

Fixes and improvements in the sixth printing

The following fixes and improvements were applied in the sixth and later printings of the book.

Page Fix Reported
66

In the first paragraph of the small-font note in the middle of this page, change:

Thus, we could write %zd instead of using %ld plus a cast for these types.

to:

Thus, we could write %zd instead of using %ld plus a cast for ssize_t, and analogously %zu for size_t.

2015-03-17

80

In the prototype box for write(), change:

void *buffer

to:

const void *buffer

2014-11-17

228

Toward the end of Listing 12-1 (sysinfo/procfs_pidmax.c), change:

    if (argc > 1) {
        if (write(fd, argv[1], strlen(argv[1])) != strlen(argv[1]))
            fatal("write() failed");

        system("echo /proc/sys/kernel/pid_max now contains "
               "`cat /proc/sys/kernel/pid_max`");
    }

to:

    if (argc > 1) {
        if (lseek(fd, 0, SEEK_SET) == -1)
            errExit("lseek");
 
        if (write(fd, argv[1], strlen(argv[1])) != strlen(argv[1]))
            fatal("write() failed");

        system("echo /proc/sys/kernel/pid_max now contains "
               "`cat /proc/sys/kernel/pid_max`");
    }

Explanation:

The code as originally given takes advantage of the fact that the file offset is ignored when writing new values into /proc/sys files. However, this approach isn't very pedagogically correct. Furthermore, with the default value of 0 in the /proc/sys/kernel/sysctl_strict_writes file that was added in Linux 3.16 (see the proc(5) man page), it is now actively discouraged. Therefore, it's better to explicitly set the file offset before performing a write().

Reported by Haiyan Meng.

2015-05-09

239

In the sentence following the fflush() prototype box, change:

If stream is NULL, fflush() flushes all stdio buffers.

to:

If stream is NULL, fflush() flushes all stdio buffers that are associated with output streams.

2015-01-28

385

In the first shell session shown at the top of the page, change:

$ mkdir dir2/ddd
Read 32 bytes from inotify fd
    wd = 1; mask = IN_CREATE IN_ISDIR
        name = ddd

to:

$ mkdir dir2/ddd
Read 32 bytes from inotify fd
    wd = 2; mask = IN_CREATE IN_ISDIR
        name = ddd

Reported by Eric Dickinson.

2015-01-13

407

At the bottom of this page, change the text in the prototype box for sigismember() and the sentence immediately following the prototype box as follows:

Returns 1 if sig is a member of set, otherwise 0

The sigismember() function returns 1 (true) if sig is a member of set, and 0 (false) otherwise

to:

Returns 1 if sig is a member of set, 0 if it is not, or –1 on error

The sigismember() function returns 1 (true) if sig is a member of set, 0 (false) if it is not a member, or –1 on error (e.g., sig is not a valid signal number)

2015-06-15

443

In the first bullet point on this page, change the second sentence:

The I/O system calls are interruptible, and hence automatically restarted by SA_RESTART only when operating on a "slow" device.

to:

The I/O system calls are interruptible, and hence automatically restarted by SA_RESTART, only when operating on a "slow" device.


Reported by Ulf (Ulfalizer) Magnusson.

2015-02-01

457

In the second and third lines of the paragraph starting "SUSv3 requires..." that follows the two bullet points at the top of the page, change:

The Linux kernel defines 32 different realtime signals, numbered from 32 to 63.

to:

The Linux kernel defines 33 different realtime signals, numbered from 32 to 64.

2015-02-18

651

A few lines from the top of the page, inside Listing 30-4 (threads/thread_multijoin.c), change:

          if (thread[idx].state == TS_TERMINATED){

to:

          if (thread[idx].state == TS_TERMINATED) {

2015-01-30

808

In the small-font note at the top of the page, change the URL:

http://freshmeat.net/projects/libcap-ng

to:

https://people.redhat.com/sgrubb/libcap-ng/

Explanation:

In print run 7, a small correction was made to this erratum, changing http to https.

2015-02-09

1055

In the small-font note at the bottom of the page, change:

However, in glibc versions before 2.7, the POSIX_MADV_DONTNEED operation is implemented using madvise() MADV_DONTNEED, which does affect the semantics of a program, as described earlier. Since glibc 2.7, the posix_madvise() wrapper implements POSIX_MADV_DONTNEED to do nothing, so that it does not affect the semantics of a program.

to:

However, in glibc versions before 2.6, the POSIX_MADV_DONTNEED operation is implemented using madvise() MADV_DONTNEED, which does affect the semantics of a program, as described earlier. Since glibc 2.6, the posix_madvise() wrapper implements POSIX_MADV_DONTNEED to do nothing, so that it does not affect the semantics of a program.

2015-02-04

1069

Toward the bottom of the page, in Listing 52-2 (pmsg/pmsg_create.c), change:

    attr.mq_maxmsg = 50;

to:

    attr.mq_maxmsg = 10;

Explanation:

The default ceiling on the mq_msgsize attribute is 10, so assigning a default value of 50 to this queue attribute makes no sense.

2014-11-05

1082

In Listing 52-7 (pmsg/mq_notify_thread.c), at the end of threadFunc(), change:

    free(buffer);
    }

to:

    free(buffer);
}

Explanation:

This white space error is present only in the fifth print run of the book.

2014-12-01

1101

Change the entire first paragraph:

The NPTL sem_init() implementation ignores pshared, since no special action is required for either type of sharing. Nevertheless, portable and future-proof applications should specify an appropriate value for pshared.

to:

The initial NPTL sem_init() implementation ignored pshared. However, since glibc version 2.7, this argument must be set appropriately so that the implementation provides correct behavior.

2014-11-10

1140

In the first item in the numbered list at the bottom of this page, change:

1. The ordinal number of the lock within the set of all locks held for this file.

to:

1. The ordinal number of the lock within the set of all locks shown in this file.

2015-01-21

1341

About half way down in the code shown on this page, change:

    ready = poll(pollFd, numPipes, -1);
    if (ready == -1)
        errExit("poll");

to:

    ready = poll(pollFd, numPipes, 0);
    if (ready == -1)
        errExit("poll");

Explanation:

Because the num-writes argument to this program is required to be greater than zero, the poll() call can never block. Nevertheless, it makes the operation of the program a little clearer to explicitly specify a timeout of 0 milliseconds.

2014-11-29

Fixes and improvements in the fifth printing

The following fixes and improvements were applied in the fifth and later printings of the book.

Page Fix Reported
xliii

Above the heading Feedback, insert the following text:

Training courses

I teach courses on system programming, based on the content of this book, as well as a number of other related topics, including network programming. For details, see http://man7.org/training/.

2014-02-17

62

Not quite half way down the page, under the description of the _BSD_SOURCE macro, change:

If defined (with any value), expose BSD definitions. Defining this macro also defines _POSIX_C_SOURCE with the value 199506.

to:

If defined (with any value), expose BSD definitions.

2014-01-06

90

In the first paragraph of Section 5.1, change:

All system calls are executed atomically. By this, we mean that the kernel guarantees that all of the steps in a system call are completed as a single operation, without being interrupted by another process or thread.

to:

Various system call operations are executed atomically. By this, we mean that the kernel guarantees that all of the steps in the operation are completed without being interrupted by another process or thread.


Explanation:

While the rest of this section makes the idea of atomicity clear, these two sentences as originally given were confusing. Most obviously, blocking system calls are not atomic.

Reported by Christopher Morris.

2013-11-22

90

At the beginning of Listing 5-1, change:

fd = open(argv[1], O_WRONLY);       /* Open 1: check if file exists */
    if (fd != -1) {                     /* Open succeeded */

to:

    fd = open(argv[1], O_WRONLY);       /* Open 1: check if file exists */
    if (fd != -1) {                     /* Open succeeded */

2013-09-16

97

At the end of the paragraph following the prototype box for dup2(), change:

(Any error that occurs during this close is silently ignored; safer programming practice is to explicitly close() newfd if it is open before the call to dup2().)

to:

(Any error that occurs during this close is silently ignored.) The closing and reuse of newfd are performed atomically, which avoids the possibility that newfd is reused between the two steps in a signal handler or a parallel thread that allocates a file descriptor.


Explanation:

The text completely omitted to mention a key aspect of the operation of dup2(), and the suggestion in the deleted text to close() newfd before calling dup2() allows the possibility of the very race condition that the atomicity of the call was designed to avoid. For further details, see the (recently amended) dup2() man page.

2014-05-30

107

This erratum consists of two related changes.

In the first line of the second paragraph of Section 5.11, change:

Opening one of the files in the /dev/fd directory is equivalent to duplicating the corresponding file descriptor.

to:

On some systems (but not Linux), opening one of the files in the /dev/fd directory is equivalent to duplicating the corresponding file descriptor.

Just above the small-font at the foot of the page, insert a new normal-size paragraph:

On Linux, opening one of the files in /dev/fd is equivalent to reopening the original file; that is, the new file descriptor is associated with a new open file description (and thus has distinct file status flags and file offset).


Explanation:

On at least Solaris and FreeBSD, opening /dev/fd/n is equivalent to duplicating file descriptor n. However, this is not the case on Linux. In common practical use (i.e., the use of /dev/stdin and /dev/stderr in shell pipelines used to connect filters), this difference in semantics is normally invisible.

2013-09-20

123

In the second paragraph, change:

An example of this technique is provided by the gzip(1), gunzip(1), and zcat(1) commands, all of which are links to the same executable file.

to:

An example of this technique is provided by the gzip(1), gunzip(1), and zcat(1) commands: on some distributions, these are all links to the same executable file.


Explanation:

Yongzhi Pan pointed out that the observation about the three commands linking to the same file is not true on some distributions.

Reported by Yongzhi Pan.

2013-12-02

275

In the bullet point at the bottom of the page, change:

A tmpfs file system mounted at /dev/shm

to:

A tmpfs file system mounted at /dev/shm (/run/shm on some systems)


Explanation:

See the erratum for page 1108.

2013-09-18

340

The bottommost rectangle in the I-node table should be gray, as are the other two areas between explicitly shown i-nodes further up in the table.

2013-11-14

383

In Listing 19-1 (inotify/demo_inotify.c), near the top of the page, change:

int
main(int argc, char *argv[])
{
    int inotifyFd, wd, j;
    char buf[BUF_LEN];

to:

int
main(int argc, char *argv[])
{
    int inotifyFd, wd, j;
    char buf[BUF_LEN] __attribute__((aligned(8)));

Explanation:

This change is required because further down in the program there is the following assignment, which casts p (which was initialized to point to buf):

event = (struct inotify_event *) p;

The code as originally given will work on many architectures, since the compiler will most likely align buf to some natural boundary (e.g., 4-byte) on the machine. Furthermore, on x86, things will work even if buf is aligned on a boundary other than 4-byte, since x86 tends to be very forgiving about alignments.

However, the C standards do not guarantee that compilers will align variables in this fashion and do not guarantee that one can cast possibly nonaligned integer fields as I've shown in the original code (i.e., an architecture may require that the integer fields in struct inotify are aligned on a suitable boundary). Therefore, one should force buf to be suitably aligned. The gcc aligned attribute shown above forces buf to be aligned on a 8-byte boundary, which provides correct alignment even on ILP64 platforms, where the size of int is 64 bits.

A more precise (albeit long-winded) way of ensuring the correct alignment would be to declare buf as follows:

    char buf[BUF_LEN]
        __attribute__ ((aligned(__alignof__(struct inotify_event))));

(The gcc __alignof__ keyword yields the alignment of an object.)

Yet another approach that is a little less explicit, but does not depend on gcc features, is to declare buf as an array of struct inotify_event, relying on the fact that C aligns arrays of structures to the alignment requirements of the base structure. Then, some casts are needed for the subsequent pointer operations. The required changes to the code are as follows:

@@ -32,13 +32,11 @@
         printf("        name = %s\n", i->name);
 }

-#define BUF_LEN (10 * (sizeof(struct inotify_event) + NAME_MAX + 1))
-
 int
 main(int argc, char *argv[])
 {
     int inotifyFd, wd, j;
-    char buf[BUF_LEN] __attribute__ ((aligned(8)));
+    struct inotify_event buf[1000];
     ssize_t numRead;
     char *p;
     struct inotify_event *event;
@@ -59,7 +57,7 @@
     }

     for (;;) {                                  /* Read events forever */
-        numRead = read(inotifyFd, buf, BUF_LEN);
+        numRead = read(inotifyFd, buf, sizeof(buf));
         if (numRead == 0)
             fatal("read() from inotify fd returned 0!");

@@ -70,7 +68,7 @@

         /* Process all of the events in buffer returned by read() */

-        for (p = buf; p < buf + numRead; ) {
+        for (p = (char *) buf; p < (char *) buf + numRead; ) {
             event = (struct inotify_event *) p;
             displayInotifyEvent(event);

Note, however, that is approach will cause gcc -Wpedantic to give a warning "invalid use of structure with flexible array member" because of the field definition char name[] at the end of the inotify_event structure.

This erratum was originally composed on 2013-11-18, but then modified (and the explanation considerably expanded) after a conversation with Heinrich Schuchardt.

Reported by Matt Wojciak.

2014-05-21

402

A 2012-07-02 erratum that was incorporated into the fourth print run unfortunately added a technical error into the text. That erratum read:

In the final bullet point on the page, replace the third sentence:

Excluding the effective user ID of the target from the check serves a complementary purpose: it prevents one user from sending signals to another user's process that is running a set-user-ID program belonging to the user trying to send the signal.

with:

Excluding the receiver's effective user ID from the check serves a complementary purpose: it allows a set-user-ID program to retain privileges while preventing the delivery of signals from the user that started the program (by using setresuid() to change the saved set-user-ID to the same value as the real user ID).

The words "from the user that started the program" should be "from the user that owns the executable". However, an even better replacement text is the following:

Furthermore, on Linux and other systems that provide the setresuid() system call, a set-user-ID program can take advantage of this rule to prevent itself being sent signals by the user that owns the executable, by using setresuid() to make its saved set-user-ID the same as the real user ID.

Thus the entire text of the bullet point should now read:

An unprivileged process can send a signal to another process if the real or effective user ID of the sending process matches the real user ID or saved set-user-ID of the receiving process, as shown in Figure 20-2. This rule allows users to send signals to set-user-ID programs that they have started, regardless of the current setting of the target process's effective user ID. Furthermore, on Linux and other systems that provide the setresuid() system call, a set-user-ID program can take advantage of this rule to prevent itself being sent signals by the user that owns the executable, by using setresuid() to make its saved set-user-ID the same as the real user ID. (SUSv3 mandates the rules shown in Figure 20-2, but Linux followed slightly different rules in kernel versions before 2.0, as described in the kill(2) manual page.)


Reported by Lorenzo Monti

2014-01-05

407

In the second prototype box on this page, change:

int sigaddset(sigset_t *set, int sig);
int sigdelset(sigset_t *set, int sig);

to:

int sigaddset(const sigset_t *set, int sig);
int sigdelset(const sigset_t *set, int sig);

Reported by Robert P. J. Day.

2013-09-24

451

In the first paragraph, change:

If, at the time it was execed, a program finds that the disposition of a terminal-generated signals

to:

If, at the time it was execed, a program finds that the disposition of a terminal-generated signal


Reported by Vince Manapat.

2014-01-05

476

In the prototype box for sigvec(), change:

int sigvec(int sig, struct sigvec *vec, struct sigvec *ovec);

to:

int sigvec(int sig, const struct sigvec *vec, struct sigvec *ovec);

2014-05-21

487

In Listing 23-2 (timers/timed_read.c), in the line of code fifth from the top of the page, change:

    numRead = read(STDIN_FILENO, buf, BUF_SIZE - 1);

to:

    numRead = read(STDIN_FILENO, buf, BUF_SIZE);

Explanation:

A small error that crept in as a thinko or via cut-and-paste. Since there's no need to tack a terminating null byte onto the end of this buffer, it's okay to allow read() to use the complete buffer.

2014-06-30

487

In the first line of Section 23.4.1, change:

The sleep() function suspends execution of the calling process

to:

The sleep() function suspends execution of the calling thread

2014-04-05

539

In Exercise 25-1, change:

If a child process makes the call exit(-1), what exit status (as returned by WEXITSTATUS()) will be seen by the parent?

to:

If a child process makes the call exit(-1), what exit status will be seen by the parent?


Explanation:

Mention of WEXITSTATUS()), which is only covered in the next chapter, isn't needed to solve this exercise.

2013-10-24

592

In the last line on this page (in Listing 28-1, procexec/acct_on.c), change:

        usageErr("%s [file]\n");

to:

        usageErr("%s [file]\n", argv[0]);

Reported by Liu Jiaming.

2013-09-13

600

In the CLONE_NEWUTS row of Table 28-2, change:

Child gets new UTS (utsname()) namespace

to:

Child gets new UTS (uname()) namespace


Reported by Yongzhi Pan.

2013-12-20

636

Near the top of Listing 30-2 (threads/thread_incr_mutex.c), change:

static int glob = 0;

to:

static volatile int glob = 0;

Explanation:

See also the notes for the erratum on page 632.

Reported by Arnaud Frugier.

2013-12-04

673

Three lines above the heading for Section 32.3, change:

When a thread calls exec(), the cancelability type and state

to:

When a thread calls exec(), the cancelability state and type


Reported by Liu Jiaming.

2013-09-13

690

In the first bullet point below the heading LinuxThreads deviations from specified behavior, change:

Calls to getppid() reflect the fact that every thread other than the main thread is created by the process's manager thread (i.e., getppid() returns the process ID of the manager thread).

to:

Calls to getppid() in threads other than the main thread return the process ID of the manager thread, which reflects the fact that every thread other than the main thread is created by the manager thread.


Explanation:

Although the original wording was not incorrect, Yongzhi Pan pointed out that it was hard to understand, and suggested the new text.

Reported by Yongzhi Pan.

2013-12-02

695

In the third line of the second paragraph, change:

can grep though

to:

can grep through


Reported by Yongzhi Pan.

2013-12-02

699

In the last two paragraphs on the page, change:

A process group is a set of one or more processes sharing the same process group identifier (PGID). A process group ID is a number of the same type (pid_t) as a process ID. A process group has a process group leader, which is the process that creates the group and whose process ID becomes the process group ID of the group. A new process inherits its parent's process group ID.

A process group has a lifetime, which is the period of time beginning when the leader creates the group and ending when the last member process leaves the group.

to:

A process group is a set of one or more processes sharing the same process group identifier (PGID). A process group ID is a number of the same type (pid_t) as a process ID. A process group has a process group leader, which is the process that becomes the first member of the group and whose process ID becomes the process group ID of the group. A new process inherits its parent's process group ID.

A process group has a lifetime, which is the period of time beginning when the leader joins the group and ending when the last member process leaves the group.


Explanation:

As is made clear on page 702 and 703, a process, X, can become a process group leader when another process changes X's process group ID to be the same as its process ID.

This errata was added following Marek Mularczyk's report of a similar imprecision on page 730.

2014-05-02

707

About one third of the way down the page, change the second item in the numbered list:

The controlling terminal loses its association with the session, and can therefore be acquired as the controlling process by another session leader.

to:

The controlling terminal loses its association with the session, and can therefore be acquired as the controlling terminal by another session leader.


Reported by Liu Jiaming.

2013-09-17

707

In the code snippet toward the bottom of the page, change:

if (ioctl(fd, TIOCSCTTY) == -1)
    errExit("ioctl");

to:

if (ioctl(fd, TIOCSCTTY, 0) == -1)
    errExit("ioctl");

Explanation:

In fact, the third argument isn't accessed on FreeBSD (nor, probably, on the other BSDs), or (in the usual case) on Linux, so that the call as originally given on page 707 will work. Furthermore, the FreeBSD (and OpenBSD and NetBSD) tty(4) man page describes the third argument as void, meaning that officially no argument is required. However, scanning the source code of some utilities on BSD shows that they all seem to specify 0 or NULL as the third argument of ioctl(TIOCSCTTY). And some sources document that the third argument should be zero, so it is probably best safe practice to specify the argument as 0, even though none of the BSDs seems to need it.

On Linux, the situation is more complex. By default, the TIOCSCTTY ioctl() does not try to use a third argument, and so omitting it is (almost) safe. (See the function tiocsctty() in the Linux source file drivers/tty/tty_io.c.) But, it does access the third argument if the terminal is a controlling terminal for another process, in which case the terminal will be stolen if the third argument is 1 and the caller has the CAP_SYS_ADMIN capability. Therefore, on the unlikely chance that we are calling ioctl(TIOCSCTTY) from a privileged process and the terminal is already a controlling terminal for another process, then we better specify the third argument of ioctl() as zero, to avoid the possibility that the call randomly gets a value of 1 in the register used for the argument, and inadvertently steals another process's controlling terminal.

Summary: the BSDs invented TIOCSCTTY, but they don't need a third argument. Linux piggybacked an extra use for TIOCSCTTY, which means that the third argument should be specified as 0 in the usual case.

Reported by Liu Jiaming.

2013-11-12

730

In the first paragraph of Section 34.8, change:

A session leader is the process that created the session using setsid(). Similarly, a process group leader is the process that created the group using setpgid(). All of the members of a process group share the same process group ID (which is the same as the process ID of the process group leader), and all processes in the process groups that constitute a session have the same session ID (which is the same as the process ID of the session leader).

to:

A session leader is the process that created the session using setsid(). A process group leader is the first member of a process group. All of the members of a process group share the same process group ID (which is the same as the process ID of the process group leader), and all processes in the process groups that constitute a session have the same session ID (which is the same as the process ID of the session leader).


Explanation:

See the erratum for page 699.

Reported by Marek Mularczyk.

2014-05-02

732

In Exercise 34-4, change:

Modify the program in Listing 34-4 (disc_SIGHUP.c) to verify that, if the controlling process doesn't terminate as a consequence of receiving SIGHUP, then the kernel doesn't send SIGHUP to the members of the foreground process.

to:

Modify the program in Listing 34-4 (disc_SIGHUP.c) to verify that, if the controlling process doesn't terminate as a consequence of receiving SIGHUP (i.e., it instead ignores or catches the signal and continues execution), then the kernel doesn't send SIGHUP to the members of the foreground process group.


Explanation:

As Li Wenjun noted, the original wording could mislead the reader into believing that if the controlling process terminated for some reason other than receiving SIGHUP, then the members of the foreground process group would not receive SIGHUP. (As pointed out on page 705, if the controlling process terminates for any reason, then SIGHUP is sent to all members of the foreground process group.)

Reported by Li Wenjun.

2013-09-06

737

About half-way through the program listing on this page (procpri/t_setpriority.c), change:

    if (setpriority(which, who, prio) == -1)
        errExit("getpriority");

to:

    if (setpriority(which, who, prio) == -1)
        errExit("setpriority");

Reported by Liu Jiaming.

2013-09-22

740

In the second paragraph of Section 35.2.3, change:

The difference is that the SCHED_BATCH policy causes jobs that frequently wake up to be scheduled less often. This policy is intended for batch-style execution of processes.

to:

This policy is intended for batch-style execution of processes. The scheduler takes account of the nice value, but assumes that this is a CPU-intensive job that does not require low latency scheduling in response to wake-up events, and thus mildly disfavors it in scheduling decisions.

2013-09-23

740

In the second line of the third paragraph of Section 35.2.3, change:

but provides functionality equivalent to a very low nice value (i.e., lower than +19)

to:

but schedules the process with a very low priority (lower even than a nice value of +19)


Explanation:

Liu Jiaming pointed out that the original wording "low nice value" is a little ambiguous, since it could be understood to mean a low-number nice value (i.e., a high priority), when of course the intended sense is a low priority.

Reported by Liu Jiaming.

2013-09-23

778

In the second line from the bottom of the page, change:

facility can't be generated from the user-space programs

to:

facility can't be generated from user-space programs

2013-12-20

807

In the first numbered list in Section 39.7, change:

  1. If the process doesn't have the CAP_SETPCAP capability in its effective set, then the new inheritable set must be a subset of the combination of the existing inheritable and permitted sets.
  2. The new inheritable set must be a subset of the combination of the existing inheritable set and the capability bounding set.

to:

  1. If the process doesn't have the CAP_SETPCAP capability in its effective set, then the new inheritable set must be a subset of the combination (union) of the existing inheritable and permitted sets.
  2. The new inheritable set must be a subset of the combination (union) of the existing inheritable set and the capability bounding set.

Explanation:

Liu Jiaming pointed out that the word "combination" is a little unclear—does it mean "union" or "intersection"?

Reported by Liu Jiaming.

2013-09-27

814

At the end of the second bullet point in Section 39.10, add the following text:

(This also disables the securebits mechanism described in Section 39.8.)


Explanation:

See also the erratum for list item 3 on page 816.

2013-09-27

816

Change the entire text of list item 3:

Set the SECBIT_KEEP_CAPS flag (or use the prctl() PR_SET_KEEPCAPS operation to achieve the same result), so that the next step does not drop capabilities.

to:

Employ the call prctl(PR_SET_KEEPCAPS, 1) so that the next step does not drop capabilities (see Section 39.8).


Explanation:

As pointed out by Liu Jiaming, on the older kernels that are being discussed in this section, the SECBIT_KEEP_CAPS flag either does not exist (Linux 2.6.25 and earlier), or (Linux 2.6.26 to Linux 2.6.32) is unavailable if file capabilities are disabled in the kernel. This point was made in passing in the first paragraph at the top of page 812, and made more explicit in an erratum on page 814.

Reported by Liu Jiaming.

2013-09-27

855

In the first line of the shell session at the bottom of the page, change:

$ gcc -g -c -fPIC -Wall -c foo.c

to:

$ gcc -g -c -fPIC -Wall foo.c

Reported by Liu Jiaming.

2013-10-11

863

About half way down the page, change:

ip = (int *) dlsym(symbol, "myvar");

to:

ip = (int *) dlsym(handle, symbol);

Reported by Liu Jiaming.

2013-10-13

863

In the second-to-last paragraph, change:

The reason is that the C99 standard forbids assignment between a function pointer and void *.

to:

The reason is that the ISO C standard leaves the results of casting between function pointers and void * undefined.

2014-01-02

864

In the fourth paragraph from the top of the page, change:

notes that the C99 standard

to:

notes that the ISO C standard

2014-01-02

866

In the definition of the Dl_info structure in the bottom half of the page, change:

    const char *dli_sname;      /* Name of nearest run-time symbol
                                   with an address <=  'addr' */

to:

    const char *dli_sname;      /* Name of symbol whose definition
                                   overlaps  'addr' */

2014-01-15

899

In the third line of the last paragraph on this page, change:

If file descriptor 0 was already closed prior to the above steps,

to:

If file descriptor 0 happened to be closed between the call to pipe() and the calls to close() and dup(),


Explanation:

The rewording describes the case more clearly; otherwise the reader might be wondering if "prior to the above steps" meant "before the pipe() call".

2013-10-30

905

Near the end of Listing 44-5 (pipes/popen_glob.c), change:

        printf("pclose() status == %#x\n", (unsigned int) status);

to:

        printf("pclose() status = %#x\n", (unsigned int) status);

Explanation:

Without this change, the program is inconsistent with the output shown in the shell session log lower down on the same page.

Reported by Liu Jiaming.

2013-10-22

917

In the code snippet about two thirds of the way down the page, change:

int flags;
flags = fcntl(fd, F_GETFL);     /* Fetch open files status flags */
flags |= O_NONBLOCK;            /* Enable O_NONBLOCK bit */
fcntl(fd, F_SETFL, flags);      /* Update open files status flags */

to:

int flags;
flags = fcntl(fd, F_GETFL);     /* Fetch open file status flags */
flags |= O_NONBLOCK;            /* Enable O_NONBLOCK bit */
fcntl(fd, F_SETFL, flags);      /* Update open file status flags */

2013-10-31

961

In Listing 46-9 (svmsg/svmsg_file_client.c) at the region labeled with a circled 5, change:

    if (resp.mtype == RESP_MT_FAILURE) {
        printf("%s\n", resp.data);      /* Display msg from server */
        if (msgctl(clientId, IPC_RMID, NULL) == -1)
            errExit("msgctl");
        exit(EXIT_FAILURE);
    }

to:

    if (resp.mtype == RESP_MT_FAILURE) {
        printf("%s\n", resp.data);      /* Display msg from server */
        exit(EXIT_FAILURE);
    }

Explanation:

At the point in the program labeled with a circled 3 (shown on the previous page), an exit handler was established to automatically remove the client's message queue when the client exits. Thus, the code removed in the change above was redundant.

Reported by Liu Jiaming.

2013-10-17

978

In the prototype box for semop() at the bottom of the page, change:

unsigned int nsops

to:

size_t nsops

2014-05-01

980

In the prototype box for semtimedop() in the middle of the page, change:

unsigned int nsops

to:

size_t nsops

2014-05-01

987

In the text below the heading Example of the effect of SEM_UNDO, change:

The following shell session log shows the effect of performing operations on two semaphores: one operation with the SEM_UNDO flag and one without. We begin by creating a set containing two semaphores:

$ ./svsem_create -p 2
131073

to:

The following shell session log shows the effect of performing operations on two semaphores: one operation with the SEM_UNDO flag and one without. We begin by creating a set containing two semaphores that we initialize to 0:

$ ./svsem_create -p 2
131073
$ ./svsem_setall 131073 0 0
Semaphore values changed (PID=2220)

Explanation:

The example works as originally shown, because Linux happens to initialize the semaphores in a new set to 0. However, this is not guaranteed by the standards, so that, as in the example shell session shown on page 984, the semaphores should be explicitly initialized.

Reported by Liu Jiaming.

2013-10-19

1006

In the first sentence on this page, change:

The following shell session demonstrates the use of the programs in Listing 48-2 and Listing 46-9.

to:

The following shell session demonstrates the use of the programs in Listing 48-2 and Listing 48-3.


Reported by Guilherme Maciel Ferreira.

2013-09-30

1010

About three quarters of the way down the page, change:

*p = (target - baseaddr);       /* Place offset in *p */

to:

*p = target - baseaddr;         /* Place offset in *p */

Reported by Yongzhi Pan.

2013-12-02

1021

In the sentence immediately preceding the heading Memory protection in more detail, change:

We say more about file mappings in Section 49.5.

to:

We say more about file mappings in Section 49.4.


Reported by Guilherme Maciel Ferreira.

2013-10-11

1031

In the third line from the bottom of the page, change:

and len is rounded up

to:

and length is rounded up


Reported by Liu Jiaming.

2013-10-22

1068

In the second line, change:

(all of which are analogous to the use to file descriptors)

to:

(all of which are analogous to the use of file descriptors)


Reported by Robert P. J. Day.

2013-09-18

1082

In Listing 52-7 (pmsg/mq_notify_thread.c), at the end of threadFunc(), change:

    free(buffer);
    pthread_exit(NULL);

to:

    free(buffer);

Explanation:

The pthread_exit() call does no harm, but is redundant: the thread will anyway terminate when threadFunc() returns.

2013-11-25

1090

In the paragraph following the bullet list in Section 53.2, change:

mounted under the directory /dev/shm.

to:

mounted under the directory /dev/shm (/run/shm on some systems).


Explanation:

See the erratum for page 1108.

2013-09-18

1108

In the second paragraph, change:

mounted under the directory /dev/shm.

to:

mounted under the directory /dev/shm (/run/shm on some systems).


Explanation:

Strictly speaking this is an update, rather than an erratum (likewise for the errata on pages 275 and 1090), since /run/shm was not in wide use at the time TLPI went to press. At least some versions of Debian and its derivatives nowadays use /run/shm rather than /dev/shm, and possibly other distributions may make this change in the future. For some more detail, read the Debian notes on the transition to using the /run directory.

Robert reports that /run/shm is in use on Ubuntu 12.04 and 12.10, and I see the same on Ubuntu 13.04 My Fedora 19 system uses /dev/shm.

Reported by Robert P. J. Day.

2013-09-18

1109

In the small-font note near the bottom of the page, change:

However, SUSv3 says that results of using O_TRUNC with a read-only open is undefined,

to:

However, SUSv3 says that the result of using O_TRUNC with a read-only open is undefined,


Reported by Liu Jiaming.

2013-10-25

1121

Near the top of Listing 55-1 (filelock/t_flock.c), change:

        usageErr("%s file lock [sleep-time]\n"
                 "    'lock' is 's' (shared) or 'x' (exclusive)\n"
                 "        optionally followed by 'n' (nonblocking)\n"
                 "    'secs' specifies time to hold lock\n", argv[0]);

to:

        usageErr("%s file lock [sleep-time]\n"
                 "    'lock' is 's' (shared) or 'x' (exclusive)\n"
                 "        optionally followed by 'n' (nonblocking)\n"
                 "    'sleep-time' specifies time to hold lock\n", argv[0]);

Reported by Liu Jiaming.

2013-10-25

1134

In the last sentence of the bullet point at the top of the page, change:

If one of more processes hold conflicting locks, then this function returns a nonzero value (i.e., true)—the process ID of one the processes holding a conflicting lock.

to:

If one or more processes hold conflicting locks, then this function returns a nonzero value (i.e., true)—the process ID of one of the processes holding a conflicting lock.


Reported by Liu Jiaming.

2013-10-27

1141

In the second line from the top of the page, change:

lock for fcntl()

to:

lock for flock()


Reported by Liu Jiaming.

2013-10-27

1143

In the second line on page 1143, change:

using kill(pid, 0), as described in Section 20.5.

to:

using kill(pid, 0), as described in Section 20.6.


Reported by Michael Jobs.

2013-09-05

1158

In the second line of the fourth paragraph, change:

should be specified as NULL and 0, respectively.

to:

should both be specified as NULL.

2014-07-24

1161

In the last line of the last normal-size paragraph on the page, change:

The addrlen argument specifies the size of addr.

to:

The addrlen argument specifies the size of dest_addr.

2013-11-04

1171

In Listing 57-5 (sockets/ud_ucase.h), change:

#define BUF_SIZE 10             /* Maximum size of messages exchanged
                                   between client to server */

to:

#define BUF_SIZE 10             /* Maximum size of messages exchanged
                                   between client and server */

Reported by Liu Jiaming.

2013-11-01

1218

In the prototype box for getnameinfo, change:

int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, char *host,
                size_t hostlen, char *service, size_t servlen, int flags);

to:

int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, char *host,
                socklen_t hostlen, char *service, socklen_t servlen, int flags);

2014-05-20

1219

In the bullet point at the bottom of the page, change:

Initialize the server's sequence number either to 1

to:

Initialize the server's sequence number either to 0


Reported by Liu Jiaming.

2013-11-01

1223

In the shell session at the bottom of the page, change:

Trying 127.0..0.1...

to:

Trying 127.0.0.1...

Reported by Liu Jiaming.

2013-11-01

1235

In the second line of the small-font note at the top of the page, change:

files/t_getservbyname.c

to:

sockets/t_getservbyname.c


Reported by Liu Jiaming.

2013-11-01

1236

In the seventh line of the second paragraph in Section 59.16, change:

standards exist to deal this problem

to:

standards exist to deal with this problem


Reported by Liu Jiaming.

2013-11-01

1242

Near the start of Listing 60-3 (sockets/id_echo_cl.c), change:

    if (argc < 2 || strcmp(argv[1], "--help") == 0)
        usageErr("%s: host msg...\n", argv[0]);

to:

    if (argc < 2 || strcmp(argv[1], "--help") == 0)
        usageErr("%s host msg...\n", argv[0]);

Reported by Liu Jiaming.

2013-11-07

1256

In the fifth line of the last paragraph, change:

See Figure 5-1, on page 91.

to:

See Figure 5-2, on page 95.


Reported by Jerome Zago.

2013-08-03

1277

In the shell session at the bottom of the page, change:

$ tcpdump -t -N 'port 55555'

to:

$ sudo tcpdump -t -N 'port 55555'

Explanation:

Privilege is, of course, required to run tcpdump.

Reported by Liu Jiaming.

2013-11-07

1278

In the first line of the last paragraph on this page, change:

The level argument specifies the protocol to which the socket option applies

to:

The level argument specifies the protocol level to which the socket option applies

2013-11-11

1308

In the first paragraph following the heading MIN == 0, TIME == 0 (polling read), change:

with the lesser of the number of bytes available or the number of bytes requested.

to:

with the lesser of the number of bytes available and the number of bytes requested.


Reported by Yongzhi Pan.

2013-12-02

1308

In the second paragraph following the heading MIN == 0, TIME == 0 (polling read), change:

This mode is somewhat similar to setting the O_NONBLOCK flag for the terminal (Section 5.9).

to:

This mode is somewhat similar to setting the O_NONBLOCK flag (Section 5.9) for the terminal while in canonical mode.


Reported by Peter Hurley.

2014-03-21

1308

In the first paragraph following the heading MIN > 0, TIME == 0 (blocking read), change:

The read() blocks (possibly indefinitely) until the lesser of the number of bytes requested or MIN bytes are available, and returns the lesser of the two values.

to:

The read() blocks (possibly indefinitely) until MIN bytes are available, and returns up to the number of bytes requested.


Reported by Peter Hurley.

2013-12-02

1308

In the paragraph following the heading MIN > 0, TIME > 0 (read with interbyte timeout), change:

The read() returns when either the lesser of MIN bytes or the number of bytes requested have been read, or

to:

The read() returns when either the lesser of MIN bytes and the number of bytes requested has been read, or


Explanation:

As an aside here, it's worth noting that Linux differs from POSIX and some other implementations (e.g., Solaris) here. POSIX specifies that read() may terminate when MIN bytes have been read, but makes no mention of termination when the requested number of bytes has been read,

2013-12-02

1336

Near the end of Listing 63-1 (altio/t_select.c), change:

    if (pto != NULL)
        printf("timeout after select(): %ld.%03ld\n",
               (long) timeout.tv_sec, (long) timeout.tv_usec / 10000);

to:

    if (pto != NULL)
        printf("timeout after select(): %ld.%03ld\n",
               (long) timeout.tv_sec, (long) timeout.tv_usec / 1000);

Reported by Liu Jiaming.

2013-11-11

1338

In the paragraph immediately below Table 63-2, change:

It is permissible to specify events as 0 if we are not interested in events on a particular file descriptor. Furthermore, specifying a negative value for the fd field (e.g., negating its value if nonzero) causes the corresponding events field to be ignored and the revents field always to be returned as 0. Either of these techniques can be used to (perhaps temporarily) disable monitoring of a single file descriptor, without needing to rebuild the entire fds list.

to:

If events is specified as 0, the only bits that can be returned in revents are POLLERR, POLLHUP, and POLLNVAL.

      Specifying
a negative value for the fd field (e.g., negating its value if nonzero) causes the corresponding events field to be ignored and the revents field always to be returned as 0. This technique can be used to (perhaps temporarily) disable monitoring of a single file descriptor, without needing to rebuild the entire fds list.

2013-11-08

1341

Toward the end of Listing 63-2 (altio/poll_pipes.c), change:

    ready = poll(pollFd, numPipes, -1);         /* Nonblocking */
    if (ready == -1)
        errExit("poll");

    printf("poll() returned: %d\n", ready);

    /* Check which pipes have data available for reading */

    for (j = 0; j < numPipes; j++)
        if (pollFd[j].revents & POLLIN)
            printf("Readable: %d %3d\n", j, pollFd[j].fd);

to:

    ready = poll(pollFd, numPipes, -1);
    if (ready == -1)
        errExit("poll");

    printf("poll() returned: %d\n", ready);

    /* Check which pipes have data available for reading */

    for (j = 0; j < numPipes; j++)
        if (pollFd[j].revents & POLLIN)
            printf("Readable: %3d\n", pollFd[j].fd);

Explanation:

The first change removes a bogus comment. The second change is required to make the program consistent with the output in the shell session shown at the top of page 1340. (Unfortunately, an inconsistency between program versions crept into the text.)

Reported by Liu Jiaming.

2013-11-11

1343

In the last row of Table 63-6, change:

Stream socket peer closed connection or executed shutdown(SHUT_WR)

to:

Stream socket peer closed connection

Explanation:

When the peer shuts down the write side of a socket connection, then select() and poll() indicate the socket as being readable. However, this has no affect on whether the local socket is writable; that depends only on whether the write side of the local channel has been filled with data. By contrast, if the peer connection has been closed, then the socket will be ready for writing—writes will fail with a "broken pipe" error.

Reported by Jiefu Xia.

2014-03-27

1360

In the first line of the second item of the dashed list in the bottom half of the page (i.e., about two thirds of the way down the page), change:

It the epoll_wait() call

to:

If the epoll_wait() call


Reported by Yongzhi Pan.

2013-12-02

1368

In Listing 63-7, near the start of the main() function, change:

    sa.sa_sigaction = handler;

to:

    sa.sa_handler = handler;

Reported by Liu Jiaming.

2013-12-03

1370

In Listing 63-8, about half way through the listing, change:

    sa.sa_sigaction = handler;

to:

    sa.sa_handler = handler;

Reported by Liu Jiaming.

2013-12-03

1427

In the solution to Exercise 10-1, change:

The maximum unsigned 32-bit integer value is 4,294,967,295. Divided by 100 clock ticks per second, this corresponds to slightly more than 497 days. Divided by 1 million (CLOCKS_PER_SEC), this corresponds to 71 minutes and 35 seconds.

to:

The maximum signed 32-bit integer value is 2,147,483,647. Divided by 100 clock ticks per second, this corresponds to somewhat more than 248 days. Divided by 1 million (CLOCKS_PER_SEC), this corresponds to 36 minutes and 47 seconds.


Explanation:

The clock_t type is a signed integer. Therefore, on 32-bit systems, wrapping occurs at 2^31, not 2^32.

2013-11-13

1436

At the top of the page, in the line labeled 62-1, change:

The tcgetattr() fails if it is applied to a file descriptor that doesn't refer to a terminal.

to:

The tcgetattr() function fails if it is applied to a file descriptor that doesn't refer to a terminal.

2013-09-29

Fixes and improvements in the fourth printing

The following fixes and improvements were applied in the fourth and later printings of the book.

Page Fix Reported
52

About a quarter of the way through Listing 3-2 (lib/error_functions.h), change:

/* This macro stops 'gcc -Wall' complaining that "control reaches

to:

    /* This macro stops 'gcc -Wall' complaining that "control reaches

2012-09-04

61

In the second sentence of the first paragraph of Section 3.6.1, change:

Some of these standards are defined by standards bodies such The Open Group

to:

Some of these standards are defined by standards bodies such as The Open Group


Reported by Murray McAllister.

2012-04-30

96

In the first sentence of the second-to-last paragraph, change:

The shell achieves the redirection of standard error by duplicating file descriptor 2 so that it refers to the same open file description as file descriptor 1

to:

The shell redirects standard error by making file descriptor 2 a duplicate of file descriptor 1, so that it refers to the same open file description


Explanation:

The intent of the original wording was probably clear, but was potentially confusing.

Reported by Jeffrey Thompson.

2012-12-18

175

Completely replace the second small-font paragraph at the top of this page, changing:

In all versions of glibc (including modern ones), setegid(egid) is implemented as setregid(–1, egid). As with seteuid(), this means that we can specify egid as the same value as the current effective group ID, although this behavior is not specified in SUSv3. It also means that setegid() changes the saved set-group-ID if the effective group ID is set to a value other than the current real group ID. (A similar remark applies for the older implementation of seteuid() using setreuid().) Again, this behavior is not specified in SUSv3.

to:

Analogous remarks apply for the glibc implementation of setegid(egid), except that the switch in implementation from setregid(–1, egid) to setresgid(–1, egid, –1) occurred in glibc 2.2 or 2.3 (the precise version depends on the hardware architecture).


Explanation:

The old text was based on ancient knowledge that for a certain period (i.e., glibc 2.1 to 2.2), the implementations of seteuid() and setegid() were not analogous. Somehow, I overlooked the fact that the two implementations later came back into sync in glibc 2.2/2.3.

The new text drops some details of the effect of the old seteuid() and setegid() implementations on the saved set IDs. However, this is probably not too important: the information applies only for rather old versions of glibc and can anyway be deduced from the description of setreuid() and setregid().

Reported by Junjiro Okajima.

2012-06-30

181

Replace the first bullet point on this page:

The glibc implementations of seteuid() (as setresuid(–1, e, –1)) and setegid() (as setregid(–1, e)) also allow the effective ID to be set to the same value it already has, but this is not specified in SUSv3. The setegid() implementation also changes the saved set-group-ID if the effective user ID is set to a value other than that of the current real user ID. (SUSv3 doesn't specify that setegid() makes changes to the saved set-group-ID.)

with:

The glibc implementations of seteuid() and setegid() also allow the effective ID to be set to the same value it already has, but this is not specified in SUSv3.


Explanation:

See the erratum for page 175 for some background to this erratum.

This erratum renders obsolete an earlier (2011-01-03) erratum by Justin Pryzby. That erratum changed:

The setegid() implementation also changes the saved set-group-ID if the effective user ID is set to a value other than that of the current real user ID.

to:

The setegid() implementation also changes the saved set-group-ID if the effective group ID is set to a value other than that of the current real group ID.

Reported by Junjiro Okajima.

2012-07-02

200

In the last sentence of the paragraph that precedes Section 10.4, change:

the preceding form is less readable than the Linux-specific near equivalent:

to:

the preceding form is less readable than the near equivalent:


Explanation:

As described at the start of Section 10.3, a timezone string that starts with a colon (as shown in the line following the the quoted text) is standardized in SUSv3. It is only the form without a preceding colon that is nonstandard.

Reported by Junjiro Okajima.

2012-07-02

225

In the third line of the third paragraph from the bottom of the page, change:

/proc/1968/1 is a symbolic link

to:

/proc/1968/fd/1 is a symbolic link


Reported by Junjiro Okajima.

2012-09-27

230

At the end of the first small-font paragraph at the top of the page, change:

the Linux-specific /proc/hostname file

to:

the Linux-specific /proc/sys/kernel/hostname file


Reported by Ursache Vladimir

2012-04-26

230

At the end of the second small-font paragraph at the top of the page, change:

the Linux-specific /proc/domainname file

to:

the Linux-specific /proc/sys/kernel/domainname file


Reported by Ursache Vladimir

2012-04-26

230

At the top of Listing 12-2 (sysinfo/t_uname.c), change:

#define _GNU_SOURCE

to:

#ifdef __linux__
#define _GNU_SOURCE
#endif

Explanation:

This change improves portability to non-Linux systems, by ensuring that the program doesn't attempt to use the Linux-specific uts.domainname field on those systems.

2013-03-11

264

Two changes in Table 14-1.

In the line for the flag MS_REC, change:

Recursive mount (since Linux 2.6.20)

to:

Recursive mount (since Linux 2.4.11)

In the line for the flag MS_RELATIME, change:

Update last access time only if older than last modification time or last status change time (since Linux 2.4.11)

to:

Update last access time only if older than last modification time or last status change time (since Linux 2.6.20)


Explanation:

The version numbers for these two flags were inadvertently swapped. The main text on page 266 showed the correct version numbers for the flags.

Reported by Damien Grassart.

2013-07-17

266

In the paragraph at the bottom of the page describing MS_REMOUNT, change:

When using this flag, the source and target arguments should be the same as for the original mount() call, and the fstype argument is ignored.

to:

When using this flag, the target argument should be the same as for the original mount() call, and the source and fstype arguments are ignored.


Reported by Junjiro Okajima.

2012-07-04

271

In the second sentence of the first paragraph of Section 14.9.1, change:

From kernel 2.4 onward, a file system can be mounted at multiple locations within the file system.

to:

From kernel 2.4 onward, a file system can be mounted at multiple locations within the single directory hierarchy.


Explanation:

The rewording is more precise, and coherent with Figure 14-4 and the text in Section 14-7.

Reported by Guilherme Maciel Ferreira.

2013-07-21

277

In the third bullet point from the top of the page, change:

The f_fsid field is used on some UNIX implementations to return a unique identifier for the file system–for example, a value based on the identifier of the device on which the file system resides. For most Linux file systems, this field contains 0.

to:

The f_fsid field is used to return a unique identifier for the file system–for example, a value based on the identifier of the device on which the file system resides. On older Linux kernels, many types of file systems return 0 in this field.


Explanation:

A few years ago, f_fsid was unsupported on most types of file systems. I overlooked the fact that over time most Linux file systems have added support for this field.

Reported by Junjiro Okajima.

2012-07-04

292

At the bottom of the page, delete this entire sentence:

(However, if we use the chown(1) command under a root login to change the ownership of a file, then, after calling chown(2), the chown command uses the chmod() system call to reenable the set-user-ID and set-group-ID bits.)


Explanation:

I recall testing this point years ago, but unfortunately I didn't record a log of my test. Junjiro pointed out that the chown command on his system doesn't behave as was described in the deleted sentence, and on my current systems I can't reproduce the behavior either. Furthermore, a web search turns up no description of this point. Thus, either things have changed or my earlier testing was incorrect. At this point, it seems safest to remove that text, but if anyone can confirm the behavior that I thought I saw, please let me know.

Update: Justin Pryzby kindly pointed me at Debian Bug 112597, which shows that the behavior described in the now-deleted text did indeed occur in the past, but was subsequently changed.

Reported by Junjiro Okajima.

2012-07-05

305

In the shell session near the top of the page, change:

    $ chattr +ai myfile             Turn on Append Only and Immutable flags

to:

    $ sudo chattr +ai myfile        Turn on Append Only and Immutable flags

Explanation:

As shown in Table 15-8 and explained on page 306, privilege is required to set the Append Only and Immutable attributes.

Reported by Liu Jiaming.

2013-06-05

317

At the bottom of this page (in Listing 16-1, xattr/xattr_view.c), change:

    if (optind >= argc + 2)
        usageError(argv[0]);

to:

    if (optind >= argc)
        usageError(argv[0]);

Reported by Liu Jiaming.

2013-06-05

358

In the last line of the page, change:

(say 10 or more)

to:

(say 20 or more)


Explanation:

A recommendation of "10 or more" isn't wrong as such. However, one case where we might use nftw() is to traverse the entire file system under the root directory, and commonly the directory tree for that case would be deeper than 10 levels (for example, on one of my personal systems, the file system tree runs to a depth of 19).

Reported by Junjiro Okajima.

2012-07-09

362

The same typo, where a "1" should be an "l", appears twice on this page.

About half way down the page, change:

In the above output, we can see that the symbolic link s1 was resolved.

to:

In the above output, we can see that the symbolic link sl was resolved.

Further down the page, change:

From the above output, we can see that the symbolic link s1 was not resolved.

to:

From the above output, we can see that the symbolic link sl was not resolved.


Reported by Junjiro Okajima.

2013-04-16

367

Just over half way down the page, change:

The classic example of the use of chroot() is in the ftp program. As a security measure, when a user logs in anonymously under FTP, the ftp program uses chroot() to set the root directory for the new process to the directory specifically reserved for anonymous logins.

to:

The classic example of the use of chroot() is in the ftpd program (the FTP server daemon). As a security measure, when a user logs in anonymously under FTP, the ftpd program uses chroot() to set the root directory for the new process to the directory specifically reserved for anonymous logins.


Reported by Junjiro Okajima.

2012-07-09

392

In the third line of the paragraph describing SIGLOST, change:

by the those processes

to:

by those processes


Reported by Simon Durrant.

2012-12-12

441

Near the top of of this table, change the entry in the second column for the row SI_USER from:

A user process via kill() or raise()

to:

A user process via kill()


Explanation:

Once upon a time, raise() did produce an si_code of SI_USER, but modern glibc versions implement raise() raise using tgkill(), which yields an si_code of SI_TKILL. The simplest fix here is to remove mention of raise() in this table. I made some recent changes to the raise() man page to allow programmers to deduce the details.

2012-04-20

447

Remove the fifth bullet point on this page:

interruption of system calls by signal handlers, and how to automatically restart interrupted system calls;


Explanation:

This topic was covered in the previous chapter, in Section 21-5 (and indeed, there's a very similar bullet point on the first page of Chapter 21). At a certain point during some chapter reorganizations, I overlooked this superfluous piece of text at the start of Chapter 22.

Reported by Trevor Woerner.

2012-09-28

447

In the second-to-last bullet point on the page, change:

the use of signalfd() to receive a signal via file descriptor; and

to:

the use of signalfd() to receive a signal via a file descriptor; and


Reported by Murray McAllister.

2012-05-16

457

In the third line of the paragraph starting "SUSv3 requires..." that follows the two bullet points at the top of the page, change:

The <signal.h> header file

to:

The <limits.h> header file


Reported by Trevor Woerner.

2012-09-30

468

In the paragraph immediately following the sigwaitinfo() prototype box, change:

The sigwaitinfo() system call suspends execution of the process until one of the signals in the signal set pointed to by set is delivered. If one of the signals in set is already pending at the time of the call, sigwaitinfo() returns immediately. The delivered signal is removed from the process's list of pending signals, and the signal number is returned as the function result

to:

The sigwaitinfo() system call suspends execution of the process until one of the signals in the signal set pointed to by set becomes pending. If one of the signals in set is already pending at the time of the call, sigwaitinfo() returns immediately. One of the signals is removed from the process's list of pending signals, and the signal number is returned as the function result


Explanation:

The sigwaitinfo() system call does not "deliver" signals. Rather, it removes a signal from the caller's set of pending signals, blocking if necessary until a signal becomes pending. The use of "delivered" in this context was a little sloppy, since it may mislead the reader into thinking of a signal being delivered to a signal handler (and indeed, in normal usage, signals are blocked while calling sigwaitinfo() so that they can't be caught by a signal handler).

Reported by Daniel Zingaro.

2012-07-20

476

In the definition of the sigvec structure shown in the middle of the page, change:

    void (*sv_handler)();

to:

    void (*sv_handler)(int);

Reported by Li Wenjun.

2012-09-06

477

In the second sentence of the second paragraph of Section 22.14, change:

Asynchronous generation occurs when a signal is sent a process by the kernel

to:

Asynchronous generation occurs when a signal is sent to a process by the kernel


Reported by Murray McAllister.

2012-05-16

477

In the second line of the third paragraph from the bottom of the page, change:

suspend execution until a signal arrives, The atomicity of

to:

suspend execution until a signal arrives. The atomicity of

(i.e., change a comma to a period.)


Reported by Murray McAllister.

2012-05-16

478

In the last line of Exercise 22-2, change:

period time

to:

period of time

2012-09-29

484

In Listing 23-1 (timers/real_timer.c) in the line marked with a circled 4, change:

    if (setitimer(ITIMER_REAL, &itv, 0) == -1)

to:

    if (setitimer(ITIMER_REAL, &itv, NULL) == -1)

Explanation:

Strictly speaking, using 0 for the last argument of setitimer() is exactly equivalent to using NULL, since the C standard guarantees that 0 is treated as NULL in locations where a pointer expected. However, using NULL more clearly indicates that this is a pointer, and is what I do in all of the rest of the code in the book.

Reported by Trevor Woerner.

2012-09-30

492

In the second of the two small-font paragraphs in the middle of the page, change the first sentence:

Linux 2.6.32 adds two more new clocks to those listed in Table 23-1: CLOCK_REALTIME_COARSE and CLOCK_MONOTIC_COARSE.

to:

Linux 2.6.32 adds two more new clocks to those listed in Table 23-1: CLOCK_REALTIME_COARSE and CLOCK_MONOTONIC_COARSE.


Reported by Junjiro Okajima.

2012-08-07

502

Remove an unneeded line from the top of Listing 23-6 (timers/itimerspec_from_str.c). Change:

#define _POSIX_C_SOURCE 199309
#include <string.h>
#include <stdlib.h>

to:

#include <string.h>
#include <stdlib.h>

2013-07-11

502

In Listing 23-6 (timers/itimerspec_from_str.c), change:

void
itimerspecFromStr(char *str, struct itimerspec *tsp)
{
    char *cptr, *sptr;

    cptr = strchr(str, ':');
    if (cptr != NULL)
        *cptr = '\0';
    sptr = strchr(str, '/');
    if (sptr != NULL)
        *sptr = '\0';

    tsp->it_value.tv_sec = atoi(str);

to:

void
itimerspecFromStr(char *str, struct itimerspec *tsp)
{
    char *dupstr, *cptr, *sptr;

    dupstr = strdup(str);
 
    cptr = strchr(dupstr, ':');
    if (cptr != NULL)
        *cptr = '\0';
    sptr = strchr(dupstr, '/');
    if (sptr != NULL)
        *sptr = '\0';

    tsp->it_value.tv_sec = atoi(dupstr);

Explanation:

With this change, the itimerspecFromStr() function no longer modifies its string argument. Without this fix, the programs shown in Listing 23-5 (timers/ptmr_sigev_signal.c) and Listing 23-7 (timers/ptmr_sigev_thread.c) will not produce exactly the output shown on pages 503 and 505. In particular, the lines near the top of each of the shell sessions, which show the timer ID and the corresponding command-line argument, will not correctly echo the command-line argument strings. (This error occurred because the text and source code tarballs inadvertently included an older version of the source code.)

Reported by Liu Jiaming.

2013-07-11

503

At the end of Listing 23-6 (timers/itimerspec_from_str.c), change:

        tsp->it_interval.tv_sec = atoi(cptr + 1);
        tsp->it_interval.tv_nsec = (sptr != NULL) ? atoi(sptr + 1) : 0;
    }
}

to:

        tsp->it_interval.tv_sec = atoi(cptr + 1);
        tsp->it_interval.tv_nsec = (sptr != NULL) ? atoi(sptr + 1) : 0;
    }
    free(dupstr);
}

Explanation:

See the explanation for the erratum on page 502.

Reported by Liu Jiaming.

2013-07-11

505

In the shell session in the bottom half of the page change five instances of:

    main(): count =

to:

    main(): expireCnt =

Explanation:

After a late change to the source code, I missed refreshing the shell session log.

Reported by Trevor Woerner.

2012-10-02

513

In the first paragraph of this page, change:

In this and the next three chapters, we look at how a process is created and terminates, and how a process can execute a new program. This chapter covers process creation. However, before diving into that subject, we present a short overview of the main system calls covered in these four chapters.

to:

In this and the next four chapters, we look at how a process is created and terminates, and how a process can execute a new program. This chapter covers process creation. However, before diving into that subject, we present a short overview of the main system calls covered in these chapters.


Explanation:

Late in the writing work, there was a chapter split: Chapters 25 and 26 were once one chapter. During the split, this text at start of the Chapter 24 did not get fixed.

Reported by Trevor Woerner.

2012-10-03

516

In the second sentence of the second paragraph, change:

are initially exact duplicates of the corresponding parts the parent's memory

to:

are initially exact duplicates of the corresponding parts of the parent's memory


Reported by Murray McAllister.

2012-05-19

519

In the first line of the last paragraph on the page, change:

If sharing of file descriptors

to:

If sharing of open file attributes


Reported by Simon Durrant.

2012-12-12

521

In the fourth line from the end of the second bullet point, change:

This new page copy is assigned to the faulting process, and the corresponding page-table entry for the child is adjusted appropriately.

to:

This new page copy is assigned to the faulting process, and the corresponding page-table entry for the other process is adjusted appropriately.


Reported by Trevor Woerner.

2012-10-16

555

Change the second and third sentences of the first paragraph of Section 26.3:

A parent can't predict when one of its child will terminate. (Even if the parent sends a SIGKILL signal to the child, the exact time of termination is still dependent on when the child is next scheduled for use of a CPU.)

to:

A parent can't predict when one of its children will terminate. (Even if the parent sends a SIGKILL signal to a child, the exact time of termination is still dependent on when the child is next scheduled for use of a CPU.)


Reported by Murray McAllister.

2012-05-31

600

In the CLONE_NEWNET row of Table 28-2, change:

(2.4.24 onward)

to:

(2.6.24 onward)


Reported by Renato Westphal.

2012-06-05

604

In the second sentence of the first paragraph under the heading for CLONE_THREAD, change:

If this flag not set

to:

If this flag is not set


Reported by Murray McAllister.

2012-05-31

604

In the first sentence of the second paragraph under the heading for CLONE_THREAD, change:

Threads groups were introduced in Linux 2.4

to:

Thread groups were introduced in Linux 2.4


Reported by Murray McAllister.

2012-05-31

646

At the bottom of the page, add the following small-font note:

Chapter 32, which describes thread cancellation, notes that pthread_cond_wait() is a cancellation point. If a thread is canceled while blocked in a call to pthread_cond_wait(), then the mutex is relocked before the first cancellation cleanup handler is called. This means a cleanup handler can safely unlock the mutex (as is done, for example, in Listing 32-2).


Explanation:

Page 636 notes that it is an error to call pthread_mutex_unlock() on a mutex that is unlocked. Page 646 notes that pthread_cond_wait() unlocks its mutex before blocking. Chia Hao Lo pointed out that lacking any further details, the example in Listing 32-2 seemed to be at odds with those statements: if the thread is canceled while blocked in pthread_cond_wait(), wouldn't the cleanup handler then be unlocking a mutex that had already been unlocked by pthread_cond_wait()? The text added by the erratum resolves this question.

Reported by Chia Hao Lo.

2012-03-07

656

In the second-to-last bullet point on the page, change:

The ecvt(), fcvt(), gcvt(), gethostbyname(), and gethostbyaddr() are removed

to:

The ecvt(), fcvt(), gcvt(), gethostbyname(), and gethostbyaddr() functions are removed


Reported by Murray McAllister.

2012-08-20

664

In the sentence that immediately precedes Listing 31-1, change:

points to the string Invalid operation

to:

points to the string Invalid argument


Reported by Junjiro Okajima.

2012-08-18

677

At the end of the first bullet point on this page (starting "If no command-line argument is supplied"), add the following text:

(When a pthread_cond_wait() call is canceled, the mutex is automatically relocked before the cleanup handler is invoked. This means the mutex can be safely unlocked in the cleanup handler.)


Explanation:

See the erratum for page 646.

Reported by Chia Hao Lo.

2012-03-07

684

In the first sentence of Section 33.2.3, change:

to another thread in the same process

to:

to a thread in the same process as the caller


Explanation:

Writing "another" implies that this function can't be used to send a signal to the calling thread, which is of course untrue (see page 404).

Reported by Junjiro Okajima.

2012-08-18

684

In the last line of the page, change:

to another thread in the same process

to:

to a thread in the same process as the caller


Explanation:

Writing "another" implies that this function can't be used to send a signal to the calling thread, which is untrue.

Reported by Junjiro Okajima.

2012-08-18

687

Beginning in the third line of the third paragraph, change:

Similarly, parent_func and child_func are added to a list functions

to:

Similarly, parent_func and child_func are added to a list of functions


Reported by Murray McAllister.

2012-09-19

687

Under the heading Threads and exit(), change:

If any thread calls exit() or, equivalently, the main thread does a return, all threads immediately vanish

to:

If any thread calls exit() or, equivalently, the main thread does a return, all threads vanish after higher-level cleanups have been performed (e.g., calling C++ destructors)


Explanation:

Stefan demonstrated a case where a C++ destructor was blocked in a call to pthread_join() that was trying to join with another thread that was not terminating. As a consequence, the call to exit() in the main thread blocked indefinitely.

Reported by Stefan Puiu.

2013-04-08

688

In the second line of the third paragraph below the heading One-to-one (1:1) implementations (kernel-level threads), change:

are slower on a 1:1 implementations

to:

are slower on a 1:1 implementation


Reported by Murray McAllister.

2012-09-19

691

In the third bullet point from the bottom of the page, change:

Threads don't share nice value set by setpriority().

to:

Threads don't share the nice value set by setpriority().


Reported by Murray McAllister.

2012-09-19

691

In the second bullet point from the bottom of the page, change:

are not shared between the threads.

to:

are not shared between threads.

2012-09-19

704

In the first line of the second paragraph under the heading Other (obsolete) interfaces…", change:

In the beginning, 4.2BSD provided a getprgp(pid) system call

to:

In the beginning, 4.2BSD provided a getpgrp(pid) system call


Reported by Jordi Sanfeliu.

2013-03-18

739

In the second sentence of the last paragraph on the page, change:

The most notable difference is the existence of strictly distinct priority levels

to:

The most notable difference is that the SCHED_RR policy has strictly distinct priority levels


Explanation:

This is just a minor rewording to make it clearer which parts of the paragraph describe the SCHED_RR policy and which parts describe the SCHED_OTHER policy.

Reported by Junjiro Okajima.

2012-08-18

743

Near the beginning of Listing 35-2 (procpri/sched_set.c) change:

    if (argc < 3 || strchr("rfo", argv[1][0]) == NULL)

to:

    if (argc < 3 || strchr("rfobi", argv[1][0]) == NULL)

Explanation:

This is an imperfect fix for the problem that the original version of the program did not accept the 'b' and 'i' values for the policy argument. The change given above allows these values to be accepted, but, on systems that don't define SCHED_BATCH or SCHED_IDLE, the code further on in the program will set the scheduling policy to SCHED_OTHER. A better fix can be found in the distribution version of the program; however there isn't sufficient space on the page to make this change in the version of the program shown in the book.

Reported by Brian Blumberg.

2013-03-09

752

At the end of Exercise 35-3, add the following sentence:

If your system has multiple CPUs, then, in order to demonstrate the behavior described in this exercise, you will need to confine all processes to a single CPU. This can be done by calling sched_setaffinity() before creating the child process or from the command line by using the taskset command.


Reported by Trevor Woerner.

2012-11-23

778

In the fourth line of the paragraph describing LOG_NDELAY at the top of the page, change:

O_NDELAY flag is useful

to:

LOG_NDELAY flag is useful


Reported by Greg Drysdale.

2012-09-25

782

In the third paragraph of Section 37.6, change:

according to the dictates of the syslogd.conf configuration file

to:

according to the dictates of the syslog.conf configuration file


Reported by Junjiro Okajima.

2012-08-21

785

In the last sentence of the paragraph immediately below the heading Drop privileges permanently when they will never again be required, change:

Dropping privileges permanently is accomplished by resetting all process user (group) IDs to the same value as the real (group) ID.

to:

Dropping privileges permanently is accomplished by resetting all process user (group) IDs to the same value as the real user (group) ID.


Reported by Junjiro Okajima.

2012-08-22

799

In the first line of the first small-font note, change:

The getpcap program

to:

The getpcaps program


Reported by Trevor Woerner.

2012-11-29

801

In Table 39-1, in the entry for CAP_SYS_ADMIN, change:

employ CLONE_NEWNS flag with clone() and unshare();

to:

employ namespace-creation flags with clone() and unshare();


Explanation:

CLONE_NEWNS is just one of several namespace-creation flags whose operation is governed by CAP_SYS_ADMIN. (The others are CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWPID, and CLONE_NEWUTS.)

2012-03-03

802

In the last line of the small-font note near the top of the page, change:

setting both the effective user ID and the saved set-user ID to nonzero values

to:

setting both the effective user ID and the saved set-user-ID to nonzero values


Reported by Junjiro Okajima.

2012-08-23

819

In the second paragraph from the bottom of the page, change:

(the last two are for System-V and BSD-style pseudoterminals, respectively)

to:

(the last two are for System V-style and BSD-style pseudoterminals, respectively)


Reported by Junjiro Okajima.

2012-08-23

873

In the second sentence of the paragraph immediately following the heading The _init() and _fini() functions, change:

The void _init(void) function contains code that is to executed

to:

The void _init(void) function contains code that is to be executed


Reported by Murray McAllister.

2012-11-28

892

At the start of the third paragraph following the prototype box for pipe(), change:

We can also use the stdio functions (printf(), scanf(), and so on)

to:

We can also use the stdio functions (fprintf(), fscanf(), and so on)


Reported by Simon Durrant.

2013-02-12

918

In Table 44-3, in the table cell that beginning "Write n bytes, change:

may block until sufficient data read

to:

may block until sufficient data is read


Reported by Murray McAllister.

2012-12-09

924

In the fourth line of the paragraph half way down the page that begins "For message queues and semaphores", change:

operation of System IPC objects

to:

operation of System V IPC objects


Reported by Junjiro Okajima.

2012-08-25

925

At the end of the second bullet point, change:

a new IPC object that is guaranteed to have a unique key.

to:

a new IPC object that is guaranteed to have a unique identifier.


Reported by Reza Mostafid.

2012-10-29

925

Change the subheading in the middle of the page:

Generating a unique key with IPC_PRIVATE

to:

Generating a unique identifier with IPC_PRIVATE

2012-10-29

936

In the fourth line of the second paragraph of Section 45.9, change:

Each IPC mechanism also has a corresponding a ctl call

to:

Each IPC mechanism also has a corresponding ctl call


Reported by Murray McAllister.

2012-12-09

936

In the second line of the paragraph half way down the page that begins "The ipcs command lists", change:

remove System IPC objects

to:

remove System V IPC objects


Reported by Junjiro Okajima.

2012-08-25

940

In the last sentence of the second paragraph of Section 46.2 (beginning "This definition is really"), change:

Thus, the mgsp argument

to:

Thus, the msgp argument


Reported by Junjiro Okajima.

2012-08-26

949

In the list item under msg_qbytes, change:

INT_MAX (2,147,483,647 on 32-bit platforms) bytes

to:

INT_MAX (2,147,483,647) bytes


Explanation:

INT_MAX is of course 2,147,483,647 on most platforms, including most 64-bit platforms.

Reported by Junjiro Okajima.

2012-08-26

957

In the second line of the last item of the bulleted list, change:

A request with an mtype

to:

A response with an mtype


Reported by Junjiro Okajima.

2012-08-26

979

In the third bullet point on the page change:

If sem_op is less than 0, decrease the value of the semaphore by the amount specified in sem_op.

to:

If sem_op is less than 0, decrease the value of the semaphore by the absolute value of sem_op.


Explanation:

The meaning of the original text was probably clear, but the language was a little imprecise; the rewording removes any potential for confusion.

Reported by Junjiro Okajima.

2012-08-26

993

In the fifth bullet point in Section 47.11, change:

The programming interface provided by System V is overly complex.

to:

The programming interface provided by System V semaphores is overly complex.


Reported by Junjiro Okajima.

2012-08-26

1021

In the second line below the heading Memory protection in more detail, change:

or a mask of one of more

to:

or a mask of one or more


Reported by Murray McAllister.

2012-12-28

1022

At the end of the paragraph that follows the heading Alignment restrictions… at the top of the page, change:

SUSv4 returns to the looser requirement:

to:

SUSv4 returns to the looser requirements:


Reported by Murray McAllister.

2012-12-28

1040

Starting in the second line from the top of the page, change:

having a low nice value (i.e., one that is greater than 0)

to:

having a low priority (i.e., a nice value that is greater than 0)


Explanation:

This change is consistent with errata made on pages 734 and 736, and made for a similar reason.

Reported by Junjiro Okajima.

2012-09-05

1054

In the first line of Section 50.4, change:

The madvise() system call is used is to

to:

The madvise() system call is used to


Reported by Murray McAllister.

2012-12-28

1058

In the third line from the bottom of the page, change:

followed by one of more nonslash characters

to:

followed by one or more nonslash characters


Reported by Junjiro Okajima.

2012-09-05

1061

Change the one-sentence paragraph that precedes Section 51.2:

On Linux, programs employing the POSIX IPC mechanisms must be linked with the realtime library, librt, by specifying the –lrt option to the cc command.

to:

On Linux, programs employing POSIX message queues and shared memory must be linked with the realtime library, librt, using the cc –lrt option. Programs employing POSIX semaphores must be compiled using the cc –pthread option.


Explanation:

At the time of publication, cc –lrt was sufficient for all three POSIX IPC mechanisms, and for POSIX semaphores cc –pthread could alternatively be used. Some recent toolchain changes mean that now only cc –pthread can be used for POSIX semaphores.

2012-05-11

1066

In the paragraph just above the heading Removing a message queue, change:

As close() for files,

to:

As with close() for files,


Reported by Murray McAllister.

2012-12-28

1068

In the second line of the second bullet point of Section 52.4, change:

associated with a message descriptor

to:

associated with a message queue descriptor


Reported by Junjiro Okajima.

2012-09-05

1072

In the last line of this page, change:

if (mq_setattr(mqd, &attr, NULL) == -1)
    errExit("mq_getattr");

to:

if (mq_setattr(mqd, &attr, NULL) == -1)
    errExit("mq_setattr");

Reported by Kanak Kshetri.

2013-03-23

1079

In the structure definition at the top of the page, change:

    void *sigev_notify_attributes;   /* Really 'pthread_attr_t' */

to:

    void *sigev_notify_attributes;   /* Really 'pthread_attr_t *' */

Reported by Junjiro Okajima.

2012-09-05

1084

In a line of the shell session not quite half way down the page, change:

    $ exit

to:

    # exit

Reported by Junjiro Okajima.

2012-09-05

1090

In the second bullet point in Section 53.2, change:

The sem_post(sem) and sem_wait(sem) functions

to:

The sem_post() and sem_wait() functions


Reported by Junjiro Okajima.

2012-09-05

1091

In the third paragraph from the top of the page (beginning "If sem_open()"), change:

However, if O_CREAT is specified in flags,

to:

However, if O_CREAT is specified in oflag,


Reported by Junjiro Okajima.

2012-09-05

1099

In the second bullet point in Section 53.4, change:

The sem_destroy(sem) function

to:

The sem_destroy() function


Reported by Junjiro Okajima.

2012-09-05

1103

In the last sentence of the second paragraph, change:

If the semaphore resides in a POSIX shared memory region, then it should be destroyed after all processes have ceased using the semaphore and before the shared memory object is unlinked with shm_unlink().

to:

If the semaphore resides in a POSIX shared memory region, then it should be destroyed after all processes have ceased using the semaphore and before the last process unmaps the region.


Reported by Simon Durrant.

2013-02-12

1105

In the list item at the top of the page under SEM_VALUE_MAX, change:

INT_MAX (2,147,483,647 on Linux x86-32).

to:

INT_MAX (2,147,483,647).


Explanation:

INT_MAX is of course 2,147,483,647 on most platforms, including most 64-bit platforms.

2012-08-26

1108

In the first sentence on the page, change:

POSIX shared memory allows to us to share

to:

POSIX shared memory allows us to share


Reported by Murray McAllister.

2012-12-28

1124

In the code snippet about three quarters of the way down the page change:

    fcntl(fd, cmd, &flockstr);          /* Place lock defined by 'fl' */

to:

    fcntl(fd, cmd, &flockstr);          /* Place lock defined by 'flockstr' */

Reported by Junjiro Okajima.

2012-09-05

1133

In the last line of the first bullet point in Section 55.3.3, change:

flockstr structure that is used to place the lock

to:

flock structure that is used to place the lock


Reported by Junjiro Okajima.

2012-09-05

1170

In the first line of a paragraph about three quarters of the way down the page, change:

At this point, the child has completed.

to:

At this point, the client has completed.


Reported by Junjiro Okajima.

2012-09-05

1194

In the bullet point starting "Congestion Avoidance and Control", change:

Jacobsen

to:

Jacobson


Reported by Junjiro Okajima.

2012-09-20

1203

In the third line of the small-font note in the middle of the page, change:

or four 32-byte

to:

or four 4-byte


Reported by Junjiro Okajima.

2012-09-07

1214

In the definition of the addrinfo structure, change:

    size_t ai_addrlen;          /* Size of structure pointed to by ai_addr */

to:

    socklen_t ai_addrlen;       /* Size of structure pointed to by ai_addr */

2012-04-15

1214

In the eighth line from the bottom of the page, change:

an in_addr structure for IPv4 or an in6_addr structure for IPv6

to:

a sockaddr_in structure for IPv4 or a sockaddr_in6 structure for IPv6


Reported by Matthias Rahlf.

2012-09-03

1240

In the last sentence of the second paragraph of Section 60.2, change:

The header file for the server is shown in Listing 60-1.

to:

The header file for the server (and the client that we discuss in a moment) is shown in Listing 60-1.


Reported by Junjiro Okajima.

2012-09-07

1241

In Listing 60-2 (sockets/id_echo_sv.c), change:

int
main(int argc, char *argv[])
{
    int sfd;
    ssize_t numRead;
    socklen_t addrlen, len;
    struct sockaddr_storage claddr;
    char buf[BUF_SIZE];
    char addrStr[IS_ADDR_STR_LEN];

    if (becomeDaemon(0) == -1)
        errExit("becomeDaemon");

    sfd = inetBind(SERVICE, SOCK_DGRAM, &addrlen);

to:

int
main(int argc, char *argv[])
{
    int sfd;
    ssize_t numRead;
    socklen_t len;
    struct sockaddr_storage claddr;
    char buf[BUF_SIZE];
    char addrStr[IS_ADDR_STR_LEN];

    if (becomeDaemon(0) == -1)
        errExit("becomeDaemon");

    sfd = inetBind(SERVICE, SOCK_DGRAM, NULL);

Explanation:

As explained on page 1226, the final argument to inetBind() can (if not NULL) be used to return the size of the address structure required for the socket that is created by this call. However, this program doesn't need that information, since it makes use of a sockaddr_storage structure that is allocated on the stack. Thus, the third argument of the call can be specified as NULL, and an unnecessary variable can be eliminated.

Reported by Steve Griffis.

2013-02-11

1267

In the list entry a little more than half way down the page describing the PSH flag, change:

This flag is described in RFC 993.

to:

This flag is described in RFC 793.


Reported by Gregory Potamianos.

2012-11-13

1284

In the second-to-last line on the page, change:

Unlike passing file credentials

to:

Unlike passing file descriptors


Reported by Junjiro Okajima.

2012-09-07

1319

In the caption for Table 62-5, change:

Values for the tcflush() action argument

to:

Values for the tcflow() action argument


Reported by Junjiro Okajima.

2012-09-08

1326

In the second line of the first normal-size paragraph at the top of the page, change:

In particular, some applications need to able to

to:

In particular, some applications need to be able to


Reported by Murray McAllister.

2012-12-30

1340

In Listing 63-2 (altio/poll_pipes.c), change:

    pfds = calloc(numPipes, sizeof(int [2]));
    if (pfds == NULL)
        errExit("malloc");
    pollFd = calloc(numPipes, sizeof(struct pollfd));
    if (pollFd == NULL)
        errExit("malloc");

to:

    pfds = calloc(numPipes, sizeof(int [2]));
    if (pfds == NULL)
        errExit("calloc");
    pollFd = calloc(numPipes, sizeof(struct pollfd));
    if (pollFd == NULL)
        errExit("calloc");

2013-01-02

1342

In the first line of the last paragraph on the page, change:

Table 63-5 summarizes the details for the write end of a pipe.

to:

Table 63-5 summarizes the details for the write end of a pipe or FIFO.


Reported by Junjiro Okajima.

2012-09-08

1363

Toward the end of Listing 63-5 (altio/epoll_input.c), change:

            } else if (evlist[j].events & (EPOLLHUP | EPOLLERR)) {

                /* If EPOLLIN and EPOLLHUP were both set, then there might
                   be more than MAX_BUF bytes to read. Therefore, we close
                   the file descriptor only if EPOLLIN was not set.
                   We'll read further bytes after the next epoll_wait(). */

                printf("    closing fd %d\n", evlist[j].data.fd);
                if (close(evlist[j].data.fd) == -1)
                    errExit("close");
                numOpenFds--;
            }

to:

            } else if (evlist[j].events & (EPOLLHUP | EPOLLERR)) {

                /* After the epoll_wait(), EPOLLIN and EPOLLHUP may both have
                   been set. But we'll only get here, and thus close the file
                   descriptor, if EPOLLIN was not set. This ensures that all
                   outstanding input (possibly more than MAX_BUF bytes) is
                   consumed (by further loop iterations) before the file
                   descriptor is closed. */

                printf("    closing fd %d\n", evlist[j].data.fd);
                if (close(evlist[j].data.fd) == -1)
                    errExit("close");
                numOpenFds--;
            }

Explanation:

The original comment text was correct, but perhaps a little harder to grasp than the alternative that replaces it.

In print run 7, a small white space fix was additionally applied to this erratum, because the first line of comment text ("After the…") was not quite correctly aligned.

Reported by Junjiro Okajima.

2012-09-10

1369

In the code segment in the middle of the page that explains the operation of pselect(), change:

    sigprocmask(SIG_SETMASK, &sigmask, &origmask);
    ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
    sigprocmask(SIG_SETMASK, &origmask, NULL);        /* Restore signal mask */

to:

    pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
    ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
    pthread_sigmask(SIG_SETMASK, &origmask, NULL);    /* Restore signal mask */

Explanation:

The original text considered pselect() from the perspective of a single-threaded process However, it is more precise to explain the call in terms of a multithreaded process. (since there is no restriction against calling pselect() from a multithreaded process). (See also page 684.)

Reported by David Majnemer.

2012-08-18

1428

In the fourth line of the solution to Exercise 18-1, change:

It is permissible to unlink an executable file.

to:

It is permissible to unlink an executable file that is running.


Reported by Junjiro Okajima.

2012-09-10

1442

Under the bibliography entry for "Mochel, P.", change:

Mochel, P. "The sysfs Filesystem," Proceedings of the Ottawa Linux Symposium 2002.

to:

Mochel, P. 2005. "The sysfs Filesystem," Proceedings of the Linux Symposium 2005.


Reported by Junjiro Okajima.

2012-07-04

1470

Change the index entry:

Jacobsen, V., 1194

to:

Jacobson, V., 1194

2012-09-20

Fixes and improvements in the third printing

The following fixes and improvements were applied in the third and later printings of the book.

Page Fix Reported
11

In the second paragraph, change:

which was subsequently adopted in 1990 as an International Standards Organization (ISO) standard

to:

which was subsequently adopted in 1990 as an International Organization for Standardization (ISO) standard


Explanation:

Although often referred to as the International Standards Organization, the correct title is International Organization for Standardization.

2011-08-04

38

In the last sentence of the first paragraph in Section 2.12, change:

However, each thread has it own stack…

to:

However, each thread has its own stack…


Reported by Jaewook Yu.

2011-04-09

73

Near the end of Listing 4-2, change:

    fd = open("w.log", O_WRONLY | O_CREAT | O_TRUNC | O_APPEND,
                       S_IRUSR | S_IWUSR);

to:

    fd = open("w.log", O_WRONLY | O_CREAT | O_APPEND,
                       S_IRUSR | S_IWUSR);

Explanation:

The code as originally given was technically correct, but the presence of O_TRUNC was superfluous for the purpose of the example, and its semantics were not explained in the accompanying comment.

Reported by Jessica T McKellar.

2011-05-26

74

In Table 4-3, move two rows to a different part of the table:

Flag Purpose SUS?
O_RDONLY Open for reading only v3
O_WRONLY Open for writing only v3
O_RDWR Open for reading and writing v3
O_CLOEXEC Set the close-on-exec flag (since Linux 2.6.23) v4
O_CREAT Create file if it doesn't already exist v3
O_DIRECT File I/O bypasses buffer cache  
O_DIRECTORY Fail if pathname is not a directory v4
O_EXCL With O_CREAT: create file exclusively v3
O_LARGEFILE Used on 32-bit systems to open large files  
O_NOATIME Don't update file last access time on read() (since Linux 2.6.8)  
O_NOCTTY Don't let pathname become the controlling terminal v3
O_NOFOLLOW Don't dereference symbolic links v4
O_TRUNC Truncate existing file to zero length v3
O_APPEND Writes are always appended to end of file v3
O_ASYNC Generate a signal when I/O is possible  
O_DSYNC Provide synchronized I/O data integrity (since Linux 2.6.33) v3
O_NONBLOCK Open in nonblocking mode v3
O_SYNC Make file writes synchronous v3

to give the following:

Flag Purpose SUS?
O_RDONLY Open for reading only v3
O_WRONLY Open for writing only v3
O_RDWR Open for reading and writing v3
O_CLOEXEC Set the close-on-exec flag (since Linux 2.6.23) v4
O_CREAT Create file if it doesn't already exist v3
O_DIRECTORY Fail if pathname is not a directory v4
O_EXCL With O_CREAT: create file exclusively v3
O_LARGEFILE Used on 32-bit systems to open large files  
O_NOCTTY Don't let pathname become the controlling terminal v3
O_NOFOLLOW Don't dereference symbolic links v4
O_TRUNC Truncate existing file to zero length v3
O_APPEND Writes are always appended to end of file v3
O_ASYNC Generate a signal when I/O is possible  
O_DIRECT File I/O bypasses buffer cache  
O_DSYNC Provide synchronized I/O data integrity (since Linux 2.6.33) v3
O_NOATIME Don't update file last access time on read() (since Linux 2.6.8)  
O_NONBLOCK Open in nonblocking mode v3
O_SYNC Make file writes synchronous v3

Explanation:

As noted on page 93, O_DIRECT and O_NOATIME are open file status flags that can be retrieved and modified using fcntl(). These flags were accidentally misplaced in the file creation flags section of Table 4-3.

Reported by Madhavan Kasthurirangan.

2011-09-04

93

In the prototype box for fcntl() at the top of the page, change:

Return on success depends on cmd, or –1 on error

to:

Return value on success depends on cmd; returns –1 on error


Explanation:

A minor wording improvement.

2011-06-16

97

About one third of the way down the page (in the paragraph starting "Assuming the normal situation"), change:

dup() will create the duplicate of descriptor 1 using file 3.

to:

dup() will create the duplicate of descriptor 1 using descriptor 3.


Reported by Simon Durrant.

2011-08-07

102

At the end of the paragraph second from the bottom of the page, add a sentence:

The preadv() and pwritev() system calls perform the same task as readv() and writev(), but perform the I/O at the file location specified by offset (like pread() and pwrite()). These system calls don't change the file offset.


Explanation:

The reader may have been able to draw this implication from the text (since it is explained that these system calls are like readv() and writev()), but it's better to make the point explicit.

Reported by Sun Jian.

2011-06-15

104

In the small-font at the top of the page, change:

The main difference was that a nonblocking write() on System V returned 0 if a write() could not be completed or if no input was available to satisfy a read().

to:

The main difference was that a nonblocking write() on System V returned 0 if a write() could not be completed and a nonblocking read() returned 0 if no input was available.


Reported by Sun Jian.

2011-06-15

104

In the first line of the small-font note toward the bottom of the page, change:

(e.g., Alpha, IA-64)

to:

(e.g., x86-64, Alpha, and IA-64)


Explanation:

It was an oversight to omit the most common 64-bit architecture here.

2011-12-11

110

In the first line of Exercise 5-1, change:

Modify the program in Listing 5-3

to:

If you have access to a 32-bit Linux system, modify the program in Listing 5-3


Explanation:

From Section 5.10, the reader can deduce that this exercise applies only to 32-bit systems. However, it is of course better to make that assumption explicit.

Reported by Sandipan Razzaque.

2011-12-11

136

In the last paragraph on this page, change:

When we compile the program in Listing 6-6 normally, we see the expected output:

to:

When we compile the program in Listing 6-6 without optimization, we see the expected output:


Explanation:

By default (i.e., "normally"), gcc compiles without optimization. This change makes that point more explicit.

Reported by Bill McConnaughey.

2011-06-28

141

In the paragraph below the prototype box for malloc(), change:

aligned on a byte boundary suitable for any type of C data structure.

to:

aligned on a byte boundary suitable for efficient access to any type of C data structure.


Explanation:

This change makes the intended idea behind the explanation clearer.

Reported by Sun Jian.

2011-04-07

142

Add a line at the top of Listing 7-1 (memalloc/free_and_sbrk.c):

#define _BSD_SOURCE
#include "tlpi_hdr.h"

#define MAX_ALLOCS 1000000

Explanation:

As noted on page 140, a feature test macro definition is needed for sbrk(). Without it, gcc complains ("implicit declaration of function 'sbrk'") when invoked with (for example) -std=c99.

I missed this problem because of the combination of two reasons: (1) the feature test macro requirements for sbrk() changed in the version of glibc (2.12) that was released not long before the book went to press (see the man page for details), and (2) there was a breakage in my main Makefile (now fixed in the latest version of the source code tarball).

Reported by Lei Yang.

2011-07-05

148

In the first line of the example code near the top of this page, change:

    struct { /* Some field definitions */ } myStruct;

to:

    struct myStruct { /* Some field definitions */ };

Reported by Sangman Lee.

2011-04-13

154

In the second bullet point, change:

In this case, the password field in /etc/passwd conventionally contains the letter x (although any nonempty character string may appear), and the encrypted password is instead stored in the shadow password file (Section 8.2).

to:

In this case, the password field in /etc/passwd contains the letter x, and the encrypted password is instead stored in the shadow password file (Section 8.2).

2012-02-08

166

In Exercise 8-1, completely replace the text:

8-1. When we execute the following code, we find that it displays the same number twice, even though the two users have different IDs in the password file. Why is this?

    printf("%ld %ld\n", (long) (getpwnam("avr")->pw_uid),
                        (long) (getpwnam("tsr")->pw_uid));

with the following text:

8-1. When we execute the following code, which attempts to display the usernames for two different user IDs, we find that it displays the same username twice. Why is this?

    printf("%s %s\n", getpwuid(uid1)->pw_name,
                      getpwuid(uid2)->pw_name);

Explanation:

The exercise was intended to demonstrate the nonreentrant nature of getpwnam() (and related functions, such as getpwuid()), but the experiment I proposed to do so was wrong. (And unfortunately it looks as though I didn't test this exact example.) As René Thomsen pointed out, the code of the original exercise wouldn't yield the results described in the text "since after each argument a copy of the value in the passwd structure for pw_uid is passed, and since this a direct value (not a reference) there should be no trouble [with] the next call overwriting this value, since it still has the copy."

The revised exercise correctly shows the point I wanted to make. A complete program that can be used to demonstrate the point is shown below.

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <pwd.h>

int
main(int argc, char *argv[])
{
    if (argc != 3 || strcmp(argv[1], "--help") == 0) {
        fprintf(stderr, "Usage: %s uid1 uid2\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    printf("%s %s\n", getpwuid(atoi(argv[1]))->pw_name,
                      getpwuid(atoi(argv[2]))->pw_name);

    exit(EXIT_SUCCESS);
}

Reported by René Thomsen.

2011-07-14

179

About two thirds of the way down the page, change:

Alternatively, an application can make a call to getgroups() specifying gidtsetsize as 0.

to:

Alternatively, an application can make a call to getgroups() specifying gidsetsize as 0.


Reported by Simon Durrant.

2011-08-12

180

In the fifth line from the top of the page, change:

IDs by scanning /etc/groups

to:

IDs by scanning /etc/group


Reported by Yang Yang.

2011-11-30

186

At the end of the last paragraph on this page, add a sentence:

This argument is now obsolete and should always be specified as NULL. (SUSv4 marks gettimeofday() obsolete, presumably in favor of the POSIX clocks API described in Section 23.5.)


Explanation:

The fact that SUSv4 marks gettimeofday() obsolescent was noted in page 16, but it should also be mentioned here.

2011-08-12

187

At the end of the page, add a small-font note as follows:

SUSv4 marks ctime() and asctime() obsolete, because they do not return localized strings (and they are nonreentrant).


Explanation:

This point was noted on page 16, but it bears repeating here, where these functions are described in detail.

2011-08-12

217

Starting in the sixth line of the small-font note about one third of the way down the page, change:

which is the per-user limit on the number of processes that may created by this process

to:

which is the per-user limit on the number of processes that may be created by this process


Reported by Yongzhi Pan.

2011-09-20

241

In the fifth line from the top of the page, change:

synchronized I/O data completion

to:

synchronized I/O data integrity completion


Reported by Junjiro Okajima.

2012-01-16

243

Two changes in the third paragraph.

In the fourth line, change:

synchronized I/O data integrity (i.e., prior to performing the read…

to:

synchronized I/O data integrity completion (i.e., prior to performing the read…

In the seventh and eighth lines, change:

synchronized I/O file integrity (i.e., prior to performing the read…

to:

synchronized I/O file integrity completion (i.e., prior to performing the read…


Reported by Junjiro Okajima.

2012-01-16

245

In the last sentence of the paragraph explaining POSIX_FADV_SEQUENTIAL, change:

to the twice the default size

to:

to twice the default size


Reported by Simon Durrant.

2011-08-23

252

In the second bullet point half-way down the page, change:

Examples of block devices include disks and tape drives.

to:

Disks are a common example of block devices.


Explanation:

Tapes are normally character devices.

Reported by Junjiro Okajima.

2012-01-16

258

In the "Key" box in the upper right hand corner of Figure 14-2, change:

2IPB = Double IBP

to:

2IPB = Double IPB


Reported by Yongzhi Pan.

2011-09-20

265

In the paragraph near the top of the page describing MS_BIND, change:

If this flag is specified, then the fstype, mountflags, and data arguments are ignored.

to:

If this flag is specified, then the fstype and data arguments are ignored, as are flags in mountflags other than MS_REC (described below).

2011-04-02

265

In the paragraph in the middle of the page describing MS_MOVE, change the last sentence:

When this flag is specified, the fstype, mountflags, and data arguments are ignored.

to:

When this flag is specified, the fstype and data arguments are ignored, as are the remaining flags in mountflags.


Reported by Sangman Lee.

2011-04-02

273

In the sentence immediately preceding Section 14.9.5, change:

we can simply create bind mounts for these directories (possibly mounted read-only) within the jail

to:

we can simply create bind mounts for these directories within the jail


Explanation:

It isn't possible to make read-only bind mounts to read-write directories. As noted on page 265, mountflags other than MS_REC are ignored when MS_BIND is specified.

Reported by Junjiro Okajima.

2012-01-16

278

In the third line from the bottom of the page, change:

(x000001, x000001, x0000002, and so on)

to:

(x000000, x000001, x0000002, and so on)


Reported by Junjiro Okajima.

2012-01-16

280

In the third line of the small-font note at the bottom of the page, change:

None of other fields

to:

None of the other fields


Reported by Simon Durrant.

2011-08-23

280

At the end of the small-font note at the bottom of the page (after the sentence beginning "On Linux, lstat() returns…"), add the following sentence:

SUSv4 tightens the requirements on an implementation, requiring lstat() to return valid information in all fields of the stat structure except the permission bits of st_mode.

2011-09-30

286

In Table 15-2, in the entry for truncate(), change:

Same for ftruncate(); timestamps change only if file size changes

to:

Same for ftruncate()


Explanation:

The original text did not completely match Linux behavior, but the story here is complicated. On Linux, ftruncate() always changes the file timestamps, even if the file size is not changed. However, Linux truncate() only changes the timestamps if the file size changes. This more or less mirrors the specifications in SUSv3. SUSv3 makes no mention of file size changes when specifying that ftruncate() changes the file timestamps. By contrast, SUSv3 says for truncate() that if the file size is changed, the file timestamps are updated, implying that if the file size is unchanged, the timestamps should not be changed. A planned fix to SUSv4 would require that truncate() behave like ftruncate().

2012-02-14

287

In the sentence immediately preceding heading 15.2.1, change:

can be accessed using field names such st_atim.tv_nsec

to:

can be accessed using field names such as st_atim.tv_nsec


Reported by Yongzhi Pan.

2011-09-20

290

In the prototype box for futimens() at the bottom of the page change:

#include _GNU_SOURCE

to:

#define _GNU_SOURCE

Reported by Junjiro Okajima.

2012-01-16

297

Add some words to the last sentence in the small-font note at the top of the page:

To ensure that we are using an unadulterated ls, we can specify the full pathname of the command (/bin/ls) or precede the ls command with a backslash to prevent alias substitution.


Reported by Junjiro Okajima.

2012-01-16

306

In the first line on the page, change:

The various FL_* flags and their meanings are as follows:

to:

The various FS_* flags and their meanings are as follows:


Reported by Douglas Luu.

2011-11-12

313

In the first sentence of the paragraph that precedes the bulleted list in Section 16.2, change:

It is only possible to place user EAs on files and directories.

to:

It is only possible to place user EAs on regular files and directories.


Explanation:

From the bulleted list, the reader can deduce that this sentence is referring to regular files, but it's better to make that explicit.

Reported by Junjiro Okajima.

2012-01-16

331

In the second paragraph under the heading Retrieving entries from an in-memory ACL, change the last sentence:

Thus, we can loop through all of the entries in an ACL by specifying type as ACL_FIRST_ENTRY in the first call to acl_get_entry() and specifying type as ACL_NEXT_ENTRY in subsequent calls.

to:

Thus, we can loop through all of the entries in an ACL by specifying entry_id as ACL_FIRST_ENTRY in the first call to acl_get_entry() and specifying entry_id as ACL_NEXT_ENTRY in subsequent calls.


Reported by Yongzhi Pan.

2011-09-20

334

In the first sentence of the small-font note near the top of the page, change:

The acl_check() and acl_error() functions (the latter is a Linux extension)

to:

The acl_check() and acl_error() functions (both are Linux extensions)


Reported by Junjiro Okajima.

2012-01-16

336

About one third of the way down the page, change:

        /* Retrieve and display optional tag qualifier */

        if (tag == ACL_USER) {
            uidp = acl_get_qualifier(entry);
            if (uidp == NULL)
                errExit("acl_get_qualifier");

            name = groupNameFromId(*uidp);

to:

        /* Retrieve and display optional tag qualifier */

        if (tag == ACL_USER) {
            uidp = acl_get_qualifier(entry);
            if (uidp == NULL)
                errExit("acl_get_qualifier");

            name = userNameFromId(*uidp);

Reported by René Thomsen.

2011-08-11

381

In the third line of the paragraph near the top of the page that starts "The cookie field…", change:

and then an IN_MOVED_TO is

to:

and then an IN_MOVED_TO event is

2011-12-08

381

Add a sentence at the end of the paragraph near the top of the page that starts "The cookie field…":

These two events will have the same unique value in their cookie field, thus allowing the application to associate them. For all other types of event, the cookie field is set to 0.


Explanation:

This point was made implicitly in the program in Listing 19-1 (where the displayInotifyEvent() function contains the check if (i->cookie > 0)), but should of course have been made explicit in the text.

Reported by Yang Yang.

2011-12-06

381

In the third paragraph from the bottom of the page (starting "Using a larger buffer size"), change:

A read() from an inotify file descriptor returns the minimum of the number of events that are available and the number of events that will fit in the supplied buffer.

to:

A read() from an inotify file descriptor reads the minimum of the number of events that are available and the number of events that will fit in the supplied buffer.


Explanation:

This change is made to prevent the possible misinterpretation that the return value of read() is the number of items read. As shown in Figure 19-2, a read() from an inotify file descriptor returns the number of bytes read (as with a read() from other types of file descriptors).

Reported by Junjiro Okajima.

2012-01-16

392

In the third line of the paragraph describing SIGLOST, change:

if the NSF client fails

to:

if the NFS client fails


Reported by Suse Shi.

2011-04-22

404

In the paragraph just above the heading for Section 20-7, change:

This program takes two command-line arguments, a signal number and a process ID,

to:

This program takes two command-line arguments, a process ID and a signal number,


Explanation:

This makes the order of the arguments given in the text match the order of the arguments for Listing 20-3.

2011-05-18

405

In Listing 20-3 (signals/t_kill.c), change:

    if (argc != 3 || strcmp(argv[1], "--help") == 0)
        usageErr("%s sig-num pid\n", argv[0]);

to:

    if (argc != 3 || strcmp(argv[1], "--help") == 0)
        usageErr("%s pid sig-num\n", argv[0]);

Explanation:

See also the erratum on page 404.

Reported by Madhavan Kasthurirangan.

2011-05-18

407

In the first sentence below the prototype box for sigemptyset() and sigfillset(), change:

One of sigemptyset() or sigaddset() must be used to initialize a signal set.

to:

One of sigemptyset() or sigfillset() must be used to initialize a signal set.


Reported by Bill McConnaughey.

2011-07-18

408

In the last line of the prototype box at the top of the page, change:

Returns 1 if sig is empty, otherwise 0

to:

Returns 1 if set is empty, otherwise 0


Reported by Junjiro Okajima.

2012-01-16

428

In the first paragraph of Section 21.1.3, in the third line from end of the paragraph, change:

using the volatile attribute

to:

using the volatile keyword


Explanation:

Tightening up the terminology, so as to avoid possible confusion with the gcc attribute feature.

Reported by Yang Yang.

2011-12-09

457

In the second line of the last paragraph on this page, change:

that may be queued to a process

to:

that may be queued by a process

2011-12-12

458

Replace the first two paragraphs on this page:

On Linux, this call returns –1. The reason for this is that Linux employs a different model for limiting the number of realtime signals that may be queued to a process. In Linux versions up to and including 2.6.7, the kernel enforces a system-wide limit on the total number of realtime signals that may be queued to all processes. This limit can be viewed and (with privilege) changed via the Linux-specific /proc/sys/kernel/rtsig-max file. The default value in this file is 1024. The number of currently queued realtime signals can be found in the Linux-specific /proc/sys/kernel/rtsig-nr file.

Starting with Linux 2.6.8, this model was changed, and the aforementioned /proc interfaces were removed. Under the new model, the RLIMIT_SIGPENDING soft resource limit defines a limit on the number of signals that can be queued to all processes owned by a particular real user ID. We describe this limit further in Section 36.3.

with:

In systems with glibc versions before 2.4, this call returns –1. Since glibc 2.4, the return value depends on the kernel version. Before Linux 2.6.8, the call returns the value in the Linux-specific /proc/sys/kernel/rtsig-max file. This file defines a system-wide limit on the number of realtime signals that may be queued to all processes. The default value is 1024, but a privileged process can change it. The Linux-specific /proc/sys/kernel/rtsig-nr file shows the number of currently queued realtime signals.

Starting with Linux 2.6.8, these /proc files disappear. In their place, the RLIMIT_SIGPENDING resource limit (Section 36.3) limits the number of signals that can be queued to all processes owned by a particular real user ID. Since glibc 2.10, the sysconf() call returns the RLIMIT_SIGPENDING limit. (The SigQ field of the Linux-specific /proc/PID/status file displays the number of realtime signals pending for a process.)


Explanation:

Once upon a time, sysconf(_SC_SIGQUEUE_MAX) always returned –1. Although the other points in the text relating to the /proc files and the RLIMIT_SIGPENDING limit were all correct, I missed the fact that the glibc behavior changed, and to best capture the implications of that fact required reworking the entire text.

Reported by Yang Yang.

2011-12-12

460

In the second paragraph from the top of the page, change the second sentence:

This program takes up to four arguments, of which the first three are mandatory: a signal number, a target process ID, and an integer value to accompany the realtime signal.

to:

This program takes up to four arguments, of which the first three are mandatory: a target process ID, a signal number, and an integer value to accompany the realtime signal.


Explanation:

This change makes the order of the text match the order of the arguments in the program in Listing 22-2.

Reported by Sangman Lee.

2011-04-02

463

At the end of Listing 22-3 (signals/catch_rtsigs.c), change:

    while (!allDone)                    /* Wait for incoming signals */
        pause();
}

to:

    while (!allDone)                    /* Wait for incoming signals */
        pause();

    printf("Caught %d signals\n", sigCnt);
    exit(EXIT_SUCCESS);
}

Explanation:

These lines were accidentally deleted from the published version of the program. Without them, the line of output "Caught 6 signals" will not appear in the shell session shown on page 462.

Reported by Yang Yang.

2011-12-12

469

At the top of the page, insert a small-font note as follows:

According to SUSv3, calling sigwaitinfo() without blocking the signals in set results in undefined behavior.

2011-12-08

472

In the definition of the struct signalfd_siginfo structure, change:

    int32_t ssi_fd;        /* File descriptor (SIGPOLL/SIGIO) */
    uint32_t ssi_tid;      /* Kernel timer ID (POSIX timers) */
    uint32_t ssi_band;     /* Band event (SIGPOLL/SIGIO) */
    uint32_t ssi_tid;      /* (Kernel-internal) timer ID (POSIX timers) */
    uint32_t ssi_overrun;  /* Overrun count (POSIX timers) */

to:

    int32_t ssi_fd;        /* File descriptor (SIGPOLL/SIGIO) */
    uint32_t ssi_tid;      /* (Kernel-internal) timer ID (POSIX timers) */
    uint32_t ssi_band;     /* Band event (SIGPOLL/SIGIO) */
    uint32_t ssi_overrun;  /* Overrun count (POSIX timers) */

Explanation:

The definition of the ssi_tid field was accidentally duplicated, with two different accompanying comments. The fix removes the duplicate while retaining the slightly more detailed comment.

Reported by Sangman Lee.

2011-05-12

476

In the prototype box at the bottom of the page, change:

int sigmask(sig);

to:

int sigmask(int sig);

Reported by Junjiro Okajima.

2012-01-16

481

In the third paragraph, change:

When the timer reaches 0, the corresponding signal is sent to the process, and then, if the interval (it_interval) is nonzero, the timer value (it_value) is reloaded, and counting down toward 0 recommences.

to:

When the timer reaches 0, the corresponding signal is sent to the process, and then, if the interval (it_interval) is nonzero, the timer value (it_value) is reloaded with the interval value, and counting down toward 0 recommences.


Explanation:

This change helps avoid reader misunderstanding by reiterating a point already made (in different words) at the foot of page 480.

Reported by Sangman Lee.

2011-04-02

482

In the fourth line from the top of the page, change:

non-async-signal-functions

to:

non-async-signal-safe functions


Reported by Kiju Kim.

2012-02-15

491

About two thirds of the way down the page, in the paragraph beginning "The time value", change the last sentence in the paragraph:

The clock_getres() system call returns a pointer to a timespec structure containing the resolution of the clock specified in clockid.

to:

The clock_getres() system call returns, via the argument res, a pointer to a timespec structure containing the resolution of the clock specified in clockid.


Explanation:

The reader could probably deduce this information from the declaration in the preceding prototype box, but this change removes any ambiguity about whether the information is returned as the function result or via the argument res.

Reported by Bill McConnaughey.

2011-08-12

496

In the definition of the sigevent structure in the middle of the page, change:

        pid_t      _tid;      /* ID of thread to be signaled /

to:

        pid_t      _tid;      /* ID of thread to be signaled */

Reported by Junjiro Okajima.

2012-01-16

497

Add a sentence under the description of SIGEV_THREAD_ID; change:

(With SIGEV_SIGNAL notification, a signal is queued to the process as a whole, and, if there are multiple threads in the process, the signal will be delivered to an arbitrarily selected thread in the process.)

to:

(With SIGEV_SIGNAL notification, a signal is queued to the process as a whole, and, if there are multiple threads in the process, the signal will be delivered to an arbitrarily selected thread in the process. See Section 33.2 for a discussion of the interaction of threads and signals.)

2011-07-19

516

In the second paragraph from the bottom of the page, change:

that it inherits during the during the fork().

to:

that it inherits during the fork().


Reported by Bill McConnaughey.

2011-07-18

521

In the last line of the first bullet point at the top of the page, change:

virtual memory page frames

to:

physical memory page frames


Reported by Madhavan Kasthurirangan.

2011-09-04

530

In Exercise 24-3, change:

at a given moment in time

to:

at a given point in the program


Explanation:

The revised wording is more precise.

Reported by Bill McConnaughey.

2011-07-18

535

In the first normal-size paragraph at the top of the page, change:

2,147,482,647

to:

2,147,483,647


Reported by Junjiro Okajima.

2012-01-16

555

In Listing 26-4 (procexec/make_zombie.c), change:

    default:    /* Parent */
        sleep(3);               /* Give child a chance to start and exit */
        snprintf(cmd, CMD_SIZE, "ps | grep %s", basename(argv[0]));
        cmd[CMD_SIZE - 1] = '\0';       /* Ensure string is null-terminated */
        system(cmd);            /* View zombie child */

to:

    default:    /* Parent */
        sleep(3);               /* Give child a chance to start and exit */
        snprintf(cmd, CMD_SIZE, "ps | grep %s", basename(argv[0]));
        system(cmd);            /* View zombie child */

Explanation:

While writing a few code examples, it looks like I forgot that snprintf() always terminates its argument, even when it completely fills the buffer. (This was probably me confusing with the behavior of strncpy(). See page 793 of the book.)

Reported by Madhavan Kasthurirangan.

2011-09-02

565

In the explanation of ETXTBSY, change:

The file referred to by pathname is open for writing by another process

to:

The file referred to by pathname is open for writing by one or more processes


Explanation:

The ETXTBSY error could of course occur if the caller of execve() has itself opened pathname for writing.

Reported by Junjiro Okajima.

2012-01-16

572

In the fifth line of the first paragraph of the small-font note on this page, change:

(which is given as an argument to the script,

to:

(which is given as an argument to the interpreter,


Reported by Junjiro Okajima.

2012-01-16

572

In the fourth line of the second paragraph of the small-font note on this page, change:

(and these are passed as separate words to the script)

to:

(and these are passed as separate words to the interpreter)


Reported by Junjiro Okajima.

2012-01-16

580

In the third of the bullet points at the top of the page, change:

If all system calls succeed, then system() returns the termination status of the child shell used to execute command. (The termination status of a shell is the termination status of the last command it executes.)

to:

If all system calls succeed, then system() returns the termination status of the child shell used to execute command. The termination status of a shell is the exit status of the last command it executes; if that command is killed by a signal, most shells exit with the value 128+n, where n is the signal number. (If the child shell is itself killed by a signal, the termination status is as described in Section 26.1.3.)


Explanation:

There are two clarifications here. The first of these reiterates a point made in a small-font note near the top of page 532: when a command is terminated by a signal, the shell indicates that fact with a status of 128 plus the signal number.

The second, more significant fix (regarding what happens if the child shell is killed by a signal) results from Yang Yang's report. Yang Yang noted that on his Ubuntu system, the output in the last few lines of the shell session is different from that shown in the book. In the book, the following output is shown near the end of the shell session:

    $ fg
    ./t_system
    system() returned: status=0x000f (0,15)
    child killed by signal 15 (Terminated)

By contrast, Yang Yang obtained output of the form:

    $ fg
    ./t_system
    system() returned: status=0x8f00 (143,0)
    child exited, status=143

The differing output is a consequence of differences in the shells executed by system() on the two systems. A small-font note at the bottom of page 583 notes that some shells, when executing a simple command with sh -c, directly execute the command, rather than forking a new child shell to execute the command. On shells that perform this optimization, when system() is asked to execute the sleep 100 command, only a single process is created as a consequence of the call:

    system()    [process calls system("sleep 100")]
        |
    fork()
      :  \
      :   exec("sh")                  [Child process execs a shell]
      :     |
      :   exec("sleep")               [Child shell directly execs "sleep"]
      :     |
      :   [killed by SIGTERM]
      :  /
     waitpid(..., &status, ...)       [Sees "Killed by signal 15"]

By contrast, some shells don't perform this optimization, and the call to system() creates two processes, yielding the scenario shown in Figure 27-2 on page 584:

    system()    [process calls system("sleep 100")]
        |
    fork()
      :  \
      :   exec("sh")                  [Child process execs a shell]
      :     |
      :   fork()                      [Shell creates a new child]
      :     : \
      :     :  exec("sleep")          [New child execs "sleep"]
      :     :    |
      :     :  [killed by SIGTERM]
      :     : /
      :   waitpid(..., &status, ...)  [Sees "Killed by signal 15"]
      :     |
      :   exit(128+15)
      :  /
    waitpid(..., &status, ...)        [Sees "Exited normally, with status 143"]

The output in the book corresponds to the first of the scenarios described above. The shell executed by system() is bash, which does perform the optimization. In this case, the process running sleep is the child shell created by system(), and when that process is killed the status reported back by system() is as for a process killed by a signal.

The output for Yang Yang's case corresponds to the second scenario. The shell executed by system() is one that does not perform the optimization (dash, the default shell used to execute scripts on Ubuntu since version 6.10). In this case, the process running sleep is a child of the child shell created by system(). When that process is killed by a signal, the child shell observes the termination, and itself terminates with a status of the form 128+signum, which is in turn returned to the caller of system().

Reported by Yang Yang.

2011-12-23

601

In the second bullet point on this page, at the end of the first sentence ("If CHILD_SIG is nonzero…"), add a circled "5" as a reference to the corresponding line in Listing 28-3.


Reported by Junjiro Okajima.

2012-01-16

623

Inside the prototype box for pthread_exit(), change:

include <pthread.h>

to:

#include <pthread.h>

Reported by Fabien Galand.

2011-07-13

624

Inside the prototype boxes for pthread_self() and pthread_equal() (i.e., two changes!), change:

include <pthread.h>

to:

#include <pthread.h>

Reported by Fabien Galand.

2011-07-13

625

Inside the prototype box for pthread_join(), change:

include <pthread.h>

to:

#include <pthread.h>

Reported by Fabien Galand.

2011-07-13

632

Near the top of Listing 30-1 (threads/thread_incr.c), change:

static int glob = 0;

static void *                   /* Loop 'arg' times incrementing 'glob' */
threadFunc(void *arg)

to:

static volatile int glob = 0;   /* "volatile" prevents compiler optimizations
                                   of arithmetic operations on 'glob' */
static void *                   /* Loop 'arg' times incrementing 'glob' */
threadFunc(void *arg)

Explanation:

Making glob volatile means that the program more easily produces the "incorrect" output described on pages 633 and 634, even in the face of compiler optimization. Yang Yang's email report summarized the issue well, so I will just reproduce a lightly edited version of his comments:

I think failing to declare "glob" as volatile makes the program less "incorrect", because in this case, the output is determined by both the kernel's CPU scheduling decisions (as described on page 634) and the compiler's optimization decisions.

I've compiled Listing 30-1 with –O[0123] (GCC 4.5.2):

  1. –O2 and –O3 results in the same assembly code, which ably simplifies the entire loop to a single addition "glob += n". It's very hard to observe an incorrect output even with a huge n (overflow doesn't count), although the race condition does exist.
  2. –O1 uses a register to save the intermediate sum in each iteration, and writes the result into "glob" only once when the loop ends. Thus the output will be either n (more likely with a big n) or 2*n (more likely with a small n, as the first output on page 633).
  3. –O0 faithfully executes the load-increment-store routine (as if volatile were added), and its behavior matches both outputs on page 633.

With volatile, of course, the program's behavior will always be the same "incorrect" as in the text.

Because it is a common misunderstanding, it's perhaps worth pointing out that it is not generally necessary to employ the volatile qualifier on global variables in multithreaded programs. For further information on this point, see, for example, various Stack Overflow questions: When to use volatile with multi threading?, Should ALL global variables be volatile-qualified?, and Is 'volatile' needed in this multi-threaded C++ code?.

Reported by Yang Yang.

2011-12-25

635

In the sentence that immediately precedes Section 30.1.1, change:

In order to safely handle shared variables, all threads must cooperate in their use of a mutex, abiding by the locking rules it enforces.

to:

In order to safely handle shared variables, all threads must cooperate in their use of a mutex, abiding by its locking rules.


Explanation:

The use of the word "enforces" in the original text was a little confusing, suggesting a meaning that runs counter to the point that mutex locking is advisory.

Reported by Bill McConnaughey.

2011-07-18

638

In the second-to-last line on the page, remove the extra space between the words "early" and "futex".


Reported by Fabien Galand.

2011-11-26

644

In the seventh line of the second paragraph, change:

threads are designed to perform the exactly same task

to:

threads are designed to perform exactly the same task


Reported by Simon Durrant.

2011-09-26

649

This erratum makes changes on pages 649 and 650 to fix a single problem in Listing 30-4 (threads/thread_multijoin.c).

On page 649, in the second line from the bottom of the page, change:

    int idx = *((int *) arg);

to:

    int idx = (int) arg;

On page 650, about two thirds of the way down the page, change:

        s = pthread_create(&thread[idx].tid, NULL, threadFunc, &idx);

to:

        s = pthread_create(&thread[idx].tid, NULL, threadFunc, (void *) idx);

Explanation:

There was a race condition in the program. In the main program, the value &idx was passed as the final argument of the call to pthread_create(), which is located within a for loop indexed on idx. In the new thread, the passed pointer value was dereferenced to obtain the value for the local variable idx used within the thread. However, this will only succeed if the new thread dereferences the pointer before the main thread increments idx inside the for loop.

The changes given in the erratum correct the problem, and should generally work on any platform, but, as noted toward the bottom of page 622, casting an int back and forth to a pointer type is not strictly standards-conformant. A standards-conformant solution would be to apply a patch to the original program something like the following:

@@ -16,19 +16,22 @@
     TS_JOINED                   /* Thread terminated, and joined */
 };

-static struct {                 /* Info about each thread */
+struct tinfo {                  /* Info about each thread */
     pthread_t tid;              /* ID of this thread */
     enum tstate state;          /* Thread state (TS_* constants above) */
     int sleepTime;              /* Number seconds to live before terminating */
-} *thread;
+};
+
+static struct tinfo *thread;

 static void *                   /* Start function for thread */
 threadFunc(void *arg)
 {
-    int idx = *((int *) arg);
+    struct tinfo *tptr = arg;
+    int idx = tptr - thread;    /* Obtain index in 'thread' array */
     int s;

-    sleep(thread[idx].sleepTime);       /* Simulate doing some work */
+    sleep(tptr->sleepTime);     /* Simulate doing some work */
     printf("Thread %d terminating\n", idx);

     s = pthread_mutex_lock(&threadMutex);
@@ -36,7 +39,7 @@
         errExitEN(s, "pthread_mutex_lock");

     numUnjoined++;
-    thread[idx].state = TS_TERMINATED;
+    tptr->state = TS_TERMINATED;

     s = pthread_mutex_unlock(&threadMutex);
     if (s != 0)
@@ -65,7 +68,7 @@
     for (idx = 0; idx < argc - 1; idx++) {
         thread[idx].sleepTime = getInt(argv[idx + 1], GN_NONNEG, NULL);
         thread[idx].state = TS_ALIVE;
-        s = pthread_create(&thread[idx].tid, NULL, threadFunc, &idx);
+        s = pthread_create(&thread[idx].tid, NULL, threadFunc, &thread[idx]);
         if (s != 0)
             errExitEN(s, "pthread_create");
     }

2012-10-15 follow-up: Ideally, instead of using int in the modified code, it would have been preferable to use uintptr_t:

    uintptr_t idx = (uintptr_t) arg;

The problem with using int is that the cast generates a compiler warning ("cast from pointer to integer of different size") when compiling on 64-bit systems. However, using uintptr_t would have required more substantial changes to the code: an adjustment to the format specifier used in the printf() call in threadFunc(), and a corresponding change to the declaration of idx in main(). But, there's not much point in trying to make a perfect solution here, since the correct fix is really something like the longer patch above.

Reported by Jacob Mandelson.

2011-04-02

656

In the second paragraph from the top of the page, change:

lock that mutex when the function is called, and unlock it when the mutex returns

to:

lock that mutex when the function is called, and unlock it when the function returns


Reported by Sun Jian.

2011-09-05

674

In the shell session toward the bottom of the page, change:

    $ ./t_pthread_cancel

to:

    $ ./thread_cancel

Reported by Yang Yang.

2011-12-28

704

In the last line of the prototype box for getsid() at the bottom of the page, change:

or (pid_t) –1 on error

to:

or –1 on error


Explanation:

Such a cast is not needed when the standards require the type to be a signed integer. SUSv3 and SUSv4 incorrectly documented a cast as being needed for getsid() and a few other functions. For some more details, see this Austin bug report.

2012-01-16

705

In the last line of the prototype box for setsid() at the top of the page, change:

or (pid_t) –1 on error

to:

or –1 on error


Explanation:

See the erratum for the getsid() prototype box on page 704.

2012-01-16

734

Change the last sentence on the page:

As a result, processes with low nice values receive less CPU than before, and processes with high nice values obtain a greater proportion of the CPU.

to:

As a result, processes with low priorities receive less CPU than before, and processes with high priorities obtain a greater proportion of the CPU.


Explanation:

This wording change is intended to prevent reader confusion over whether "low nice value" means a process that has a low priority (actually a high nice value) or processes whose nice value is a low number (meaning a high priority).

Reported by Bill McConnaughey.

2011-07-18

736

Change the third paragraph:

In Linux kernels before 2.6.12, an unprivileged process may use setpriority() only to (irreversibly) lower its own or another process's nice value. A privileged (CAP_SYS_NICE) process can use setpriority() to raise nice values.

to:

In Linux kernels before 2.6.12, an unprivileged process may use setpriority() only to (irreversibly) lower its own or another process's priority. A privileged (CAP_SYS_NICE) process can use setpriority() to raise priorities.


Explanation:

By rewording in terms of priorities, this wording change is intended to prevent reader confusion over whether lowering (raising) the nice value means adding a positive or negative number to the nice value. See also the erratum for page 734.

Reported by Junjiro Okajima.

2012-01-16

736

Change the initial sentences of the fourth paragraph:

Since kernel 2.6.12, Linux provides the RLIMIT_NICE resource limit, which permits unprivileged processes to increase nice values. An unprivileged process can raise its own nice value to the maximum specified by the formula 20 – rlim_cur, where rlim_cur is the current RLIMIT_NICE soft resource limit. As an example, if a process's RLIMIT_NICE soft limit is 25, then its nice value can be raised to –5.

to:

Since kernel 2.6.12, Linux provides the RLIMIT_NICE resource limit, which permits unprivileged processes to raise priorities. An unprivileged process can set its own nice value to the maximum specified by the formula 20 – rlim_cur, where rlim_cur is the current RLIMIT_NICE soft resource limit. As an example, if a process's RLIMIT_NICE soft limit is 25, then its nice value can be set to –5.


Explanation:

This wording change is intended to prevent reader confusion over whether lowering (raising) the nice value means adding a positive or negative number to the nice value. See also the erratum for page 734.

Reported by Junjiro Okajima.

2012-01-16

741

Change the last sentence of the second paragraph:

Thus, the lowest SCHED_RR priority would be specified as sched_get_priority_min(SCHED_FIFO), the next higher priority as sched_get_priority_min(SCHED_FIFO) + 1, and so on.

to:

Thus, the lowest SCHED_RR priority would be specified as sched_get_priority_min(SCHED_RR), the next higher priority as sched_get_priority_min(SCHED_RR) + 1, and so on.


Reported by Junjiro Okajima.

2012-01-16

750

In the code snippet near the top of the page, change:

    sched_setaffinity(pid, CPU_SETSIZE, &set);

to:

    sched_setaffinity(pid, sizeof(cpu_set_t), &set);

Explanation:

The setting of the second argument to sched_setaffinity() was correctly explained at the foot of page 749, and demonstrated in the example program, procpri/t_sched_setaffinity.c, provided in the source code distribution for the book, but unfortunately was shown incorrectly in this example. The fix above corrects the error. An alternative would have been to use the value CPU_SETSIZE / 8 as the second argument of the call.

Reported by Emmanuel Gras.

2011-10-14

774

In Listing 37-3 (daemons/daemon_SIGHUP.c), change:

static volatile sig_atomic_t hupReceived = 0;
                                    /* Set nonzero on receipt of SIGHUP */
 from
static void
sighupHandler(int sig)
{

to:

static volatile sig_atomic_t hupReceived = 0;
                                    /* Set nonzero on receipt of SIGHUP */

static void
sighupHandler(int sig)
{

Explanation:

This was a random piece of text that crept into the text during production; the problem didn't occur in the actual source code of the example program.

Reported by Simon Durrant.

2011-10-04

774

This change spans the code at the bottom of page 774 and top of page 775.

Change the lines:

        if (hupReceived) {              /* If we got SIGHUP... */
            logClose();
            logOpen(LOG_FILE);
            readConfigFile(CONFIG_FILE);
            hupReceived = 0;            /* Get ready for next SIGHUP */
        }

to:

        if (hupReceived) {              /* If we got SIGHUP... */
            hupReceived = 0;            /* Get ready for next SIGHUP */
            logClose();
            logOpen(LOG_FILE);
            readConfigFile(CONFIG_FILE);
        }

Explanation:

This fixes a race condition that occurs if two SIGHUP signals arrive in quick succession (i.e., there were two closely spaced attempts to reinitialize the daemon).

In the original ordering of the code, if the second SIGHUP signal was received just after (say) the call to readConfigFile(), then, because hupReceived is immediately reset to zero, the daemon would not be reinitialized a second time.

Reported by Yacine Belkadi.

2011-04-19

776

At the bottom of the page, after the small-font note beginning "Although syslog(2) and syslog(3) share the same name", add a further small-font paragraph:

Some modern implementations of syslogd, such as rsyslog and syslog-ng, dispense with the need for a separate klogd daemon by instead themselves reading directly from /proc/kmsg.


Reported by Przemysław Pawełczyk.

2011-06-12

814

In the second bullet point in Section 39.10, change:

Since Linux 2.6.24, file capabilities can be disabled if the kernel is built without the CONFIG_SECURITY_FILE_CAPABILITIES option.

to:

In Linux kernel versions 2.6.24 through to 2.6.32, file capabilities can be disabled if the kernel is built without the CONFIG_SECURITY_FILE_CAPABILITIES option.

2011-09-17

827

In the prototype box at the top of the page, change:

void updwtmpx(char *wtmpx_file, struct utmpx *ut);

to:

void updwtmpx(const char *wtmpx_file, const struct utmpx *ut);

Reported by Yang Yang.

2012-01-23

857

Change the first sentence under the heading Further information:

Various information related to static and shared libraries can be found in the ar(1), gcc(1), ld(1), ldconfig(8), ld.so(8), dlopen(3), and objdump(1) manual pages and in the info documentation for ld and readelf.

to:

Various information related to static and shared libraries can be found in the ar(1), gcc(1), ld(1), ldconfig(8), ld.so(8), dlopen(3), readelf(1), and objdump(1) manual pages and in the info documentation for ld.


Explanation:

The readelf documentation is available via info, but it's actually a manual page, and so is best listed as such here.

Reported by Yang Yang.

2012-01-27

879

In the fourth line from the bottom of the page, change:

The data exchanged via pipes, FIFOs, and datagram sockets

to:

The data exchanged via pipes, FIFOs, and stream sockets


Reported by Yang Yang.

2012-01-28

885

In the first bullet point at the top of the page, change:

The System V IPC facilities are connectionless. These facilities provide no notion of a handle (like a file descriptor) referring to an open IPC object. In later chapters, we'll sometimes talk of "opening" a System V IPC object, but this is really just shorthand to describe the process of obtaining a handle to refer to the object.

to:

The System V IPC facilities are connectionless. These facilities provide no notion of a handle (like a file descriptor) referring to an open IPC object. In later chapters, we'll sometimes talk of "opening" a System V IPC object, but this is really just shorthand to describe the process of obtaining an identifier that refers to the object.


Explanation:

The original text is a little confusing, since it talks of "obtaining a handle to refer to the object", whereas the preceding sentence says that there is "no notion … of a handle referring to an open IPC object". The reader can probably deduce the intended sense (that there is no notion of something like an open file handle) from the rest of the chapter, and the point is made quite clear in Section 45.1, but the wording here could be better.

Reported by Yang Yang.

2012-01-28

904

In Listing 44-5 (pipes/popen_glob.c), at the bottom of the page, change:

        /* Build and execute command to glob 'pat' */

        snprintf(popenCmd, PCMD_BUF_SIZE, POPEN_FMT, pat);
        popenCmd[PCMD_BUF_SIZE - 1] = '\0';     /* Ensure string is
                                                   null-terminated */
        fp = popen(popenCmd, "r");

to:

        /* Build and execute command to glob 'pat' */

        snprintf(popenCmd, PCMD_BUF_SIZE, POPEN_FMT, pat);

        fp = popen(popenCmd, "r");

Explanation:

See the erratum for page 555.

2011-09-02

907

About one third of the way down the page (three lines above the mkfifo() prototype box), change:

and ls –F appends an the pipe symbol (|) to the FIFO pathname.

to:

and ls –F appends a pipe symbol (|) to the FIFO pathname.


Reported by Fabien Galand.

2011-11-26

916

In the first line of the second bullet point at the top of the page, change:

If the FIFO is being opened FIFO for writing,

to:

If the FIFO is being opened for writing,


Reported by Yang Yang.

2012-02-02

1008

In the shell session output at the top of the page, change:

$ ./svshm_create -p 102400
9633796
$ ./svshm_create -p 3276800
9666565
$ ./svshm_create -p 102400
1015817
$ ./svshm_create -p 3276800
1048586

to:

$ ./svshm_create -p 102400
9633796
$ ./svshm_create -p 3276800
9666565

Explanation:

By accident, two superfluous invocations of the svshm_create program were included in the shell session output. These superfluous invocations do no harm (the remainder of the shell session output is still correct), but they are unnecessary and thus confusing.

Reported by Fabien Galand.

2011-11-26

1040

In the second paragraph of Section 49.10 (i.e., nearly two thirds of the way down the page), change:

when explaining why it usually preferable to specify

to:

when explaining why it is usually preferable to specify


Reported by Fabien Galand.

2011-11-26

1042

In the code snippet about half way down the page, change:

remap_file_pages(addr, ps, 0, 2, 0);
                         /* Maps page 0 of file into page 2 of region */
remap_file_pages(addr + 2 * ps, ps, 0, 0, 0);
                         /* Maps page 2 of file into page 0 of region */

to:

remap_file_pages(addr, ps, 0, 2, 0);
                         /* Maps page 2 of file into page 0 of region */
remap_file_pages(addr + 2 * ps, ps, 0, 0, 0);
                         /* Maps page 0 of file into page 2 of region */

Reported by Madhavan Kasthurirangan.

2011-09-18

1049

In the third line of the second-to-last paragraph (beginning "The munlock() system call…"), change:

in the same way as for munlock().

to:

in the same way as for mlock().


Reported by Simon Durrant.

2011-12-13

1079

In the last line of the paragraph describing, SIGEV_NONE, change:

when a new messages arrives on an empty queue.

to:

when a new message arrives on an empty queue.


Reported by Bill McConnaughey.

2011-07-18

1141

A bit more than half way down the page, change:

    $ ls -li /dev/sda7 | awk '$6 == "3," && $7 == 10'

to:

    $ ls -li /dev | awk '$6 == "3," && $7 == 7'

Explanation:

Initial report by Corentin Chary (2011-08-23). My initial errata improved after a note from Gerald Demitre (2012-01-14).

Reported by Corentin Chary and Gerald Demitre

2012-01-14

1161

At the start of the third paragraph, change:

The src_addr and addrlen arguments

to:

The remaining arguments


Reported by Simon Durrant.

2011-12-13

1189

In the second paragraph on this page, change:

The range of IANA registered ports is 1024 to 41951.

to:

The range of IANA registered ports is 1024 to 49151.


Explanation:

A typo…

Reported by Yang Firo.

2011-05-04

1190

In the right-hand "TCP endpoint" box in Figure 58-8, the boxes labeled "send buffer" and "receive buffer" should be reversed, so that "receive buffer" is at the top and "send buffer" is at the bottom.


Reported by Yongzhi Pan.

2012-01-15

1203

In the first line of the first paragraph, change:

The sin_family field is set to AF_INET6.

to:

The sin6_family field is set to AF_INET6.


Reported by Simon Durrant.

2012-01-02

1214

At the end of the ninth line from the bottom of the page, change:

The in_addr field

to:

The ai_addr field


Reported by Simon Durrant.

2012-01-02

1230

In Listing 59-9 (sockets/inet_sockets.c), near the end of the listing, change:

    if (getnameinfo(addr, addrlen, host, NI_MAXHOST,
                    service, NI_MAXSERV, NI_NUMERICSERV) == 0)
        snprintf(addrStr, addrStrLen, "(%s, %s)", host, service);
    else
        snprintf(addrStr, addrStrLen, "(?UNKNOWN?)");

    addrStr[addrStrLen - 1] = '\0';     /* Ensure result is null-terminated */
    return addrStr;

to:

    if (getnameinfo(addr, addrlen, host, NI_MAXHOST,
                    service, NI_MAXSERV, NI_NUMERICSERV) == 0)
        snprintf(addrStr, addrStrLen, "(%s, %s)", host, service);
    else
        snprintf(addrStr, addrStrLen, "(?UNKNOWN?)");

    return addrStr;

Explanation:

See the erratum for page 555.

2011-09-02

1235

In the last line of the first paragraph in Section 59.14, change:

In the case,

to:

In this case,


Reported by Simon Durrant.

2012-01-02

1261

In the small-font note near the bottom of the page, change:

Performance benefits could also be obtained if sendfile() could be used to transfer bytes between two regular files. On Linux 2.4 and earlier, out_fd could refer to a regular file. Some reworking of the underlying implementation meant that this possibility disappeared in the 2.6 kernel. However, this feature may be reinstated in a future kernel version

to:

Performance benefits could also be obtained if sendfile() could be used to transfer bytes between two regular files. On Linux 2.4 and earlier, out_fd could refer to a regular file. Some reworking of the underlying implementation meant that this possibility disappeared in the 2.6 kernel. Some later changes restored this feature in Linux 2.6.33.

2011-09-11

1337

In the first sentence after the poll() prototype box, change:

The fds argument and the pollfd array (nfds) specify the file descriptors that poll() is to monitor.

to:

The pollfd array (fds) and the nfds argument specify the file descriptors that poll() is to monitor.


Reported by Simon Durrant.

2012-01-02

1349

Just over half way down the page, change:

        if (gotSigio) {                 /* Is input available? */

            /* Read all available input until error (probably EAGAIN)
               or EOF (not actually possible in cbreak mode) or a
               hash (#) character is read */

            while (read(STDIN_FILENO, &ch, 1) > 0 && !done) {
                printf("cnt=%d; read %c\n", cnt, ch);
                done = ch == '#';
            }
 
            gotSigio = 0;
        }

to:

        if (gotSigio) {                 /* Is input available? */
            gotSigio = 0;

            /* Read all available input until error (probably EAGAIN)
               or EOF (not actually possible in cbreak mode) or a
               hash (#) character is read */

            while (read(STDIN_FILENO, &ch, 1) > 0 && !done) {
                printf("cnt=%d; read %c\n", cnt, ch);
                done = ch == '#';
            }
        }

Explanation:

This fixes a race condition that occurs if two SIGIO signals arrive in quick succession.

The race condition in the original code is as follows. Suppose that some input has been supplied to the program, a SIGIO signal has been generated, and we have just returned from the signal handler and have entered the block of code shown in the erratum. If further input arrived and a second SIGIO signal was received just after the while loop terminated, then, because gotSigio is immediately reset to zero, the program would not read the new input (until the delivery of a further SIGIO signal).

Reported by Yacine Belkadi.

2011-04-19

1355

In the fourth line from the top of the page, change:

The F_GETOWN_EX operation is the converse of the F_GETOWN_EX operation.

to:

The F_GETOWN_EX operation is the converse of the F_SETOWN_EX operation.


Reported by Bill McConnaughey.

2011-07-18

1361

In the shell session in the middle of this page, change:

$ fg
./epoll_input p q
About to epoll_wait()
Ready: 2
  fd=4; events: EPOLLIN
    read 4 bytes: ppp

  fd=5; events: EPOLLIN EPOLLHUP
    read 4 bytes: qqq

    closing fd 5
About to epoll_wait()

to:

$ fg
./epoll_input p q
About to epoll_wait()
Ready: 2
  fd=4; events: EPOLLIN
    read 4 bytes: ppp

  fd=5; events: EPOLLIN EPOLLHUP
    read 4 bytes: qqq

About to epoll_wait()
Ready: 1
  fd=5; events: EPOLLHUP
    closing fd 5
About to epoll_wait()

Explanation:

The shell output originally shown was produced by an earlier version of the program that contained a small bug. The bug was fixed, but I overlooked to refresh the shell session demonstrating the use of the program.

Reported by Pedro Dominguez.

2011-04-28

1361

Change the second-to-last paragraph on the page:

The two blank lines in the above output are the newlines that were read by the instances of cat, written to the FIFOs, and then read and echoed by our monitoring program.

to:

The two blank lines in the above output are the newlines that were read by the instances of cat, written to the FIFOs, and then read and echoed by our program.


Explanation:

This isn't an error fix at all. The change (which shortens the paragraph by one line) was made solely to create enough vertical space on the page to accommodate the previous change (which added three lines to the page).

2011-04-28

1366

In the code snippet a bit more than half way down the page, change:

    if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, ev) == -1)

to:

    if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1)

Reported by tjuer.

2011-12-16

1418

At the end of the last paragraph on this page, add a sentence:

This file can be viewed using zcat(1) and searched using zgrep(1). The /proc/config.gz file is itself only available if the kernel was configured with the CONFIG_IKCONFIG and CONFIG_IKCONFIG_PROC configuration options enabled.


Explanation:

In print run 7, a fix was made to remedy a minor error that occurred when applying this erratum: /proc/config.z was changed to /proc/config.gz.

Reported by Bill McConnaughey.

2011-07-18

1426

In the solution for Exercise 8-1, change:

8-1. The two getpwnam() calls are executed before the printf() output string is constructed, and—since getpwnam() returns its result in a statically allocated buffer—the second call overwrites the result returned by the first call.

to:

8-1. The two getpwuid() calls are executed before the printf() output string is constructed, and—since getpwuid() returns its pw_name result in a statically allocated buffer—the second call overwrites the result returned by the first call.


Explanation:

See the erratum for Exercise 8-1 on page 166.

Reported by René Thomsen.

2011-07-14

1439

Under the bibliography entry for "Gont, F. 2008", change the URL:

http://www.cpni.gov.uk/Docs/InternetProtocol.pdf

to:

http://www.gont.com.ar/papers/InternetProtocol.pdf


Reported by Kiju Kim.

2012-01-20

1439

Under the bibliography entry for "Gont, F. 2009 (a)", change the URL:

http://www.cpni.gov.uk/Docs/tn-03-09-security-assessment-TCP.pdf

to:

http://www.gont.com.ar/papers/tn-03-09-security-assessment-TCP.pdf


Reported by Kiju Kim.

2012-01-20

1440

Under the bibliography entry for "Hallyn, S. 2007", change the URL:

http://www.ibm.com/developerworks/library/l-posixcap.html

to:

http://www.ibm.com/developerworks/library/l-posixcap/index.html


Reported by Kiju Kim.

2012-01-20

1440

In the small-font note under the bibliography entry for "Josey, A. (ed.). 2004", change:

A newer version of this guide (published in 2010) for version 4 of the specification can be ordered online at http://www.opengroup.org/bookstore/catalog/.

to:

A newer version of this guide (published in 2010) for version 4 of the specification can be found at http://www.unix.org/version4/theguide.html.


Reported by Kiju Kim.

2012-01-20

1440

Under the bibliography entry for "Kent, A., and Mogul, J.C. 1987", change the URL:

http://www.acm.org/sigcomm/ccr/archive/1995/jan95/ccr-9501-mogulf1.pdf

to:

http://ccr.sigcomm.org/archive/1995/jan95/ccr-9501-mogulf1.html


Reported by Kiju Kim.

2012-01-20

1441

Under the bibliography entry for "Lu, H.J. 1995" replace the URL:

http://www.trunix.org/programlama/os/elf-hl/Documentation/elf/elf.html

to:

This paper can be found online at a variety of locations.


Explanation:

There is currently a version at https://www.linux.co.cr/free-unix-os/review/acrobat/950517.pdf, but I'm not sure how stable that location is.

Reported by Kiju Kim.

2012-01-20

1444

Under the bibliography entry for "Stone, J., and Partridge, C. 2000", change the URL:

http://www.acm.org/sigcomm/sigcomm2000/conf/abstract/9-1.htm

to:

http://dl.acm.org/citation.cfm?doid=347059.347561


Reported by Kiju Kim.

2012-01-20

1469

Change the index entry:

International Standards Organization (ISO), 11

to:

International Organization for Standardization (ISO), 11


Explanation:

See the erratum for page 11.

2011-08-04

1470

Change the index entry:

ISO (International Standards Organization), 11

to:

ISO (International Organization for Standardization), 11


Explanation:

See the erratum for page 11.

2011-08-04

Fixes and improvements in the second printing

The following fixes and improvements were applied in the second and later printings of the book.

Page Fix Reported
xli

Under the heading Web site and source code of example programs, change:

http://www.man7.org/tlpi/.

to:

http://man7.org/tlpi/.

2011-01-08

xli

Under the heading Feedback, following the sentence:

Book bugs and general suggestions about how the explanations in the book can be improved are also welcome.

add the sentence:

(A current list of errata can be found at http://man7.org/tlpi/errata/.)

2011-01-08

15

In the paragraph just above the heading Legacy features, change:

When using interfaces that are unspecified or weakly specified, we have few guarantees when porting applications to other UNIX implementations.

to:

When using interfaces that are unspecified or weakly specified, we have few guarantees when porting applications to other UNIX implementations, and portable applications should avoid relying on the behavior of a specific implementation.


Explanation:

The added text simply emphasizes a point that was already implied.

2011-02-28

16

On the fourth line from the bottom of the page, remove the superfluous right parenthesis following "mid-1990s".


Reported by Justin Pryzby.

2010-12-28

32

In the fourth paragraph, change:

the operation performed execve()

to:

the operation performed by execve()


Reported by Sun Jian.

2011-02-25

48

In the fourth line, change:

(in #ifdef statements)

to:

(in #if statements)


Reported by Richard Moore.

2011-01-26

57

In the first line of text below the example program, change:

The file enames.c.inc

to:

The file ename.c.inc


Reported by Kiju Kim.

2011-02-13

57

In the third line of the second normal-size font paragraph on the page (i.e., a bit more than half way down the page), change:

names separate by a slash.

to:

names separated by a slash.


Reported by Subu Rama.

2010-12-03

67

A bit more than half way down the page, immediately following the heading Using macros that may not be present on all implementations change:

In some cases, a macro may be not be defined

to:

In some cases, a macro may not be defined


Reported by Markus Boje.

2010-11-22

75

In the first bullet point at the top of the page, change:

They can be retrieved using the fcntl() F_GETFL operation (Section 5.3).

to:

Only one of these values should be specified in flags. The access mode can be retrieved using the fcntl() F_GETFL operation (Section 5.3).


Explanation:

This is just a clarification that reiterates a point made on page 72.

Suggested from a conversation with Krzysztof Żelechowski.

2010-12-16

75

In the third line of the O_CLOEXEC paragraph at the bottom of the page, change:

additional fcntl() F_SETFD and F_SETFD operations

to:

additional fcntl() F_GETFD and F_SETFD operations


Reported by Subu Rama.

2010-11-30

83

In the middle of the last paragraph (small-font note), change:

This allows an application to be sure that a later write() to the file won't fail because disk space is exhausted (which could otherwise occur if a hole in the file was filled in, or some other application consumed space on the disk).

to:

This allows an application to be sure that a later write() to the file won't fail because disk space is exhausted (which could otherwise occur if a hole in the file was filled in, or some other application consumed space on the disk before the full extent of the file was written).


Explanation:

The parenthetical clause describes two reasons why the write() might fail. The added words simply clarify the second reason a little.

Reported by Kiju Kim.

2011-03-19

99

In the paragraph at the bottom of the page, change:

The integer count specifies

to:

The integer iovcnt specifies


Reported by Subu Rama.

2010-11-30

110

(Exercise 5-3) In the first sentence of the last paragraph on the page, change:

This file should open

to:

This program should open


Reported by Justin Pryzby.

2010-12-28

111

In Exercise 5-6, change the line:

    write(fd2, "world", 6);

to:

    write(fd2, " world", 6);

(i.e., add a space before world).


Reported by John Wiersba. (I initially wrote up this erratum incorrectly; fixed on 2010-12-07, after a note from Geoff Clare.)

2010-11-30

115

In the last paragraph of Section 6.2, change:

the Ppid field

to:

the PPid field

(i.e., make the second "P" upper case).


Reported by Subu Rama.

2010-12-20

118

About one third of the way down the page, change:

    extern char etext, edata, end;
            /* For example, &etext gives the address of the end
               of the program text / start of initialized data */

to:

    extern char etext, edata, end;
            /* For example, &etext gives the address of the next byte past
               the end of the program text / start of initialized data */

Explanation:

The original wording was intended (the details are explained in the text), but the rewording is more precise and avoids possibilities for confusion.

Reported by Sun Jian.

2011-02-16

155

In the last paragraph of Section 8.2, change the sentence:

SUSv3 doesn't specify shadow passwords, and not all UNIX implementations provide this feature.

to:

SUSv3 doesn't specify shadow passwords. Not all UNIX implementations provide this feature, and on implementations where it is provided the details of file locations and APIs vary.


Explanation:

It's worth emphasizing that even on systems where shadow passwords are provided, the details are unstandardized and therefore vary across systems.

Reported by John Wiersba.

2010-12-13

156

In the bullet point for Encrypted password ID about one third of the way down the page, change:

the passwd command

to:

the gpasswd command


Reported by Justin Pryzby.

2010-12-28

156

In the bullet point for Group ID about half way down the page, change the sentence:

There is normally one group defined with the group ID 0, named root (like the /etc/passwd record with user ID of 0).

to:

There is normally one group defined with the group ID 0, named root (like the /etc/passwd record with user ID of 0, but unlike the user ID 0, this group has no special privileges).


Explanation:

The added detail prevents possible misunderstandings about the significance of GID 0.

Reported by John Wiersba.

2010-12-13

180

In the third paragraph, change:

Instead, it defines the initial real user ID, effective user ID, and saved set-user-ID of the login shell.

to:

Instead, it defines the initial real group ID, effective group ID, and saved set-group-ID of the login shell.


Reported by Justin Pryzby.

2011-01-03

187

In the third line of the small-font note about two thirds of the way down the page, change:

4.3BSD added the more precise

to:

4.2BSD added the more precise

2010-12-26

199

In the final paragraph, change the opening piece of the first sentence:

The std and dst components are strings identifying the standard and DST timezones;

to:

The std and dst components are strings that define names for the standard and DST timezones;


Explanation:

This is a clarification to remove the ambiguity of the word "identifying" (does "identifying" mean "defining a name for" or "selecting"?).

Suggested from a conversation with Subu Rama.

2010-12-20

200

About a quarter of the way down the page, add a closing quote at the end of the first code snippet, so that it reads:

    TZ="CET-1:00:00CEST-2:00:00,M3.5.0,M10.5.0"

Reported by Subu Rama.

2010-12-20

204

In the last line of the page, change "4.3BSD" to "4.2BSD".


Reported by Roger M. Levasseur.

2010-12-26

262

In Figure 14-4, the arrow pointing from Mount points to the windows icon should point instead to the C icon.


Reported by Dave Walker.

2010-11-28

276

In the first sentence of the bullet point at the bottom of the page, change:

For most file Linux systems

to:

For most Linux file systems


Reported by Subu Rama.

2011-01-04

280

Near the end of the small-font note at the bottom of the page, change:

(On all major contemporary UNIX implementations, symbolic links are implemented as i-nodes.)

to:

(On all major contemporary UNIX implementations, symbolic links are implemented as i-nodes. See Section 18.2 for further details.)


Explanation:

Adding this forward reference to the relevant section is helpful for the reader.

Reported by Justin Pryzby.

2010-12-28

307

In the last line of the paragraph for FS_NOTAIL_FL, change:

mount -notail

to:

mount -o notail


Reported by Justin Pryzby.

2010-12-28

324

In the first bullet point, about half way down the page, change:

Simply modifying the ACL_GROUP_OBJ and ACL_USER_OBJ entries

to:

Simply modifying the ACL_GROUP_OBJ and ACL_OTHER entries


Reported by Jorge Merlino.

2011-03-08

353

Below the prototype box for readdir() (about half way down the page), change:

Each call to readdir() reads the next directory from the directory stream

to:

Each call to readdir() reads the next directory entry from the directory stream


Reported by Subu Rama.

2011-01-12

354

In the second line, change:

stat() on the pathname

to:

lstat() (or stat(), if a symbolic link should be dereferenced) on the pathname


Suggested by a report by Justin Pryzby.

2010-12-28

354

At the end of the small-font note near the top of the page, add a sentence:

Although not specified in SUSv3, scandir() is provided on most UNIX implementations. SUSv4 added a specification for scandir().


Reported by Geoff Clare.

2010-12-31

360

Near the top of the page, change:

The fourth argument to func, ftwbuf, is pointer to a structure defined as follows:

to:

The fourth argument to func, ftwbuf, is a pointer to a structure defined as follows:


Reported by Subu Rama.

2011-01-12

378

Two changes associated with the description of inotify_rm_watch().

1. In the prototype box at the top of the page, change the line:

int inotify_rm_watch(int fd, uint32_t wd);

to:

int inotify_rm_watch(int fd, int wd);

2. In the paragraph below the prototype box, remove the sentence:

(The uint32_t data type is an unsigned 32-bit integer.)


Explanation:

The calling signature of this function changed in glibc 2.10.

2010-10-25

387

Change the opening sentence on this page:

This chapter and next two chapters discuss signals.

to:

This chapter and the next two chapters discuss signals.

2011-01-03

387

In the fourth bullet point, change:

the use of a process signal mask

to:

the use of the process signal mask


Explanation:

Each process has a single signal mask, so using the definite article "the" is better than the indefinite article "a".

2011-01-03

408

In the prototype box at the top of the page, change the line:

int sigandset(sigset_t *set, sigset_t *left, sigset_t *right);

to:

int sigandset(sigset_t *dest, sigset_t *left, sigset_t *right);

Reported by Matias Virsu.

2010-12-22

419

Change the text of Exercise 20-4:

Implement siginterrupt() using sigaction().

to:

Implement the siginterrupt() function described in Section 21.5 using sigaction().


Explanation:

This exercise should have been at the end of Chapter 21. However, as an exercise in the use of sigaction(), it works fine in Chapter 20 as well.

Reported by Subu Rama.

2011-01-14

431

In the shell session about one quarter of the way down the page, change the second line:

    $ ./sigmask_siglongjmp x

to:

    $ ./sigmask_siglongjmp

Explanation:

The "x" argument is superfluous (it was used in an earlier version of the program).

Reported by Subu Rama.

2011-01-14

439

Near the top of the page, in the paragraph under si_code, change:

as shown in Table 21-1.

to:

as shown in Table 21-2.


Reported by Subu Rama.

2011-01-14

468

In the prototype box, change:

Returns number of delivered signal on success

to:

Returns signal number on success


Explanation:

The new wording is a little more concise, and avoids the possible misreading "number of delivered signals". (See also the erratum below about rewording "is delivered" to "becomes pending".)

2011-01-03

468

At the end of the second-to-last paragraph, change:

(see Exercise 22-3.)

to:

(see Exercise 22-3)

2011-01-04

471

In the prototype box near the top of the page, change:

Returns number of delivered signal on success

to:

Returns signal number on success


Explanation:

The new wording is a little more concise, and avoids the possible misreading "number of delivered signals". (See also the erratum for page 468 about rewording "is delivered" to "becomes pending".)

2011-01-03

477

In the first line of Section 22.14, change:

This file contains

to:

Core dumps contain


Explanation:

This is a rewording that clarifies the text a little.

Reported by Justin Pryzby.

2010-12-28

480

In the paragraph in the middle of the page beginning "The default disposition", change:

we must to establish

to:

we must establish


Reported by Eric Arora.

2011-01-05

480

In the sixth line from the bottom of the page, change:

the timer is expires

to:

the timer expires


Reported by Eric Arora.

2011-01-05

483

This fix applies to a segment of code that spans the bottom of page 483 and the top of page 484. The change moves a block of code to a location lower down in the program (but otherwise leaves the lines of code unchanged). Change:

    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sa.sa_handler = sigalrmHandler;
    if (sigaction(SIGALRM, &sa, NULL) == -1)
        errExit("sigaction");

    /* Exit after 3 signals, or on first signal if interval is 0 */

    maxSigs = (itv.it_interval.tv_sec == 0 &&
                itv.it_interval.tv_usec == 0) ? 1 : 3;

    displayTimes("START:", FALSE);

    /* Set timer from the command-line arguments */

    itv.it_value.tv_sec = (argc > 1) ? getLong(argv[1], 0, "secs") : 2;
    itv.it_value.tv_usec = (argc > 2) ? getLong(argv[2], 0, "usecs") : 0;
    itv.it_interval.tv_sec = (argc > 3) ? getLong(argv[3], 0, "int-secs") : 0;
    itv.it_interval.tv_usec = (argc > 4) ? getLong(argv[4], 0, "int-usecs") : 0;

    if (setitimer(ITIMER_REAL, &itv, 0) == -1)
        errExit("setitimer");

to:

    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sa.sa_handler = sigalrmHandler;
    if (sigaction(SIGALRM, &sa, NULL) == -1)
        errExit("sigaction");

    /* Set timer from the command-line arguments */

    itv.it_value.tv_sec = (argc > 1) ? getLong(argv[1], 0, "secs") : 2;
    itv.it_value.tv_usec = (argc > 2) ? getLong(argv[2], 0, "usecs") : 0;
    itv.it_interval.tv_sec = (argc > 3) ? getLong(argv[3], 0, "int-secs") : 0;
    itv.it_interval.tv_usec = (argc > 4) ? getLong(argv[4], 0, "int-usecs") : 0;

    /* Exit after 3 signals, or on first signal if interval is 0 */

    maxSigs = (itv.it_interval.tv_sec == 0 &&
                itv.it_interval.tv_usec == 0) ? 1 : 3;

    displayTimes("START:", FALSE);
    if (setitimer(ITIMER_REAL, &itv, 0) == -1)
        errExit("setitimer");

Explanation:

The initialization of itv produces a value used in the initialization of maxSigs. Therefore the initializations must be in that order. (When I originally wrote the program the initializations were in the correct order, but during the production phase of the book I accidentally made a change that introduced the error.)

Reported by Subu Rama.

2011-01-17

538

In the second bullet point, half way down the page, change:

in an application that creates child processes, typically only one of the processes (most often the parent) should terminate via exit(), while the other processes should terminate via _exit().

to:

in an application that creates child processes that don't exec new programs, typically only one of the processes (most often the parent) should terminate via exit(), while the other processes should terminate via _exit().


Explanation:

This change clarifies the intended meaning of the explanation by adding an important detail.

Suggested by a conversation with Justin Pryzby.

2011-01-03

574

In the last line of the small-font note at the top of the page (starting "The Linux 2.2 kernel…"), change:

would show just the value echo.

to:

would show just the value necho.


Reported by Subu Rama.

2011-01-27

585

In the second-to-last line of the first bulleted paragraph on the page, change:

executable it if it is

to:

able to be executed if it is


Explanation:

There are two fixes here. One rewords a slightly cumbersome double use of the word executable, and the other removes a superfluous word, it.

Reported by Justin Pryzby.

2010-12-28

617

In the last line of the page, change:

case of a multithreaded processes

to:

case of a multithreaded process


Reported by Subu Rama.

2011-01-27

618

In the second paragraph on the page (starting "The threads in a process"), change the second sentence:

On a multiprocessor system, multiple threads can execute parallel.

to:

On a multiprocessor system, multiple threads can execute in parallel.


Reported by Eric Arora.

2011-01-05

618

In the second paragraph on the page (starting "The threads in a process"), change:

(Although it sometimes useful

to:

(Although it is sometimes useful


Reported by Subu Rama.

2011-01-27

621

In the code segment at the bottom of the page, change:

    pthread_t *thread;

to:

    pthread_t thread;

Reported by Subu Rama.

2011-02-17

624

In the sentence just above the box containing the prototype for pthread_equal(), change:

The pthread_equal() function allows us check

to:

The pthread_equal() function allows us to check


Reported by Jens Thoms Toerring.

2011-02-27

642

In the fourth line from the top of the page, change:

    s = pthread_mutex_init(mtx, &mtxAttr);

to:

    s = pthread_mutex_init(&mtx, &mtxAttr);

Reported by Gary Hu.

2011-01-05

668

About half way down the page, change:

    static __thread buf[MAX_ERROR_LEN];

to:

    static __thread char buf[MAX_ERROR_LEN];

Reported by Sangman Lee.

2011-03-09

702

In the middle of the page, change this entire paragraph:

If the pid and pgid arguments specify the same process (i.e., pgid is 0 or matches the process ID of the process specified by pid), then a new process group is created, and the specified process is made the leader of the new group (i.e., the process group ID of the process is made the same as its process ID). If the two arguments specify different values (i.e., pgid is not 0 and doesn't match the process ID of the process specified by pid), then setpgid() is being used to move a process between process groups.

to:

If setpgid() changes the process group ID of the target process (the process specified by pid) to be the same as its process ID, then the target process is made the leader of a new process group whose ID is the same as the process ID. If setpgid() changes the process group ID of the target process to a value different from its process ID, then the target process is moved to the existing process group specified by pgid. If setpgid() leaves the process group ID of the target process unchanged, then the call has no effect on the target process.


Explanation:

The original text was correct, but (1) was more complex than is needed to explain the behavior of setpgid(), and (2) didn't clearly explain the behavior if the process group ID was unchanged. The new text rectifies both of these problems.

In the sixth print run, a typo in this erratum was corrected: a mistyped "septgid()" was corrected to "setpgid()". Thanks to Ernesto Messina for reporting this in August 2015.

Reported by Jonathan Nieder.

2011-02-16

708

In the second line of Section 34.5, change:

Within a session, only one process can be in the foreground

to:

Within a session, only one process group can be in the foreground


Reported by Justin Pryzby.

2010-12-28

730

Two changes in the first paragraph of Section 34.8.

1. Change:

(which is the same as the process group ID of the process group leader)

to:

(which is the same as the process ID of the process group leader)

2. Change:

(which is the same as the ID of the session leader)

to:

(which is the same as the process ID of the session leader)


The first of these changes was suggested by Justin Pryzby.

2011-01-03

740

In the first and third bullet points (i.e., two changes) in Section 35.2.2, change:

as described for the SCHED_FIFO policy above

to:

as described for the SCHED_RR policy above


Reported by Sangman Lee.

2011-03-11

760

In the second line of the small-font note about a third of the way down the page, change:

limits larger that

to:

limits larger than


Reported by Justin Pryzby.

2010-12-28

769

In the third line from the bottom of the page, change:

becomeDaeomon()

to:

becomeDaemon()


Reported by Justin Pryzby.

2010-12-28

780

In the code snippet at the top of the page, change the line:

   syslog(LOG_ERROR, "Bad argument: %s", argv[1]);

to:

   syslog(LOG_ERR, "Bad argument: %s", argv[1]);

Reported by Greg Olin.

2010-12-06

785

In the code segment at the bottom of the page, change two instances of:

    if (setuid(getuid() == -1)

to:

    if (setuid(getuid()) == -1)

Reported by Sangman Lee.

2011-03-16

786

In the fourth line of text, change:

is insufficient to change the set-user-ID identifier.

to:

is insufficient to change the saved set-user-ID.


Explanation:

The text as originally given was intended and the meaning is probably clear, but Przemysław commented on this wording and suggested the change, and I agree that it's better (and more consistent with the wordings I use elsewhere).

Reported by Przemysław Pawełczyk.

2010-11-28

786

Add a sentence to the end of the third paragraph:

SUSv3 doesn't specify this feature, but many other implementations behave the same way as Linux. SUSv4 does specify this feature.


Explanation:

SUSv4 added a specification for this behavior, and I noted the difference between SUSv3 and SUSv4 on page 176, but failed to do the same on this page.

Reported by Geoff Clare.

2010-12-07

822

In the second line of the second bullet point (in the middle of the page), change:

then getutxent() finds the next

to:

then getutxid() finds the next


Reported by Sangman Lee.

2011-03-16

849

Change the shell command in line 9:

    # ls -l libdemo* | awk '{print $1, $$9, $10, $11}'

to:

    # ls -l libdemo* | awk '{print $1, $9, $10, $11}'

Reported by Zerksis Umrigar.

2010-12-25

895

In the seventh line from the bottom of the page, change:

The parent then closes the read end of the pipe (10)

to:

The parent then closes the write end of the pipe (10)


Reported by Sangman Lee.

2011-03-16

1017

In the first line of the small-font note at the bottom of the page, change:

and one is that is

to:

and one that is


Reported by Justin Pryzby.

2010-12-28

1083

In the second of the three bullet points in the middle of the page, change:

timer notifications

to:

message notifications


Reported by Subu Rama.

2011-02-17

1102

In the first comment of the main() function in Listing 53-6 (psem/thread_incr_psem.c), change:

    /* Initialize a thread-shared mutex with the value 1 */

to:

    /* Initialize a semaphore with the value 1 */

Reported by Subu Rama.

2011-02-17

1119

In the third line from the bottom of the page, change:

the value into operation

to:

the value LOCK_NB into operation


Reported by Justin Pryzby.

2010-12-28

1176

At the end of the first paragraph, change these entire sentences:

The remaining bytes of the sun_path field then define the abstract name for the socket. These bytes are interpreted in their entirety, rather than as a null-terminated string.

to:

The name of the abstract socket is then defined by the remaining bytes (including any null bytes) in sun_path up to the length defined for the size of the address structure (i.e., addrlen – sizeof(sa_family_t)).


Explanation:

This change fixes a factual mistake (found as a consequence of a man-pages bug report by Lennart Poettering).

2010-10-25

1176

In Listing 57-8 (sockets/us_abstract_bind.c), change

    /* addr.sun_path[0] has already been set to 0 by memset() */

    strncpy(&addr.sun_path[1], "xyz", sizeof(addr.sun_path) - 2);
                /* Abstract name is "xyz" followed by null bytes */

    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd == -1)
        errExit("socket");

    if (bind(sockfd, (struct sockaddr *) &addr,
            sizeof(struct sockaddr_un)) == -1)
        errExit("bind");

to:

    /* addr.sun_path[0] has already been set to 0 by memset() */

    str = "xyz";        /* Abstract name is "\0xyz" */
    strncpy(&addr.sun_path[1], str, strlen(str));

    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd == -1)
        errExit("socket");

    if (bind(sockfd, (struct sockaddr *) &addr,
            sizeof(sa_family_t) + strlen(str) + 1) == -1)
        errExit("bind");

Explanation:

The program as originally provided was correct (i.e., the resulting abstract socket name is valid), but changing the code better reflects the spirit of the changed explanatory text (see the previous erratum).

In print run 7, a fix was made to a white-space error that was made when originally applying this erratum. (A space had been accidentally inserted after the first strlen.)

This erratum was updated on 18 April 2017.

2010-10-25

1192

In the eighth line of the paragraph headed Flow control, change:

which allows unacknowledged segments containing a total of up N

to:

which allows unacknowledged segments containing a total of up to N


Reported by Subu Rama.

2011-02-17

1218

In the last sentence of the paragraph somewhat more than half way down the page starting "The resulting host and service names…", change:

define one of the feature text macros

to:

define one of the feature test macros


Reported by Subu Rama.

2011-02-17

1247

In the fifth and sixth lines of the third paragraph (beginning "One of the simplest approaches…"), change:

Successive requests to the DNS server to resolve the domain name return these IP addresses in a different order, in a round-robin fashion.

to:

Successive requests to the DNS server to resolve the domain name return these IP addresses in round-robin order.


Explanation:

This rewording is simpler and clearer.

2011-01-06

1247

In the fourth paragraph (beginning "Round-robin DNS has the advantage…"), change the second and third sentences:

However, it does present some problems. One of these is the caching performed by remote DNS servers, which means that future requests from clients on a particular host (or set of hosts) bypass the round-robin DNS server and are always handled by the same server.

to:

However, it does have some shortcomings. A DNS server performing iterative resolution may cache its results (see Section 59.8), with the result that future queries on the domain name return the same IP address, instead of the round-robin sequence generated by the authoritative DNS server.


Explanation:

This rewording clarifies the intended meaning a little and provides a helpful cross reference.

Suggested from a conversation with Justin Pryzby.

2010-12-28

1247

In the second sentence of Section 60.5, change:

a large number server processes

to:

a large number of server processes


Reported by Justin Pryzby.

2010-12-28

1260

About two thirds of the way down the page, change the paragraph:

Of the above flags, only MSG_OOB is specified by SUSv3. MSG_DONTWAIT appears on a few other UNIX implementations, and MSG_NOSIGNAL and MSG_MORE are Linux-specific.

to:

Of the above flags, only MSG_OOB is specified by SUSv3. SUSv4 adds a specification for MSG_NOSIGNAL. MSG_DONTWAIT is not standardized, but appears on a few other UNIX implementations. MSG_MORE is Linux-specific.


Explanation:

I overlooked the fact that SUSv4 added a specification for MSG_NOSIGNAL.

2010-12-31

1262

In the example code at the bottom of the page, change the two lines that read:

    setsockopt(sockfd, IPPROTO_TCP, TCP_CORK, sizeof(optval));

to:

    setsockopt(sockfd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval));

Reported by Przemysław Pawełczyk.

2010-11-28

1276

In the fourth line of Section 61.8, change:

all kinds of TCP/IP packets

to:

all kinds of network packets


Explanation:

Using the term "TCP/IP" in this context is slightly confusing; better to use the more generic term "network".

Reported by Justin Pryzby.

2010-12-28

1327

Change the last sentence on the page:

POSIX AIO is described in [Gallmeister, 1995] and [Robbins & Robbins, 2003].

to:

POSIX AIO is described in [Gallmeister, 1995], [Robbins & Robbins, 2003], and the aio(7) manual page.


Explanation:

After TLPI went to press, I wrote manual pages for the POSIX AIO API.

2011-01-06

1356

Below the prototype box for epoll_create(), change:

(Since Linux 2.6.8, the size argument is ignored, because…

to:

(Since Linux 2.6.8, the size argument must be greater than zero but is otherwise ignored, because…


Explanation:

This fix was found as a consequence of a man-pages bug report by Joern Heissler.

2010-12-03

1358

Change the second-to-last line of Listing 63-4:

    if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, ev) == -1)

to:

    if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1)

Reported by Sangman Lee.

2011-03-07

1406

In the second paragraph of the small-font note two thirds of the way down the page (beginning "SUSv3 specifies..."), change:

that consist of one of more

to:

that consist of one or more


Reported by Subu Rama.

2011-01-04

1503

In the entry for "uint32_t data type", remove the page reference "378".


Explanation:

This change is required because of the changes on page 378.

2010-10-25