Introduction to operating system
Table of Contents
Virtual Memory
Virtual memory is a fundamental concept in modern operating systems that allows multiple processes to run efficiently at the same time. Each process is given the illusion of having its own large, continuous block of memory, even though the actual physical memory of the system is much smaller and shared among many processes.
This is achieved by introducing a layer of abstraction between the process and the physical hardware. Instead of directly accessing physical memory addresses, processes work with virtual addresses. These virtual addresses are then mapped to real physical addresses using a combination of the operating system and specialized hardware known as the Memory Management Unit (MMU).
To make this possible, physical memory is divided into small fixed-size units called pages. Similarly, the virtual address space of each process is divided into pages of the same size. The system then establishes a mapping between virtual pages and physical pages. This mapping ensures that processes remain isolated from each other, while also making efficient use of available memory.
Virtual memory provides several important benefits:
Process Isolation: Each process runs in its own protected memory space, preventing accidental or malicious interference.
Efficient Memory Use: Physical memory is shared dynamically, so processes can use more memory than physically available through techniques like paging and swapping.
Flexibility: Programs do not need to know the actual physical layout of memory, making programming easier and safer.
Security: Unauthorized access to another process's memory is prevented by the operating system and MMU.
What does the process address space consist of?
In modern operating systems such as Linux, Windows, or macOS, every process is given the illusion that it owns a large, private block of memory. This abstraction is known as the virtual address space. It is called “virtual” because the addresses a program works with are not actual physical memory locations. Instead, the operating system and the Memory Management Unit (MMU) map these virtual addresses to physical memory, or in some cases, to data stored on disk.
The virtual address space is typically divided into distinct regions, each serving a different purpose. At the top lies the text segment, which stores the program's machine instructions. This region is usually marked as read-only and executable, preventing programs from accidentally modifying their own code. Below this, we find the data segment, which is itself split into two parts: the initialized data section, which holds global and static variables with explicit initial values, and the uninitialized data section (often called BSS), which contains globals and statics without initial values, defaulting them to zero.
Another important region is the heap, where dynamically allocated memory (from calls like malloc or new) resides. The heap typically grows upward, toward higher memory addresses, as more memory is requested during the lifetime of the process. On the other end of the spectrum, the stack grows downward, toward lower addresses. The stack stores function call frames, including local variables, return addresses, and saved registers, making it essential for program execution and function management.
Finally, there are memory-mapped regions, which the system uses for shared libraries, mapped files, and anonymous allocations such as large buffers. This mechanism allows processes to share code efficiently like linking against libc.so or a Windows DLL(Dynamic link library is a file containing shared code and data that multiple programs can use simultaneously to perform specific tasks) or directly map files from disk into memory space for faster access.
What is the Kernel?
The kernel is the central component of an operating system (OS), serving as the critical bridge between hardware and software. It is responsible for managing system resources—such as memory, CPU time, and devices—while providing essential services that allow applications to run smoothly and securely.
Resource Management: The kernel allocates CPU, memory, and input/output devices among processes. It ensures that resources are shared efficiently and prevents conflicts between competing programs.
System Calls: Applications do not interact with hardware directly. Instead, they use system calls to request services from the kernel, such as file operations, memory access, or network communication.
Security and Stability: The kernel enforces access control, making sure that one process cannot interfere with another. This protects both system stability and security by reducing the chances of crashes or unauthorized access.
Process Management: The kernel oversees the entire lifecycle of a process, from creation to termination. It handles scheduling—deciding which process runs on the CPU—and ensures fair sharing of system resources.
Scheduling: Modern kernels use sophisticated schedulers to balance workload across processes. Many, including Linux, employ preemptive scheduling, which allows the kernel to interrupt a running process and switch to another. This prevents a single program from monopolizing the CPU.
Context Switching: When switching between processes, the kernel saves the state of the currently running process and loads the state of the next one. This mechanism, known as context switching, allows the CPU to resume each process as if it had never been interrupted.
Process Scheduling Algorithms
Process scheduling algorithms determine how processes are selected for execution by the CPU. Broadly, they can be classified as non-preemptive or preemptive. In non-preemptive scheduling, once a process starts running, it cannot be interrupted until it finishes its execution. In preemptive scheduling, however, a running process can be interrupted if a higher-priority process becomes ready to execute. Different algorithms balance fairness, efficiency, and responsiveness in different ways.
First-Come, First-Served (Non-preemptive): Processes are executed in the order they arrive in the ready queue. It is simple to implement but may lead to long average waiting times, especially if a lengthy process is at the front of the queue.
Shortest Job Next (Non-preemptive): The process with the shortest estimated execution time is chosen next. This minimizes average waiting time but requires accurate knowledge of each process’s burst time in advance, which is difficult in real-world scenarios.
Priority Scheduling(Preemptive or Non-preemptive): Each process is assigned a priority, and the scheduler selects the highest-priority process to run first. While this is effective for ensuring important tasks are handled quickly, lower-priority processes risk starvation if higher-priority ones continuously arrive.
Round Robin (Preemptive): Each process is given a fixed time slice (called a quantum). If a process does not finish within its time slice, it is placed back into the queue, ensuring fairness among processes. While RR prevents starvation, frequent context switching may introduce overhead.
Multilevel Queue Scheduling(Preemptive or Non-preemptive): The ready queue is divided into multiple queues, each with its own scheduling policy. Processes are permanently assigned to a queue based on characteristics such as priority or memory requirements. Each queue may use a different scheduling algorithm, and higher-priority queues are typically served before lower-priority ones.