2009-09-01

Chapter 30: Threads: Thread Synchronization

In this chapter, we describe two tools that threads can use to synchronize their actions: mutexes and condition variables. Mutexes allow threads to synchronize their use of a shared resource, so that, for example, one thread doesn't try to access a shared variable at the same time as another thread is modifying it. Condition variables perform a complementary task: They allow threads to inform each other that a shared variable (or other shared resource) has changed state.

30 Threads: Thread Synchronization
30.1 Protecting Accesses to Shared Variables: Mutexes
        30.1.1 Statically Allocated Mutexes
        30.1.2 Locking and Unlocking a Mutex
        30.1.3 Performance of Mutexes
        30.1.4 Mutex Deadlocks
        30.1.5 Dynamically Initializing a Mutex
        30.1.6 Mutex Attributes
        30.1.7 Mutex Types
30.2 Signaling Changes of State: Condition Variables
        30.2.1 Statically Allocated Condition Variables
        30.2.2 Signaling and Waiting on Condition Variables
        30.2.3 Testing a Condition Variable’s Predicate
        30.2.4 Example Program: Joining Any Terminated Thread
        30.2.5 Dynamically Allocated Condition Variables
30.3 Summary
30.4 Exercises

Chapters 38, 39, and 40 are in copyedit

Chapter 37 came back from coyedit. Chapters 38, 39, and 40 are now in copyedit.

2009-08-31

Chapter 29: Threads: Introduction

In this and the next few chapters, we describe POSIX threads, often known as Pthreads. We won't attempt to cover the entire Pthreads API, since it is rather large. Various sources of further information about threads are listed at the end of this chapter.

These chapters mainly describe the standard behavior specified for the Pthreads API. In Section 33.5, we discuss those points where the two main Linux threading implementations--LinuxThreads and Native POSIX Threads Library (NPTL)--deviate from the standard.

In this chapter, we provide an overview of the operation of threads, and then look at how threads are created and how they terminate. We conclude with a discussion of some factors that may influence the choice of a multithreaded approach versus a multiprocess approach when designing an application.

29 Threads: Introduction
29.1 Overview
29.2 Background Details of the Pthreads API
29.3 Thread Creation
29.4 Thread Termination
29.5 Thread IDs
29.6 Joining with a Terminated Thread: pthread_join()
29.7 Detaching a Thread: pthread_detach()
29.8 Thread Attributes
29.9 Threads Versus Processes
29.10 Summary
29.11 Exercises

2009-08-29

Chapter 28: Process Creation and Program Execution in More Detail

This chapter extends the material presented in Chapters 24 to 27 by covering a variety of topics related to process creation and program execution. We describe process accounting, a kernel feature that writes an accounting record about each process on the system as it terminates. We then look at the Linux-specific clone() system call, which is the low-level API that is used to create threads on Linux. We follow this with some comparisons of the performance of fork(), vfork(), and clone(). We conclude with a summary of the effects of fork() and exec() on the attributes of a process.

28 Process Creation and Program Execution in More Detail
28.1 Process Accounting
28.2 The clone() System Call
        28.2.1 The clone() flags Argument
        28.2.2 Extensions to waitpid() for Cloned Children
28.3 Speed of Process Creation
28.4 Effect of exec() and fork() on Process Attributes
28.5 Summary
28.6 Exercises

2009-08-28

Chapter 37 is in copyedit

Chapter 34 already came back from copyedit, and Chapters 35 and 36 also went to copyedit and came back already. Now Chapter 37 is in copyedit.

Chapter 27: Program Execution

This chapter follows from our discussion of process creation and termination in the previous chapters. We now look at how a process can use the execve() system call to replace the program that it is running by a completely new program. We then show how to implement the system() function, which allows its caller to execute an arbitrary shell command.

27 Program Execution
27.1 Executing a New Program: execve()
27.2 The exec() Library Functions
        27.2.1 The PATH Environment Variable
        27.2.2 Specifying Program Arguments As a List
        27.2.3 Passing the Caller's Environment to the New Program
        27.2.4 Executing a File Referred to by a Descriptor: fexecve()
27.3 Interpreter Scripts
27.4 File Descriptors and exec()
27.5 Signals and exec()
27.6 Executing a Shell Command: system()
27.7 Implementing system()
27.8 Summary
27.9 Exercises

2009-08-27

Chapter 26: Monitoring Child Processes

In many application designs, a parent process needs to know when one of its child processes changes state--when the child terminates or is stopped by a signal. This chapter describes two techniques used in monitoring child processes: the wait() system call (and its variants) and the use of the SIGCHLD signal.

26 Monitoring Child Processes
26.1 Waiting on a Child Process
        26.1.1 The wait() System Call
        26.1.2 The waitpid() System Call
        26.1.3 The Wait Status Value
        26.1.4 Process Termination from a Signal Handler
        26.1.5 The waitid() System Call
        26.1.6 The wait3() and wait4() System Calls
26.2 Orphans and Zombies
26.3 The SIGCHLD Signal
        26.3.1 Establishing a Handler for SIGCHLD
        26.3.2 Delivery of SIGCHLD for Stopped Children
        26.3.3 Ignoring Dead Child Processes
26.4 Summary
26.5 Exercises

2009-08-26

Chapter 25: Process Termination

This chapter describes what happens when a process terminates. We begin by describing the use of exit() and _exit() to terminate a process. We discuss the use of exit handlers to automatically perform cleanups when a process calls exit(). We also consider some interactions between fork(), stdio buffers, and exit().

25 Process Termination
25.1 Terminating a Process: _exit() and exit()
25.2 Details of Process Termination
25.3 Exit Handlers
25.4 Interactions Between fork(), stdio Buffers, and _exit()
25.5 Summary
25.6 Exercises

2009-08-25

Chapter 24: Process Creation

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.

24 Process Creation
24.1 Overview of fork(), exit(), wait(), and execve()
24.2 Creating a New Process: fork()
        24.2.1 File Sharing Between Parent and Child
        24.2.2 Memory Semantics of fork()
24.3 The vfork() System Call
24.4 Race Conditions after fork()
24.5 Avoiding Race Conditions by Synchronizing with Signals
24.6 Summary
24.7 Exercises

2009-08-24

Chapter 23: Timers and Sleeping

A timer allows a process to schedule a notification for itself to occur at some time in the future. Sleeping allows a process (or thread) to suspend execution for a period of time. This chapter describes the interfaces used for setting timers and for sleeping. It covers the following topics:
  • the classical Unix APIs for setting interval timers (setitimer() and alarm()) to notify a process when a certain amount of time has passed;
  • the APIs that allow a process to sleep for a specified interval;
  • the POSIX.1b clocks and timers APIs; and
  • the Linux-specific timerfd facility, which allows the creation of timers whose expirations can be read from a file descriptor.

23 Timers and Sleeping
23.1 Interval Timers
23.2 Scheduling and Accuracy of Timers
23.3 Setting Timeouts on Blocking Operations
23.4 Suspending Execution for a Fixed Interval (Sleeping)
        23.4.1 Low-resolution Sleeping: sleep()
        23.4.2 High-resolution Sleeping: nanosleep()
23.5 POSIX Clocks
        23.5.1 Retrieving the Value of a Clock: timer_gettime()
        23.5.2 Updating the Value of a Clock: timer_gettime()
        23.5.3 Obtaining the Clock ID of a Specific Process or Thread
        23.5.4 Improved High-resolution Sleeping: clock_nanosleep()
23.6 POSIX Interval Timers
        23.6.1 Creating a Timer: timer_create()
        23.6.2 Arming and Disarming a Timer: timer_settime()
        23.6.3 Retrieving the Current Value of a Timer: timer_gettime()
        23.6.4 Deleting a Timer: timer_delete()
        23.6.5 Notification via a Signal (SIGEV_SIGNAL)
        23.6.6 Timer Overruns
        23.6.7 Notification via a Thread (SIGEV_THREAD)
23.7 Timers That Notify via File Descriptors: the timerfd API
23.8 Summary
23.9 Exercises

2009-08-22

Chapter 34 is in copyedit

Chapters 29 to 33 are back from copyedit. Chapter 34 has gone to copyedit.

2009-08-21

Chapter 22: Signals: Advanced Features

In this chapter, we continue our discussion of signals, covering a number of more advanced topics, including:
  • core dump files;
  • special cases regarding signal delivery, disposition, and handling;
  • synchronous and asynchronous generation of signals;
  • when and in what order signals are delivered;
  • interruption of system calls by signal handlers, and how to automatically restart interrupted system calls;
  • realtime signals;
  • using sigsuspend() to set the process signal mask wait for a signal to arrive;
  • using sigwaitinfo() (and sigtimedwait()) to synchronously wait for a signal to arrive;
  • using signalfd() to receive a signal via file descriptor; and
  • the older BSD and System V signal APIs.

22 Signals: Advanced Features
22.1 Core Dump Files
22.2 Special Cases for Signal Delivery, Disposition, and Handling
22.3 Interruptible and Uninterruptible Process Sleep States
22.4 Hardware-generated Signals
22.5 Synchronous and Asynchronous Signal Generation
22.6 Timing and Order of Signal Delivery
22.7 Implementation and Portability of signal()
22.8 Realtime Signals
        22.8.1 Sending Realtime Signals
        22.8.2 Handling Realtime Signals
22.9 Waiting for a Signal Using a Mask: sigsuspend()
22.10 Synchronously Waiting for a Signal
22.11 Fetching Signals via a File Descriptor
22.12 Interprocess Communication with Signals
22.13 Earlier Signal APIs (System V and BSD)
22.14 Summary
22.15 Exercises