Expand description
§KeOS: KAIST Educational Operating System
Welcome to the KeOS project!
Operating systems (OS) form the backbone of modern computing, managing hardware resources and providing a foundation for applications to run efficiently. Understanding how an OS works is crucial for grasping the inner workings of computer systems and software performance.
KeOS is designed to offer a hands-on learning experience with core OS components. Through this project, you will explore the fundamental principles of OS design while building a minimal but functional operating system from the ground up.
We prioritize simplicity, focusing on the most essential aspects of OS development. You won’t have to worry about handling obscure edge cases or hidden test cases. The score you receive after running the grading scripts is your final score. Our goal is to make this project as straightforward as possible. If you have suggestions on how we can further minimize unnecessary complexity and focus on the core concepts, we encourage your feedback.
§⚠️ IMPORTANT NOTES on GRADING
- DO NOT make public forks of this project.
- The KeOS license explicitly prohibits public redistribution of this work.
- You MUST NOT share or distribute your work based on the provided template.
- Cheating, plagiarism, or uploading your code online is strictly prohibited and will result in disqualification.
Failure to comply may result in academic integrity violations.
For the grading, please refer to the following policy:
- Your submission must pass all test cases without modifying the non-whitelisted code in each project.
- Submissions that fail to compile will receive 0 points.
§Why Rust?
We have chosen Rust for this project because of its memory safety, zero-cost abstractions, and most importantly, its concurrency model. These features make Rust an excellent choice for operating system development.
In traditional system programming languages, concurrency and memory bugs such as data races, use-after-free errors, and null pointer dereferences are common. Rust prevents these issues at compile time by enforcing strict ownership, borrowing, and lifetime rules. This allows you to write safe and efficient concurrent code without sacrificing performance, and reduces debugging time for those bugs.
By using Rust in KeOS, you will:
- Develop safe and efficient concurrent programs without the risk of data races.
- Avoid common concurrency pitfalls such as race conditions.
§Project Structure
The KeOS project is divided into five projects:
System Call– Learn how the OS interacts with user applications.Memory Management– Implement basic memory management and user-space execution.Advanced Memory Management– Expand the KeOS’s memory management system with advanced features.Process Management– Implement the advanced process management, including round robin scheduler and sychronization primitives.File System– Develop a simple yet functional filesystem for data storage.
Each project builds upon the previous ones, helping you progressively develop a deeper understanding of OS design.
§Implementation Notes
In KeOS, each process/thread is assigned a fixed execution stack of
STACK_SIZE bytes. While KeOS attempts to detect stack overflows, its
detection is not perfect. A stack overflow may lead to mysterious kernel
panics. To avoid this:
- Avoid declaring large data structures on the stack.
let v: [u8; 0x200000]; // ERROR: This may cause a stack overflow- Instead, allocate large data structures on the heap using
Box.
let v = Box::new([0u8; 0x200000]); // OK: Allocates on the heap§Implementation Strategy
We recommend using a “TODO-driven” approach to build KeOS systematically. This method ensures an incremental and structured development process:
- Run the code and identify
todo!()placeholders that cause panics. - Implement the missing functionality, ensuring it aligns with the expected behavior described in the project requirements.
- Repeat steps 1 and 2 until all test cases pass and the system behaves correctly.
This approach allows you to build your OS one step at a time, making debugging and understanding the system easier.
§Getting Started
To set up your KeOS development environment, run the following commands:
$ mkdir keos
$ cd keos
$ curl https://raw.githubusercontent.com/casys-kaist/KeOS/refs/heads/main/scripts/install.sh | shWe recommend using VS Code as the editor, along with rust-analyzer for
Rust support.
- DO NOT make public forks of this project.
- The KeOS license explicitly prohibits public redistribution of this work.
- You MUST NOT share or distribute your work based on the provided template.
Failure to comply may result in academic integrity violations.
§Selectively run tests
In KeOS, you can run one or more specific test cases by passing their names as arguments to the test runner. For example:
$ cargo run -- syscall::pipe_normal syscall::pipe_partialThis command runs exactly the listed test cases, syscall::pipe_normal and
syscall::pipe_partial. You may specify a single test case or multiple test
cases, depending on your needs.
§Grading Policy
During grading, we will overwrite all files except those explicitly whitelisted for each project. Any modifications to non-whitelisted files may result in a zero score, even if your implementation otherwise works correctly.
You can run the cargo grade command to check your current score locally.
This reported score will be treated as your final grade, as long as your
submission complies with the whitelist policy.
⚠️ IMPORTANT NOTES:
- Your submission must pass all test cases without modifying the test code.
- Submissions that fail to compile will receive 0 points.
- Cheating, plagiarism, or uploading your code online is strictly prohibited and will result in disqualification.
Grading rubrics and the list of whitelisted files can be found in each
grader’s .grade-target file.
§Debugging with GDB
KeOS supports debugging with GDB and QEMU. This section provides step-by-step instructions on how to set up and use GDB for effective debugging.
§Running GDB
To launch KeOS in debug mode, run the following command inside each grader directory:
$ GDB=1 cargo run <TESTCASE>You must specify a single test case to debug with a GDB.
This starts QEMU and waits for a GDB connection on TCP port 1234.
A .gdbinit script will also be generated to automate the debugging setup.
In a separate terminal, start GDB using:
$ rust-gdb keos_kernelWhy rust-gdb?
We recommend rust-gdb, as it provides better support for Rust-specific
data structures and improves debugging readability.
§One-time setup
Before using rust-gdb, you may need to modify your ~/.gdbinit file
to allow script execution. Add the following line:
set auto-load safe-path /After launching rust-gdb, the execution will halt at the startup stage,
showing output similar to this:
$ rust-gdb
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0x000000000000fff0 in ?? ()
(gdb)Now, you can continue execution by typing:
§Inspect Each Core
In QEMU, each CPU core is treated as a separate thread. When debugging multi-core execution, be aware that some cores may panic while others continue running.
To inspect all active cores, use:
(gdb) info threadsThis will display the state of each thread, including which CPU core it belongs to and its current stack frame. For example:
(gdb) info threads
Id Target Id Frame
* 1 Thread 1 (CPU#0 [running]) 0x000000000000fff0 in ?? ()
2 Thread 2 (CPU#1 [running]) 0x000000000000fff0 in ?? ()
3 Thread 3 (CPU#2 [running]) 0x000000000000fff0 in ?? ()
4 Thread 4 (CPU#3 [running]) 0x000000000000fff0 in ?? ()To switch to a specific CPU core (thread), use:
(gdb) thread {thread_id}This allows you to inspect registers, call stacks, and execution state per core.
§Analyzing Execution State
§Viewing the Call Stack (Backtrace)
Use backtrace (or bt) to display the call stack of the current
thread:
(gdb) btEach function call in the stack is represented as a frame. To switch to a specific frame, use:
(gdb) frame {frame_id}Once inside a frame, you can inspect variables:
(gdb) info args
(gdb) info locals
(gdb) i rDebugging a Panic: If you encounter a kernel panic during a test, use:
info threadsto locate the crashing corebtto examine the backtraceframe {frame_id}to inspect function parameters
§Setting Breakpoints
Breakpoints help stop execution at specific points. However, in multi-core debugging, regular breakpoints may not always work correctly. Instead, use hardware breakpoints:
(gdb) hb * {address_of_breakpoint}To view the source code that the current CPU is executing, use:
(gdb) layout asm
(gdb) layout src§Examples
Here are some examples of how to set breakpoints in GDB:
(gdb) hbreak function_name # Example: hbreak keos::fs::Directory::open
(gdb) hbreak *address # Example: hbreak *0x1000
(gdb) hbreak (file:)line # Example: hbreak syscall.rs:164§Example 1
To debug the syscall::read_normal test case in project 1, and set a
breakpoint at syscall.rs:150, use:
(gdb) hbreak syscall.rs:150Alternatively, you can set a breakpoint by the test case’s name:
(gdb) hbreak project1_grader::syscall::read_normalYou can even set a breakpoint on the closure entry, for instance, to set a
on a closure of sync::semaphore::sema_0 test case in project 3:
(gdb) hbreak project3_grader::sync::semaphore::{{closure}}To limit debugging to one core, use thread apply:
(gdb) thread apply 1 hbreak syscall.rs:150
(gdb) c§Example 2
To stop at a breakpoint only when a specific condition is met (e.g., when a
parameter is 0xcafe0000), use:
(gdb) hbreak walk if va.__0 == 0xcafe0000This approach allows you to focus on specific conditions and skip over unrelated calls.
§Stopping an execution
When KeOS got stuck in deadlock or does not automatically shut down after it panicked, you may need to forcibly shut down the QEMU.
For execution in cargo grade or cargo run without argument in project 5,
press Ctrl-C to stop execution.
Otherwise, such as running KeOS by cargo run in project 1-4, press
Ctrl-A, then press X to stop execution.
Modules§
- channel
- Multi-producer, multi-consumer FIFO queue communication primitives.
- fs
- Filesystem abstraction.
- interrupt 🔒
- Interrupt management.
- intrinsics
- Intrinsics of x86_64 not included in
core::arch::x86_64. - lang 🔒
- Rust-specific implemenations.
- mm
- Memory Management.
- sync
- Synchronization primitives.
- syscall
- System call infrastructure.
- task
- Task trait for interact with user process.
- teletype
- A teletype (TTY) interface for character-based I/O.
- thread
- Thread abstration, an abstraction of a cpu core.
- util
- Debugging Utilities.
Macros§
- debug
- Display a debug message.
- info
- Display an information message.
- Prints out the message.
- println
- Prints out the message with a newline.
- warning
- Display a warning message.
Structs§
- System
Configuration Builder - A builder for system configuration settings.
- Test
Driver - A driver for running tests.
- TryFrom
Error - The given
isizedoes not indicate anKernelError.
Enums§
- Kernel
Error - Enum representing errors that can occur during a kernel operation.
Constants§
- MAX_CPU
- Maximum number of CPU the kernel can support.
Statics§
- PANIC_
DEPTH - Panic depth level.
Functions§
- rust_
ap_ ⚠main - The entry of the KeOS for application processor.
- rust_
main ⚠ - The entry of the KeOS for bootstrap processor.