Expand description
§Multithreaded Process
Modern applications increasingly rely on multithreading to achieve responsiveness, scalability, and efficient utilization of multicore processors. By enabling a single process to run multiple threads concurrently, the system can perform I/O and computation in parallel, reduce latency, and better respond to user interactions or real-time events.
To support such workloads, the operating system must provide robust mechanisms for creating, scheduling, and synchronizing threads within a single process. This includes managing shared memory access, ensuring thread-safe operations, and allowing coordination through synchronization primitives such as mutexes, condition variables, and semaphores.
In KeOS, extending the process abstraction to support multithreading is a critical step toward building a realistic and capable system. This enhances your knowledge about how the multithreading, the foundation for concurrent programming models, works in real-world operating system.
§Multithreading in KeOS
Previously, a process in KeOS consisted of a single thread. Your goal is extending the process to run multiple concurrent threads, each with its own execution context but sharing the same address space and resources. Each thread maintains its own register states while sharing the same states.
In earlier projects, each Process owned its own FileStruct and
MmStruct. However, on the multi-threaded model, these components are
shared across all threads of a process. That is, they become shared
resources, requiring proper synchronization. To support shared and mutable
access, these resources are wrapped inside an Arc<Mutex<_>>.
Arcprovides shared ownership with reference counting.Mutexensures exclusive access to mutable state.
This allows multiple threads to safely access and modify shared structures like file tables and virtual memory mappings.
§Thread Life Cycle
KeOS supports a lightweight threading model within a single process, enabling multiple threads to execute concurrently while sharing the same address space. The life cycle of a thread is managed through four key system calls:
thread_create: Creates a new thread within the same process, executing a given function on a user-supplied stack.thread_join: Waits for a specified thread to terminate and retrieves its return value.exit: Terminates the calling thread without affecting other threads in the same process.exit_group: Terminates all threads within the process simultaneously.
When creating a new thread via thread_create, the user must provide a
pointer to a valid, writable memory region that will serve as the new
thread’s stack. This approach mirrors Linux’s clone() system call and
gives userspace full control over stack allocation and reuse. The kernel
validates that the provided stack lies within a properly mapped and writable
memory region to ensure memory safety.
Threads can be terminated individually using the exit system call,
which affects only the calling thread. Other threads in the same process
continue executing. To coordinate with thread termination, a thread may
invoke thread_join, which blocks until the target thread exits and
returns its result. This can be implemented using a Semaphore
initialized with zero permits, where the exiting thread signals completion
by releasing a permit.
In contrast, exit_group is used when the entire process must be
terminated, bringing down all associated threads by calling
thread::kill_by_tid. This is necessary in scenarios such as a fatal
error in the main thread, unhandled signals, or explicit process termination
by the application. Unlike exit, which only marks the calling thread for
termination, exit_group ensures that all threads in the process are
promptly and safely terminated, and that the process is cleaned up
consistently. This behavior aligns with the semantics of multi-threaded
processes in modern operating systems and prevents resource leaks or partial
process shutdowns.
Together, these mechanisms provide a simple yet robust model for managing thread life cycles in KeOS, balancing fine-grained control with process-wide coordination.
§Implementation Requirements
You need to implement the followings:
ThreadThread::from_file_mm_structThread::with_file_struct_mutThread::with_mm_struct_mutThread::thread_createThread::exitThread::thread_joinThread::exit_group
By implementing this section, you can move on to the next section with
the final form of execution model that widely used in modern OSes:
+========= Process =========+
| Shared States: |
| - MmStruct |
| - FileStruct |
| |
| Threads: |
| +----- Thread 1 -----+ |
| | - Register State | |
| | - User Stack | |
| +--------------------+ |
| ... |
| +----- Thread N -----+ |
| | - Register State | |
| | - User Stack | |
| +--------------------+ |
+===========================+Structs§
- Thread
- A thread state of project 4, which contains file and memory state.