20 Matching Annotations
  1. Sep 2026
    1. An operating system is software that manages a computer's hardware. It also provides a basis for application programs and acts as an intermediary between the computer user and the computer hardware. An amazing aspect of operating systems is how they vary in accomplishing these tasks in a wide variety of computing environments. Operating systems are everywhere, from cars and home appliances that include “Internet of Things” devices, to smart phones, personal computers, enterprise computers, and cloud computing environments.

      gwgwgweg

    2. Because an operating system is large and complex, it must be created piece by piece. Each of these pieces should be a well-delineated portion of the system, with carefully defined inputs, outputs, and functions.

      I am testing this!.

    3. On modern computers, from mobile devices to servers, multiprocessor systems now dominate the landscape of computing. Traditionally, such systems have two (or more) processors, each with a single-core CPU. The processors share the computer bus and sometimes the clock, memory, and peripheral devices. The primary advantage of multiprocessor systems is increased throughput. That is, by increasing the number of processors, we expect to get more work done in less time. The speed-up ratio with N processors is not N, however; it is less than N. When multiple processors cooperate on a task, a certain amount of overhead is incurred in keeping all the parts working correctly. This overhead, plus contention for shared resources, lowers the expected gain from additional processors.

      These days, whether you're looking at mobile devices or full-scale servers, multiprocessor systems have essentially taken over as the dominant model in computing. Historically, this meant two or more processors, each built around a single-core CPU, sharing access to the computer's bus and sometimes the clock, memory, and peripherals as well. The big draw of going multiprocessor is greater throughput—the idea being that adding more processors should let you get more work done in less time. That said, the speed-up you get from N processors is never actually N; it always falls short of that. Whenever multiple processors work together on the same task, some overhead is inevitable just to keep everything coordinated properly, and that overhead, combined with processors competing for shared resources, eats into the gains you'd otherwise expect from adding more processing power.

    4. Operating systems are an essential part of any computer system. Similarly, a course on operating systems is an essential part of any computer science education.

      An operating system isn't just some optional layer—it's a core, essential part of any computer system, full stop. And in much the same way, a proper computer science education really can't be considered complete without a course dedicated to studying operating systems.

    5. The definition of multiprocessor has evolved over time and now includes multicore systems, in which multiple computing cores reside on a single chip. Multicore systems can be more efficient than multiple chips with single cores because on-chip communication is faster than between-chip communication. In addition, one chip with multiple cores uses significantly less power than multiple single-core chips, an important issue for mobile devices as well as laptops.

      The way we define "multiprocessor" has shifted over the years, and these days it covers multicore systems too—setups where several computing cores are packed onto a single chip. There's a real efficiency edge to multicore designs over having multiple separate single-core chips, mainly because communication happening on-chip is quicker than communication that has to travel between separate chips. On top of that, a single chip housing multiple cores draws noticeably less power compared to several individual single-core chips, which matters a lot for laptops and mobile devices where battery life is a constant concern.

    6. Many years ago, most computer systems used a single processor containing one CPU with a single processing core. The core is the component that executes instructions and registers for storing data locally. The one main CPU with its core is capable of executing a general-purpose instruction set, including instructions from processes. These systems have other special-purpose processors as well. They may come in the form of device-specific processors, such as disk, keyboard, and graphics controllers.

      Many years ago, most computer systems used a single processor containing one CPU with a single processing core. The core is the component that executes instructions and registers for storing data locally. The one main CPU with its core is capable of executing a general-purpose instruction set, including instructions from processes. These systems have other special-purpose processors as well. They may come in the form of device-specific processors, such as disk, keyboard, and graphics controllers.

    7. A large portion of operating system code is dedicated to managing I/O, both because of its importance to the reliability and performance of a system and because of the varying nature of the devices. Recall from the beginning of this section that a general-purpose computer system consists of multiple devices, all of which exchange data via a common bus. The form of interrupt-driven I/O described in Section 1.2.1 is fine for moving small amounts of data but can produce high overhead when used for bulk data movement such as NVS I/O. To solve this problem, direct memory access (DMA) is used. After setting up buffers, pointers, and counters for the I/O device, the device controller transfers an entire block of data directly to or from the device and main memory, with no intervention by the CPU. Only one interrupt is generated per block, to tell the device driver that the operation has completed, rather than the one interrupt per byte generated for low-speed devices. While the device controller is performing these operations, the CPU is available to accomplish other work.

      A significant chunk of any operating system's codebase is devoted to managing I/O—partly because it's so central to how reliable and performant the whole system is, and partly because the devices involved vary so widely from one another. Going back to what we covered earlier, a general-purpose computer is really made up of a bunch of devices all sharing data over a common bus. The interrupt-driven I/O approach discussed earlier works fine when you're moving small chunks of data, but it starts generating serious overhead once you're dealing with bulk transfers, like NVS I/O. Direct memory access, or DMA, exists to get around that problem. Once buffers, pointers, and counters have been set up for the device in question, the controller can transfer an entire block of data directly between the device and main memory, without needing the CPU involved at all during the transfer. Instead of firing off an interrupt for every single byte—the way slower devices tend to—DMA only triggers one interrupt per block, just to let the driver know the operation has wrapped up. And while all that's happening on the controller's end, the CPU is free to go handle other tasks in the meantime.

    8. In a larger sense, however, the storage structure that we have described—consisting of registers, main memory, and secondary storage—is only one of many possible storage system designs. Other possible components include cache memory, CD-ROM or blu-ray, magnetic tapes, and so on. Those that are slow enough and large enough that they are used only for special purposes—to store backup copies of material stored on other devices, for example—are called tertiary storage. Each storage system provides the basic functions of storing a datum and holding that datum until it is retrieved at a later time. The main differences among the various storage systems lie in speed, size, and volatility

      Zooming out a bit, the storage setup we've described so far—registers, main memory, secondary storage—is really just one possible arrangement among many. Other pieces can enter the picture too, like cache memory, CD-ROM or Blu-ray drives, magnetic tape, and similar technologies. Whenever a storage type is slow and large enough that it only makes sense for niche uses—like keeping backup copies of data stored elsewhere—it gets classified as tertiary storage. Regardless of which specific storage system we're talking about, they all share the same basic job: store a piece of data and hold onto it until something retrieves it later. What sets them apart from one another really comes down to three things: how fast they are, how much they can hold, and whether or not they're volatile.

    9. A typical instruction–execution cycle, as executed on a system with a von Neumann architecture, first fetches an instruction from memory and stores that instruction in the instruction register. The instruction is then decoded and may cause operands to be fetched from memory and stored in some internal register. After the instruction on the operands has been executed, the result may be stored back in memory. Notice that the memory unit sees only a stream of memory addresses. It does not know how they are generated (by the instruction counter, indexing, indirection, literal addresses, or some other means) or what they are for (instructions or data). Accordingly, we can ignore how a memory address is generated by a program. We are interested only in the sequence of memory addresses generated by the running program.

      On a system built around the classic von Neumann architecture, a standard instruction-execution cycle starts by pulling an instruction out of memory and placing it into the instruction register. From there, that instruction gets decoded, which may trigger operands being fetched from memory and stashed in internal registers. Once the instruction actually executes against those operands, the resulting output may get written back out to memory. It's worth pointing out that, from memory's perspective, all it ever sees is a stream of addresses—it has no idea how those addresses came to be (whether through an instruction counter, indexing, indirection, a literal address, or something else entirely), nor does it know whether a given address points to an instruction or to data. Because of that, we can set aside the question of how a program generates its memory addresses in the first place; what actually matters to us is simply the sequence of addresses the running program produces.

    10. Computers use other forms of memory as well. For example, the first program to run on computer power-on is a bootstrap program, which then loads the operating system. Since RAM is volatile—loses its content when power is turned off or otherwise lost—we cannot trust it to hold the bootstrap program. Instead, for this and some other purposes, the computer uses electrically erasable programmable read-only memory (EEPROM) and other forms of firmware—storage that is infrequently written to and is nonvolatile. EEPROM can be changed but cannot be changed frequently. In addition, it is low speed, and so it contains mostly static programs and data that aren't frequently used. For example, the iPhone uses EEPROM to store serial numbers and hardware information about the device.

      Beyond main memory, computers depend on a few other memory types for specific jobs. Take the bootstrap program, for instance—it's the very first thing that runs when a computer powers on, and its job is to load the operating system itself. Because RAM is volatile and loses everything the moment power drops, it's not a reliable place to store something as critical as the bootstrap program. So instead, computers turn to EEPROM (electrically erasable programmable read-only memory) and similar firmware-type storage for this and a handful of other needs—storage that's nonvolatile and only rarely rewritten. EEPROM can technically be updated, but not something you'd want to do often, and it's also fairly slow, which is why it's mostly reserved for static data and programs that don't need to change much. As one real-world example, iPhones use EEPROM to hold onto things like serial numbers and other hardware-specific details.

    11. The CPU can load instructions only from memory, so any programs must first be loaded into memory to run. General-purpose computers run most of their programs from rewritable memory, called main memory (also called random-access memory, or RAM). Main memory commonly is implemented in a semiconductor technology called dynamic random-access memory (DRAM).

      Since the CPU can only fetch instructions straight out of memory, any program has to be loaded into memory first before it can actually run. On general-purpose machines, the bulk of program execution happens out of main memory—rewritable storage more commonly referred to as RAM, or random-access memory. Under the hood, main memory is usually built using a semiconductor technology called dynamic random-access memory, or DRAM for short.

    12. The basic interrupt mechanism works as follows. The CPU hardware has a wire called the interrupt-request line that the CPU senses after executing every instruction. When the CPU detects that a controller has asserted a signal on the interrupt-request line, it reads the interrupt number and jumps to the interrupt-handler routine by using that interrupt number as an index into the interrupt vector. It then starts execution at the address associated with that index. The interrupt handler saves any state it will be changing during its operation, determines the cause of the interrupt, performs the necessary processing, performs a state restore, and executes a return_from_interrupt instruction to return the CPU to the execution state prior to the interrupt. We say that the device controller raises an interrupt by asserting a signal on the interrupt request line, the CPU catches the interrupt and dispatches it to the interrupt handler, and the handler clears the interrupt by servicing the device. Figure 1.4 summarizes the interrupt-driven I/O cycle.

      At its core, the interrupt mechanism relies on a dedicated wire on the CPU—the interrupt-request line—which the CPU checks after every single instruction it executes. The moment the CPU picks up on a controller asserting a signal along that line, it reads off the interrupt number and uses it as an index into the interrupt vector, jumping straight to the corresponding interrupt-handler routine and beginning execution at that address. From there, the handler saves any state it's about to alter, figures out what actually caused the interrupt, does whatever processing is needed, restores the saved state, and finally executes a return_from_interrupt instruction to bring the CPU back to exactly where it was before the interrupt hit. In this framing, we'd say the device controller "raises" the interrupt by asserting the signal, the CPU "catches" it and routes it to the handler, and the handler then "clears" it by servicing whatever the device needed. Figure 1.4 lays out this whole interrupt-driven I/O cycle in visual form.

    13. The interrupt architecture must also save the state information of whatever was interrupted, so that it can restore this information after servicing the interrupt. If the interrupt routine needs to modify the processor state—for instance, by modifying register values—it must explicitly save the current state and then restore that state before returning. After the interrupt is serviced, the saved return address is loaded into the program counter, and the interrupted computation resumes as though the interrupt had not occurred.

      On top of dispatching to the right handler, the interrupt architecture also has to preserve whatever state existed at the moment the interruption occurred, so it can be put back afterward once the interrupt has been dealt with. If the routine handling the interrupt needs to touch the processor state at all—changing register values, for example—it's on the hook for explicitly saving that state beforehand and restoring it before handing control back. Once the interrupt has been fully serviced, the previously saved return address gets loaded back into the program counter, letting the interrupted computation pick up right where it left off, as if nothing had ever happened.

    14. Interrupts are an important part of a computer architecture. Each computer design has its own interrupt mechanism, but several functions are common. The interrupt must transfer control to the appropriate interrupt service routine. The straightforward method for managing this transfer would be to invoke a generic routine to examine the interrupt information. The routine, in turn, would call the interrupt-specific handler. However, interrupts must be handled quickly, as they occur very frequently. A table of pointers to interrupt routines can be used instead to provide the necessary speed. The interrupt routine is called indirectly through the table, with no intermediate routine needed. Generally, the table of pointers is stored in low memory (the first hundred or so locations). These locations hold the addresses of the interrupt service routines for the various devices. This array, or interrupt vector, of addresses is then indexed by a unique number, given with the interrupt request, to provide the address of the interrupt service routine for the interrupting device. Operating systems as different as Windows and UNIX dispatch interrupts in this manner.

      Interrupts sit at the heart of computer architecture, and while every design implements its own version of the mechanism, the underlying functions tend to look pretty similar across the board. At minimum, an interrupt needs to hand control over to whatever service routine is meant to deal with it. One simple way to do that would be to route everything through a generic routine that inspects the interrupt details and then calls the correct handler—but because interrupts happen so often and need to be dealt with fast, that extra layer of indirection ends up being too slow in practice. A faster alternative is to keep a table of pointers to the various interrupt routines, letting the system jump straight to the right handler through that table without needing a middleman routine at all. This table is typically tucked away in low memory, often within the first hundred or so memory locations, and it stores the addresses of the service routines tied to each device. That array—known as the interrupt vector—gets indexed using a unique number that comes attached to the interrupt request itself, which then points directly to the address of the correct service routine for whichever device triggered it. Even operating systems as different from one another as Windows and UNIX rely on this same general approach for dispatching interrupts.

    15. Consider a typical computer operation: a program performing I/O. To start an I/O operation, the device driver loads the appropriate registers in the device controller. The device controller, in turn, examines the contents of these registers to determine what action to take (such as “read a character from the keyboard”). The controller starts the transfer of data from the device to its local buffer. Once the transfer of data is complete, the device controller informs the device driver that it has finished its operation. The device driver then gives control to other parts of the operating system, possibly returning the data or a pointer to the data if the operation was a read. For other operations, the device driver returns status information such as “write completed successfully” or “device busy”. But how does the controller inform the device driver that it has finished its operation? This is accomplished via an interrupt.

      Take a routine example, like a program carrying out an I/O request, to see how this all plays out in practice. The process kicks off when the device driver sets the relevant registers on the device controller, and from there the controller reads those register values to figure out exactly what it's being asked to do—something like "grab a character from the keyboard." It then handles moving the data from the actual device over into its own local buffer. Once that transfer wraps up, the controller needs a way to let the device driver know the job is done, so it can hand control back to the rest of the operating system—which might involve passing along the data itself (or a pointer to it, in the case of a read), or simply reporting a status like "write completed successfully" or "device busy." That leaves one question: how exactly does the controller signal the driver that it's finished? The answer is through an interrupt.

    16. A modern general-purpose computer system consists of one or more CPUs and a number of device controllers connected through a common bus that provides access between components and shared memory (Figure 1.2). Each device controller is in charge of a specific type of device (for example, a disk drive, audio device, or graphics display). Depending on the controller, more than one device may be attached. For instance, one system USB port can connect to a USB hub, to which several devices can connect. A device controller maintains some local buffer storage and a set of special-purpose registers. The device controller is responsible for moving the data between the peripheral devices that it controls and its local buffer storage.

      A typical general-purpose computer today is built around one or more CPUs along with a collection of device controllers, all tied together through a shared bus that gives everything access to each other and to shared memory. Each of these device controllers is dedicated to managing a particular category of device—say, a disk drive, an audio component, or a graphics display—and depending on the controller in question, it might handle just one device or several at once. A good example is a single USB port on a system, which can connect to a hub that then branches out to multiple peripherals. Controllers also come equipped with a small amount of local buffer storage plus a set of specialized registers, and their main function is shuttling data back and forth between the physical peripheral devices they oversee and that local buffer.

    17. From the computer's point of view, the operating system is the program most intimately involved with the hardware. In this context, we can view an operating system as a resource allocator. A computer system has many resources that may be required to solve a problem: CPU time, memory space, storage space, I/O devices, and so on. The operating system acts as the manager of these resources. Facing numerous and possibly conflicting requests for resources, the operating system must decide how to allocate them to specific programs and users so that it can operate the computer system efficiently and fairly.

      Flip the perspective around to the computer's side of things, and the operating system is really the piece of software with the closest, most direct relationship to the hardware. Seen this way, it makes sense to think of the operating system as a resource allocator—something that manages the pool of resources a computer has available whenever it's trying to solve a problem, whether that's CPU cycles, memory, storage, or various I/O devices. Since requests for these resources come in constantly and often clash with one another, the operating system has to figure out how to divide things up among competing programs and users in a way that keeps the whole system running both efficiently and fairly.

    18. An operating system is software that manages a computer's hardware. It also provides a basis for application programs and acts as an intermediary between the computer user and the computer hardware. An amazing aspect of operating systems is how they vary in accomplishing these tasks in a wide variety of computing environments. Operating systems are everywhere, from cars and home appliances that include “Internet of Things” devices, to smart phones, personal computers, enterprise computers, and cloud computing environments.

      An operating system is software that manages a computer's hardware. It also provides a basis for application programs and acts as an intermediary between the computer user and the computer hardware. An amazing aspect of operating systems is how they vary in accomplishing these tasks in a wide variety of computing environments. Operating systems are everywhere, from cars and home appliances that include “Internet of Things” devices, to smart phones, personal computers, enterprise computers, and cloud computing environments.

    19. An operating system acts as an intermediary between the user of a computer and the computer hardware. The purpose of an operating system is to provide an environment in which a user can execute programs in a convenient and efficient manner.

      An operating system's core role is to stand between the person using a computer and the raw hardware underneath, acting as a kind of translator or go-between. Ultimately, what it's trying to do is create an environment where users can run their programs smoothly—without needing to wrestle with the hardware directly—while also keeping things efficient under the hood.