|
| 1 | +# Linmo: A Simple Multi-tasking Operating System Kernel |
| 2 | +``` |
| 3 | + ▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▖ |
| 4 | + ▐██████████████ |
| 5 | + ▐█████▜▆▆▛█████ |
| 6 | + ▗███▉▜██▟▀▀▙██▇███▆▖ |
| 7 | + ▗█████▜██▅▅▅▅██▀████▉ |
| 8 | + ▕███▙▇███▛▘▝████▆███▛ ▝▇▇▇▘ ▝▇▇▛▘▝▇▇▄ ▝▇▛▘ ▇▇▙ ▇▇▀ ▗▅▇▀▀▇▅▖ |
| 9 | + ▝████▛▀▔ ▔▀▜███▉ ▐█▋ ██▍ ▐██▙▖ ▐▌ ▐██▖ ▟██▎ ▗█▛ ██▖ |
| 10 | + █▔█▍▝▀▀▀╸ ━▀▀▀ █▛▜▋ ▐█▋ ██▍ ▐▋▝▜█▃▐▌ █▎▜█▖▐▛▐█▌ ██▌ ▐█▋ |
| 11 | + ▜▖▜▌╺▄▃▄╸ ┓╺▄▄━ █▋▟▍ ▐█▋ ▅▏ ██▍ ▐▋ ▜██▌ █▏ ███ ▐█▋ ▐█▙ ▟█▘ |
| 12 | + ▐▛▀▊ ▝ ▕█▀█ ▗▟██▅▅▇▛ ▅██▙▖▗▟█▖ ▝█▌ ▅█▅ ▝█▘╶▟██▖ ▝▜▇▅▄▇▀▘ |
| 13 | + ▝▙▅█▖ ▀━┷▘ ▟█▅▛ |
| 14 | + ▗██▌█▖ ╺━▇▇━ ▄▊▜█▉ ▆▍ ▕▆ |
| 15 | + ▗██▛▗█▛▅▂▝▀▀▁▃▇██▝██▙ █▍▅▛ ▗▆▀▜▅ ▇▆▀▕▇┻▀▆ ▗▇▀▇▖▕█ |
| 16 | + ▄███▇██▎ ▀▀▀▀▀▔ ▐█████▙▁ █▛▜▄ ▜█▀▜▉ █▎ ▕█▏ █▎▜▛▀▜▉▕█ |
| 17 | + ▀▀▔▔▜▄▀▜▅▄▂▁▁▃▄▆▀▚▟▀▔▀▀▘ ▀▘ ▀▘ ▀▀▀▔ ▀ ▀ ▀ ▀▀▀▔ ▀ |
| 18 | + ▝▀▆▄▃████▃▄▆▀▔ |
| 19 | +``` |
| 20 | + |
| 21 | +Linmo is a preemptive, multi-tasking operating system kernel built as an educational showcase for resource-constrained systems. |
| 22 | +It offers a lightweight environment where all tasks share a single address space, |
| 23 | +keeping the memory footprint to a minimum. |
| 24 | + |
| 25 | +Target Platform: |
| 26 | +* RISC-V (32-bit): RV32I architecture, tested with QEMU. |
| 27 | + |
| 28 | +Features: |
| 29 | +* Minimal kernel size. |
| 30 | +* Lightweight task model with a shared address space. |
| 31 | +* Preemptive and cooperative scheduling using a priority-based round-robin algorithm. |
| 32 | +* Support for a user-defined real-time scheduler. |
| 33 | +* Task synchronization and IPC primitives: semaphores, mutex / condition variable, pipes, and message queues. |
| 34 | +* Software timers with callback functionality. |
| 35 | +* Dynamic memory allocation. |
| 36 | +* A compact C library. |
| 37 | + |
| 38 | +## Getting Started |
| 39 | +This section guides you through building the Linmo kernel and running example applications. |
| 40 | + |
| 41 | +### Prerequisites |
| 42 | +* A RISC-V cross-compilation toolchain: `riscv-none-elf`, which can be download via [riscv-gnu-toolchain](https://github.com/riscv-collab/riscv-gnu-toolchain). |
| 43 | +* The QEMU emulator for RISC-V: `qemu-system-riscv32`. |
| 44 | + |
| 45 | +### Building the Kernel |
| 46 | +To build the kernel, run the following command: |
| 47 | + |
| 48 | +```bash |
| 49 | +make |
| 50 | +``` |
| 51 | + |
| 52 | +This command compiles the kernel into a static library (`liblinmo.a`) located in the `build/target` directory. |
| 53 | +This library can then be linked against user applications. |
| 54 | + |
| 55 | +### Building and Running an Application |
| 56 | +To build and run an application (e.g., the `hello` example) on QEMU for 32-bit RISC-V: |
| 57 | +1. Build the application: |
| 58 | + ```bash |
| 59 | + make hello |
| 60 | + ``` |
| 61 | +2. Run the application in QEMU: |
| 62 | + ```bash |
| 63 | + make run |
| 64 | + ``` |
| 65 | + To exit QEMU, press `Ctrl+a` then `x`. |
| 66 | + |
| 67 | +## Core Concepts |
| 68 | + |
| 69 | +### Tasks |
| 70 | +Tasks are the fundamental units of execution in Linmo. |
| 71 | +Each task is a function that typically runs in an infinite loop. |
| 72 | +All tasks operate within a shared memory space. |
| 73 | +At startup, the kernel initializes all registered tasks and allocates a dedicated stack for each. |
| 74 | + |
| 75 | +### Memory Management |
| 76 | + |
| 77 | +#### Stack Allocation |
| 78 | +Each task is allocated a dedicated stack from the system heap. |
| 79 | +The heap is a shared memory region managed by Linmo's dynamic memory allocator and is available to both the kernel and the application. |
| 80 | +
|
| 81 | +A task's stack stores local variables, function call frames, and the CPU context during interrupts or context switches. |
| 82 | +The stack size is configurable per-task and must be specified upon task creation. |
| 83 | +Each target architecture defines a default stack size through the `DEFAULT_STACK_SIZE` macro in its HAL. |
| 84 | +It is crucial to tune the stack size for each task based on its memory requirements. |
| 85 | + |
| 86 | +#### Dynamic Memory Allocation |
| 87 | +Linmo provides standard dynamic memory allocation functions (`malloc`, `calloc`, `realloc`, `free`) for both the kernel and applications. |
| 88 | + |
| 89 | +### Scheduling |
| 90 | +Linmo supports both cooperative and preemptive multitasking. |
| 91 | +The scheduling mode is determined by the return value of the `app_main()` function at startup: |
| 92 | +* Cooperative Scheduling (return value `0`): Tasks retain control of the CPU until they explicitly yield it by calling `mo_task_yield()`. |
| 93 | + This mode gives the developer full control over when a context switch occurs. |
| 94 | +* Preemptive Scheduling (return value `1`): The kernel automatically schedules tasks based on a periodic interrupt, |
| 95 | + ensuring that each task gets a fair share of CPU time without needing to yield manually. |
| 96 | +
|
| 97 | +The default scheduler uses a priority-based round-robin algorithm. |
| 98 | +All tasks are initially assigned `TASK_PRIO_NORMAL`, and the scheduler allocates equal time slices to tasks of the same priority. |
| 99 | +Task priorities can be set during initialization in `app_main()` or dynamically at runtime using the `mo_task_priority()` function. |
| 100 | +
|
| 101 | +The available priority levels are: |
| 102 | +* `TASK_PRIO_CRIT` (Critical) |
| 103 | +* `TASK_PRIO_REALTIME` (Real-time) |
| 104 | +* `TASK_PRIO_HIGH` (High) |
| 105 | +* `TASK_PRIO_ABOVE` (Above Normal) |
| 106 | +* `TASK_PRIO_NORMAL` (Normal) |
| 107 | +* `TASK_BELOW_PRIO` (Below Normal) |
| 108 | +* `TASK_PRIO_LOW` (Low) |
| 109 | +* `TASK_PRIO_IDLE` (Lowest) |
| 110 | +
|
| 111 | +For more advanced scheduling needs, Linmo supports a user-defined real-time scheduler. |
| 112 | +If provided, this scheduler overrides the default round-robin policy for tasks designated as real-time. |
| 113 | +Real-time tasks are configured using the `mo_task_rt_priority()` function and linked to the custom scheduler via the kernel's control block. |
| 114 | +
|
| 115 | +### Inter-Task Communication (IPC) |
| 116 | +Linmo provides several primitives for task synchronization and data exchange, which are essential for building complex embedded applications: |
| 117 | +* Semaphores: Counting semaphores for mutual exclusion (mutex) and signaling between tasks. |
| 118 | +* Pipes: Unidirectional, byte-oriented channels for streaming data between tasks. |
| 119 | +* Message Queues and Event Queues: For structured message passing and event-based signaling (can be enabled in the configuration). |
| 120 | +
|
| 121 | +These IPC mechanisms ensure safe and coordinated interactions between concurrent tasks in the shared memory environment. |
| 122 | +
|
| 123 | +## Hardware Abstraction Layer (HAL) |
| 124 | +Linmo uses a Hardware Abstraction Layer (HAL) to separate the portable kernel code from the underlying hardware-specific details. |
| 125 | +This design allows applications to be compiled for different target architectures without modification. |
| 126 | +
|
| 127 | +## C Library Support (LibC) |
| 128 | +Linmo includes a minimal C library to reduce the overall footprint of the system. |
| 129 | +The provided functions are implemented as macros that alias internal library functions. |
| 130 | +For applications requiring more features or full standard compliance, these macros can be overridden or removed in the HAL to link against an external C library. |
| 131 | +
|
| 132 | +Functions like `printf()` are simplified implementations that provide only essential functionality, optimized for code size. |
| 133 | +
|
| 134 | +The following table lists the supported C library functions: |
| 135 | +
|
| 136 | +| LibC | | | | | |
| 137 | +| :----------- | :---------- | :---------- | :---------- | :---------- | |
| 138 | +| `strcpy()` | `strncpy()` | `strcat()` | `strncat()` | `strcmp()` | |
| 139 | +| `strncmp()` | `strstr()` | `strlen()` | `strchr()` | `strpbrk()` | |
| 140 | +| `strsep()` | `strtok()` | `strtok_r()`| `strtol()` | `atoi()` | |
| 141 | +| `itoa()` | `memcpy()` | `memmove()` | `memcmp()` | `memset()` | |
| 142 | +| `random_r()` | `random()` | `srand()` | `puts()` | `gets()` | |
| 143 | +| `fgets()` | `getline()` | `printf()` | `sprintf()` | `free()` | |
| 144 | +| `malloc()` | `calloc()` | `realloc()` | `abs()` | | |
| 145 | +
|
| 146 | +## Reference |
| 147 | +* [Operating System in 1000 Lines](https://operating-system-in-1000-lines.vercel.app/en/) |
| 148 | +* [egos-2000](https://github.com/yhzhang0128/egos-2000) |
0 commit comments