#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <queue>
#include <climits>
#include <unistd.h>
#include <cstdlib>
using namespace std;
// Node structure for the Process linked list
struct Process {
int id, arrivalTime, burstTime, remainingBurstTime, priority;
int startTime, completionTime, turnAroundTime, waitingTime;
Process* next;
Process(int id, int arrival, int burst, int prio = 0)
: id(id), arrivalTime(arrival), burstTime(burst),
remainingBurstTime(burst), priority(prio),
startTime(-1), completionTime(0), turnAroundTime(0),
waitingTime(0), next(nullptr) {}
};
void insertProcess(Process*& head, int id, int arrival, int burst, int
priority);
void Results(Process* head, const string& outputFile);
void sjfPreemptive(Process* head);
void priorityNonPreemptive(Process* head);
void roundRobin(Process* head, int timeQuantum);
void firstComeFirstServe(Process* head);
void sjfNonPreemptive(Process* head);
void priorityPreemptive(Process* head);
void processFile(const string& inputFile, const string& outputFile, int
algorithm, int timeQuantum = 1);
//count processes
int countProcesses(Process* head) {
int processCount = 0;
for (Process* temp = head; temp != nullptr; temp = temp->next) {
processCount++;
}
return processCount;
}
// Function to insert a process into the linked list
void insertProcess(Process*& head, int id, int arrival, int burst, int
priority) {
// Dynamically allocate memory for a new process node
Process* newProcess = new(std::nothrow) Process(id, arrival, burst,
priority);//erro handling method that return a nullptr
// Check if nothrow returned a nullptr and handle the error
if (!newProcess) {
cerr << "Error: Memory allocation failed for process " << id <<
endl;
return;
}
// If the list is empty,the new process becomes the head
if (!head) {
head = newProcess;
} else {
// Otherwise, traverse to the end and add the new process
Process* temp = head;
while (temp->next) {
temp = temp->next;
}
temp->next = newProcess;
}
}
// Function to print results to an output file
void Results(Process* head, const string& outputFile, int
algorithmNumber) {
ofstream outFile(outputFile, ios::app); // Open in append mode to
add rows for each algorithm
if (!outFile) {
cerr << "Error: Could not open output file." << endl;
return;
}
// Write the algorithm number as the first column
outFile << algorithmNumber;
// Calculate waiting times and average
double totalWaitingTime = 0.0;
int processCount = 0;
for (Process* temp = head; temp != nullptr; temp = temp->next) {
outFile << ":" << temp->waitingTime; // Append waiting times
totalWaitingTime += temp->waitingTime;
processCount++;
}
// Append average waiting time in the last column
double averageWaitingTime = (processCount > 0) ? totalWaitingTime /
processCount : 0.0;
outFile << ":" << averageWaitingTime << endl;
outFile.close();
cout << "Results for algorithm " << algorithmNumber << " written to
'" << outputFile << "' successfully." << endl;
}
// SJF Preemptive Scheduling
void sjfPreemptive(Process* head) {
int processCount = countProcesses(head);
int currentTime = 0, completed = 0;
while (true) {
Process* shortestJob = nullptr;
int minBurst = INT_MAX;
for (Process* temp = head; temp != nullptr; temp = temp->next)
{
if (temp->arrivalTime <= currentTime && temp-
>remainingBurstTime > 0 && temp->remainingBurstTime < minBurst) {
minBurst = temp->remainingBurstTime;
shortestJob = temp;
}
}
if (shortestJob) {
if (shortestJob->startTime == -1) shortestJob->startTime =
currentTime;
shortestJob->remainingBurstTime--;
currentTime++;
if (shortestJob->remainingBurstTime == 0) {
shortestJob->completionTime = currentTime;
shortestJob->turnAroundTime = shortestJob-
>completionTime - shortestJob->arrivalTime;
shortestJob->waitingTime = shortestJob->turnAroundTime
- shortestJob->burstTime;
completed++;
}
} else {
currentTime++;
}
if (completed == processCount) break;
}}
// Priority Scheduling Non-Preemptive
void priorityNonPreemptive(Process* head) {
int processCount = countProcesses(head);
int currentTime = 0, completed = 0;
while (completed < processCount) {
Process* highestPriorityProcess = nullptr;
int highestPriority = INT_MAX;
for (Process* temp = head; temp; temp = temp->next) {
if (temp->arrivalTime <= currentTime && temp-
>remainingBurstTime > 0 && temp->priority < highestPriority) {
highestPriority = temp->priority;
highestPriorityProcess = temp;
}
}
if (highestPriorityProcess) {
highestPriorityProcess->startTime =
(highestPriorityProcess->startTime == -1) ? currentTime :
highestPriorityProcess->startTime;
currentTime += highestPriorityProcess->burstTime;
highestPriorityProcess->remainingBurstTime = 0;
highestPriorityProcess->completionTime = currentTime;
highestPriorityProcess->turnAroundTime = currentTime -
highestPriorityProcess->arrivalTime;
highestPriorityProcess->waitingTime =
highestPriorityProcess->turnAroundTime - highestPriorityProcess-
>burstTime;
completed++;
} else {
currentTime++;
}
}
}
// Round Robin Scheduling
void roundRobin(Process* head, int timeQuantum) {
queue<Process*> readyQueue;
int processCount=countProcesses( head);
int currentTime = 0, completed = 0;
for (Process* temp = head; temp; temp = temp->next)
if (temp->arrivalTime == 0) readyQueue.push(temp);
while (completed < processCount) {
if (!readyQueue.empty()) {
Process* current = readyQueue.front();
readyQueue.pop();
if (current->startTime == -1) current->startTime =
currentTime;
int timeSlice = min(timeQuantum, current-
>remainingBurstTime);
current->remainingBurstTime -= timeSlice;
currentTime += timeSlice;
for (Process* temp = head; temp; temp = temp->next)
if (temp->arrivalTime <= currentTime && temp-
>remainingBurstTime > 0 && temp != current)
readyQueue.push(temp);
if (current->remainingBurstTime > 0) {
readyQueue.push(current);
} else {
current->completionTime = currentTime;
current->turnAroundTime = current->completionTime -
current->arrivalTime;
current->waitingTime = current->turnAroundTime -
current->burstTime;
completed++;
}
} else {
currentTime++;
}
}
}
// First-Come-First-Serve Scheduling
void firstComeFirstServe(Process* head) {
int currentTime = 0, completed = 0;
// Traverse the list of processes to schedule them in arrival order
while (head) {
Process* current = head;
// If it's the first time this process is being executed, set
the start time
if (current->arrivalTime > currentTime) {
currentTime = current->arrivalTime; // Wait for the
process to arrive
}
current->startTime = currentTime;
currentTime += current->burstTime; // Add burst time to
current time for completion
current->completionTime = currentTime;
current->turnAroundTime = current->completionTime - current-
>arrivalTime;
current->waitingTime = current->turnAroundTime - current-
>burstTime;
completed++;
head = head->next; // Move to the next process in the list
}
}
// Shortest-Job-First Non-Preemptive Scheduling
void sjfNonPreemptive(Process* head) {
int processCount = countProcesses(head);
int currentTime = 0, completed = 0;
// Sort processes by arrival time, then by burst time
// Note: This assumes that the list is already ordered by arrival
time
while (completed < processCount) { // Assuming 4 processes for
completion check, can be dynamic
Process* shortestJob = nullptr;
int minBurst = INT_MAX;
// Find the process with the shortest burst time that has
arrived and not yet completed
for (Process* temp = head; temp != nullptr; temp = temp->next)
{
if (temp->arrivalTime <= currentTime && temp-
>remainingBurstTime > 0 && temp->remainingBurstTime < minBurst) {
minBurst = temp->remainingBurstTime;
shortestJob = temp;
}
}
// If a process is found, execute it
if (shortestJob) {
if (shortestJob->startTime == -1) {
shortestJob->startTime = currentTime;
}
currentTime += shortestJob->burstTime;
shortestJob->completionTime = currentTime;
shortestJob->turnAroundTime = shortestJob->completionTime -
shortestJob->arrivalTime;
shortestJob->waitingTime = shortestJob->turnAroundTime -
shortestJob->burstTime;
completed++;
} else {
currentTime++; // If no process is ready, just increment
the time
}
}
}
void priorityPreemptive(Process* head) {
int processCount = countProcesses(head);
int currentTime = 0, completed = 0;
while (completed < processCount) {
Process* highestPriorityProcess = nullptr;
int highestPriority = INT_MAX;
// Find the process with the highest priority that has arrived
and is still pending
for (Process* temp = head; temp; temp = temp->next) {
if (temp->arrivalTime <= currentTime && temp-
>remainingBurstTime > 0 && temp->priority > highestPriority) {
highestPriority = temp->priority;
highestPriorityProcess = temp;
}
}
if (highestPriorityProcess) {
// If it's the first time this process is being executed,
set the start time
highestPriorityProcess->startTime =
(highestPriorityProcess->startTime == -1) ? currentTime :
highestPriorityProcess->startTime;
// Execute the process by reducing its remaining burst time
highestPriorityProcess->remainingBurstTime--;
currentTime++;
// If the process has finished its execution
if (highestPriorityProcess->remainingBurstTime == 0) {
highestPriorityProcess->completionTime = currentTime;
highestPriorityProcess->turnAroundTime=
highestPriorityProcess->completionTime - highestPriorityProcess-
>arrivalTime;
highestPriorityProcess->waitingTime =
highestPriorityProcess->turnAroundTime - highestPriorityProcess-
>burstTime;
completed++;
}
}
else {
// If no process is available to run, just increment the
current time
currentTime++;
}
}
}
//Function to proces sthe input file
void processFile(const string& inputFile, const string& outputFile, int
algorithm, int timeQuantum) {
ifstream inFile(inputFile);
if (!inFile) {
cerr << "Error: Could not open input file." << endl;
return;
}
Process* head = nullptr;
string line;
int id = 0;
// Read input file and create linked list of processes
while (getline(inFile, line)) {
stringstream ss(line);
string value;
int arrivalTime, burstTime, priority;
getline(ss, value, ':'); arrivalTime = stoi(value);
getline(ss, value, ':'); burstTime = stoi(value);
getline(ss, value, ':'); priority = stoi(value);
insertProcess(head, id++, arrivalTime, burstTime, priority);
}
inFile.close();
// Clear output file for a fresh run (only on the first algorithm
call)
static bool firstRun = true;
if (firstRun) {
ofstream outFile(outputFile, ios::trunc); // Clear file content
if (!outFile) {
cerr << "Error: Could not reset output file." << endl;
return;
}
outFile.close();
firstRun = false;
}
switch (algorithm) {
case 1: sjfPreemptive(head); break;
case 2: roundRobin(head, timeQuantum); break;
case 3: sjfNonPreemptive(head); break;
case 4: priorityPreemptive(head); break;
case 5: priorityNonPreemptive(head); break;
case 6: firstComeFirstServe(head); break;
default:
cerr << "Invalid algorithm choice." << endl;
return;
}
// Write results for the current algorithm (using the algorithm
number)
Results(head, outputFile, algorithm);
// Cleanup the linked list to prevent memory leaks
while (head) {
Process* temp = head;
head = head->next;
delete temp;
}
}
void displayUsage() {
cout << "Usage: ./scheduler -t <time_quantum> -f <input_file> -o
<output_file>\n";
cout << " -t <time_quantum> Specify the time quantum for Round
Robin scheduling.\n";
cout << " -f <input_file> Specify the input file containing
process data.\n";
cout << " -o <output_file> Specify the output file to write
results.\n";
exit(EXIT_FAILURE);
}
int main(int argc, char* argv[]) {
int timeQuantum = -1;
string inputFile, outputFile;
// Parse command-line arguments
int opt;
while ((opt = getopt(argc, argv, "t:f:o:")) != -1) {
switch (opt) {
case 't': // Time quantum
timeQuantum = atoi(optarg);
if (timeQuantum <= 0) {
cerr << "Error: Time quantum must be a positive
integer.\n";
displayUsage();
return 1; // Exit after showing usage
}
break;
case 'f': // Input file
inputFile = optarg;
break;
case 'o': // Output file
outputFile = optarg;
break;
default: // Invalid argument
cerr << "Error: Invalid argument.\n";
displayUsage();
return 1; // Exit after showing usage
}
}
// Validate that all arguments are provided
if (inputFile.empty() || outputFile.empty()) {
cerr << "Error: Missing required arguments.\n";
displayUsage();
return 1; // Exit after showing usage
}
cout << "Time Quantum: " << (timeQuantum > 0 ?
to_string(timeQuantum) : "N/A") << "\n";
cout << "Input File: " << inputFile << "\n";
cout << "Output File: " << outputFile << "\n";
// Run all cpu scheduling algorithms
for (int algorithm = 1; algorithm <= 6; ++algorithm) {
if (algorithm == 2 && timeQuantum == -1) { // Round Robin
requires a time quantum
cerr << "Error: Time quantum is required for Round Robin
scheduling.\n";
return 1;
}
processFile(inputFile, outputFile, algorithm, timeQuantum);
}
cout << "Scheduling results have been written to '" << outputFile
<< "' successfully.\n";
return 0;
}
Here’s a simple explanation of the six CPU scheduling algorithms you’ve mentioned:
1. SJF (Shortest Job First) Non-Preemptive
What it does: The CPU selects the process with the shortest burst time and
completes it before moving to the next process.
Key Point: Once a process starts, it cannot be interrupted even if a shorter job
arrives.
Analogy: Imagine a line at a printer. If you have a 1-page print job and someone else
has a 10-page print job, your print job is completed first, no matter when other jobs
arrive.
2. SJF (Shortest Job First) Preemptive
What it does: The CPU selects the process with the shortest burst time. If a new
process with a shorter burst time arrives, the CPU switches to it immediately.
Key Point: The current process can be interrupted if a shorter one arrives.
Analogy: Imagine you're assembling sandwiches in a restaurant. If a customer orders
a simple sandwich, you’ll drop your current complex order to quickly finish the
simpler one first.
3. SRTF (Shortest Remaining Time First)
What it does: A special case of SJF Preemptive, where the CPU always chooses the
process with the least remaining burst time.
Key Point: The decision considers how much time a process has left, not the total
burst time.
Analogy: If you’re cleaning up multiple tasks and a 5-minute task is halfway done,
you'll finish it before starting a new 10-minute task, even if the new task seems
shorter.
4. Round Robin (RR)
What it does: Each process gets a fixed time slice (quantum). If it doesn’t finish, it
moves to the end of the queue, and the next process gets its turn.
Key Point: Fair and time-shared, but processes might take longer to finish.
Analogy: Imagine a teacher giving every student 5 minutes to speak in a debate, then
moving to the next student. If someone’s time runs out, they wait for the next round.
5. Priority Scheduling Non-Preemptive
What it does: The CPU selects the process with the highest priority and completes it
before moving to the next.
Key Point: Processes cannot interrupt each other; once started, a process finishes.
Analogy: At an airport, VIPs are served first regardless of when others arrived, but
once a VIP is being served, no one else can interrupt.
6. Priority Scheduling Preemptive
What it does: The CPU selects the process with the highest priority. If a new
process with a higher priority arrives, it interrupts the current one.
Key Point: Current processes can be stopped mid-execution for higher-priority tasks.
Analogy: If a VIP arrives at an airport while another is being served, the staff pauses
the current service to immediately attend to the new VIP.