DOC: Update documentation with multi-threading support

This commit is contained in:
boreddevnl 2026-03-18 18:09:46 +01:00
parent 5604866882
commit d9bcc4aff7
6 changed files with 48 additions and 23 deletions

View file

@ -23,6 +23,9 @@ BoredOS is a functional x86_64 operating system featuring a custom Desktop Envir
### ⚙️ System Architecture ### ⚙️ System Architecture
* **64-bit Long Mode:** Fully utilizing the x86_64 architecture. * **64-bit Long Mode:** Fully utilizing the x86_64 architecture.
* **Symmetric Multi-Processing (SMP):** Full support for multi-core CPUs via Limine SMP.
* **LAPIC & IPI Scheduling:** Advanced interrupt handling and inter-processor communication for task distribution.
* **SMP-Safe Spinlocks:** Robust kernel-wide synchronization for VFS, process management, and the GUI.
* **Multiboot2 Compliant:** Bootable on real hardware and modern emulators. * **Multiboot2 Compliant:** Bootable on real hardware and modern emulators.
* **Kernel Core:** Interrupt Descriptor Table (IDT) management and a robust syscall interface. * **Kernel Core:** Interrupt Descriptor Table (IDT) management and a robust syscall interface.
* **Filesystem:** Full **FAT32** support for persistent and in-memory storage. * **Filesystem:** Full **FAT32** support for persistent and in-memory storage.

View file

@ -68,7 +68,9 @@ Applications must continuously poll for events inside an infinite `$while(1)` lo
Returns `true` if an event was waiting in the queue, populating the `ev` structure. Returns `false` if the queue is empty. Returns `true` if an event was waiting in the queue, populating the `ev` structure. Returns `false` if the queue is empty.
> [!IMPORTANT] > [!IMPORTANT]
> Because `ui_get_event` is non-blocking, you must call `sys_yield();` inside your event loop if no event was received, otherwise your app will consume 100% of the CPU timeslice. > Because `ui_get_event` is non-blocking, you must call `sys_yield();` inside your event loop if no event was received. In BoredOS's **Multi-Core (SMP)** architecture, failing to yield will pin a CPU core to 100% usage, potentially starving other processes.
>
> All UI syscalls are **Thread-Safe** at the kernel level via the global GUI spinlock.
### Graphical Event Structure ### Graphical Event Structure

View file

@ -19,7 +19,7 @@ The OS heavily relies on module separation. The `src/` directory is logically sp
- **`fs/`**: Filesystem implementations. The system uses a Virtual File System (VFS) abstraction alongside an in-memory FAT32 filesystem with support for drives over ATA that are formatted as FAT32 (plain/MBR). - **`fs/`**: Filesystem implementations. The system uses a Virtual File System (VFS) abstraction alongside an in-memory FAT32 filesystem with support for drives over ATA that are formatted as FAT32 (plain/MBR).
- **`mem/`**: Physical and virtual memory management. It controls page frame allocation, paging, and kernel heap operations. - **`mem/`**: Physical and virtual memory management. It controls page frame allocation, paging, and kernel heap operations.
- **`net/`**: The networking stack. BoredOS relies on `lwIP` for processing IPv4 and TCP/UDP traffic, interacting with a range of NICs via `net/nic/`. - **`net/`**: The networking stack. BoredOS relies on `lwIP` for processing IPv4 and TCP/UDP traffic, interacting with a range of NICs via `net/nic/`.
- **`sys/`**: System calls and process management. The ELF loader resides here, parsing userland binaries and setting them up for execution. - **`sys/`**: System calls and process management. The ELF loader resides here, alongside the Symmetric Multi-Processing (**smp.c**) bringup and Local APIC (**lapic.c**) management logic.
- **`wm/`**: The graphical subsystem. It handles drawing primitives, window structures, font rendering, and double-buffering. - **`wm/`**: The graphical subsystem. It handles drawing primitives, window structures, font rendering, and double-buffering.
- **`userland/`**: Out-of-kernel components. This includes the custom SDK/compiler environment (`libc/`) and user applications (`cli/`, `gui/`, `games/`). - **`userland/`**: Out-of-kernel components. This includes the custom SDK/compiler environment (`libc/`) and user applications (`cli/`, `gui/`, `games/`).
@ -28,24 +28,31 @@ The OS heavily relies on module separation. The `src/` directory is logically sp
BoredOS uses **Limine** as its primary bootloader. BoredOS uses **Limine** as its primary bootloader.
1. **Limine Initialization**: The machine firmware (BIOS or UEFI) loads Limine. Limine parses `limine.conf`, sets up an early graphical framebuffer, and reads the kernel ELF file into memory. 1. **Limine Initialization**: The machine firmware (BIOS or UEFI) loads Limine. Limine parses `limine.conf`, sets up an early graphical framebuffer, and reads the kernel ELF file into memory.
2. **Multiboot2 Protocol**: The kernel expects the Limine boot protocol (which is compatible with modern Multiboot specifications). Passing a framebuffer and memory map is handled natively by Limine's request structures (defined locally via `limine.h`). 2. **Multiboot2 & SMP Protocol**: The kernel expects the Limine boot protocol. It makes a specific **SMP Request** to Limine to locate and bring up all available CPU cores.
3. **Kernel Entry (`main.c`)**: The entry point `_start` is called. It immediately initializes the serial port for debugging, sets up core structures (GDT/IDT), initializes the physical memory manager based on the Limine memory map, and starts the virtual memory manager. 3. **Kernel Entry (`main.c`)**: The entry point `_start` is called on the Bootstrap Processor (BSP). It initializes the serial port, GDT/IDT, memory management, and paging.
4. **Driver Initialization**: PCI buses are scanned, finding the network card or disk controllers. The filesystem is mounted. 4. **AP Bringup**: The BSP calls `smp_init()`, which sends the Startup Inter-Processor Interrupt (SIPI) sequence to all Application Processors (APs). Each AP initializes its own local GDT, TSS, and Page Tables before entering an idle loop.
5. **Window Manager**: The UI is drawn on top of the Limine-provided framebuffer. 5. **Driver Initialization**: PCI buses are scanned, finding the network card or disk controllers. The filesystem is mounted.
6. **Window Manager**: The UI is drawn on top of the Limine-provided framebuffer.
> [!NOTE] ## 🧵 Multi-Core & Scheduling
> The kernel parses memory maps dynamically, meaning it adjusts optimally to the RAM provided by the environment or emulator.
BoredOS utilizes Symmetric Multi-Processing (SMP) to distribute workloads across all available CPU cores.
- **LAPIC & IPIs**: Each CPU has its own Local APIC. The kernel uses Inter-Processor Interrupts (IPIs) for inter-core communication, specifically for triggering the scheduler on other cores (`vector 0x41`).
- **Scheduler**: A round-robin scheduler runs on each core. Processes are pinned to specific CPUs (CPU Affinity) to maintain cache locality. The BSP timer interrupt (`60Hz`) broadcasts a scheduling IPI to all core to ensure balanced execution.
- **Spinlocks**: Since multiple cores can access kernel structures (VFS, Process List) simultaneously, the kernel uses **interrupt-safe spinlocks** to prevent race conditions.
## 🛡️ Userland Transition ## 🛡️ Userland Transition
The OS supports privilege separation (Ring 0 vs. Ring 3). When an application (like `browser.elf` or `viewer.elf`) is launched, the kernel: The OS supports privilege separation (Ring 0 vs. Ring 3). When an application is launched, the kernel:
1. Loads the ELF file from the filesystem using the ELF parser in `sys/elf.c`. 1. Loads the ELF file from the filesystem.
2. Allocates a new virtual address space (Page Directory) for the process. 2. Assigns the process to a CPU core via a round-robin distribution strategy.
3. Maps the executable segments according to the ELF headers. 3. Allocates a new virtual address space (Page Directory) for the process.
4. Switches to User Mode (Ring 3) via the `iretq` instruction, jumping into the application's entry point (`crt0.asm`). 4. Maps the executable segments according to the ELF headers.
5. Switches to User Mode (Ring 3) via the `iretq` instruction.
> [!IMPORTANT] > [!IMPORTANT]
> Programs then interact with the core kernel using system calls (`syscall.c`). Isolated processes cannot directly access hardware or kernel memory structures without faulting. > Programs interact with the core kernel using system calls (`syscall.c`). Multitasking is achieved by pre-empting user processes on their respective cores.
--- ---

View file

@ -15,6 +15,7 @@ Key VFS functionalities include:
- **File Descriptors**: Mapping integer IDs to internal file structures for userland processes. - **File Descriptors**: Mapping integer IDs to internal file structures for userland processes.
- **Standard Operations**: Standardizing `open()`, `read()`, `write()`, `close()`, `seek()`, and directory listings. - **Standard Operations**: Standardizing `open()`, `read()`, `write()`, `close()`, `seek()`, and directory listings.
- **Path Parsing**: Resolving absolute and relative paths. - **Path Parsing**: Resolving absolute and relative paths.
- **SMP Safety**: All VFS and underlying FAT32 operations are protected by a global **Spinlock**. This ensures that multiple cores can safely read from the filesystem simultaneously without corrupting internal file seek pointers or directory cache states.
## 💾 FAT32 Implementation ## 💾 FAT32 Implementation

View file

@ -14,6 +14,7 @@ The PMM is responsible for tracking which physical RAM frames (usually 4KB each)
1. **Memory Map**: During boot, Limine provides a memory map detailing the available, reserved, and unusable physical memory regions. 1. **Memory Map**: During boot, Limine provides a memory map detailing the available, reserved, and unusable physical memory regions.
2. **Bitmap Allocator**: The core PMM uses a bitmap-based allocation strategy. Each bit in the bitmap represents a single physical page (frame). If a bit is `1`, the page is in use; if `0`, it is free. 2. **Bitmap Allocator**: The core PMM uses a bitmap-based allocation strategy. Each bit in the bitmap represents a single physical page (frame). If a bit is `1`, the page is in use; if `0`, it is free.
3. **Allocation**: When a new page is requested (e.g., for userland space or kernel heap), the PMM scans the bitmap for the first available zero bit, marks it as used, and returns the physical address. 3. **Allocation**: When a new page is requested (e.g., for userland space or kernel heap), the PMM scans the bitmap for the first available zero bit, marks it as used, and returns the physical address.
4. **SMP Safety**: In a multi-core environment, the PMM and VMM are protected by **Spinlocks** to prevent two CPUs from allocating the same frame or modifying page tables simultaneously.
> [!NOTE] > [!NOTE]
> 4KB frame sizes strike a balance between allocation speed and minimal memory fragmentation, fitting directly with the page tables. > 4KB frame sizes strike a balance between allocation speed and minimal memory fragmentation, fitting directly with the page tables.
@ -22,12 +23,18 @@ The PMM is responsible for tracking which physical RAM frames (usually 4KB each)
BoredOS uses 4-level paging (PML4), a requirement for x86_64 long mode, dividing the virtual address space between the kernel and userland. BoredOS uses 4-level paging (PML4), a requirement for x86_64 long mode, dividing the virtual address space between the kernel and userland.
- **Kernel Space**: The kernel relies on a higher-half design where its code, data, and heap are mapped to high addresses (typically above `0xFFFF800000000000`). This ensures the kernel remains mapped and accessible regardless of which user process is currently active. - **Kernel Space**: The kernel relies on a higher-half design where its code, data, and heap are mapped to high addresses (typically above `0xFFFF800000000000`).
- **User Space**: Userland applications are loaded into lower virtual addresses (starting frequently around `0x40000000`). - **Per-CPU Structures**: Each CPU core maintains its own architectural state in memory:
- **Page Faults**: The `mem/` subsystem registers an Interrupt Service Routine (ISR) for page faults (Interrupt 14). If a process accesses unmapped memory, the handler determines whether to allocate a new frame (e.g., for stack growth or lazy loading) or terminate the process for a segmentation fault. * **Per-CPU GDT**: Each core is initialized with its own Global Descriptor Table.
* **Per-CPU TSS**: Each core has a dedicated Task State Segment containing the `RSP0` pointer for its own kernel stack, ensuring safe interrupt handling across cores.
- **User Space**: Userland applications are loaded into lower virtual addresses.
- **Page Faults**: The `mem/` subsystem registers an Interrupt Service Routine (ISR) for page faults (Interrupt 14). If a process accesses unmapped memory, the handler determines whether to allocate a new frame or terminate the process.
## 🏗️ Kernel Heap ## 🏗️ Kernel Heap
Dynamic allocation within the kernel (`kmalloc` and `kfree`) is layered on top of the physical allocator. The kernel maintains its own heap area in virtual memory. When the heap requires more space, it requests physical frames from the PMM and maps them into the kernel's virtual address space using the VMM. Dynamic allocation within the kernel (`kmalloc` and `kfree`) is layered on top of the physical allocator. The kernel maintains its own heap area in virtual memory. When the heap requires more space, it requests physical frames from the PMM and maps them into the kernel's virtual address space.
> [!IMPORTANT]
> The kernel heap is a shared resource; therefore, all `kmalloc` and `kfree` operations are guarded by a global spinlock to ensure thread safety during multi-core execution.
--- ---

View file

@ -32,12 +32,17 @@ The WM acts as the central hub for input routing.
2. **Hit Testing**: The WM checks these coordinates against the bounding boxes of existing windows. It handles dragging logic (if the user clicks a title bar) or focus changes. 2. **Hit Testing**: The WM checks these coordinates against the bounding boxes of existing windows. It handles dragging logic (if the user clicks a title bar) or focus changes.
3. **Event Queue**: If a userland application owns the window that was clicked, the WM packages the input (coordinates, button state) into an event message and drops it into the owning process's event queue. The application can retrieve these via the custom libc UI functions. 3. **Event Queue**: If a userland application owns the window that was clicked, the WM packages the input (coordinates, button state) into an event message and drops it into the owning process's event queue. The application can retrieve these via the custom libc UI functions.
## 🔌 Userland API (`libui.c`) - **Event Polling**: The UI loop inside an app continuously calls `ui_poll_event()` to respond to mouse clicks and window movement dispatched by the kernel WM.
Applications do not talk to the hardware directly. Instead, they use a library (`libui.c`) which makes specialized system calls (`SYS_GUI`). ## 🧵 Multi-Core Safety & Performance
- **Window Creation**: `ui_create_window()` asks the kernel to instantiate a new window object and returns a handle. With the introduction of Symmetric Multi-Processing (SMP), the Window Manager (WM) was redesigned to ensure stability and high performance across multiple cores.
- **Drawing**: Applications can request the kernel to fill rectangles or plot pixels inside their designated window area.
- **Event Polling**: The UI loop inside an app continuously calls `ui_poll_event()` to respond to mouse clicks and window movement dispatched by the kernel WM. 1. **Global GUI Lock (`wm_lock`)**: To prevent race conditions when multiple cores attempt to create windows, move cursors, or update pixels, the WM utilizes a central spinlock. All `GUI_CMD` system calls are protected by this lock.
2. **Deferred Rendering**: Previously, the desktop was repainted inside the timer interrupt. On multi-core systems, this caused severe "core starvation" as all other CPUs would spin waiting for the GUI lock during the long draw cycle.
3. **Kernel Loop Integration**: Final screen composition (`wm_paint`) is now deferred to the main kernel idle loop on the Bootstrap Processor (BSP). This allows application cores to continue processing logic while the GUI asynchronously flips the framebuffer.
> [!IMPORTANT]
> Because rendering is now asynchronous to the timer, application performance is significantly higher as they are no longer bottlenecked by interrupt-context drawing.
--- ---