Project 4
Project 4
Prof. Forsyth
1 Overview
In this project, you will implement a multiprocessor operating system simulator using a popular threading
library for Linux called pthreads. The framework for the multiprocessor OS simulator is nearly complete,
but missing one critical component: the process scheduler! Your task is to implement the process scheduler
and three different scheduling algorithms.
The simulated operating system supports only one thread per process making it similar to the systems that
we discussed in Chapter 6. However, the simulator itself will use a thread to represent each of the CPUs in
the simulated hardware. This means that the CPUs in the simulator will appear to operate concurrently.
Note: Multiple CPU cores need to be enabled for this to work correctly. The CS 2200 Docker
container should already be configured to run with 4 cores. Please see a TA if you are running
into any problems.
We have provided you with source files that constitute the framework for your simulator. You will only need
to modify answers.txt and student.c. However, just because you are only modifying two files doesn’t
mean that you should ignore the other ones.
We have provided you the following files:
1. os-sim.c - Code for the operating system simulator which calls your CPU scheduler.
2. os-sim.h - Header file for the simulator.
3. process.c - Descriptions of the simulated processes.
4. process.h - Header file for the process data.
5. student.c - This file contains stub functions for your CPU scheduler.
6. student.h - Header file for your code to interface with the OS simulator. Also contains the ready
queue struct definition.
7. answers.txt - This is a text file that you should use to write your answers to the questions listed
throughout the PDF.
Reminder: The only files that you need to edit are student.c and answers.txt. If you edit any
other files, your code may fail the autograder!
1
Project 4 CS 2200 - Systems and Networks Fall 2024
3. Preemptive Priority Scheduling with Aging - Processes with higher priority get to run first
and processes with lower priority get preempted for a process with higher priority. There is a caveat,
though. Our priority scheduler factors in the age of a process when determining priority.
4. Shortest Remaining Time First - The process with the shortest time remaining is chosen. Time
remaining includes all future CPU and IO bursts. Currently scheduled processes are preempted if a
process with a smaller time remaining is ready to be scheduled.
The above diagram should give you a good overview of how the system works in terms of the functions being
called and PCBs moving around.
Below is a second diagram that shows the entire system overview and the code that you need to write is
inside of the green cloud at the bottom. All of the items outside of the green cloud are part of the simulator
and will not need to be modified by you.
Project 4 CS 2200 - Systems and Networks Fall 2024
Compile and run the simulator with ./os-sim 2. After a few seconds, hit Control-C to exit. You will see
the output below:
The simulator generates a Gantt Chart, showing the current state of the OS at every 100ms interval. The
leftmost column shows the current time, in seconds. The next three columns show the number of Running,
Ready, and Waiting processes, respectively. The next two columns show the process currently running on
each CPU. The rightmost column shows the processes which are currently in the I/O queue, with the head
of the queue on the left and the tail of the queue on the right.
As you can see, nothing is executing. This is because we have no CPU scheduler to select processes to
execute! Once you complete Problem 1 and implement a basic FCFS scheduler, you will see the processes
executing on the CPUs.
Project 4 CS 2200 - Systems and Networks Fall 2024
3.1 Hints
• Be sure to update the state field of the PCB in all the methods above. The library will read this
field to generate the RUNNING (Ru), READY (Re), and WAITING (Wa) columns, and to print the
statistics at the end of the simulation.
• Four of the five entry points into the scheduler (idle(), yield(), terminate(), and preempt())
should cause a new process to be scheduled on the CPU. In your handlers, be sure to call schedule(),
which will select a runnable process, and then call context switch(). When these four functions
return, the library will simulate the execution of the process selected by context switch().
• context switch() takes a timeslice parameter, which is used for preemptive scheduling algorithms.
Since FCFS is non-preemptive, use -1 for this parameter to give the process an infinite timeslice.
• Make sure to use the helper functions in a thread-safe manner when adding and removing processes
from the ready queue!
• The current[] array should be used to keep track of the process currently executing on each CPU.
Since this array is accessed by multiple CPU threads, it must be protected by a mutex. current mutex
has been provided for you.
Project 4 CS 2200 - Systems and Networks Fall 2024
f unctional priority = base priority − (current time − enqueue time) ∗ age weight
We will calculate functional priority on every process in the ready queue and schedule the process with the
highest priority. This means your ready queue does not have to stay in priority order. Choosing
our process will take O(n), meaning we have to look at each process every time we choose one. (NOTE:
Lower numbers means higher priority, as in most Operating Systems.).
Project 4 CS 2200 - Systems and Networks Fall 2024
The above ready queue is an example of our priority with aging algorithm. Our example simulator is at
current time 15, has an age weight of .2, and four processes in its ready queue.
Notice the following:
• Process 1 has the lowest functional priority, meaning it will be scheduled first (remember, lower means
higher priority!)
• Based on functional priority, Process 2 is the next to be scheduled after 1.
• Then, process 3 is scheduled, and finally process 4.
order by time remaining. Choosing our process will take O(n), meaning we have to look at each process
every time we choose one.
9 Deliverables
NOTE: You need to upload student.c, and answers.txt to Gradescope, and an autograder will run to
check if your scheduler is working. The autograder might take a couple of minutes to run. Remember to
upload student.c, and answers.txt for every submission as your last submission would be one we will
grade.
Keep your answers detailed enough to cover the question, including support from simulator results if appro-
priate. Don’t write a book; but if you’re not sure about an answer, give us more information.
Project 4 CS 2200 - Systems and Networks Fall 2024
To run the other algorithms, run with the flags you implemented for round robin and priority. Remember
that round robin requires you to enter a time slice.
In case you encounter difficulties with Project 4 and are uncertain about the direction to take, various
resources are available to assist you.
10.2 GDB
Let us investigate how to debug deadlocks through a basic example:
Following the execution and compilation of the code, it appears to become unresponsive. To investigate the
root cause of this issue, it is recommended to utilize the GNU Debugger (gdb) to identify the problem:
Project 4 CS 2200 - Systems and Networks Fall 2024
As anticipated, the program continues to remain unresponsive when run within the gdb environment. To
interrupt the program, press Ctrl + c. To analyze the various threads associated with the program, utilize
the ”info threads” command within gdb, which provides detailed information regarding each active thread:
Upon examining the running threads using the ”info threads” command within gdb, we can observe that
threads 2 and 3, which were created within the main() function, are currently situated within the lll lock wait
function. To obtain the backtrace of these threads, we can use the ”thread apply all” command along with
the ”backtrace” command, which can be abbreviated as ”t a a bt”.
The backtrace command confirms that threads 2 and 3 are indeed stuck at the pthread mutex lock function.
To gain a more in-depth understanding of the specific thread’s state, we can utilize the gdb command ”thread
[thread number]” to switch to a particular thread and examine its current state.
Project 4 CS 2200 - Systems and Networks Fall 2024
By switching to thread 3 within gdb, we can identify the precise line of code where it has become deadlocked.
Once we have identified the problematic line, we can utilize gdb’s features, such as printing values or switching
stack frames, to investigate further and gain a better understanding of the issue at hand. Read the gdb
thread documentation here for more information.
Lets run Helgrind with the command ’valgrind tool=helgrind <program>’. This is the result:
Project 4 CS 2200 - Systems and Networks Fall 2024
Upon executing Valgrind’s tool Helgrind, we can observe that it has successfully identified an issue within the
program where thread2 is accessing a shared variable without acquiring a corresponding lock. Additionally,
DRD (another tool within Valgrind) also provides comparable output, albeit with fewer error lines. It is
essential to rectify these issues to ensure proper synchronization and avoid potential data race conditions.
To compare, here is the same program run with DRD (’valgrind tool=drd <program>’):
Valgrind and DRD are also able to debug other types of synchronization errors. You can read the documen-
tation about Helgrind here and DRD here.
Credit to this video from an old class.