CSCE611-OS-Project05-Process
Github Link: https://github.com/tamu-edu-students/CSCE410-611-Spring2024-Hanzhong_Liu
Files I modified:
1.Scheduler and RR Scheduler:
- Add rr_scheduler.C/H
- kernel.C : support rr_scheudler
- interrupt.C/H : enable interrupt
- thread.C/H: enable interrupt
- copykernel.sh: Modified to fit Mac OS
3.Process:
- Add process.C/H
- Add rr_scheduler.C/H
- kernel.C
- interrupt.C/H
- thread.C/H
- Add cont_frame_pool.C/H
- Add vm_pool.C/H
- Add page_table.C/H
- copykernel.sh: Modified to fit Mac OS
1. FIFO Scheduler
In the Scheduler
, implement a queue using LinkedList
to store Threads
. Each time Resume
is called, insert the thread at the end of the queue. When yield
is called, pop the first thread from the queue and switch to it using Thread::dispatch_to(Thread * _thread)
.
Design of LinkedList:

resume (append)
: This indicates that when a new thread is created or when a suspended thread is to be resumed, it is appended to the end of the queue.yield (pop)
: When function yield is called, scheduler will pop the first thread in ready queue and the useThread::dispatch_to
to start this thread.- Finally,
Thread::dispatch_to:
will use the low levelasm
code to set the current thread’s stack to hardware to start the thread.
To terminate a thread, two steps are involved: 1. releasing memory, and 2. removing the thread from the scheduler. To obtain the stack address of the current thread, I added a Thread::Stack()
function.
1 | static void thread_shutdown() { |
TEST:
Launch the kernel, the output of threads shows on UI:

We can observe that threads are executed sequentially.
2.Enable Interrupt (bonus 1)
Within the Thread
class, in the Thread::thread_start
function, add Machine::enable_interrupts();
.
In the scheduler, before resume
and yield
, call Machine::disable_interrupts()
to prevent interrupts from occurring during a process switch. The worst-case scenario is when an interrupt occurs during an active process switch, causing the switch to terminate prematurely, and simultaneously triggering a process switch via rr_schedule
, which would result in an erroneous switch.
The test for interrupt is in the RR Scheduler.
3.Round-Robin Scheduler (bonus 2)
To control the activation of the round-robin (RR) scheduler, a scheduler_conf.H
file has been added. Additionally, an eoq_timer
has been introduced, with its constructor taking an RRScheduler
. Every 50ms, an interrupt is triggered, and EOQTimer::handle_interrupt
calls the scheduler’s resume
and yield
methods to implement thread preemption.
The implementation of Interrupt
has been modified to add a function InterruptHandler::send_end_of_interrupt(REGS * _r)
. In the scheduler, before switching to the next thread, send_end_of_interrupt
is called to indicate the end of the current interrupt handling, ensuring that Interrupt
can continue processing interrupts. Moreover, in InterruptHandler::dispatch_interrupt
, if(int_no!=0) send_end_of_interrupt(_r);
is added to indicate that the timer interrupt is handled by the scheduler, preventing the redundant sending of interrupt end signals.
**TEST: **
In the top of kernel.c, use
#define _ENABLE_RR_SCHEDULER_
to switch to RR_Scheduler.
According to the figure below, we can see that thread switching includes not only the switching of threads after the “BURST execution”, but also periodic preemption switching

4.Processes (bonus 3)
After a discussion with the professor. In my operating system project, I have implemented a kernel process system
with the following features and details:
Ported the memory pool from previous assignments (MP2 to MP4) to the new project (MP5).
Enabled virtual memory and implemented a
Process
class. During the initialization of aProcess
, two virtual memory pools are declared:kernel_mem_pool
(3MB-4MB) for internal objects of the process (e.g., stack, which requires direct mapping) andprocess_mem_pool
(4MB-256MB) for allocating memory to threads during runtime, providing them with new page tables and address spaces.1
Process::Process(unsigned long address_space_size);
Within the constructor, the process initially switches to the
kernel_page_table
andkernel_obj_pool
. It then uses thenew
operator to allocate memory for the process’s page table and virtual memory pools, setting up the necessary environment for process execution.**Memory Management: **
- Frame Pools: I used two frame pools:
kernel_frame_pool
andprocess_frame_pool
- I utilized three virtual memory (VM) pools:
kernel_obj_pool
,kernel_vm_pool
, andprocess_vm_pool
:kernel_obj_pool
(2MB - 3MB): This pool is reserved for kernel objects, such as the kernel page table. When the kernel needs to define a new object, like a new process, it switches to thekernel_obj_pool
and uses thenew
operator to allocate memory for it.kernel_vm_pool
(3MB - 4MB): This pool is used for the kernel stacks of threads. Each thread’s kernel stack is allocated from this pool to ensure have a dedicated memory space.process_vm_pool
(4MB - 256MB): This pool is used for the execution of processes. When a thread starts, the kernel setscurrent_thread
to the thread’s process VM pool, allowing the process to allocate objects within its own address space.
- Frame Pools: I used two frame pools:
Retained the
Thread
class, similar to the original design, containing anesp
attribute to store the current process’s stack address, code segment, etc. It includespush
andset_context
functions to establish the context of the process.
Additionally, due to threads belonging to different address spaces, a page table address for the current address space is added to theThread
class (passed in from the process).The constructor and functions of the
Thread
class is designed as follows:1
2
3
4
5Thread::Thread(Thread_Function _tf, char * _stack, unsigned int _stack_size);
void Thread::set_process(Process * _process);
void Thread::set_pt(PageTable * pt);Thread Management: Added an
add_thread
function to theProcess
class, allowing the addition of a new thread to the process. This function first disables interrupts, then switch to page table of current process, and then allocates memory from thekernel_vm_pool
for the stack. It creates a newThread
object, and adds it to the scheduler’s queue (without implementing “Thread level scheduling”).1
Process::add_proces(Thread_Function _tf, int _stack_size)
When the
RRScheduler
handles thread switching, it first compares the page table of the next thread with the current one. If they are different, the new page table is loaded into the register (pt->load()) andcurrent_pool
will be set toprocess_vm_pool
of current process.. Since the first 4MB is directly mapped to the kernel pool, switching page tables does not affect system operation.1
2
3
4
5Console::puts("Switch CURRENT_POOL \n");
CURRENT_POOL = current_thread->process->process_pool;
PageTable * pt_ = current_thread->pt;
pt_->load();With this setup, multiple threads can be continuously added to a process, and these threads will share the same address space.
**TEST: **
For testing, two processes are started: process1
contains Thread1
and Thread3
, while process2
contains Thread2
and Thread4
.
1 | // 2 processes and 4 threads |
In the test, each thread accumulates its ticks in the memory location at 32MB + ThreadID. The expected result is that the ticks for threads within the same process are visible to each other, while the other locations are 0.
addr[1] addr[2] addr[3] addr[4]
[thread1’s tick] [thread2’s tick] [thread3’s tick] [thread4’s tick]
The test code and output (which meets expectations) is as follows:
1 | unsigned long tick_addr = 32 MB; |
Test Output:
1 | # Thread 3 finished firstly. |
So this test verified that Process1 and Process2 have different address Spaces. This test also verified the accuracy of Thread running (according to Tick number), thread switching and page table switching.
This proves that I have completed a basic kernel process.