Download Linux System Programming PDF

TitleLinux System Programming
TagsLinux Programming
File Size9.9 MB
Total Pages456
Table of Contents
Table of Contents
	Audience and Assumptions
	Contents of This Book
	Versions Covered in This Book
	Conventions Used in This Book
	Using Code Examples
	Safari® Books Online
	How to Contact Us
Chapter 1. Introduction and Essential Concepts
	System Programming
		Why Learn System Programming
		Cornerstones of System Programming
		System Calls
		The C Library
		The C Compiler
	APIs and ABIs
		POSIX and SUS History
		C Language Standards
		Linux and the Standards
		This Book and the Standards
	Concepts of Linux Programming
		Files and the Filesystem
		Users and Groups
		Interprocess Communication
		Error Handling
	Getting Started with System Programming
Chapter 2. File I/O
	Opening Files
		The open() System Call
		Owners of New Files
		Permissions of New Files
		The creat() Function
		Return Values and Error Codes
	Reading via read()
		Return Values
		Reading All the Bytes
		Nonblocking Reads
		Other Error Values
		Size Limits on read()
	Writing with write()
		Partial Writes
		Append Mode
		Nonblocking Writes
		Other Error Codes
		Size Limits on write()
		Behavior of write()
	Synchronized I/O
		fsync() and fdatasync()
		The O_SYNC Flag
	Direct I/O
	Closing Files
		Error Values
	Seeking with lseek()
		Seeking Past the End of a File
		Error Values
	Positional Reads and Writes
		Error Values
	Truncating Files
	Multiplexed I/O
		poll() Versus select()
	Kernel Internals
		The Virtual Filesystem
		The Page Cache
		Page Writeback
Chapter 3. Buffered I/O
	User-Buffered I/O
		Block Size
	Standard I/O
		File Pointers
	Opening Files
	Opening a Stream via File Descriptor
	Closing Streams
		Closing All Streams
	Reading from a Stream
		Reading a Character at a Time
		Reading an Entire Line
		Reading Binary Data
	Writing to a Stream
		Writing a Single Character
		Writing a String of Characters
		Writing Binary Data
	Sample Program Using Buffered I/O
	Seeking a Stream
		Obtaining the Current Stream Position
	Flushing a Stream
	Errors and End-of-File
	Obtaining the Associated File Descriptor
	Controlling the Buffering
	Thread Safety
		Manual File Locking
		Unlocked Stream Operations
	Critiques of Standard I/O
Chapter 4. Advanced File I/O
	Scatter/Gather I/O
		readv() and writev()
	Event Poll
		Creating a New Epoll Instance
		Controlling Epoll
		Waiting for Events with Epoll
		Edge- Versus Level-Triggered Events
	Mapping Files into Memory
		Mapping Example
		Advantages of mmap()
		Disadvantages of mmap()
		Resizing a Mapping
		Changing the Protection of a Mapping
		Synchronizing a File with a Mapping
		Giving Advice on a Mapping
	Advice for Normal File I/O
		The posix_fadvise() System Call
		The readahead() System Call
		Advice Is Cheap
	Synchronized, Synchronous, and Asynchronous Operations
		Asynchronous I/O
	I/O Schedulers and I/O Performance
		Disk Addressing
		The Life of an I/O Scheduler
		Helping Out Reads
		Selecting and Configuring Your I/O Scheduler
		Optimzing I/O Performance
Chapter 5. Process Management
	Programs, Processes, and Threads
	The Process ID
		Process ID Allocation
		The Process Hierarchy
		Obtaining the Process ID and Parent Process ID
	Running a New Process
		The Exec Family of Calls
		The fork() System Call
	Terminating a Process
		Other Ways to Terminate
	Waiting for Terminated Child Processes
		Waiting for a Specific Process
		Even More Waiting Versatility
		BSD Wants to Play: wait3() and wait4()
		Launching and Waiting for a New Process
	Users and Groups
		Real, Effective, and Saved User and Group IDs
		Changing the Real or Saved User or Group ID
		Changing the Effective User or Group ID
		Changing the User and Group IDs, BSD Style
		Changing the User and Group IDs, HP-UX Style
		Preferred User/Group ID Manipulations
		Support for Saved User IDs
		Obtaining the User and Group IDs
	Sessions and Process Groups
		Session System Calls
		Process Group System Calls
		Obsolete Process Group Functions
Chapter 6. Advanced Process Management
	Process Scheduling
		I/O- Versus Processor-Bound Processes
		Preemptive Scheduling
	The Completely Fair Scheduler
	Yielding the Processor
		Legitimate Uses
	Process Priorities
		getpriority() and setpriority()
		I/O Priorities
	Processor Affinity
		sched_getaffinity() and sched_setaffinity()
	Real-Time Systems
		Hard Versus Soft Real-Time Systems
		Latency, Jitter, and Deadlines
		Linux’s Real-Time Support
		Linux Scheduling Policies and Priorities
		Setting Scheduling Parameters
		Precautions with Real-Time Processes
	Resource Limits
		The Limits
		Setting and Retrieving Limits
Chapter 7. Threading
	Binaries, Processes, and Threads
		Costs of Multithreading
		Alternatives to Multithreading
	Threading Models
		User-Level Threading
		Hybrid Threading
		Coroutines and Fibers
	Threading Patterns
		Event-Driven Threading
	Concurrency, Parallelism, and Races
		Race Conditions
		Linux Threading Implementations
		The Pthread API
		Linking Pthreads
		Creating Threads
		Thread IDs
		Terminating Threads
		Joining and Detaching Threads
		A Threading Example
		Pthread Mutexes
	Further Study
Chapter 8. File and Directory Management
	Files and Their Metadata
		The Stat Family
		Extended Attributes
		Extended Attribute Operations
		The Current Working Directory
		Creating Directories
		Removing Directories
		Reading a Directory’s Contents
		Hard Links
		Symbolic Links
	Copying and Moving Files
	Device Nodes
		Special Device Nodes
		The Random Number Generator
	Out-of-Band Communication
	Monitoring File Events
		Initializing inotify
		inotify Events
		Advanced Watch Options
		Removing an inotify Watch
		Obtaining the Size of the Event Queue
		Destroying an inotify Instance
Chapter 9. Memory Management
	The Process Address Space
		Pages and Paging
		Memory Regions
	Allocating Dynamic Memory
		Allocating Arrays
		Resizing Allocations
		Freeing Dynamic Memory
	Managing the Data Segment
	Anonymous Memory Mappings
		Creating Anonymous Memory Mappings
		Mapping /dev/zero
	Advanced Memory Allocation
		Fine-Tuning with malloc_usable_size() and malloc_trim()
	Debugging Memory Allocations
		Obtaining Statistics
	Stack-Based Allocations
		Duplicating Strings on the Stack
		Variable-Length Arrays
	Choosing a Memory Allocation Mechanism
	Manipulating Memory
		Setting Bytes
		Comparing Bytes
		Moving Bytes
		Searching Bytes
		Frobnicating Bytes
	Locking Memory
		Locking Part of an Address Space
		Locking All of an Address Space
		Unlocking Memory
		Locking Limits
		Is a Page in Physical Memory?
	Opportunistic Allocation
		Overcommitting and OOM
Chapter 10. Signals
	Signal Concepts
		Signal Identifiers
		Signals Supported by Linux
	Basic Signal Management
		Waiting for a Signal, Any Signal
		Execution and Inheritance
		Mapping Signal Numbers to Strings
	Sending a Signal
		Sending a Signal to Yourself
		Sending a Signal to an Entire Process Group
		Guaranteed-Reentrant Functions
	Signal Sets
		More Signal Set Functions
	Blocking Signals
		Retrieving Pending Signals
		Waiting for a Set of Signals
	Advanced Signal Management
		The siginfo_t Structure
		The Wonderful World of si_code
	Sending a Signal with a Payload
		Signal Payload Example
	A Flaw in Unix?
Chapter 11. Time
	Time’s Data Structures
		The Original Representation
		And Now, Microsecond Precision
		Even Better: Nanosecond Precision
		Breaking Down Time
		A Type for Process Time
	POSIX Clocks
		Time Source Resolution
	Getting the Current Time of Day
		A Better Interface
		An Advanced Interface
		Getting the Process Time
	Setting the Current Time of Day
		Setting Time with Precision
		An Advanced Interface for Setting the Time
	Playing with Time
	Tuning the System Clock
	Sleeping and Waiting
		Sleeping with Microsecond Precision
		Sleeping with Nanosecond Resolution
		An Advanced Approach to Sleep
		A Portable Way to Sleep
		Alternatives to Sleeping
		Simple Alarms
		Interval Timers
		Advanced Timers
Appendix A. GCC Extensions to the C Language
	Inline Functions
	Suppressing Inlining
	Pure Functions
	Constant Functions
	Functions That Do Not Return
	Functions That Allocate Memory
	Forcing Callers to Check the Return Value
	Marking Functions as Deprecated
	Marking Functions as Used
	Marking Functions or Parameters as Unused
	Packing a Structure
	Increasing the Alignment of a Variable
	Placing Global Variables in a Register
	Branch Annotation
	Getting the Type of an Expression
	Getting the Alignment of a Type
	The Offset of a Member Within a Structure
	Obtaining the Return Address of a Function
	Case Ranges
	Void and Function Pointer Arithmetic
	More Portable and More Beautiful in One Fell Swoop
Appendix B. Bibliography
	Books on the C Programming Language
	Books on Linux Programming
	Books on the Linux Kernel
	Books on Operating System Design
About the Author
Document Text Contents
Page 228

Page 229

2. The SysVinit source is located at It is licensed under the GNU General
Public License v2.

Ergo, multitasking introduces indeterminism similar in nature to the unpredictability
associated with paging. The solution with respect to multitasking is the same: eliminate
it. Of course, chances are you cannot simply abolish all other processes. If that were
possible in your environment, you probably would not need Linux to begin with—a
simple custom operating system would suffice. If, however, your system has multiple
processors, you can dedicate one or more of those processors to your real-time process
or processes. In effect, you can shield the real-time processes from multitasking.

We discussed system calls for manipulating a process’s CPU affinity earlier in this chap‐
ter. A potential optimization for real-time applications is to reserve one processor
for each real-time process and let all other processes time-share on the remaining

The simplest way to achieve this is to modify Linux’s init program, SysVinit,2 to do
something similar to the following before it begins the boot process:

cpu_set_t set;
int ret;

CPU_ZERO (&set); /* clear all CPUs */
ret = sched_getaffinity (0, sizeof (cpu_set_t), &set);
if (ret == −1) {
perror ("sched_getaffinity");
return 1;

CPU_CLR (1, &set); /* forbid CPU #1 */
ret = sched_setaffinity (0, sizeof (cpu_set_t), &set);
if (ret == −1) {
perror ("sched_setaffinity");
return 1;

This snippet grabs init’s current set of allowed processors, which we expect is all of them.
It then removes one processor, CPU #1, from the set and updates the list of allowed

Because the set of allowed processors is inherited from parent to child, and init is the
super-parent of all processes, all of the system’s processes will run with this set of allowed
processors. Consequently, no processes will ever run on CPU #1.

Next, modify your real-time process to run only on CPU #1:

cpu_set_t set;
int ret;

Real-Time Systems | 203

Page 455

Page 456

About the Author
Robert Love has been using and contributing to Linux since its earliest days, including
significant contributions to the Linux kernel and GNOME desktop environment. Rob‐
ert is Staff Software Engineer at Google, where he was a member of the team that de‐
signed and shipped Android. He currently works on Google’s web search infrastructure.
Robert holds a B.S. in Computer Science and a B.A. in Mathematics from the University
of Florida. He lives in Boston.

The image on the cover of Linux System Programming is a man in a flying machine.
Well before the Wright brothers achieved their first controlled heavier-than-air flight
in 1903, people around the world attempted to fly by simple and elaborate machines.
In the second or third century, Zhuge Liang of China reportedly flew in a Kongming
lantern, the first hot air balloon. Around the fifth or sixth centuries, many Chinese
people purportedly attached themselves to large kites to fly through the air.

It is also said that the Chinese created spinning toys that were early versions of heli‐
copters, the designs of which may have inspired Leonardo da Vinci in his initial attempts
at a solution to human flight. da Vinci also studied birds and designed parachutes, and
in 1845, he designed an ornithopter, a wing-flapping machine meant to carry humans
through the air. Though he never built it, the ornithopter’s birdlike structure influenced
the design of flying machines throughout the centuries.

The flying machine depicted on the cover is more elaborate than James Means’s model
soaring machine of 1893, which had no propellers. Means later printed an instruction
manual for his soaring machine, which in part states that “the summit of Mt. Willard,
near the Crawford House, N.H., will be found an excellent place” to experiment with
the machines.

But such experimentation was often dangerous. In the late nineteenth century, Otto
Lilienthal built monoplanes, biplanes, and gliders. He was the first to show that control
of human flight was within reach, and he gained the nickname “father of aerial testing,”
as he conducted more than 2,000 glider flights, sometimes traveling more than a thou‐
sand feet. He died in 1896 after breaking his spine during a crash landing.

Flying machines are also known as mechanical birds and airships, and are occasionally
called by more colorful names such as the Artificial Albatross. Enthusiasm for flying
machines remains high, as aeronautical buffs still build early flying machines today.

The cover image and chapter opening graphics are from the Dover Pictorial Archive.
The cover font is Adobe ITC Garamond. The text font is Adobe Minion Pro; the heading
font is Adobe Myriad Condensed; and the code font is Dalton Maag’s Ubuntu Mono.

Similer Documents