What this book covers
Chapter 1, Getting Started with the Linux Systems and the POSIX Standard, introduces the reader to the reasoning behind the existence of different operating systems. The Linux specifics are discussed, and the reader proceeds to the fundamentals of Unix-based OS programming. The kernel space and user spaces are mentioned as the System Call Interface is explained thoroughly. Afterward, we use this opportunity to present POSIX and some standard function calls in order to let the reader grasp the benefits of the system programming.
Chapter 2, Learning More about Process Management, expands on learnings from the previous chapter and states that if the operating system is the main resource manager, then the process is the main resource user. It does so through a routine, which could get complex and needs to be well analyzed. Therefore, the chapter goes through the main process’s life cycle - its startup, running, and final states. The nature of the thread is presented as well. We go through the OS’s scheduling algorithms as well. A sample C++ application is introduced and its main()
function is discussed as an entry point. In addition, different ways to initiate a process are presented: fork()
, vfork()
, and exec()
. Other fundamental functions such as wait()
, exit()
, pthread_create()
, and pthread_join()
are discussed as well.
Chapter 3, Navigating through the Filesystems, shows how the file is the basic resource representation in Linux - both for data and access to I/O devices. This abstraction allows the user to manipulate streams or store data in the same manner, through the same system interfaces. The file system structure - metadata and inodes, is discussed. Examples of C++ file system operations are presented to the reader. We use this opportunity to introduce the pipes as an initial instrument for inter-process communication. The string_view
C++20 object is offered as well. At the end, we mention signal handling as it will be required for later chapters.
Chapter 4, Diving Deep into the C++ Object, guides the reader through some core C++ features like the process of object creation and its initialization. We discuss lifetime object problems, temporaries, RVO, RAII pattern, and C++20. We also cover function objects and lambda expressions together with their specifics and guidance on how to use them. Next, we will get deeper into lambdas. In the end, we will focus on some specific examples of how to use lambdas in STL and multithreading.
Chapter 5, Handling Errors with C++, explores the different kinds of error reporting in C++ programming for Unix-based operating systems, such as error codes, exceptions, and asserts. We will discuss the best practices in exception handling and exception manipulation and what happens with uncaught exceptions in the system. We will discuss the exception specifications and why we prefer the noexcept
keyword. We will go through the performance impact when using exceptions and the mechanics behind them. Next, we will discuss how we can use std::optional
to handle errors. At the end, we will discuss what std::uncaught_exceptions
functionality provides.
Chapter 6, Concurrent System Programming with C++, discusses the fundamentals and the theory behind processes and threads in Unix-based operating systems. We will go through the changes in the memory model of C++ in order to natively support concurrency. We will get acquainted with the C++ primitives which enable multithreading support - thread, jthread, and task. Next, we will learn how to synchronize the execution of parallel code using C++ synchronization primitives. We will also investigate what STL provides in the direction of parallel algorithms. In the end, we will learn how to write lock-free code.
Chapter 7, Proceeding with Inter-process Communication, guides readers through the basic IPC mechanisms in the Linux environment (as they already have the impression of the multithreading’s challenges). It is important that the processes are able to communicate with each other easily, therefore, we go quickly through message queues. They allow the exchange of data without blocking processes. We will spend some time discussing synchronization mechanisms – semaphore and mutex- and then proceed with the shared memory. It provides quick access to some data and, at the same time, allows heterogeneous systems to have a common point for data exchange. At last, the sockets are frequently used, but mainly for their possibility to allow communication between computer systems on the network.
Chapter 8, Using Clocks, Timers, and Signals in Linux, introduces the signals and timers in Unix-based operating systems. We will initially present how the signaling system works and how the user can effectively manage the time of operations. We will cover what C++ language provides as functionality to handle clocks and timers. We will introduce the standard time API, std::chrono
, predefined clocks and times. Next, we will cover how to use them correctly and what to expect from them. Next, we will focus on the duration capabilities that the standard provides and user-defined clocks. Ultimately, we will cover the calendar and time zone libraries introduced in C++20.
Chapter 9, Understanding the C++ Memory Model, explores some new C++20 features. It guides the reader through some crucial remarks on how and why to manage dynamic resources. It proceeds with a discussion on the conditional variables and mutex usages, as well as lazy initialization and cache friendliness. An introduction to the C++ memory order follows as we discuss ways to choose from different synchronization mechanisms. The spinlock/ticketlock techniques are also presented.
Chapter 10, Using Coroutines in C++ for System Programming, talks about coroutines, an already existing term with implementations in some programming languages, but now they are introduced in C++20. They are described as stackless functions suspended during execution and resumed later. The chapter discusses those exact valuable features in the area of system programming. Their disadvantages are also discussed, for example, keeping the suspended coroutine state on the heap. Some practical usages are presented.