171 Matching Annotations
  1. Last 7 days
    1. This output tells us the timestamp when the I/O operation occurred, whether the I/O was a Read or Write operation, and how many bytes were involved in the I/O. The final column reflects the duration (expressed as latency or LAT) in milliseconds of the I/O.

      This explanation makes it clear how BCC tools like disksnoop provide detailed insights into disk performance. I find it interesting that latency is included, as it directly shows performance bottlenecks. I wonder how this information could be used to optimize system performance.

    2. The specifics of writing custom BCC tools are beyond the scope of this text, but the BCC package (which is installed on the Linux virtual machine we provide) provides a number of existing tools that monitor several areas of activity in a running Linux kernel. As an example, the BCC disksnoop tool traces disk I/O activity. Entering the command

      It’s helpful that BCC comes with prebuilt tools like disksnoop, so I can monitor disk I/O without having to write custom eBPF programs. I wonder what other areas of the kernel can be traced with existing BCC tools and how much detail they provide.

    3. Although eBPF provides a rich set of features for tracing within the Linux kernel, it traditionally has been very difficult to develop programs using its C interface. BCC was developed to make it easier to write tools using eBPF by providing a front-end interface in Python. A BCC tool is written in Python and it embeds C code that interfaces with the eBPF instrumentation, which in turn interfaces with the kernel. The BCC tool also compiles the C program into eBPF instructions and inserts it into the kernel using either probes or tracepoints, two techniques that allow tracing events in the Linux kernel.

      I like how the BCC simplifies using the eBPF by letting the developers write their tools in the Python while embedding C for the kernel-level tracing. It’s interesting that the Python acts as a front-end, compiling and also inserting the C code as the eBPF instructions. I’m curious how the probes and the tracepoints differ in practice when monitoring the kernel events.

    4. BCC (BPF Compiler Collection) is a rich toolkit that provides tracing features for Linux systems. BCC is a front-end interface to the eBPF (extended Berkeley Packet Filter) tool. The BPF technology was developed in the early 1990s for filtering traffic across a computer network. The “extended” BPF (eBPF) added various features to BPF. eBPF programs are written in a subset of C and are compiled into eBPF instructions, which can be dynamically inserted into a running Linux system. The eBPF instructions can be used to capture specific events (such as a certain system call being invoked) or to monitor system performance (such as the time required to perform disk I/O). To ensure that eBPF instructions are well behaved, they are passed through a verifier before being inserted into the running Linux kernel. The verifier checks to make sure that the instructions do not affect system performance or security.

      I find it interesting that the BCC uses an eBPF to dynamically insert the instructions into the running Linux system for tracing and the performance monitoring. The verifier seems like a crucial safeguard, ensuring that these types of instructions don’t compromise the security or the system performance. I wonder how complex it is to write the eBPF programs in practice, given the limited C subset and the need to pass the verification.This keeps it reflective, humanized, and shows the curiosity about the technical aspects.

    5. Debugging the interactions between user-level and kernel code is nearly impossible without a toolset that understands both sets of code and can instrument their interactions. For that toolset to be truly useful, it must be able to debug any area of a system, including areas that were not written with debugging in mind, and do so without affecting system reliability. This toolset must also have a minimal performance impact—ideally it should have no impact when not in use and a proportional impact during use. The BCC toolkit meets these requirements and provides a dynamic, secure, low-impact debugging environment.

      Debugging seems incredibly complex when both user-level and kernel code interact. It's very impressive that the BCC toolkit can dynamically monitor and debug the system with minimal performance impact. I’m curious about how it will ensure the security while accessing the parts of the system that weren’t designed for debugging.

    6. Whereas counter-based tools simply inquire on the current value of certain statistics that are maintained by the kernel, tracing tools collect data for a specific event—such as the steps involved in a system-call invocation.

      It’s interesting how tracing tools differ from counters. While counters give a snapshot of ongoing statistics, tracing actually follows specific events as they happen. I wonder how much performance overhead tracing adds compared to just reading counters.

    7. Windows systems provide the Windows Task Manager, a tool that includes information for current applications as well as processes, CPU and memory usage, and networking statistics. A screen shot of the task manager in Windows 10 appears in Figure 2.19.

      I use the Task Manager all the time to check which of the programs are using the most resources. It’s clear to see how the Windows provides both the process-level and the overall system statistics in one place. I’m curious how the Task Manager gathers this type of data behind the scenes—does it use counters like the Linux tools?

    8. Operating systems keep track of system activity through a series of counters, such as the number of system calls made or the number of operations performed to a network device or disk. The following are examples of Linux tools that use counters:

      It’s interesting to see how the operating systems use the simple counters to monitor the activity. I didn’t realize that just counting things like the system calls or the disk operations could provide such valuable insight into system performance. I wonder how accurate this method is compared to other monitoring techniques.

    9. We mentioned earlier that performance tuning seeks to improve performance by removing processing bottlenecks. To identify bottlenecks, we must be able to monitor system performance. Thus, the operating system must have some means of computing and displaying measures of system behavior. Tools may be characterized as providing either per-process or system-wide observations. To make these observations, tools may use one of two approaches—counters or tracing. We explore each of these in the following sections.

      It makes sense that before you can be able to fix the performance issues, you need to measure them. I like how much the text distinguishes between the monitoring of the individual processes versus the whole system—this seems key to spotting exactly where the bottleneck is.

    10. Operating-system debugging and process debugging frequently use different tools and techniques due to the very different nature of these two tasks. Consider that a kernel failure in the file-system code would make it risky for the kernel to try to save its state to a file on the file system before rebooting. A common technique is to save the kernel's memory state to a section of disk set aside for this purpose that contains no file system. If the kernel detects an unrecoverable error, it writes the entire contents of memory, or at least the kernel-owned parts of the system memory, to the disk area. When the system reboots, a process runs to gather the data from that area and write it to a crash dump file within a file system for analysis. Obviously, such strategies would be unnecessary for debugging ordinary user-level processes.

      It’s interesting how the kernel debugging has to plan for the fact that the file system itself might be broken. Writing the memory state to the dedicated disk area outside of the file system is clever—it ensures that crash data isn’t lost even if the system fails completely.

    11. Debugging user-level process code is a challenge. Operating-system kernel debugging is even more complex because of the size and complexity of the kernel, its control of the hardware, and the lack of user-level debugging tools. A failure in the kernel is called a crash. When a crash occurs, error information is saved to a log file, and the memory state is saved to a crash dump.

      It’s overwhelming to see how much harder it is to debug the kernel as compared to the regular programs. The idea of a ‘crash dump’ makes sense—capturing the kernel’s memory state seems to be essential since normal user-level tools can’t reach it.

    12. If a process fails, most operating systems write the error information to a log file to alert system administrators or users that the problem occurred. The operating system can also take a core dump—a capture of the memory of the process—and store it in a file for later analysis. (Memory was referred to as the “core” in the early days of computing.) Running programs and core dumps can be probed by a debugger, which allows a programmer to explore the code and memory of a process at the time of failure.

      I like how the text explains the core dumps—it’s interesting that the ‘core’ comes from the older terminology for the memory. I also find it useful such that the modern OS is able to capture the process’s memory when it fails, so that the developers can be able to inspect exactly what had gone wrong.

    13. We have mentioned debugging from time to time in this chapter. Here, we take a closer look. Broadly, debugging is the activity of finding and fixing errors in a system, both in hardware and in software. Performance problems are considered bugs, so debugging can also include performance tuning, which seeks to improve performance by removing processing bottlenecks. In this section, we explore debugging process and kernel errors and performance problems. Hardware debugging is outside the scope of this text.

      I find it interesting that debugging isn’t just about fixing crashes or wrong outputs—it also includes improving performance. I didn’t realize that the performance issues are the considered bugs which need debugging at the system or the kernel level.

    14. Finally, boot loaders for most operating systems—including Windows, Linux, and macOS, as well as both iOS and Android—provide booting into recovery mode or single-user mode for diagnosing hardware issues, fixing corrupt file systems, and even reinstalling the operating system. In addition to hardware failures, computer systems can suffer from software errors and poor operating-system performance, which we consider in the following section.

      It’s useful that modern boot loaders offer recovery or single-user modes. This makes troubleshooting hardware or software problems much easier, and it’s interesting that this feature is standard across Windows, Linux, macOS, iOS, and Android.

    15. To save space as well as decrease boot time, the Linux kernel image is a compressed file that is extracted after it is loaded into memory. During the boot process, the boot loader typically creates a temporary RAM file system, known as initramfs. This file system contains necessary drivers and kernel modules that must be installed to support the real root file system (which is not in main memory). Once the kernel has started and the necessary drivers are installed, the kernel switches the root file system from the temporary RAM location to the appropriate root file system location. Finally, Linux creates the systemd process, the initial process in the system, and then starts other services (for example, a web server and/or database). Ultimately, the system will present the user with a login prompt. In Section 11.5.2, we describe the boot process for Windows.

      It’s interesting how Linux uses a temporary RAM file system (initramfs) to get everything started before switching to the real root file system. I wonder how this compares in speed and reliability to the Windows boot process mentioned later.

    16. Many recent computer systems have replaced the BIOS-based boot process with UEFI (Unified Extensible Firmware Interface). UEFI has several advantages over BIOS, including better support for 64-bit systems and larger disks. Perhaps the greatest advantage is that UEFI is a single, complete boot manager and therefore is faster than the multistage BIOS boot process.

      It’s surprising how the UEFI replaces the old BIOS process. I’m curious—how much faster is UEFI in practice, and does it make a noticeable difference when starting up modern computers?

    17. Some computer systems use a multistage boot process: When the computer is first powered on, a small boot loader located in nonvolatile firmware known as BIOS is run. This initial boot loader usually does nothing more than load a second boot loader, which is located at a fixed disk location called the boot block. The program stored in the boot block may be sophisticated enough to load the entire operating system into memory and begin its execution. More typically, it is simple code (as it must fit in a single disk block) and knows only the address on disk and the length of the remainder of the bootstrap program.

      It’s interesting how the boot process is broken into stages. I wonder why the first boot loader has to be so tiny—just enough to load the next stage. Is it mainly because of space constraints in the BIOS firmware?

    18. At a slightly less tailored level, the system description can lead to the selection of precompiled object modules from an existing library. These modules are linked together to form the generated operating system. This process allows the library to contain the device drivers for all supported I/O devices, but only those needed are selected and linked into the operating system. Because the system is not recompiled, system generation is faster, but the resulting system may be overly general and may not support different hardware configurations.

      This explains how precompiled modules help build an OS quickly without full recompilation. It makes sense for speed, but I’m curious—how often does this ‘overly general’ approach cause compatibility problems with less common hardware?

    19. Configuring the system involves specifying which features will be included, and this varies by operating system. Typically, parameters describing how the system is configured is stored in a configuration file of some type, and once this file is created, it can be used in several ways.

      So the OS can be suited by adjusting the configuration axxording to the files—this is like setting up the preferences before using the program. I wonder how much of the flexibility the different OS really gives in terms of which of the features can be included or excluded.

    20. Most commonly, a computer system, when purchased, has an operating system already installed. For example, you may purchase a new laptop with Windows or macOS preinstalled. But suppose you wish to replace the preinstalled operating system or add additional operating systems. Or suppose you purchase a computer without an operating system. In these latter situations, you have a few options for placing the appropriate operating system on the computer and configuring it for use.

      This makes me think about how operating systems are tied to hardware out of the box. It’s interesting that we have flexibility to replace or add OSes, but it also raises questions about compatibility and setup—like dual-booting or clean installations.

    21. It is possible to design, code, and implement an operating system specifically for one specific machine configuration. More commonly, however, operating systems are designed to run on any of a class of machines with a variety of peripheral configurations.

      This highlights the trade-off in the OS design: you can make a system that’s perfectly optimized for any one machine, but most of the OS developers aim for the flexibility so that the system works across many of the devices. I find it interesting to see how this will affect the performance versus the compatibility.

    22. Because Android can run on an almost unlimited number of hardware devices, Google has chosen to abstract the physical hardware through the hardware abstraction layer, or HAL. By abstracting all hardware, such as the camera, GPS chip, and other sensors, the HAL provides applications with a consistent view independent of specific hardware. This feature, of course, allows developers to write programs that are portable across different hardware platforms.

      The Hardware Abstraction Layer (HAL) is considered pretty smart— as it hides the differences between the devices so the developers won't have to worry about the specifics of every phone or the tablet. This explains why the same Android app can run on so many different devices without any modification.

    23. Software designers for Android devices develop applications in the Java language, but they do not generally use the standard Java API. Google has designed a separate Android API for Java development. Java applications are compiled into a form that can execute on the Android RunTime ART, a virtual machine designed for Android and optimized for mobile devices with limited memory and CPU processing capabilities. Java programs are first compiled to a Java bytecode .class file and then translated into an executable .dex file. Whereas many Java virtual machines perform just-in-time (JIT) compilation to improve application efficiency, ART performs ahead-of-time (AOT) compilation. Here, .dex files are compiled into native machine code when they are installed on a device, from which they can execute on the ART. AOT compilation allows more efficient application execution as well as reduced power consumption, features that are crucial for mobile systems.

      It’s interesting that the Android doesn’t use any of the standard Java API but instead has its own. The ahead-of-time (AOT) compilation in ART seems really clever—it makes apps run faster and saves battery life, which is super important for mobile devices with limited resources.

    24. The Android operating system was designed by the Open Handset Alliance (led primarily by Google) and was developed for Android smartphones and tablet computers. Whereas iOS is designed to run on Apple mobile devices and is close-sourced, Android runs on a variety of mobile platforms and is open-sourced, partly explaining its rapid rise in popularity. The structure of Android appears in Figure 2.18.

      Android’s open-source nature and the support for the multiple mobile platforms is used to help explain why it became so popular very quickly, especially when compared to the closed iOS ecosystem which only runs on the Apple devices.

    25. Apple has released the Darwin operating system as open source. As a result, various projects have added extra functionality to Darwin, such as the X-11 windowing system and support for additional file systems. Unlike Darwin, however, the Cocoa interface, as well as other proprietary Apple frameworks available for developing macOS applications, are closed.

      Apple is said to have released the Darwin operating system such as an open source. As a result, various projects have added the extra functionality to the Darwin, such as the X-11 windowing system and the support for the additional file systems. Unlike the Darwin, however, the Cocoa interface, as well as the other proprietary Apple frameworks are said to be available for the developing macOS applications, are closed.

    26. In Section 2.8.3, we described how the overhead of message passing between different services running in user space compromises the performance of microkernels. To address such performance problems, Darwin combines Mach, BSD, the I/O kit, and any kernel extensions into a single address space. Thus, Mach is not a pure microkernel in the sense that various subsystems run in user space. Message passing within Mach still does occur, but no copying is necessary, as the services have access to the same address space.

      Darwin improves the microkernel performance by combining the mac, BSD, and the I/O Kit, and the kernel extensions into the single address space. This design is used for reducing the overhead of message passing between the services because they share the memory, so no copying is needed—showing the practical way to balance the microkernel modularity with its efficiency.

    27. Beneath the system-call interface, Mach provides fundamental operating-system services, including memory management, CPU scheduling, and interprocess communication (IPC) facilities such as message passing and remote procedure calls (RPCs). Much of the functionality provided by Mach is available through kernel abstractions, which include tasks (a Mach process), threads, memory objects, and ports (used for IPC). As an example, an application may create a new process using the BSD POSIX fork() system call. Mach will, in turn, use a task kernel abstraction to represent the process in the kernel.

      Mac is used for handling the core OS tasks like the memory management, CPU scheduling, and the communication between processes using the abstractions such as the tasks, threads, memory objects, and the ports. For instance, when the program calls the POSIX fork() to create the new process, mac represents that process internally as a task—showing how the kernel translates into a high-level calls into its own mechanisms.

    28. Whereas most operating systems provide a single system-call interface to the kernel—such as through the standard C library on UNIX and Linux systems—Darwin provides two system-call interfaces: Mach system calls (known as traps) and BSD system calls (which provide POSIX functionality). The interface to these system calls is a rich set of libraries that includes not only the standard C library but also libraries that provide networking, security, and progamming language support (to name just a few).

      Darwin is very unique because it is said to offer two separate system-call interfaces: Mach calls for low-level kernel interactions and BSD calls for POSIX functionality. This dual interface, combined with extensive libraries for networking, security, and programming, gives developers a lot of flexibility compared to typical single-interface systems.

    29. The iOS operating system is generally much more restricted to developers than macOS and may even be closed to developers. For example, iOS restricts access to POSIX and BSD APIs on iOS, whereas they are openly available to developers on macOS.

      iOS is much more locked down as compared to the macOS. Developers have very limited access to the low-level APIs like the POSIX and the BSD, which are freely available on the macOS. This highlights how the Apple prioritizes security and the control on the mobile devices.

    30. Because macOS is intended for desktop and laptop computer systems, it is compiled to run on Intel architectures. iOS is designed for mobile devices and thus is compiled for ARM-based architectures. Similarly, the iOS kernel has been modified somewhat to address specific features and needs of mobile systems, such as power management and aggressive memory management. Additionally, iOS has more stringent security settings than macOS.

      This section defines how the same underlying OS (Darwin) is adapted for a different hardware. macOS targets the Intel desktops/laptops, while the iOS is optimized for the ARM mobile devices with the extra features like an advanced power management and the stricter security controls.

    31. Kernel environment. This environment, also known as Darwin, includes the Mach microkernel and the BSD UNIX kernel. We will elaborate on Darwin shortly.

      This highlights the foundation of the macOS and the iOS. Darwin, with its Mach microkernel and the BSD UNIX components, forms the core of the operating system, handling the essential functions like the process management, memory, and the system calls.

    32. Core frameworks. This layer defines frameworks that support graphics and media including, Quicktime and OpenGL.

      This layer shows just how the operating systems are said to provide the built-in support for the multimedia and the graphics. QuickTime and the OpenGL make it easier for the developers to create the media-rich applications without having to handle the low-level graphics or the video processing themselves.

    33. Application frameworks layer. This layer includes the Cocoa and Cocoa Touch frameworks, which provide an API for the Objective-C and Swift programming languages. The primary difference between Cocoa and Cocoa Touch is that the former is used for developing macOS applications, and the latter by iOS to provide support for hardware features unique to mobile devices, such as touch screens.

      It’s surprising how the Cocoa and Cocoa Touch serve the similar purposes but are said to be tailored for the different devices. Cocoa Touch supporting the touch-specific hardware really highlights how the APIs need to adapt to the features of the device, not just the operating system.

    34. User experience layer. This layer defines the software interface that allows users to interact with the computing devices. macOS uses the Aqua user interface, which is designed for a mouse or trackpad, whereas iOS uses the Springboard user interface, which is designed for touch devices.

      It’s fascinating to see how the user experience layer really suits the interface to the type of device. I hadn’t realized that macOS and iOS use completely different interfaces (Aqua vs. Springboard) even though they share underlying system components. It makes me think about how much design affects usability on different devices.

    35. In practice, very few operating systems adopt a single, strictly defined structure. Instead, they combine different structures, resulting in hybrid systems that address performance, security, and usability issues. For example, Linux is monolithic, because having the operating system in a single address space provides very efficient performance. However, it also modular, so that new functionality can be dynamically added to the kernel. Windows is largely monolithic as well (again primarily for performance reasons), but it retains some behavior typical of microkernel systems, including providing support for separate subsystems (known as operating-system personalities) that run as user-mode processes. Windows systems also provide support for dynamically loadable kernel modules. We provide case studies of Linux and Windows 10 in Chapter 20 and Chapter 21, respectively. In the remainder of this section, we explore the structure of three hybrid systems: the Apple macOS operating system and the two most prominent mobile operating systems—iOS and Android.

      It’s interesting to see how most of the modern operating systems are really hybrids rather than strictly monolithic or microkernel. I hadn’t realized how theLinux combines the monolithic efficiency with the modular flexibility, and that the Windows mixes the monolithic performance with some microkernel-like features. I wonder how these hybrid designs are used to affect the speed and the stability of the OS in the real-world use.

    36. The idea of the design is for the kernel to provide core services, while other services are implemented dynamically, as the kernel is running. Linking services dynamically is preferable to adding new features directly to the kernel, which would require recompiling the kernel every time a change was made. Thus, for example, we might build CPU scheduling and memory management algorithms directly into the kernel and then add support for different file systems by way of loadable modules.

      I like how this paragraph explains about the benefit of dynamic linking. It makes sense that the kernel handles core functions like CPU scheduling and memory management, while less essential features—like different file systems—can be added on the fly. It makes me curious: how does the OS make sure a newly loaded module doesn’t interfere with the core kernel services?

    37. Perhaps the best current methodology for operating-system design involves using loadable kernel modules (LKMs). Here, the kernel has a set of core components and can link in additional services via modules, either at boot time or during run time. This type of design is common in modern implementations of UNIX, such as Linux, macOS, and Solaris, as well as Windows.

      This part emphasizes how the modern day operating systems have evolved to be more flexible. I find it interesting such that the loadable kernel modules let the OS add or remove the services without rebuilding the whole kernel. It makes me wonder: how does the system ensure the stability when the new modules are loaded at the run time?”

    38. Unfortunately, the performance of microkernels can suffer due to increased system-function overhead. When two user-level services must communicate, messages must be copied between the services, which reside in separate address spaces. In addition, the operating system may have to switch from one process to the next to exchange the messages. The overhead involved in copying messages and switching between processes has been the largest impediment to the growth of microkernel-based operating systems. Consider the history of Windows NT: The first release had a layered microkernel organization. This version's performance was low compared with that of Windows 95. Windows NT 4.0 partially corrected the performance problem by moving layers from user space to kernel space and integrating them more closely. By the time Windows XP was designed, Windows architecture had become more monolithic than microkernel. Section 2.8.5.1 will describe how macOS addresses the performance issues of the Mach microkernel.

      Microkernels often face performance challenges because communication between user-level services requires message copying and process switching. This overhead can be said to slow down the system, as seen in the early versions of the Windows NT, which can be used as a layered microkernel architecture. Over time, performance concerns led Windows NT and XP to adopt a more monolithic approach, integrating layers into the kernel. Solutions like those in macOS (discussed later) aim to address these efficiency issues while retaining microkernel benefits.

    39. Another example is QNX, a real-time operating system for embedded systems. The QNX Neutrino microkernel provides services for message passing and process scheduling. It also handles low-level network communication and hardware interrupts. All other services in QNX are provided by standard processes that run outside the kernel in user mode.

      QNX demonstrates the microkernel design in the context of the real-time and embedded systems. Its Neutrino microkernel is said to manage the essential functions like the message passing, process scheduling, network communication, and the hardware interrupts, while all the other services run as a separate user-mode processes. This separation is said to enhance the reliability and it also simplifies the system maintenance and its updates.

    40. Perhaps the best-known illustration of a microkernel operating system is Darwin, the kernel component of the macOS and iOS operating systems. Darwin, in fact, consists of two kernels, one of which is the Mach microkernel. We will cover the macOS and iOS systems in further detail in Section 2.8.5.1.

      Darwin serves as an important example of a microkernel-based on the operating system. It forms the kernel foundation for macOS and iOS, incorporating the Mach microkernel as one of its core components. This highlights how modern operating systems can use microkernel principles while supporting complex, feature-rich environments.

    41. One benefit of the microkernel approach is that it makes extending the operating system easier. All new services are added to user space and consequently do not require modification of the kernel. When the kernel does have to be modified, the changes tend to be fewer, because the microkernel is a smaller kernel. The resulting operating system is easier to port from one hardware design to another. The microkernel also provides more security and reliability, since most services are running as user—rather than kernel—processes. If a service fails, the rest of the operating system remains untouched.

      The microkernel design is said to offer the several advantages: it simplifies the extending the operating system, since the new services can be added in the user space without the changing of the kernel. The smaller the kernel is, the easier it is to modify and port across the hardware platforms. Additionally, because most of the services run in the user space, the system gains the improved security and the reliability—if a service crashes, it does not affect the rest of the operating system.

    42. The main function of the microkernel is to provide communication between the client program and the various services that are also running in user space. Communication is provided through message passing, which was described in Section 2.3.3.5. For example, if the client program wishes to access a file, it must interact with the file server. The client program and service never interact directly. Rather, they communicate indirectly by exchanging messages with the microkernel.

      In a microkernel system, the kernel’s primary role is to act as a communication hub between user-space programs and services. Instead of direct interaction, clients and services exchange messages via the microkernel. For instance, a program requesting file access communicates with the file server through the kernel, ensuring controlled, indirect interaction and maintaining the modular structure of the system.

    43. We have already seen that the original UNIX system had a monolithic structure. As UNIX expanded, the kernel became large and difficult to manage. In the mid-1980s, researchers at Carnegie Mellon University developed an operating system called Mach that modularized the kernel using the microkernel approach. This method structures the operating system by removing all nonessential components from the kernel and implementing them as user-level programs that reside in separate address spaces. The result is a smaller kernel. There is little consensus regarding which services should remain in the kernel and which should be implemented in user space. Typically, however, microkernels provide minimal process and memory management, in addition to a communication facility. Figure 2.15 illustrates the architecture of a typical microkernel.

      The microkernel approach has emerged for addressing the complexity of the large monolithic kernels, like the UNIX. By moving the nonessential services out of the kernel into the user-space programs, the kernel becomes smaller and very easier to manage. Microkernels usually are said to handle only the core tasks—such as the process and the memory management and the interprocess communication—while the other services are said to run separately, improving the modularity and the maintainability.

    44. Layered systems have been successfully used in computer networks (such as TCP/IP) and web applications. Nevertheless, relatively few operating systems use a pure layered approach. One reason involves the challenges of appropriately defining the functionality of each layer. In addition, the overall performance of such systems is poor due to the overhead of requiring a user program to traverse through multiple layers to obtain an operating-system service. Some layering is common in contemporary operating systems, however. Generally, these systems have fewer layers with more functionality, providing most of the advantages of modularized code while avoiding the problems of layer definition and interaction.

      While the layered approach is said to offer the clarity and the modularity, it is rarely used in its pure form in operating systems. Defining precise responsibilities for each layer is difficult, and performance can suffer because service requests must pass through multiple layers. Modern systems often use a compromise: fewer, broader layers that retain modular benefits while reducing overhead and complexity.

    45. Each layer is implemented only with operations provided by lower-level layers. A layer does not need to know how these operations are implemented; it needs to know only what these operations do. Hence, each layer hides the existence of certain data structures, operations, and hardware from higher-level layers.

      Each layer acts like a “black box,” using services from lower layers without needing to know their internal workings. This abstraction hides implementation details, so higher layers focus only on what operations do, not how they are carried out, which simplifies design and enhances modularity.

    46. The main advantage of the layered approach is simplicity of construction and debugging. The layers are selected so that each uses functions (operations) and services of only lower-level layers. This approach simplifies debugging and system verification. The first layer can be debugged without any concern for the rest of the system, because, by definition, it uses only the basic hardware (which is assumed correct) to implement its functions. Once the first layer is debugged, its correct functioning can be assumed while the second layer is debugged, and so on. If an error is found during the debugging of a particular layer, the error must be on that layer, because the layers below it are already debugged. Thus, the design and implementation of the system are simplified.

      The layered approach makes the building and the debugging an operating system much easier. Every layer depends solely on the ones beneath it, allowing the developers to test any one layer individually. After a lower layer is verified for the functioning properly, any errors that are detected in the higher layers must be traceable to it, simplifying that the process of identifying and resolving issues while enhancing the overall reliability of the system

    47. An operating-system layer is an implementation of an abstract object made up of data and the operations that can manipulate those data. A typical operating-system layer—say, layer M—consists of data structures and a set of functions that can be invoked by higher-level layers. Layer M, in turn, can invoke operations on lower-level layers.

      An operating-system layer functions as a fundamental component, integrating the data with the operations that manipulate that data. Each layer (such as layer M) offers the services to the layers situated above it while relying on the layers beneath it for the foundational operations. This structured method helps in the system organization, enhancing its comprehensibility, maintainability, and the adaptability.

    48. The monolithic approach is often known as a tightly coupled system because changes to one part of the system can have wide-ranging effects on other parts. Alternatively, we could design a loosely coupled system. Such a system is divided into separate, smaller components that have specific and limited functionality. All these components together comprise the kernel. The advantage of this modular approach is that changes in one component affect only that component, and no others, allowing system implementers more freedom in creating and changing the inner workings of the system.

      Monolithic kernels are said to be challenging to implement and modify due to their very large, unified structure. However, they offer high performance because system calls involve minimal overhead and internal kernel communication is very fast. This speed advantage is why monolithic designs remain common in operating systems like UNIX, Linux, and Windows despite their complexity.

    49. Despite the apparent simplicity of monolithic kernels, they are difficult to implement and extend. Monolithic kernels do have a distinct performance advantage, however: there is very little overhead in the system-call interface, and communication within the kernel is fast. Therefore, despite the drawbacks of monolithic kernels, their speed and efficiency explains why we still see evidence of this structure in the UNIX, Linux, and Windows operating systems.

      Monolithic kernels are said to be challenging to implement and modify due to their very large, unified structure. However, they offer high performance because system calls involve minimal overhead and internal kernel communication is very fast. This speed advantage is why monolithic designs remain common in operating systems like UNIX, Linux, and Windows despite their complexity.

    50. The Linux operating system is based on UNIX and is structured similarly, as shown in Figure 2.13. Applications typically use the glibc standard C library when communicating with the system call interface to the kernel. The Linux kernel is monolithic in that it runs entirely in kernel mode in a single address space, but as we shall see in Section 2.8.4, it does have a modular design that allows the kernel to be modified during run time.

      Linux, like the UNIX, follows a largely monolithic structure but this includes the modular features. The kernel operates completely in the kernel mode within the unified address space and it facilitates the loadable modules, enabling the components of the kernel to be added, removed, or updated while the system is running. Applications are used to interact with the kernel through the glibc standard C library, serving as the conduit for the system calls.

    51. An example of such limited structuring is the original UNIX operating system, which consists of two separable parts: the kernel and the system programs. The kernel is further separated into a series of interfaces and device drivers, which have been added and expanded over the years as UNIX has evolved. We can view the traditional UNIX operating system as being layered to some extent, as shown in Figure 2.12. Everything below the system-call interface and above the physical hardware is the kernel. The kernel provides the file system, CPU scheduling, memory management, and other operating-system functions through system calls. Taken in sum, that is an enormous amount of functionality to be combined into one single address space.

      The original UNIX OS illustrates a partially layered structure. While it is mostly monolithic, it separates the kernel from system programs and further divides the kernel into interfaces and device drivers. The kernel handles core functions—like file systems, CPU scheduling, and the memory management—through system calls, all within a single address space, demonstrating how even limited structuring can help organize a complex operating system.

    52. The simplest structure for organizing an operating system is no structure at all. That is, place all of the functionality of the kernel into a single, static binary file that runs in a single address space. This approach—known as a monolithic structure—is a common technique for designing operating systems.

      A monolithic structure is the simplest way for organizing the operating system: all the kernel functions are compiled into the single large binary which runs in one address space. While straightforward, this design can make the debugging, updating, and maintaining the system becomes more difficult, because every part of the kernel is tightly interconnected. Many of the early operating systems have used this approach.

    53. A system as large and complex as a modern operating system must be engineered carefully if it is to function properly and be modified easily. A common approach is to partition the task into small components, or modules, rather than have one single system. Each of these modules should be a well-defined portion of the system, with carefully defined interfaces and functions. You may use a similar approach when you structure your programs: rather than placing all of your code in the main() function, you instead separate logic into a number of functions, clearly articulate parameters and return values, and then call those functions from main().

      Modern operating systems are meant to be extremely complex, so breaking them into the modules makes the development and the maintenance manageable.Every module manages a distinct, clearly defined function and interacts with the other modules via explicit interfaces. This modular method resembles effective programming practices, as the code is separated into functions with specified inputs and outputs instead of consolidating everything within the main(). It enhances readability, maintainability, and also decreases errors.

    54. As is true in other systems, major performance improvements in operating systems are more likely to be the result of better data structures and algorithms than of excellent assembly-language code. In addition, although operating systems are large, only a small amount of the code is critical to high performance; the interrupt handlers, I/O manager, memory manager, and CPU scheduler are probably the most critical routines. After the system is written and is working correctly, bottlenecks can be identified and can be refactored to operate more efficiently.

      As is true in other systems, major performance improvements in operating systems are more likely to be the result of better data structures and algorithms than of excellent assembly-language code. In addition, although operating systems are large, only a small amount of the code is critical to high performance; the interrupt handlers, I/O manager, memory manager, and CPU scheduler are probably the most critical routines. After the system is written and is working correctly, bottlenecks can be identified and can be refactored to operate more efficiently.

    55. The advantages of using a higher-level language, or at least a systems-implementation language, for implementing operating systems are the same as those gained when the language is used for application programs: the code can be written faster, is more compact, and is easier to understand and debug. In addition, improvements in compiler technology will improve the generated code for the entire operating system by simple recompilation. Finally, an operating system is far easier to port to other hardware if it is written in a higher-level language. This is particularly important for operating systems that are intended to run on several different hardware systems, such as small embedded devices, Intel x86 systems, and ARM chips running on phones and tablets.

      Using the higher-level languages for the operating system development offers the several key benefits: code can be written more quickly, is easier to read and debug, and is generally more compact. Compiler improvements automatically enhance the efficiency of the OS through recompilation. Additionally, the high-level languages make the porting of the OS to the different hardware platforms much easier—such as a crucial advantage for the systems designed for running on diverse devices, from embedded systems to desktop PCs and mobile ARM-based devices.

    56. Early operating systems were written in assembly language. Now, most are written in higher-level languages such as C or C++, with small amounts of the system written in assembly language. In fact, more than one higher-level language is often used. The lowest levels of the kernel might be written in assembly language and C. Higher-level routines might be written in C and C++, and system libraries might be written in C++ or even higher-level languages. Android provides a nice example: its kernel is written mostly in C with some assembly language. Most Android system libraries are written in C or C++, and its application frameworks—which provide the developer interface to the system—are written mostly in Java. We cover Android's architecture in more detail in Section 2.8.5.2.

      This passage emphasizes the importance and the evolution of the operating system development from the assembly language to the higher-level languages like C and C++. Modern OS kernels often use a mix of languages: low-level routines for the hardware control in the assembly or C, system libraries in the C or C++, and the higher-level application frameworks in the languages such as Java. Android is a clear example, showing how the different layers of the OS stack are used for implementation in the different languages to balance the performance, portability, and also the developer accessibility.

    57. Policy decisions are important for all resource allocation. Whenever it is necessary to decide whether or not to allocate a resource, a policy decision must be made. Whenever the question is how rather than what, it is a mechanism that must be determined.

      This passage emphasizes the difference between the policy and the mechanism in the resource management. A policy defines what should be done—for example, deciding which of the process gets the access to the resource—while a mechanism defines how the decision should be implemented, such as a specific algorithm or the procedure used to allocate such a resource. Recognizing this separation helps in designing the flexible and adaptable operating systems.

    58. We can make a similar comparison between commercial and open-source operating systems. For instance, contrast Windows, discussed above, with Linux, an open-source operating system that runs on a wide range of computing devices and has been available for over 25 years. The “standard” Linux kernel has a specific CPU scheduling algorithm (covered in Section 5.7.1), which is a mechanism that supports a certain policy. However, anyone is free to modify or replace the scheduler to support a different policy.

      This passage illustrates the separation of policy and mechanism in practice, using the Windows versus Linux as the examples. In Linux, the CPU scheduler is used for representing a mechanism, while the scheduling algorithm (policy) determines how much of the CPU time is being allocated. Unlike most of the commercial operating systems, Linux is an open source, so the users can be able to modify or replace the scheduler to implement a different policy without changing any of the underlying mechanism. This flexibility is a key advantage of open-source systems.

    59. The separation of policy and mechanism is important for flexibility. Policies are likely to change across places or over time. In the worst case, each change in policy would require a change in the underlying mechanism. A general mechanism flexible enough to work across a range of policies is preferable. A change in policy would then require redefinition of only certain parameters of the system. For instance, consider a mechanism for giving priority to certain types of programs over others. If the mechanism is properly separated from policy, it can be used either to support a policy decision that I/O-intensive programs should have priority over CPU-intensive ones or to support the opposite policy.

      This passage highlights why separating policy from mechanism increases flexibility in an operating system. Policies often change depending on context or over time, and if mechanisms were tightly coupled to policies, any change would require redesigning the mechanism. By keeping the mechanisms general and flexible, only the policy parameters needs to be adjusted. For example, the priority mechanism can be used to support the different policies, such as giving the preference to the I/O-intensive programs or the CPU-intensive programs, without modifying any of the underlying mechanism itself.

    60. One important principle is the separation of policy from mechanism. Mechanisms determine how to do something; policies determine what will be done. For example, the timer construct (see Section 1.4.3) is a mechanism for ensuring CPU protection, but deciding how long the timer is to be set for a particular user is a policy decision.

      This principle emphasizes distinguishing between mechanisms and policies. Mechanisms define how the task is performed, while policies define what is to be done. For instance, a timer is a mechanism that enforces CPU usage limits, but setting the duration of the timer for each user is a policy decision. This separation allows flexibility in system behavior without changing the underlying implementation.

    61. Specifying and designing an operating system is a highly creative task. Although no textbook can tell you how to do it, general principles have been developed in the field of software engineering, and we turn now to a discussion of some of these principles.

      Designing an operating system requires a high degree of creativity, as there is no single formula or textbook method for doing it. However, software engineering principles provide general guidelines and best practices that can help structure the design process, ensuring the system is reliable, efficient, and maintainable.

    62. There is, in short, no unique solution to the problem of defining the requirements for an operating system. The wide range of systems in existence shows that different requirements can result in a large variety of solutions for different environments. For example, the requirements for Wind River VxWorks, a real-time operating system for embedded systems, must have been substantially different from those for Windows Server, a large multiaccess operating system designed for enterprise applications.

      There is, in short, no unique solution to the problem of defining the requirements for an operating system. The wide range of systems in existence shows that different requirements can result in a large variety of solutions for different environments. For example, the requirements for Wind River VxWorks, a real-time operating system for embedded systems, must have been substantially different from those for Windows Server, a large multiaccess operating system designed for enterprise applications.

    63. The first problem in designing a system is to define goals and specifications. At the highest level, the design of the system will be affected by the choice of hardware and the type of system: traditional desktop/laptop, mobile, distributed, or real time.

      The initial step in designing the operating system is defining the clear goals and their specifications. Key design decisions will depend on the hardware platform and the type of the system that is being developed—whether it’s the traditional desktop or the laptop, a mobile device, a distributed system, or the real-time system. These factors are used to influence the performance, capabilities, and the overall architecture for the OS.

    64. In sum, all of these differences mean that unless an interpreter, RTE, or binary executable file is written for and compiled on a specific operating system on a specific CPU type (such as Intel x86 or ARMv8), the application will fail to run. Imagine the amount of work that is required for a program such as the Firefox browser to run on Windows, macOS, various Linux releases, iOS, and Android, sometimes on various CPU architectures.

      Ultimately, an application can only run on a system if its interpreter, runtime environment (RTE), or compiled binary is designed for that specific operating system and CPU architecture. This explains why cross-platform applications, like the Firefox browser, require significant effort to support multiple OSes and hardware types, including Windows, macOS, Linux distributions, iOS, and Android, often across different processor architectures.

    65. Each operating system has a binary format for applications that dictates the layout of the header, instructions, and variables. Those components need to be at certain locations in specified structures within an executable file so the operating system can open the file and load the application for proper execution.

      Every operating system defines its own binary file format for applications, which specifies how the executable’s header, instructions, and variables are arranged. This structure ensures that the OS can correctly load the program into memory and execute it, making the binary format a critical factor in application compatibility across different systems.

    66. In theory, these three approaches seemingly provide simple solutions for developing applications that can run across different operating systems. However, the general lack of application mobility has several causes, all of which still make developing cross-platform applications a challenging task. At the application level, the libraries provided with the operating system contain APIs to provide features like GUI interfaces, and an application designed to call one set of APIs (say, those available from IOS on the Apple iPhone) will not work on an operating system that does not provide those APIs (such as Android). Other challenges exist at lower levels in the system, including the following.

      While the interpreted languages, virtual machines, and the cross-compilers can help the applications run on the multiple operating systems, achieving the true cross-platform compatibility is said to remain difficult. One major reason is that the different operating systems offer the different libraries and the APIs, particularly for the GUI and the system-level features. An app designed for the one OS (like iOS) may fail on the other(like Android) if the expected APIs aren’t available. Additionally, the lower-level differences in the system architecture, the memory management, and the file handling are used to create the further challenges for the developers trying to make the applications that are portable.

    67. In theory, these three approaches seemingly provide simple solutions for developing applications that can run across different operating systems. However, the general lack of application mobility has several causes, all of which still make developing cross-platform applications a challenging task. At the application level, the libraries provided with the operating system contain APIs to provide features like GUI interfaces, and an application designed to call one set of APIs (say, those available from IOS on the Apple iPhone) will not work on an operating system that does not provide those APIs (such as Android). Other challenges exist at lower levels in the system, including the following.

      Although the interpreted languages, the virtual machines, and the cross-compilers help with the cross-platform development, true application portability is still considered difficult. A major reason is that the operating systems are used to provide the different APIs, especially for the features like the graphical interfaces. An app is built for one OS (e.g., iOS) may fail on another (e.g., Android) because of the expected APIs aren’t said to be available. Additional challenges also arise from the low-level system differences, making this cross-platform development complex.

    68. 1. The application can be written in an interpreted language (such as Python or Ruby) that has an interpreter available for multiple operating systems. The interpreter reads each line of the source program, executes equivalent instructions on the native instruction set, and calls native operating system calls. Performance suffers relative to that for native applications, and the interpreter provides only a subset of each operating system's features, possibly limiting the feature sets of the associated applications.

      Applications developed in interpreted languages such as Python or Ruby can operate on various operating systems since the interpreter functions as an intermediary layer. The interpreter executes the program line by line, converting it into native instructions and calling the OS when needed. This enables compatibility across platforms but might reduce performance compared to native apps and could limit access to some features exclusive to specific operating systems.

    69. Based on our earlier discussion, we can now see part of the problem—each operating system provides a unique set of system calls. System calls are part of the set of services provided by operating systems for use by applications. Even if system calls were somehow uniform, other barriers would make it difficult for us to execute application programs on different operating systems. But if you have used multiple operating systems, you may have used some of the same applications on them. How is that possible?

      Each of the operating system has its own set of the system calls, which makes it very hard to run the applications across the different systems. Even if the system calls were standardized, the differences in the design and the implementation would still cause the ompatibility issues. Yet, we often are able to see the same applications (like the browsers or the word processors) working across the Windows, Linux, and the macOS. This is possible because of the applications are usually written against the APIs or the cross-platform frameworks, rather than directly using system calls, allowing them to be adapted to different operating systems.

    70. Object files and executable files typically have standard formats that include the compiled machine code and a symbol table containing metadata about functions and variables that are referenced in the program. For UNIX and Linux systems, this standard format is known as ELF (for Executable and Linkable Format). There are separate ELF formats for relocatable and executable files. One piece of information in the ELF file for executable files is the program's entry point, which contains the address of the first instruction to be executed when the program runs. Windows systems use the Portable Executable (PE) format, and macOS uses the Mach-O format.

      Executable and the object files follow the standard formats which include both the actual machine code and the metadata (like details about functions and variables). On UNIX and the Linux systems, this format is called ELF (Executable and Linkable Format), with the different versions for the relocatable and the executable files. ELF files also are used specify the entry point, which is the first instruction to run when the program starts. Other operating systems use different formats—Windows uses PE (Portable Executable), and macOS uses Mach-O.

    71. Source files are compiled into object files that are designed to be loaded into any physical memory location, a format known as an relocatable object file. Next, the linker combines these relocatable object files into a single binary executable file. During the linking phase, other object files or libraries may be included as well, such as the standard C or math library (specified with the flag -lm).

      When the programs are compiled, then the source code is initially transformed into the relocatable object files, which can be loaded into any of the memory addresses. The linker merges these types of the object file types into one of the executable file, also including external object files or libraries when necessary (for example, the math library with -lm). This process ensures that the completed program is comprehensive and ready to run

    72. The view of the operating system seen by most users is defined by the application and system programs, rather than by the actual system calls. Consider a user's PC. When a user's computer is running the macOS operating system, the user might see the GUI, featuring a mouse-and-windows interface. Alternatively, or even in one of the windows, the user might have a command-line UNIX shell. Both use the same set of system calls, but the system calls look different and act in different ways. Further confusing the user view, consider the user dual-booting from macOS into Windows. Now the same user on the same hardware has two entirely different interfaces and two sets of applications using the same physical resources. On the same hardware, then, a user can be exposed to multiple user interfaces sequentially or concurrently.

      Users primarily interact with the operating system through the interfaces (GUIs or command lines) and applications, rather than directly getting through the system calls. For example, macOS users can interact through the graphical interface or the UNIX shell, both able to use the same system calls, although they appear quite different.The Dual-booting macOS and the Windows showcases how the identical hardware can result in the distinctly different type of the user experiences and the environments, despite relying on the same foundational system resources

    73. Program loading and execution. Once a program is assembled or compiled, it must be loaded into memory to be executed. The system may provide absolute loaders, relocatable loaders, linkage editors, and overlay loaders. Debugging systems for either higher-level languages or machine language are needed as well.

      Program loading and execution services handle the process of getting compiled programs into memory so they can run. These include loaders (absolute, relocatable, overlay) and tools like linkage editors. Debugging support is also part of this category, helping programmers test and fix errors in either high-level code or machine language.

    74. File management. These programs create, delete, copy, rename, print, list, and generally access and manipulate files and directories.

      File management services provide everyday tools for working with files and directories. They let users create, delete, copy, rename, print, and list files, making it easier to organize and manage data without needing to use low-level system calls directly.

    75. Another aspect of a modern system is its collection of system services. Recall Figure 1.1, which depicted the logical computer hierarchy. At the lowest level is hardware. Next is the operating system, then the system services, and finally the application programs. System services, also known as system utilities, provide a convenient environment for program development and execution. Some of them are simply user interfaces to system calls. Others are considerably more complex. They can be divided into these categories:

      This part explains where system services fit in the computer hierarchy. They are situated between the operating system and the application programs, making it very easy for the developers and the users to interact with the system. Some services are just the simple tools which act as the front end for the system calls, while the others are more advanced and are used to provide the broader functionality. Essentially, system services (or utilities) give the programmers a convenient way for developing and running the programs without dealing directly with the low-level details.

    76. Typically, system calls providing protection include set_permission() and get_permission(), which manipulate the permission settings of resources such as files and disks. The allow_user() and deny_user() system calls specify whether particular users can—or cannot—be allowed access to certain resources. We cover protection in Chapter 17 and the much larger issue of security—which involves using protection against external threats—in Chapter 16.

      This paragraph highlights about how the operating systems are used to employ the certain system calls to manage protection and access control. Functions such as the set_permission() and the get_permission()manage permissions for its resources, whereas allow_user() and the deny_user() are used to specify which of the users can access the particular files or the devices. This indicates that the protection is meant exclusively to manage internal access rights, whereas the security (addressed later) focuses on defending against external threats

    77. Protection provides a mechanism for controlling access to the resources provided by a computer system. Historically, protection was a concern only on multiprogrammed computer systems with several users. However, with the advent of networking and the Internet, all computer systems, from servers to mobile handheld devices, must be concerned with protection.

      Why has protection become an important concern for all the computer systems, not just the multiprogrammed systems with its multiple users?

    78. Both of the models just discussed are common in operating systems, and most systems implement both. Message passing is useful for exchanging smaller amounts of data, because no conflicts need be avoided. It is also easier to implement than is shared memory for intercomputer communication. Shared memory allows maximum speed and convenience of communication, since it can be done at memory transfer speeds when it takes place within a computer. Problems exist, however, in the areas of protection and synchronization between the processes sharing memory.

      What are the main advantages and the disadvantages of using the message passing versus the shared memory for interprocess communication, and in what situations is each model more suitable?

    79. There are two common models of interprocess communication: the message-passing model and the shared-memory model. In the message-passing model, the communicating processes exchange messages with one another to transfer information. Messages can be exchanged between the processes either directly or indirectly through a common mailbox. Before communication can take place, a connection must be opened. The name of the other communicator must be known, be it another process on the same system or a process on another computer connected by a communications network. Each computer in a network has a host name by which it is commonly known. A host also has a network identifier, such as an IP address. Similarly, each process has a process name, and this name is translated into an identifier by which the operating system can refer to the process. The get_hostid() and get_processid() system calls do this translation. The identifiers are then passed to the general-purpose open() and close() calls provided by the file system or to specific open_connection() and close_connection() system calls, depending on the system's model of communication. The recipient process usually must give its permission for communication to take place with an accept_connection() call. Most processes that will be receiving connections are special-purpose daemons, which are system programs provided for that purpose. They execute a wait_for_connection() call and are awakened when a connection is made. The source of the communication, known as the client, and the receiving daemon, known as a server, then exchange messages by using read_message() and write_message() system calls. The close_connection() call terminates the communication.

      Explain the steps involved in interprocess communication using the message-passing model. Include the roles of the client, server (daemon), and system calls such as open_connection(), accept_connection(), read_message(), and close_connection().

    80. Many operating systems provide a time profile of a program to indicate the amount of time that the program executes at a particular location or set of locations. A time profile requires either a tracing facility or regular timer interrupts. At every occurrence of the timer interrupt, the value of the program counter is recorded. With sufficiently frequent timer interrupts, a statistical picture of the time spent on various parts of the program can be obtained.

      Many operating systems can track how much time a program spends running at different points in its code. This is called a time profile. To create one, the system either traces the program or uses regular timer interrupts. Every time the timer interrupts, the system records the program’s current position. By doing this often frequently, it can give a statistical view of which parts of the program take the most time to execute.

    81. Many system calls exist simply for the purpose of transferring information between the user program and the operating system. For example, most systems have a system call to return the current time() and date(). Other system calls may return information about the system, such as the version number of the operating system, the amount of free memory or disk space, and so on.

      Many system calls are used for just passing the information back and forth between the program and the operating system. For example, many systems are used for processing a function which is used to retrieve the current time and its date. Additional calls can offer the information regarding the system, such as the version of the operating system, the amount of the available memory or disk space, and other related details

    82. Once the device has been requested (and allocated to us), we can read(), write(), and (possibly) reposition() the device, just as we can with files. In fact, the similarity between I/O devices and files is so great that many operating systems, including UNIX, merge the two into a combined file–device structure. In this case, a set of system calls is used on both files and devices. Sometimes, I/O devices are identified by special file names, directory placement, or file attributes.

      Once the device has been requested (and allocated to us), we can read(), write(), and (possibly) reposition() the device, just as we can with files. In fact, the similarity between I/O devices and files is so great that many operating systems, including UNIX, merge the two into a combined file–device structure. In this case, a set of system calls is used on both files and devices. Sometimes, I/O devices are identified by special file names, directory placement, or file attributes.

    83. The various resources controlled by the operating system can be thought of as devices. Some of these devices are physical devices (for example, disk drives), while others can be thought of as abstract or virtual devices (for example, files). A system with multiple users may require us to first request() a device, to ensure exclusive use of it. After we are finished with the device, we release() it. These functions are similar to the open() and close() system calls for files. Other operating systems allow unmanaged access to devices. The hazard then is the potential for device contention and perhaps deadlock, which are described in Chapter 8.

      The resources that an operating system manages can be thought of as devices. Some of these are physical, like the disk drives, while the others are abstract or virtual, like the files. In the systems where the multiple users, a program may need to request() the device to ensure that it has an exclusive access, and then release() it when it's finished. These actions are similar to open() and close() for files. Some operating systems let programs access devices without this kind of control, but doing so can lead to problems like device conflicts or deadlocks, which we’ll discuss in Chapter 8.

    84. We may need these same sets of operations for directories if we have a directory structure for organizing files in the file system. In addition, for either files or directories, we need to be able to determine the values of various attributes and perhaps to set them if necessary. File attributes include the file name, file type, protection codes, accounting information, and so on. At least two system calls, get_file_attributes() and set_file_attributes(), are required for this function. Some operating systems provide many more calls, such as calls for file move() and copy(). Others might provide an API that performs those operations using code and other system calls, and others might provide system programs to perform the tasks. If the system programs are callable by other programs, then each can be considered an API by other system programs.

      We often need similar operations for directories as we do for files, especially when using a directory structure to organize files. For both files and directories, it’s important to check or modify their attributes when necessary. Attributes can include things like the name, type,or the access permissions, and the accounting information. To handle this, operating systems usually provide system calls such as get_file_attributes() and set_file_attributes(). Some systems go further, offering extra calls for tasks like moving or copying files. In other cases, these actions are handled through APIs or system programs. If other programs can call these system programs, they effectively act as the APIs themselves.

    85. The file system is discussed in more detail in Chapter 13 through Chapter 15. Here, we identify several common system calls dealing with files. We first need to be able to create() and delete() files. Either system call requires the name of the file and perhaps some of the file's attributes. Once the file is created, we need to open() it and to use it. We may also read(), write(), or reposition() (rewind or skip to the end of the file, for example). Finally, we need to close() the file, indicating that we are no longer using it.

      This part elaborates on the primary file-management system calls offered by an operating system. A program can begin by generating the new file or removing the existing one, identifying its name and the additional attributes as said to be required. Once when the file is generated, it can be accessed by using the open(), allowing the program to engage with it—either by reading, writing, or the repositioning the file pointer with the reposition(). After the program has completed its operations within the file, close() is called for indicating that the file is no longer in use or accessed

    86. There are so many facets of and variations in process control that we next use two examples—one involving a single-tasking system and the other a multitasking system—to clarify these concepts. The Arduino is a simple hardware platform consisting of a microcontroller along with input sensors that respond to a variety of events, such as changes to light, temperature, and barometric pressure, to just name a few. To write a program for the Arduino, we first write the program on a PC and then upload the compiled program (known as a sketch) from the PC to the Arduino's flash memory via a USB connection. The standard Arduino platform does not provide an operating system; instead, a small piece of software known as a boot loader loads the sketch into a specific region in the Arduino's memory

      This passage explains process control in simple and multitasking systems using the Arduino as an example. The Arduino is a microcontroller platform with sensors that detect various events. Programs, called sketches, are written and compiled on a PC and then uploaded to the Arduino’s flash memory. Unlike more complex systems, the standard Arduino does not use a full operating system; a bootloader simply loads the sketch into memory, demonstrating a single-tasking environment.

    87. Quite often, two or more processes may share data. To ensure the integrity of the data being shared, operating systems often provide system calls allowing a process to lock shared data. Then, no other process can access the data until the lock is released. Typically, such system calls include acquire_lock() and release_lock().

      This paragraph explores how operating systems handle the data exchanged among various processes. For the data integrity preservation, the OS can protect the shared data via the system calls like acquire_lock(), stopping other processes from accessing it until it is freed with the release_lock().This mechanism is crucial for avoiding conflicts and ensuring consistent data in environments with simultaneous processing

    88. A process executing one program may want to load() and execute() another program. This feature allows the command interpreter to execute a program as directed by, for example, a user command or the click of a mouse. An interesting question is where to return control when the loaded program terminates. This question is related to whether the existing program is lost, saved, or allowed to continue execution concurrently with the new program. If control returns to the existing program when the new program terminates, we must save the memory image of the existing program; thus, we have effectively created a mechanism for one program to call another program. If both programs continue concurrently, we have created a new process to be multiprogrammed. Often, there is a system call specifically for this purpose (create_process()).

      This text explains how one application can load and run another application, for instance, when a user issues a command or selects an icon. It emphasizes the main problem of control flow once the new program ends: control might revert to the original program, necessitating its memory image to be preserved, or both programs could operate simultaneously, resulting in a multiprogramming situation. The excerpt mentions that operating systems typically offer a specific system call, like create_process(), to enable this functionality.

    89. A running program needs to be able to halt its execution either normally (end()) or abnormally (abort()). If a system call is made to terminate the currently running program abnormally, or if the program runs into a problem and causes an error trap, a dump of memory is sometimes taken and an error message generated. The dump is written to a special log file on disk and may be examined by a debugger—a system program designed to aid the programmer in finding and correcting errors, or bugs—to determine the cause of the problem. Under either normal or abnormal circumstances, the operating system must transfer control to the invoking command interpreter. The command interpreter then reads the next command. In an interactive system, the command interpreter simply continues with the next command; it is assumed that the user will issue an appropriate command to respond to any error. In a GUI system, a pop-up window might alert the user to the error and ask for guidance. Some systems may allow for special recovery actions in case an error occurs. If the program discovers an error in its input and wants to terminate abnormally, it may also want to define an error level. More severe errors can be indicated by a higher-level error parameter. It is then possible to combine normal and abnormal termination by defining a normal termination as an error at level 0. The command interpreter or a following program can use this error level to determine the next action automatically.

      This passage explains how a running program can terminate either normally using end() or abnormally using abort(). In the case of abnormal termination or an error trap, the operating system may create a memory dump and an error log for debugging. After termination, control is returned to the command interpreter, which continues processing user commands or provides GUI prompts for guidance. The passage also highlights the use of error levels to indicate the severity of errors, allowing subsequent programs or the command interpreter to respond appropriately.

    90. System calls can be grouped roughly into six major categories: process control, file management, device management, information maintenance, communications, and protection. Below, we briefly discuss the types of system calls that may be provided by an operating system. Most of these system calls support, or are supported by, concepts and functions that are discussed in later chapters. Figure 2.8 summarizes the types of system calls normally provided by an operating system. As mentioned, in this text, we normally refer to the system calls by generic names. Throughout the text, however, we provide examples of the actual counterparts to the system calls for UNIX, Linux, and Windows systems.

      This section explains that system calls can be categorized into six primary groups: process management, file handling, device control, information upkeep, communication, and security. The text emphasizes that most system calls relate to concepts discussed later and provides examples from UNIX, Linux, and Windows. Figure 2.8 gives a summary of these categories.

    91. hree general methods are used to pass parameters to the operating system. The simplest approach is to pass the parameters in registers. In some cases, however, there may be more parameters than registers. In these cases, the parameters are generally stored in a block, or table, in memory, and the address of the block is passed as a parameter in a register (Figure 2.7). Linux uses a combination of these approaches.

      This passage describes three ways that system-call parameters can be passed to the operating system. The simplest method is using CPU registers to hold the parameters. If there are too many parameters for the available registers, the parameters are then placed in the memory block or the table, and the address of that block is passed into the register. Linux uses the mix of both the methods depending on the situation.

    92. System calls occur in different ways, depending on the computer in use. Often, more information is required than simply the identity of the desired system call. The exact type and amount of information vary according to the particular operating system and call. For example, to get input, we may need to specify the file or device to use as the source, as well as the address and length of the memory buffer into which the input should be read. Of course, the device or file and length may be implicit in the call.

      This passage explains that system calls often require additional information beyond identifying the call itself. Parameters—like the source file or the device, memory buffer address, and buffer length—may need to be specified so the operating system understands how to process the request. The precise specifics rely on the particular operating system and the system calls being utilized

    93. The caller need know nothing about how the system call is implemented or what it does during execution. Rather, the caller need only obey the API and understand what the operating system will do as a result of the execution of that system call. Thus, most of the details of the operating-system interface are hidden from the programmer by the API and are managed by the RTE.

      This text highlights the importance of abstraction within system calls. Programmers working with an API do not have to understand the internal mechanisms or execution specifics of a system call. They just need to follow to the API's instructions and also understand the expected outcome. The run-time environment (RTE) manages the inner networks of interacting with the operating system, successfully concealing the underlying specifics from the developer.

    94. Another important factor in handling system calls is the run-time environment (RTE)—the full suite of software needed to execute applications written in a given programming language, including its compilers or interpreters as well as other software, such as libraries and loaders. The RTE provides a system-call interface that serves as the link to system calls made available by the operating system. The system-call interface intercepts function calls in the API and invokes the necessary system calls within the operating system. Typically, a number is associated with each system call, and the system-call interface maintains a table indexed according to these numbers

      This passage describes the role of the run-time environment (RTE) in managing system calls. The RTE includes compilers, interpreters, libraries, and loaders, and provides a system-call interface that connects API function calls to the operating system’s system calls. Each system call is typically assigned a number, and the interface uses a table indexed by these numbers to invoke the correct system call within the OS.

    95. Why would an application programmer prefer programming according to an API rather than invoking actual system calls? There are several reasons for doing so. One benefit concerns program portability. An application programmer designing a program using an API can expect her program to compile and run on any system that supports the same API (although, in reality, architectural differences often make this more difficult than it may appear). Furthermore, actual system calls can often be more detailed and difficult to work with than the API available to an application programmer. Nevertheless, there often exists a strong correlation between a function in the API and its associated system call within the kernel. In fact, many of the POSIX and Windows APIs are similar to the native system calls provided by the UNIX, Linux, and Windows operating systems.

      This passage describes why the application programmers prefer using the APIs instead of directly invoking the system calls. APIs provide the portability, allowing the programs to run on any system which supports the same API, and also simplify the programming by offering the higher-level, easier-to-use functions. While the system calls are often more detailed and complex, APIs usually have a close correspondence with the underlying system calls, as seen in the POSIX and the Windows APIs.

    96. As you can see, even simple programs may make heavy use of the operating system. Frequently, systems execute thousands of system calls per second. Most programmers never see this level of detail, however. Typically, application developers design programs according to an application programming interface (API). The API specifies a set of functions that are available to an application programmer, including the parameters that are passed to each function and the return values the programmer can expect. Three of the most common APIs available to application programmers are the Windows API for Windows systems, the POSIX API for POSIX-based systems (which include virtually all versions of UNIX, Linux, and macOS), and the Java API for programs that run on the Java virtual machine

      This passage highlights that even simple programs rely heavily on the operating system through system calls, often executing thousands per second. However, the programmers usually interact with the higher-level APIs rather than making the system calls directly. APIs likethe Windows API, POSIX API, and the Java API provide the standardized functions, parameters, and the expected return values, simplifying the program development while hiding the underlying OS complexity.

    97. When both files are set up, we enter a loop that reads from the input file (a system call) and writes to the output file (another system call). Each read and write must return status information regarding various possible error conditions. On input, the program may find that the end of the file has been reached or that there was a hardware failure in the read (such as a parity error). The write operation may encounter various errors, depending on the output device (for example, no more available disk space).

      This passage emphasizes that reading from and writing to files in a program involves repeated system calls, each of which must report status and handle potential errors. It illustrates how the operating system monitors both input and output operations, accounting for conditions like reaching the end of a file, hardware read failures, or insufficient disk space during writing.

    98. Once the two file names have been obtained, the program must open the input file and create and open the output file. Each of these operations requires another system call. Possible error conditions for each system call must be handled. For example, when the program tries to open the input file, it may find that there is no file of that name or that the file is protected against access. In these cases, the program should output an error message (another sequence of system calls) and then terminate abnormally (another system call).

      This passage explains that each file operation—opening an input file, creating and opening an output file—requires a separate system call. It highlights the need for handling potential errors, such as a missing file or insufficient access permissions, using system calls to display error messages and terminate the program if necessary.

    99. Before we discuss how an operating system makes system calls available, let's first use an example to illustrate how system calls are used: writing a simple program to read data from one file and copy them to another file. The first input that the program will need is the names of the two files: the input file and the output file. These names can be specified in many ways, depending on the operating-system design

      This passage introduces the concept of using system calls with a practical example: a program that reads from one file and writes to another. It emphasizes that the program first needs the file names and notes that how these names are specified can vary depending on the operating system’s design.

    100. System calls provide an interface to the services made available by an operating system. These calls are generally available as functions written in C and C++, although certain low-level tasks (for example, tasks where hardware must be accessed directly) may have to be written using assembly-language instructions.

      This passage explains that system calls act as the bridge between programs and the operating system’s services. Most system calls are accessible through high-level languages like C and C++, but some low-level operations—especially those requiring direct hardware access—may need to be implemented in assembly language.

    101. Although there are apps that provide a command-line interface for iOS and Android mobile systems, they are rarely used. Instead, almost all users of mobile systems interact with their devices using the touch-screen interface. The user interface can vary from system to system and even from user to user within a system; however, it typically is substantially removed from the actual system structure. The design of a useful and intuitive user interface is therefore not a direct function of the operating system. In this book, we concentrate on the fundamental problems of providing adequate service to user programs. From the point of view of the operating system, we do not distinguish between user programs and system programs.

      This passage emphasizes that mobile users almost exclusively use touch-screen interfaces rather than command-line interfaces. While user interfaces may differ across systems and users, their design is largely separate from the underlying operating system. The focus of the book, as noted here, is on the operating system’s role in providing consistent and adequate service to programs, treating user and system programs equivalently.

    102. In contrast, most Windows users are happy to use the Windows GUI environment and almost never use the shell interface. Recent versions of the Windows operating system provide both a standard GUI for desktop and traditional laptops and a touch screen for tablets. The various changes undergone by the Macintosh operating systems also provide a nice study in contrast.

      This passage differntiates the typical Windows users with the command-line users, noting that most Windows users rely primarily on the GUI and rarely use the shell. Modern Windows versions support both desktop GUIs and touch interfaces for tablets. The passage also points out that the evolution of Macintosh operating systems offers a useful comparison in understanding how GUI design and user interaction have developed over time.

    103. The choice of whether to use a command-line or GUI interface is mostly one of personal preference. System administrators who manage computers and power users who have deep knowledge of a system frequently use the command-line interface. For them, it is more efficient, giving them faster access to the activities they need to perform. Indeed, on some systems, only a subset of system functions is available via the GUI, leaving the less common tasks to those who are command-line knowledgeable

      This text emphasizes that the decision between using the graphical user interface (GUI) and the command-line interface (CLI) usually depends on individual preference and the user's skill level. System administrators and experienced users typically prefer the CLI for its quicker, more efficient access to system features Certain tasks might only be accessible through the CLI, highlighting the significance for users requiring specific or uncommon functions.

    104. Because a either a command-line interface or a mouse-and-keyboard system is impractical for most mobile systems, smartphones and handheld tablet computers typically use a touch-screen interface. Here, users interact by making gestures on the touch screen—for example, pressing and swiping fingers across the screen. Although earlier smartphones included a physical keyboard, most smartphones and tablets now simulate a keyboard on the touch screen

      This text demonstrates that mobile devices like smartphones and tablets depend on touch-screen interfaces rather than conventional command-line or mouse-and-keyboard systems. Users typically engage directly with the display by using gestures like tapping or swiping.While the early smartphones had physical keyboards, modern devices typically display a virtual keyboard on the touch screen for the input, optimizing portability and also usability.

    105. Graphical user interfaces first appeared due in part to research taking place in the early 1970s at Xerox PARC research facility. The first GUI appeared on the Xerox Alto computer in 1973. However, graphical interfaces became more widespread with the advent of Apple Macintosh computers in the 1980s. The user interface for the Macintosh operating system has undergone various changes over the years, the most significant being the adoption of the Aqua interface that appeared with macOS. Microsoft's first version of Windows—Version 1.0—was based on the addition of a GUI interface to the MS-DOS operating system

      This passage outlines how the historical development of the graphical user interfaces (GUIs). GUIs were at the beginning examined at the Xerox PARC in the early 1970s, with the Xerox Alto being the first computer to have one. Widespread usage took place in the 1980s with Apple’s Macintosh computers. Over time, GUIs evolved, such as Apple’s adoption of the Aqua interface in macOS. Microsoft also integrated a GUI with Windows 1.0, layering it over the MS-DOS operating system.

    106. In one approach, the command interpreter itself contains the code to execute the command. For example, a command to delete a file may cause the command interpreter to jump to a section of its code that sets up the parameters and makes the appropriate system call. In this case, the number of commands that can be given determines the size of the command interpreter, since each command requires its own implementing code.

      This passage explains one method of implementing the commands in a command interpreter: the interpreter directly contains the code for executing each command. For instance, a delete-file command triggers a specific section of the interpreter’s code to set parameters and perform the system call. The number of supported commands directly affects the interpreter’s size, as each command needs its own dedicated code.

    107. The main function of the command interpreter is to get and execute the next user-specified command. Many of the commands given at this level manipulate files: create, delete, list, print, copy, execute, and so on. The various shells available on UNIX systems operate in this way. These commands can be implemented in two general ways.

      This passage highlights how the command interpreter’s primary role is to receive and execute user commands, many of which involve file manipulation, such as creating, deleting, or copying the files. It also notes that the UNIX shells implement these commands, which can be carried out using two general approaches.

    108. Most operating systems, including Linux, UNIX, and Windows, treat the command interpreter as a special program that is running when a process is initiated or when a user first logs on (on interactive systems). On systems with multiple command interpreters to choose from, the interpreters are known as shells. For example, on UNIX and Linux systems, a user may choose among several different shells, including the C shell, Bourne-Again shell, Korn shell, and others

      This passage explains that the command interpreter, or shell, is a special program that runs when a process starts or when a user logs on. On systems like UNIX and Linux, multiple shells are available, allowing users to choose their preferred interface for entering commands.

    109. Protection and security. The owners of information stored in a multiuser or networked computer system may want to control use of that information. When several separate processes execute concurrently, it should not be possible for one process to interfere with the others or with the operating system itself. Protection involves ensuring that all access to system resources is controlled. Security of the system from outsiders is also important.

      This passage describes that operating systems enforce protection and security by controlling access to system resources. In multiuser or the networked environments, this ensures that processes do not interfere with one another, and also safeguards the system against the external threats.

    110. Logging. We want to keep track of which programs use how much and what kinds of computer resources. This record keeping may be used for accounting (so that users can be billed) or simply for accumulating usage statistics. Usage statistics may be a valuable tool for system administrators who wish to reconfigure the system to improve computing services.

      This passage explains how the operating systems maintains the logs of the program resource usage. These logs can support accounting, billing, or help administrators analyze usage patterns to optimize system performance.

    111. Resource allocation. When there are multiple processes running at the same time, resources must be allocated to each of them. The operating system manages many different types of resources. Some (such as CPU cycles, main memory, and file storage) may have special allocation code, whereas others (such as I/O devices) may have much more general request and release code.

      This passage highlights that the operating system is responsible for resource allocation, distributing CPU time, memory, file storage, and I/O devices among multiple running processes to ensure fair and efficient usage.

    112. Error detection. The operating system needs to be detecting and correcting errors constantly. Errors may occur in the CPU and memory hardware (such as a memory error or a power failure), in I/O devices (such as a parity error on disk, a connection failure on a network, or lack of paper in the printer), and in the user program (such as an arithmetic overflow or an attempt to access an illegal memory location).

      This passage explains that the operating system continuously detects and handles errors. These errors can arise in hardware (CPU, memory, or I/O devices) or in user programs, such as illegal memory access or arithmetic overflow, ensuring system stability.

    113. Communications. There are many circumstances in which one process needs to exchange information with another process. Such communication may occur between processes that are executing on the same computer or between processes that are executing on different computer systems tied together by a network

      This passage describes that operating systems provide mechanisms for interprocess communication, allowing processes to exchange the information either on the same computer or across different computers connected via a network.

    114. File-system manipulation. The file system is of particular interest. Obviously, programs need to read and write files and directories. They also need to create and delete them by name, search for a given file, and list file information. Finally, some operating systems include permissions management to allow or deny access to files or directories based on file ownership.

      This passage explains that operating systems manage file-system operations, including reading, writing, creating, deleting, searching, and listing files and directories. Some systems also enforce permissions to control access based on file ownership.

    115. Program execution. The system must be able to load a program into memory and to run that program. The program must be able to end its execution, either normally or abnormally (indicating error).

      This passage highlights that an operating system manages program execution by loading programs into memory, running them, and handling their termination, whether it ends normally or due to an error.

    116. An operating system provides an environment for the execution of programs. It makes certain services available to programs and to the users of those programs. The specific services provided, of course, differ from one operating system to another, but we can identify common classes.

      This passage states that an operating system provides a platform for running programs, offering services to both programs and users. While the specific services vary across operating systems, there are common classes of services that can generally be identified.

    117. We can view an operating system from several vantage points. One view focuses on the services that the system provides; another, on the interface that it makes available to users and programmers; a third, on its components and their interconnections. In this chapter, we explore all three aspects of operating systems, showing the viewpoints of users, programmers, and operating system designers. We consider what services an operating system provides, how they are provided, how they are debugged, and what the various methodologies are for designing such systems. Finally, we describe how operating systems are created and how a computer starts its operating system.

      This passage explains that operating systems can be understood from multiple perspectives: the services they provide, the interfaces available to users and programmers, and their internal components and connections. The chapter will explore these viewpoints, covering OS services, debugging, design methodologies, creation processes, and how a computer boots its operating system.

    118. Another advantage of working with open-source operating systems is their diversity. GNU/Linux and BSD UNIX are both open-source operating systems, for instance, but each has its own goals, utility, licensing, and purpose. Sometimes, licenses are not mutually exclusive and cross-pollination occurs, allowing rapid improvements in operating-system projects. For example, several major components of OpenSolaris have been ported to BSD UNIX. The advantages of free software and open sourcing are likely to increase the number and quality of open-source projects, leading to an increase in the number of individuals and companies that use these projects.

      Another benefit of utilizing the open-source operating systems is that their variety. GNU/Linux and the BSD UNIX are both considered the open-source operating systems, for example, yet they each have distinct goals, functions, licenses, and purposes. Occasionally, licenses are not exclusive, and cross-pollination takes place, facilitating swift advancements in operating-system initiatives. For instance, numerous key elements of OpenSolaris have been adapted to BSD UNIX. The benefits of free software and open sourcing are expected to enhance the quantity and quality of open-source projects, resulting in a rise in the number of people and businesses that utilize these projects.

    119. The free-software movement is driving legions of programmers to create thousands of open-source projects, including operating systems. Sites like http://freshmeat.net/ and http://distrowatch.com/ provide portals to many of these projects. As we stated earlier, open-source projects enable students to use source code as a learning tool. They can modify programs and test them, help find and fix bugs, and otherwise explore mature, full-featured operating systems, compilers, tools, user interfaces, and other types of programs. The availability of source code for historic projects, such as Multics, can help students to understand those projects and to build knowledge that will help in the implementation of new projects.

      This passage highlights how the free-software movement motivates the programmers to create the numerous open-source projects, including the operating systems. Portals like FreshMeat and DistroWatch provide access to these projects. Open-source code serves as a learning tool, allowing students to modify, test, and debug programs, explore full-featured systems, and study historic projects like Multics to gain knowledge useful for developing new software.

    120. Solaris is the commercial UNIX-based operating system of Sun Microsystems. Originally, Sun's SunOS operating system was based on BSD UNIX. Sun moved to AT&T's System V UNIX as its base in 1991. In 2005, Sun open-sourced most of the Solaris code as the OpenSolaris project. The purchase of Sun by Oracle in 2009, however, left the state of this project unclear

      This passage outlines the history of Solaris, Sun Microsystems’ commercial UNIX-based OS. SunOS was initially based on BSD UNIX, but in 1991 it switched to System V UNIX. In 2005, most Solaris code was open-sourced as OpenSolaris, though Oracle’s acquisition of Sun in 2009 left the project’s future uncertain.

    121. As with many open-source projects, this source code is contained in and controlled by a version control system—in this case, “subversion” (https://subversion.apache.org/source-code). Version control systems allow a user to “pull” an entire source code tree to his computer and “push” any changes back into the repository for others to then pull. These systems also provide other features, including an entire history of each file and a conflict resolution feature in case the same file is changed concurrently. Another version control system is git, which is used for GNU/Linux, as well as other programs (http://www.git-scm.com).

      This text describes how open-source projects typically utilize version control systems to oversee the source code. Subversion (employed by BSD) and Git (utilized by GNU/Linux) enable the users for extracting the code, implement the modifications, and then subsequently upload the updates back to the repository. These systems monitor file histories, handle simultaneous changes, and assist in conflict resolution, facilitating collaborative development and effective code management

    122. Just as with Linux, there are many distributions of BSD UNIX, including FreeBSD, NetBSD, OpenBSD, and DragonflyBSD. To explore the source code of FreeBSD, simply download the virtual machine image of the version of interest and boot it within Virtualbox, as described above for Linux. The source code comes with the distribution and is stored in /usr/src/. The kernel source code is in /usr/src/sys. For example, to examine the virtual memory implementation code in the FreeBSD kernel, see the files in /usr/src/sys/vm. Alternatively, you can simply view the source code online at https://svnweb.freebsd.org.

      This passage explains how the BSD UNIX, like the Linux, has the multiple distributions such as the FreeBSD, NetBSD, OpenBSD, and the DragonflyBSD. FreeBSD’s source code is included with its distribution and can be explored locally (e.g., in /usr/src/ and /usr/src/sys) or online via the FreeBSD repository. Virtual machine images allows the users to boot and examine the OS safely, making it accessible for learning and also experimentation.

    123. BSD UNIX has a longer and more complicated history than Linux. It started in 1978 as a derivative of AT&T's UNIX. Releases from the University of California at Berkeley (UCB) came in source and binary form, but they were not open source because a license from AT&T was required. BSD UNIX's development was slowed by a lawsuit by AT&T, but eventually a fully functional, open-source version, 4.4BSD-lite, was released in 1994.

      This passage summarizes the history of BSD UNIX. Originating in 1978 as a derivative of AT&T UNIX, early BSD releases from UC Berkeley required an AT&T license and were not fully open source. Development was delayed by legal issues, but a fully functional open-source version, 4.4BSD-lite, was eventually released in 1994.

    124. The resulting GNU/Linux operating system (with the kernel properly called Linux but the full operating system including GNU tools called GNU/Linux) has spawned hundreds of unique distributions, or custom builds, of the system. Major distributions include Red Hat, SUSE, Fedora, Debian, Slackware, and Ubuntu. Distributions vary in function, utility, installed applications, hardware support, user interface, and purpose. For example, Red Hat Enterprise Linux is geared to large commercial use. PCLinuxOS is a live CD—an operating system that can be booted and run from a CD-ROM without being installed on a system's boot disk. A variant of PCLinuxOS—called PCLinuxOS Supergamer DVD—is a live DVD that includes graphics drivers and games. A gamer can run it on any compatible system simply by booting from the DVD. When the gamer is finished, a reboot of the system resets it to its installed operating system.

      This passage discusses GNU/Linux as an example of a free and open-source operating system. By 1991, the GNU Project had developed most components except for a fully functional kernel. Linus Torvalds then released a basic UNIX-like kernel like using the GNU tools and the invited global contributions, leading to the development of the Linux kernel and the complete GNU/Linux system.

    125. As an example of a free and open-source operating system, consider GNU/Linux. By 1991, the GNU operating system was nearly complete. The GNU Project had developed compilers, editors, utilities, libraries, and games—whatever parts it could not find elsewhere. However, the GNU kernel never became ready for prime time. In 1991, a student in Finland, Linus Torvalds, released a rudimentary UNIX-like kernel using the GNU compilers and tools and invited contributions worldwide.

      This passage discusses GNU/Linux as an example of a free and open-source operating system. By 1991, the GNU Project had developed most components except for a fully functional kernel. Linus Torvalds then released a basic UNIX-like kernel using GNU tools and invited global contributions, leading to the development of the Linux kernel and the complete GNU/Linux system.

    126. The FSF uses the copyrights on its programs to implement “copyleft,” a form of licensing invented by Stallman. Copylefting a work gives anyone that possesses a copy of the work the four essential freedoms that make the work free, with the condition that redistribution must preserve these freedoms. The GNU General Public License (GPL) is a common license under which free software is released. Fundamentally, the GPL requires that the source code be distributed with any binaries and that all copies (including modified versions) be released under the same GPL license. The Creative Commons “Attribution Sharealike” license is also a copyleft license; “sharealike” is another way of stating the idea of copyleft.

      This passage explains how the “copyleft,” is a licensing approach that was developed by Richard Stallman and used by the Free Software Foundation (FSF). Copyleft ensures that the software remains free by granting the users the four essential freedoms while requiring that any of the redistribution preserves about these freedoms. The GNU General Public License (GPL) is a widely used copyleft license, mandating that source code accompany binaries and that modified versions remain under the same license. Creative Commons’ “Attribution Sharealike” license follows a similar principle.

    127. To counter the move to limit software use and redistribution, Richard Stallman in 1984 started developing a free, UNIX-compatible operating system called GNU (which is a recursive acronym for “GNU's Not Unix!”). To Stallman, “free” refers to freedom of use, not price. The free-software movement does not object to trading a copy for an amount of money but holds that users are entitled to four certain freedoms: (1) to freely run the program, (2) to study and change the source code, and to give or sell copies either (3) with or (4) without changes. In 1985, Stallman published the GNU Manifesto, which argues that all software should be free. He also formed the Free Software Foundation (FSF) with the goal of encouraging the use and development of free software.

      This passage explains how the Richard Stallman’s creation of the GNU operating system in the 1984 to promote about the software freedom. “Free” refers to liberty, not price, granting users the rights to run, study, modify, and distribute software with or without changes. Stallman’s GNU Manifesto and the Free Software Foundation (FSF) advocate for these freedoms and encourage the development and use of free software.

    128. Computer and software companies eventually sought to limit the use of their software to authorized computers and paying customers. Releasing only the binary files compiled from the source code, rather than the source code itself, helped them to achieve this goal, as well as protecting their code and their ideas from their competitors. Although the Homebrew user groups of the 1970s exchanged code during their meetings, the operating systems for hobbyist machines (such as CPM) were proprietary. By 1980, proprietary software was the usual case.

      This passage explains how computer and software companies began restricting software use to authorized users and paying customers. By distributing only compiled binaries instead of source code, companies protected their intellectual property and ideas. While early hobbyist groups shared code freely, operating systems like CPM were proprietary, and by 1980, proprietary software had become the norm.

    129. In the early days of modern computing (that is, the 1950s), software generally came with source code. The original hackers (computer enthusiasts) at MIT's Tech Model Railroad Club left their programs in drawers for others to work on. “Homebrew” user groups exchanged code during their meetings. Company-specific user groups, such as Digital Equipment Corporation's DECUS, accepted contributions of source-code programs, collected them onto tapes, and distributed the tapes to interested members. In 1970, Digital's operating systems were distributed as source code with no restrictions or copyright notice.

      This passage explains how the early history of software distribution in the 1950s–1970s. The Software often came with the source code, and the communities of the enthusiasts—like the MIT hackers, Homebrew groups, and company user groups such as DECUS—shared, modified, and distributed programs freely. Digital Equipment Corporation even distributed operating systems as unrestricted source code, highlighting the collaborative culture of early computing.

    130. There are many benefits to open-source operating systems, including a community of interested (and usually unpaid) programmers who contribute to the code by helping to write it, debug it, analyze it, provide support, and suggest changes. Arguably, open-source code is more secure than closed-source code because many more eyes are viewing the code. Certainly, open-source code has bugs, but open-source advocates argue that bugs tend to be found and fixed faster owing to the number of people using and viewing the code.

      This passage highlights how the benefits of open-source operating systems. A community of the programmers contributes by the writing, debugging, analyzing, and also improving the code. The Open-source code can be more secure and reliable than closed-source software because more people examine it, helping to identify and fix bugs more quickly.

    131. Starting with the source code allows the programmer to produce binary code that can be executed on a system. Doing the opposite—reverse engineering the source code from the binaries—is quite a lot of work, and useful items such as comments are never recovered. Learning operating systems by examining the source code has other benefits as well. With the source code in hand, a student can modify the operating system and then compile and run the code to try out those changes, which is an excellent learning tool.

      This passage explains the advantages of studying operating systems using source code. Starting from the source allows programmers to compile executable binaries directly, whereas reverse-engineering binaries is difficult and loses valuable information like comments. Access to source code also lets students modify, compile, and test the OS, providing a hands-on learning experience.

    132. The study of operating systems has been made easier by the availability of a vast number of free software and open-source releases. Both free operating systems and open-source operating systems are available in source-code format rather than as compiled binary code. Note, though, that free software and open-source software are two different ideas championed by different groups of people (see http://gnu.org/philosophy/open-source-misses-the-point.html for a discussion on the topic).

      This passage highlights how the studying of the operating systems is easier thanks to free and open-source software, which is available in source-code form. While both provide access to the code, free software and open-source software are distinct concepts promoted by different communities.

    133. A real-time system has well-defined, fixed time constraints. Processing must be done within the defined constraints, or the system will fail. For instance, it would not do for a robot arm to be instructed to halt after it had smashed into the car it was building. A real-time system functions correctly only if it returns the correct result within its time constraints. Contrast this system with a traditional laptop system where it is desirable (but not mandatory) to respond quickly.

      This passage explains that real-time systems have strict, well-defined timing requirements. The system must process data and respond within set time constraints, or it fails—unlike traditional computers, where fast responses are desirable but not critical. For example, a robot arm must stop on time to avoid the damage, illustrating the importance of timing in real-time systems.

    134. Embedded systems almost always run real-time operating systems. A real-time system is used when rigid time requirements have been placed on the operation of a processor or the flow of data; thus, it is often used as a control device in a dedicated application. Sensors bring data to the computer. The computer must analyze the data and possibly adjust controls to modify the sensor inputs.

      This passage explains how the embedded systems typically run real-time operating systems (RTOS). RTOS are used when strict timing is required for processing or data flow, such as in control applications. Sensors provide data, and the system must quickly analyze it and adjust controls as needed.

    135. The use of embedded systems continues to expand. The power of these devices, both as standalone units and as elements of networks and the web, is sure to increase as well. Even now, entire houses can be computerized, so that a central computer—either a general-purpose computer or an embedded system—can control heating and lighting, alarm systems, and even coffee makers. Web access can enable a home owner to tell the house to heat up before she arrives home. Someday, the refrigerator will be able to notify the grocery store when it notices the milk is gone.

      This passage highlights the growing use and potential of embedded systems. They are increasingly very powerful, both as sthe tandalone devices and as the networked components. Examples include smart homes such as where a central computer can control the heating, lighting, alarms, and the appliances, and the future possibilities like the refrigerators that are automatically notifying stores when supplies run out.

    136. These embedded systems vary considerably. Some are general-purpose computers, running standard operating systems—such as Linux—with special-purpose applications to implement the functionality. Others are hardware devices with a special-purpose embedded operating system providing just the functionality desired

      This passage notes that the embedded systems can be varied widely. Some are the general-purpose computers running standard OSs likethe Linux with their specialized applications, while the others use a dedicated embedded operating systems that provide only the specific functionality required for that device.

    137. Embedded computers are the most prevalent form of computers in existence. These devices are found everywhere, from car engines and manufacturing robots to optical drives and microwave ovens. They tend to have very specific tasks. The systems they run on are usually primitive, and so the operating systems provide limited features.

      This passage explains how the embedded computers are the most common type of computers, found in the devices like the car engines, robots, and the household appliances. They are designed for the specific tasks, and their operating systems are considered typically simple, offering only essential features.

    138. Certainly, there are traditional operating systems within many of the types of cloud infrastructure. Beyond those are the VMMs that manage the virtual machines in which the user processes run. At a higher level, the VMMs themselves are managed by cloud management tools, such as VMware vCloud Director and the open-source Eucalyptus toolset. These tools manage the resources within a given cloud and provide interfaces to the cloud components, making a good argument for considering them a new type of operating system.

      Cloud infrastructure uses the traditional OSs and the virtual machine monitors (VMMs) to manage the virtual machines. Tools like the VMware vCloud Director and the Eucalyptus manage the VMMs and provide the interfaces, acting as a higher-level OS for the cloud environments.

    139. Cloud computing is a type of computing that delivers computing, storage, and even applications as a service across a network. In some ways, it's a logical extension of virtualization, because it uses virtualization as a base for its functionality. For example, the Amazon Elastic Compute Cloud (ec2) facility has thousands of servers, millions of virtual machines, and petabytes of storage available for use by anyone on the Internet.

      This passage explains how the cloud computing delivers computing power, storage, and applications as services over a network. It builds on virtualization, allowing resources to be shared efficiently. For example, Amazon EC2 provides millions of virtual machines and massive storage that can be accessed by users over the Internet.

    140. Skype is another example of peer-to-peer computing. It allows clients to make voice calls and video calls and to send text messages over the Internet using a technology known as voice over IP (VoIP). Skype uses a hybrid peer-to-peer approach

      This passage describes how Skype as an example of peer-to-peer (P2P) computing. It enables the voice and the video calls, as well as the text messaging, over the Internet using the voice over IP (VoIP) technology. Skype employs a hybrid P2P approach, combining direct peer connections with centralized services for tasks like user authentication.

    141. Peer-to-peer networks gained widespread popularity in the late 1990s with several file-sharing services, such as Napster and Gnutella, that enabled peers to exchange files with one another. The Napster system used an approach similar to the first type described above: a centralized server maintained an index of all files stored on peer nodes in the Napster network, and the actual exchange of files took place between the peer nodes

      This passage describes how the peer-to-peer (P2P) networks became popular in the late 1990s through file-sharing services like Napster and Gnutella. Napster used a hybrid approach: a central server kept an index of files, while the actual file transfers occurred directly between peers, combining centralized indexing with distributed file sharing.

    142. Another structure for a distributed system is the peer-to-peer (P2P) system model. In this model, clients and servers are not distinguished from one another. Instead, all nodes within the system are considered peers, and each may act as either a client or a server, depending on whether it is requesting or providing a service. Peer-to-peer systems offer an advantage over traditional client–server systems. In a client–server system, the server is a bottleneck; but in a peer-to-peer system, services can be provided by several nodes distributed throughout the network.

      This passage explains the peer-to-peer (P2P) model of distributed systems, where all nodes are equal and can act as either client or server. Unlike traditional client–server systems, which can have a server bottleneck, P2P systems distribute services across multiple nodes, improving scalability and reducing single points of failure.

    143. Two operating systems currently dominate mobile computing: Apple iOS and Google Android. iOS was designed to run on Apple iPhone and iPad mobile devices. Android powers smartphones and tablet computers available from many manufacturers. We examine these two mobile operating systems in further detail in Chapter 2.

      This passage notes that the mobile computing market is dominated by two operating systems: Apple iOS, which runs on iPhones and iPads, and Google Android, which powers devices from multiple manufacturers. The text indicates that these two OSs will be explored in more detail in Chapter 2.

    144. To provide access to on-line services, mobile devices typically use either IEEE standard 802.11 wireless or cellular data networks. The memory capacity and processing speed of mobile devices, however, are more limited than those of PCs. Whereas a smartphone or tablet may have 256 GB in storage, it is not uncommon to find 8 TB in storage on a desktop computer. Similarly, because power consumption is such a concern, mobile devices often use processors that are smaller, are slower, and offer fewer processing cores than processors found on traditional desktop and laptop computers.

      This passage explains how the mobile devices connect to the online services through the Wi-Fi (IEEE 802.11) or the cellular networks. However, they have the limitations as compared with PCs: less storage, slower and smaller processors, and fewer cores, mainly to conserve power. For example, a smartphone might have 256 GB of storage, while a desktop could have 8 TB.

    145. Today, mobile systems are used not only for e-mail and web browsing but also for playing music and video, reading digital books, taking photos, and recording and editing high-definition video. Accordingly, tremendous growth continues in the wide range of applications that run on such devices. Many developers are now designing applications that take advantage of the unique features of mobile devices, such as global positioning system (GPS) chips, accelerometers, and gyroscopes. An embedded GPS chip allows a mobile device to use satellites to determine its precise location on Earth.

      This passage highlights that the expanding of the capabilities of the mobile devices beyond the basic tasks like email and web browsing. Modern devices are used to handle the media playback, digital books, photography, and the high-definition video editing. Developers are creating the applications which are used to leverage the built-in features like GPS, accelerometers, and gyroscopes, enabling location-based services and motion-sensing functionality.

    146. Mobile computing refers to computing on handheld smartphones and tablet computers. These devices share the distinguishing physical features of being portable and lightweight. Historically, compared with desktop and laptop computers, mobile systems gave up screen size, memory capacity, and overall functionality in return for handheld mobile access to services such as e-mail and web browsing. Over the past few years, however, features on mobile devices have become so rich that the distinction in functionality between, say, a consumer laptop and a tablet computer may be difficult to discern. In fact, we might argue that the features of a contemporary mobile device allow it to provide functionality that is either unavailable or impractical on a desktop or laptop computer.

      This passage explains how the mobile computing involves handheld devices like smartphones and tablets, which are portable and lightweight. While early mobile devices sacrificed screen size, memory, and functionality, modern devices now offer features comparable to—or even exceeding—those of desktops and laptops, making them highly capable for tasks like web browsing, email, and other services.

    147. Traditional time-sharing systems are rare today. The same scheduling technique is still in use on desktop computers, laptops, servers, and even mobile computers, but frequently all the processes are owned by the same user (or a single user and the operating system). User processes, and system processes that provide services to the user, are managed so that each frequently gets a slice of computer time. Consider the windows created while a user is working on a PC, for example, and the fact that they may be performing different tasks at the same time. Even a web browser can be composed of multiple processes, one for each website currently being visited, with time sharing applied to each web browser process.

      This text emphasizes that although the traditional time-sharing systems are now seen as very uncommon, the scheduling method remains to be prevalent. Contemporary computers—desktops, laptops, servers, and mobile devices—utilize the time-sharing for controlling the numerous user and system processes. For instance, a PC can manage various windows, and a web browser can execute several processes at once, with each process getting portions of CPU time.

    148. In the latter half of the 20th century, computing resources were relatively scarce. (Before that, they were nonexistent!) For a period of time, systems were either batch or interactive. Batch systems processed jobs in bulk, with predetermined input from files or other data sources. Interactive systems waited for input from users. To optimize the use of the computing resources, multiple users shared time on these systems. These time-sharing systems used a timer and scheduling algorithms to cycle processes rapidly through the CPU, giving each user a share of the resources.

      This passage explains how computing evolved when resources were limited. Early systems are either the batch (processing jobs in bulk) or the interactive (waiting for user input). Time-sharing systems were introduced to optimize the resource use, allowing multiple users to share CPU time through timers and scheduling algorithms.

    149. At home, most users once had a single computer with a slow modem connection to the office, the Internet, or both. Today, network-connection speeds once available only at great cost are relatively inexpensive in many places, giving home users more access to more data. These fast data connections are allowing home computers to serve up web pages and to run networks that include printers, client PCs, and servers. Many homes use firewalls to protect their networks from security breaches. Firewalls limit the communications between devices on a network.

      This passage describes how the home computing has evolved with faster and more affordable network connections. Modern home networks can include multiple devices like PCs, printers, and servers, and can even serve web pages. Firewalls are commonly used to protect these networks by controlling and limiting communications between devices.

    150. Today, web technologies and increasing WAN bandwidth are stretching the boundaries of traditional computing. Companies establish portals, which provide web accessibility to their internal servers. Network computers (or thin clients)—which are essentially terminals that understand web-based computing—are used in place of traditional workstations where more security or easier maintenance is desired. Mobile computers can synchronize with PCs to allow very portable use of company information. Mobile devices can also connect to wireless networks and cellular data networks to use the company's web portal (as well as the myriad other web resources).

      This passage explains how modern web technologies and faster WAN connections have expanded traditional computing. Companies now use web portals for internal access, thin clients for secure and easy-to-maintain workstations, and mobile devices that sync with PCs or connect via wireless/cellular networks, enabling flexible and portable access to company resources.

    151. As computing has matured, the lines separating many of the traditional computing environments have blurred. Consider the “typical office environment.” Just a few years ago, this environment consisted of PCs connected to a network, with servers providing file and print services. Remote access was awkward, and portability was achieved by use of laptop computers.

      This passage describes how the traditional computing environments, like the typical office setup, have evolved. Previously, offices had PCs connected to servers for file and print services, with limited remote access and portability mostly relying on laptops. It highlights how computing has become more flexible and interconnected over time.

    152. The power of bitmaps becomes apparent when we consider their space efficiency. If we were to use an eight-bit Boolean value instead of a single bit, the resulting data structure would be eight times larger. Thus, bitmaps are commonly used when there is a need to represent the availability of a large number of resources. Disk drives provide a nice illustration. A medium-sized disk drive might be divided into several thousand individual units, called disk blocks. A bitmap can be used to indicate the availability of each disk block.

      This passage highlights the space efficiency of bitmaps. Using the single bit per item instead of larger data types drastically reduces memory usage, making bitmaps ideal for tracking large numbers of resources. For example, disk drives use bitmaps to indicate which disk blocks are available or in use.

    153. A bitmap is a string of n binary digits that can be used to represent the status of n items. For example, suppose we have several resources, and the availability of each resource is indicated by the value of a binary digit: 0 means that the resource is available, while 1 indicates that it is unavailable (or vice versa). The value of the ith position in the bitmap is associated with the ith resource.

      This passage explains that a bitmap is the sequence of binary digits used to represent the status of multiple items. Each position in the bitmap corresponds to a specific resource, with values like 0 or 1 indicating whether the resource is available or is unavailable.

    154. One use of a hash function is to implement a hash map, which associates (or maps) [key:value] pairs using a hash function. Once the mapping is established, we can apply the hash function to the key to obtain the value from the hash map (Figure 1.21). For example, suppose that a user name is mapped to a password. Password authentication then proceeds as follows: a user enters her user name and password. The hash function is applied to the user name, which is then used to retrieve the password. The retrieved password is then compared with the password entered by the user for authentication.

      This text describes how hash functions can be utilized to create hash maps that hold data in key–value pairs. Using a hash function on a key allows the system to swiftly access its corresponding value. In password authentication, the username is hashed to retrieve the stored password, which is then matched against the user’s input to confirm identity

    155. One potential difficulty with hash functions is that two unique inputs can result in the same output value—that is, they can link to the same table location. We can accommodate this hash collision by having a linked list at the table location that contains all of the items with the same hash value. Of course, the more collisions there are, the less efficient the hash function is.

      This passage highlights the limitation for the hash functions: different inputs can produce the same output, causing a hash collision. To handle this, a linked list can store all the items that share the same hash index. However, the frequent collisions reduce the efficiency of the hash function, making the retrieval slower.

    156. A hash function takes data as its input, performs a numeric operation on the data, and returns a numeric value. This numeric value can then be used as an index into a table (typically an array) to quickly retrieve the data. Whereas searching for a data item through a list of size n can require up to O(n) comparisons, using a hash function for retrieving data from a table can be as good as O(1), depending on implementation details. Because of this performance, hash functions are used extensively in operating systems.

      This passage explains how that a hash function converts the data into the numeric value, which can be used as an index to quickly access the data in the table. Unlike searching a list, which can take O(n) time, a hash table can often retrieve data in O(1) time. This efficiency is why operating systems frequently use hash functions for tasks like indexing and also the quick lookups.

    157. A tree is a data structure that can be used to represent data hierarchically. Data values in a tree structure are linked through parent–child relationships. In a general tree, a parent may have an unlimited number of children. In a binary tree, a parent may have at most two children, which we term the left child and the right child. A binary search tree additionally requires an ordering between the parent's two children in which left_child <= right_child. Figure 1.20 provides an example of a binary search tree. When we search for an item in a binary search tree, the worst-case performance is O(n) (consider how this can occur). To remedy this situation, we can use an algorithm to create a balanced binary search tree. Here, a tree containing n items has at most lg n levels, thus ensuring worst-case performance of O(lg n). We shall see in Section 5.7.1 that Linux uses a balanced binary search tree (known as a red-black tree) as part its CPU-scheduling algorithm.

      This passage explains how a tree is a hierarchical data structure with the parent–child relationships. Binary trees limit the parents to two children, and the binary search trees (BSTs) impose that an order for efficient searching. In the worst case, a BST can have O(n) search time, but while balancing the tree reduces this to O(log n). Linux uses the balanced trees, such as the red-black trees, in the CPU scheduling to improve the performance.

    158. A queue, in contrast, is a sequentially ordered data structure that uses the first in, first out (FIFO) principle: items are removed from a queue in the order in which they were inserted. There are many everyday examples of queues, including shoppers waiting in a checkout line at a store and cars waiting in line at a traffic signal. Queues are also quite common in operating systems—jobs that are sent to a printer are typically printed in the order in which they were submitted, for example. As we shall see in Chapter 5, tasks that are waiting to be run on an available CPU are often organized in queues.

      What is a queue, and how does the first-in, first-out (FIFO) principle works? Give some examples of how queues are used both in everyday life and in operating systems.

    159. A stack is a sequentially ordered data structure that uses the last in, first out (LIFO) principle for adding and removing items, meaning that the last item placed onto a stack is the first item removed. The operations for inserting and removing items from a stack are known as push and pop, respectively. An operating system often uses a stack when invoking function calls. Parameters, local variables, and the return address are pushed onto the stack when a function is called; returning from the function call pops those items off the stack.

      What is a stack, and how does the last-in, first-out (LIFO) principle determine the push and pop operations? Additionally, how does an operating system use a stack during function calls?

    160. Linked lists accommodate items of varying sizes and allow easy insertion and deletion of items. One potential disadvantage of using a list is that performance for retrieving a specified item in a list of size n is linear—O(n), as it requires potentially traversing all n elements in the worst case. Lists are sometimes used directly by kernel algorithms. Frequently, though, they are used for constructing more powerful data structures, such as stacks and queues.

      This passage highlights how that the linked lists are flexible, supporting the variable-sized items and easy insertion or the deletion. However, searching for a specific element can be slow (O(n)). Lists are often used directly in kernel algorithms or as building blocks for other structures like stacks and queues.

    161. After arrays, lists are perhaps the most fundamental data structures in computer science. Whereas each item in an array can be accessed directly, the items in a list must be accessed in a particular order. That is, a list represents a collection of data values as a sequence. The most common method for implementing this structure is a linked list, in which items are linked to one another

      This section explains that the lists are a fundamental data structure where these elements are accessed sequentially rather than directly. Linked lists are a common implementation, connecting each item to the next, which allows flexible insertion and removal of elements compared to arrays.

    162. An array is a simple data structure in which each element can be accessed directly. For example, main memory is constructed as an array. If the data item being stored is larger than one byte, then multiple bytes can be allocated to the item, and the item is addressed as “item number × item size.” But what about storing an item whose size may vary? And what about removing an item if the relative positions of the remaining items must be preserved? In such situations, arrays give way to other data structures.

      This passage describes arrays as the basic data structure which allows the direct access to elements, making them simple and efficient for fixed-size items. However, arrays have limitations when storing variable-sized data or when items need to be removed while maintaining order, which is why more flexible data structures (like linked lists) are used in such cases.

    163. Some operating systems have taken the concept of networks and distributed systems further than the notion of providing network connectivity. A network operating system is an operating system that provides features such as file sharing across the network, along with a communication scheme that allows different processes on different computers to exchange messages. A computer running a network operating system acts autonomously from all other computers on the network

      This section explains that a network operating system goes beyond basic connectivity by enabling features like file sharing and inter-process communication across machines. Each computer still runs independently, but the OS provides tools to make collaboration and resource sharing possible across the network.

    164. The media to carry networks are equally varied. They include copper wires, fiber strands, and wireless transmissions between satellites, microwave dishes, and radios. When computing devices are connected to cellular phones, they create a network. Even very short-range infrared communication can be used for networking. At a rudimentary level, whenever computers communicate, they use or create a network. These networks also vary in their performance and reliability.

      This passage highlights the many types of transmission media used in networking, from traditional copper wires to advanced fiber optics and wireless methods like satellite or cellular. It shows that networks can exist at any scale—even short-range infrared—and that their performance and reliability depend on the medium used.

    165. Networks are characterized based on the distances between their nodes. A local-area network (LAN) connects computers within a room, a building, or a campus. A wide-area network (WAN) usually links buildings, cities, or countries. A global company may have a WAN to connect its offices worldwide, for example. These networks may run one protocol or several protocols

      This section explains that networks are classified by distance. LANs cover small areas like buildings or campuses, while WANs span larger regions such as cities or even countries. Companies often use WANs to connect global offices, and these networks may rely on one or multiple communication protocols.

    166. A network, in the simplest terms, is a communication path between two or more systems. Distributed systems depend on networking for their functionality. Networks vary by the protocols used, the distances between nodes, and the transport media. TCP/IP is the most common network protocol, and it provides the fundamental architecture of the Internet. Most operating systems support TCP/IP, including all general-purpose ones

      This passage emphasizes that networks are the backbone of distributed systems, enabling communication between computers. It highlights that networks differ in protocol, distance, and media, but TCP/IP has become the universal standard—forming the foundation of the Internet and being supported by nearly all major operating systems.

    167. A distributed system is a collection of physically separate, possibly heterogeneous computer systems that are networked to provide users with access to the various resources that the system maintains. Access to a shared resource increases computation speed, functionality, data availability, and reliability. Some operating systems generalize network access as a form of file access, with the details of networking contained in the network interface's device driver. Others make users specifically invoke network functions.

      This section defines distributed systems as independent computers working together through a network. The benefit is that these resources can be shared,by improving speed, functionality, andthe reliability. It also notes that the operating systems handle the networking differently—some make it seamless by treating network access like file access, while others require users to call specific network functions.

    168. Within data centers, virtualization has become a common method of executing and managing computing environments. VMMs like VMware ESXand Citrix XenServer no longer run on host operating systems but rather are the host operating systems, providing services and resource management to virtual machine processes.

      This passage explains that in data centers, virtualization is not just an add-on but the foundation of the system. Modern Virtual Machine Monitors (like VMware ESX or Citrix XenServer) act like an actual operating system, directly managing hardware resources and running virtual machines. This shows how central virtualization has become in enterprise environments.

    169. Virtualization allows operating systems to run as applications within other operating systems. At first blush, there seems to be little reason for such functionality. But the virtualization industry is vast and growing, which is a testament to its utility and importance.

      This section points out that virtualization might seem unnecessary at first because operating systems already manage multiple applications. However, its growth shows how valuable it really is—virtualization enables flexibility, testing, security isolation, and efficient use of hardware, which explains why the industry keeps expanding.

    170. Even though modern operating systems are fully capable of running multiple applications reliably, the use of virtualization continues to grow. On laptops and desktops, a VMM allows the user to install multiple operating systems for exploration or to run applications written for operating systems other than the native host

      This passage explains about why the virtualization is still widely being used even though the modern operating systems can be used for multitasking. A Virtual Machine Monitor (VMM) lets the usersto run the different operating systems on the same hardware, which is considered useful for experimenting, testing the software, or running the programs that aren’t compatible with the host system.

  2. Aug 2025