Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Simple Operating System Simulator

OSSim - Caitoa Edition

Language Platform Course Build

A C-based operating system simulator developed as a course assignment for CO2018 Operating Systems at the Faculty of Computer Science and Engineering, Ho Chi Minh City University of Technology (HCMUT - VNU-HCM).

The project simulates several fundamental operating-system mechanisms, including process scheduling, multi-level queue scheduling, system calls, virtual memory management, paging, page replacement, and kernel memory allocation.

This repository is intended to present the implementation and learning outcomes of the assignment, not to redistribute official course materials.


Table of Contents


Overview

This simulator models a simplified operating system environment. Processes are loaded from input configuration files, placed into ready queues, scheduled by the kernel, executed by simulated CPU workers, and managed through a virtual memory subsystem.

The goal of the project is not to build a real operating system, but to reproduce important kernel-level ideas in a controlled simulation environment. Through this structure, the simulator helps demonstrate how an operating system manages processes, coordinates CPU execution, translates virtual addresses, handles memory allocation, and responds to system calls.

At a high level, the simulator follows this flow:

graph TD
    A[Input Configuration Files] --> B[Process Loader]
    B --> C[Process Control Blocks]
    C --> D[Ready Queues]
    D --> E[MLQ Scheduler]
    E --> F[Simulated CPU Workers]
    F --> G[System Calls]
    G --> H[Kernel Services]
    H --> I[Virtual Memory Manager]
    I --> J[RAM and Swap Space]
Loading

Core Concepts Implemented

1. Process Management

The simulator represents each process using process-related data structures similar to a simplified Process Control Block. Each process is loaded from a process trace file and executed according to the instructions defined in that file.

Main responsibilities include:

  • Loading process metadata and instructions.
  • Maintaining process state during execution.
  • Dispatching processes to available CPU workers.
  • Returning unfinished processes to the scheduler when needed.
  • Supporting multiple simulated CPUs through POSIX threads.

2. Multi-Level Queue Scheduling

The scheduling component uses a Multi-Level Queue (MLQ) approach. Processes are separated into different priority queues, and the scheduler chooses which process should be executed next based on queue priority and scheduling rules.

The scheduler supports:

  • Multiple ready queues.
  • Priority-based process selection.
  • Re-queuing of unfinished processes.
  • Multi-CPU execution.
  • Thread synchronization for shared scheduler data.

This part of the simulator demonstrates how an operating system can manage competing processes while maintaining fairness, priority handling, and CPU utilization.


3. System Calls

Processes interact with kernel services through simulated system calls. These system calls provide a controlled interface between user-level process execution and kernel-level memory operations.

The system-call layer helps separate process behavior from internal kernel services. This reflects the basic operating-system principle that user programs should not directly access or modify protected kernel resources.

Typical operations include:

  • Requesting memory allocation.
  • Releasing allocated memory.
  • Reading from virtual memory.
  • Writing to virtual memory.
  • Triggering kernel-side memory handling routines.

4. Virtual Memory Management

The memory subsystem simulates virtual address spaces for processes. Each process owns its own virtual memory layout, while the simulator manages the mapping between virtual addresses and physical frames.

The virtual memory manager supports:

  • Process-specific address spaces.
  • Virtual memory regions.
  • Memory allocation and deallocation.
  • Address translation.
  • Paging.
  • Page fault handling.
  • RAM and swap interaction.

This allows the simulator to model how modern operating systems isolate processes and provide each process with the illusion of having its own continuous memory space.


5. Paging and Page Replacement

The simulator uses paging to divide memory into fixed-size pages and frames. When a process accesses a virtual address, the memory manager translates that address into a physical frame if the page is available in RAM.

If the required page is not currently in physical memory, the simulator handles the situation through page fault logic and swap behavior.

This part demonstrates:

  • Page table lookup.
  • Page-to-frame mapping.
  • Page fault detection.
  • Swapping between RAM and secondary storage.
  • Replacement behavior when physical memory is limited.

6. 64-bit Hierarchical Paging

The project also includes a 64-bit paging mode that models a hierarchical page-table structure. Instead of using a single flat page table, virtual addresses are divided into multiple fields, each corresponding to one level of the paging hierarchy.

The hierarchy includes:

  • PGD: Page Global Directory
  • P4D: Page Level 4 Directory
  • PUD: Page Upper Directory
  • PMD: Page Middle Directory
  • PT: Page Table
  • Page offset

This structure is closer to the way real 64-bit operating systems manage large virtual address spaces.


7. Kernel Memory Allocation

The simulator separates user-space memory management from kernel-side memory allocation. Kernel memory is handled through allocation mechanisms inspired by real operating-system design.

The implementation includes:

  • Physical frame management.
  • Buddy-style allocation for larger memory blocks.
  • Slab-style allocation for frequently used kernel objects.
  • Separation between user-space virtual memory and kernel-managed memory.

This helps illustrate why operating systems need specialized memory allocators instead of relying on only one general allocation strategy.


System Architecture

The simulator can be viewed as a set of cooperating kernel-like components:

graph LR
    A[Process Trace Files] --> B[Loader]

    B --> C[PCB List]
    C --> D[Ready Queues]

    D --> E[MLQ Scheduler]
    E --> F1[CPU Worker 1]
    E --> F2[CPU Worker 2]
    E --> F3[CPU Worker N]

    F1 --> G[System Call Interface]
    F2 --> G
    F3 --> G

    G --> H[Virtual Memory Manager]
    H --> I[Page Table]
    H --> J[RAM]
    H --> K[Swap]

    G --> L[Kernel Memory Allocator]
    L --> M[Buddy Allocator]
    L --> N[Slab Allocator]
Loading

The design follows a simplified kernel structure:

  • The loader creates processes from input files.
  • The scheduler decides which process runs next.
  • The CPU workers execute process instructions.
  • The system-call interface connects process execution with kernel services.
  • The virtual memory manager handles address translation and paging.
  • The kernel allocator manages kernel-side memory objects.

64-bit Virtual Memory Layout

The following diagram illustrates the intended 64-bit virtual memory layout used in the simulator, including the separation between user-space memory and high-half kernel memory.

64-bit Virtual Memory Layout

The lower part of the address space represents user/process memory. Each process owns an independent virtual address space, including text, data, bss, and dynamically managed virtual memory regions.

The upper part represents kernel virtual memory. It contains mapped kernel areas such as kernel text/data segments, page-table storage, slab allocation regions, and vmalloc/ioremap-style spaces.

This separation reflects an important operating-system design principle: user programs should operate in their own virtual address spaces, while the kernel remains mapped in a protected high-half region.


Project Structure

.
├── include/                 
│   └── Header files and shared definitions
│
├── src/                     
│   └── C source files for scheduler, memory, CPU, loader, and system calls
│
├── input/                   
│   ├── OS configuration files
│   └── proc/                
│       └── Process instruction traces
│
├── output/                  
│   ├── Generated simulator outputs
│   └── final_verify/        
│       ├── Verification script
│       ├── Expected outputs
│       ├── Actual outputs
│       └── Diff logs
│
├── docs/                    
│   └── Supporting diagrams and documentation
│
├── Makefile                 
├── run.sh                   
└── README.md

Build Instructions

Prerequisites

The project is designed for Unix-like environments such as Linux and macOS.

Required tools:

  • GCC or Clang
  • GNU Make
  • POSIX pthread library
  • Python 3, only for verification scripts

Build

From the project root, run:

make clean
make all

After a successful build, the executable file will be generated as:

os

Running the Simulator

Run the simulator with one configuration file from the input/ directory.

./os <config_name>

Example:

./os sched_0

Another example:

./os os_1_mlq_paging

The simulator reads the corresponding configuration file, loads the defined processes, runs the scheduling and memory simulation, and prints the execution result.


Running All Test Configurations

The repository includes a helper script for running multiple configurations.

chmod +x run.sh
./run.sh

Generated outputs are saved under:

output/

This is useful when checking whether changes in the scheduler, memory manager, or system-call handling affect existing test cases.


Verification Suite

A verification script is included to compare actual simulator outputs against reference outputs.

First, build the project:

make clean
make all

Then run:

python3 output/final_verify/run_verify.py

The verification script compares generated outputs with expected results stored in:

output/final_verify/result_memory/
output/final_verify/result_sched/

Generated verification files are saved in:

output/final_verify/actual/
output/final_verify/diff/

The diff/ directory is useful for checking exactly where an output differs from the reference result.


Implementation Notes

Header Include Handling

The project contains a header file named:

include/sched.h

On Linux systems, this name may conflict with the system header:

<sched.h>

To avoid this conflict, the Makefile uses:

-iquote include

instead of:

-Iinclude

This ensures that project headers are resolved correctly without accidentally shadowing system headers required by pthread-related functionality.


Multi-Threaded Output

Some configurations use multiple simulated CPUs. Because CPU workers are implemented using threads, certain output lines may appear in slightly different orders across machines or runs.

If a verification result differs only by thread scheduling order, inspect the files in:

output/final_verify/diff/

before assuming that the simulator logic is incorrect.


Generated Files

Build artifacts, generated outputs, and temporary verification files should not be committed to the repository. These files are ignored through .gitignore.

Typical ignored files include:

*.o
os
output/*.output
output/final_verify/actual/
output/final_verify/diff/

Limitations

This project is a simulator, not a bootable operating system kernel. It does not directly interact with hardware, real CPU privilege levels, or actual machine page tables.

Current limitations include:

  • No real hardware interrupt handling.
  • No real kernel/user privilege transition.
  • No actual MMU interaction.
  • File-based process input instead of real executable loading.
  • Thread scheduling may differ depending on the host operating system.
  • Some outputs may vary slightly under multi-CPU configurations.

These limitations are acceptable for the purpose of the assignment, since the main goal is to demonstrate operating-system mechanisms at the simulation level.


Academic Note

This project was developed for learning and demonstration purposes as part of an Operating Systems course assignment.

If this repository is public, official assignment PDFs, private test cases, or course-provided materials should be removed unless redistribution is explicitly allowed.

Please do not copy this repository as a course submission. Use it only as a reference for understanding operating-system simulation concepts.


Author

Developed by Caitoa as part of the CO2018 Operating Systems coursework at HCMUT.

Repository maintained for academic portfolio and technical documentation purposes.

About

A C-based operating system simulator featuring MLQ scheduling, virtual memory, paging, system calls, and kernel memory allocation.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages