Compare commits
4 commits
2b3c28d249
...
8f4463782c
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f4463782c | |||
| 3cc3f6332d | |||
| 06fbd2d6ad | |||
| 4e8e564b44 |
35 changed files with 1209 additions and 9 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -35,6 +35,7 @@
|
|||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
build/
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
|
|
@ -56,6 +57,7 @@ dkms.conf
|
|||
|
||||
# LaTeX
|
||||
.auctex-auto/
|
||||
_minted/
|
||||
*.fls
|
||||
*.log
|
||||
*.synctex.gz
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
echo "Building executables..."
|
||||
|
||||
cd "${FLAKE:-$(dirname $0)}"
|
||||
if command -v nix; then
|
||||
if command -v nix &>/dev/null; then
|
||||
producer="$(nix build --no-link --print-out-paths .#a2-p1-producer)/bin/producer"
|
||||
consumer="$(nix build --no-link --print-out-paths .#a2-p1-consumer)/bin/consumer"
|
||||
else
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
echo "Building executable..."
|
||||
|
||||
cd "${FLAKE:-$(dirname $0)}"
|
||||
if command -v nix; then
|
||||
if command -v nix &>/dev/null; then
|
||||
bidirectional="$(nix build --no-link --print-out-paths .#a2-p2-bidirectional)/bin/bidirectional"
|
||||
else
|
||||
gcc bidirectional.c -o bidirectional
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
echo "Building executables..."
|
||||
|
||||
cd "${FLAKE:-$(dirname $0)}"
|
||||
if command -v nix; then
|
||||
if command -v nix &>/dev/null; then
|
||||
producer="$(nix build --no-link --print-out-paths .#a2-p3-producer)/bin/producer"
|
||||
consumer="$(nix build --no-link --print-out-paths .#a2-p3-consumer)/bin/consumer"
|
||||
else
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
let
|
||||
bash-test = subdir: { runCommandLocal }:
|
||||
runCommandLocal "${subdir}-test" { FLAKE = ../.; }
|
||||
bash-test = subdir: { runCommandLocal, makeWrapper }:
|
||||
runCommandLocal "${subdir}-test" { nativeBuildInputs = [ makeWrapper ]; }
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
install ${subdir}/test $out/bin/
|
||||
install ${./${subdir}/test} $out/bin/${subdir}-test
|
||||
wrapProgram "$out/bin/${subdir}-test" --set FLAKE ${../.}
|
||||
'';
|
||||
in {
|
||||
a2-p1 = bash-test "part1";
|
||||
|
|
|
|||
21
assignment3/default.nix
Normal file
21
assignment3/default.nix
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
let
|
||||
basicC = subdir: name: { stdenv }:
|
||||
stdenv.mkDerivation {
|
||||
inherit name;
|
||||
src = ./.;
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
gcc ${subdir}/${name}.c -o ${name}.bin -pthread -lrt
|
||||
runHook postBuild
|
||||
'';
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p $out/bin
|
||||
install ${name}.bin $out/bin/${name}
|
||||
runHook postInstall
|
||||
'';
|
||||
};
|
||||
in {
|
||||
a3-producer = basicC "src" "producer";
|
||||
a3-consumer = basicC "src" "consumer";
|
||||
}
|
||||
22
assignment3/src/buffer.h
Normal file
22
assignment3/src/buffer.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef BUFFER_H_
|
||||
#define BUFFER_H_
|
||||
|
||||
#define BUFFER_SIZE 10
|
||||
#define SHM_KEY 0x1234
|
||||
#define SEM_MUTEX "/sem_mutex"
|
||||
#define SEM_EMPTY "/sem_empty"
|
||||
#define SEM_FULL "/sem_full"
|
||||
|
||||
typedef struct item_t {
|
||||
int value; // Data value
|
||||
int producer_id; // Which producer created this
|
||||
} item_t;
|
||||
|
||||
typedef struct shared_buffer_t {
|
||||
item_t buffer[BUFFER_SIZE];
|
||||
int head; // Next write position
|
||||
int tail; // Next read position
|
||||
int count; // Current items in buffer
|
||||
} shared_buffer_t;
|
||||
|
||||
#endif // BUFFER_H_
|
||||
91
assignment3/src/consumer.c
Normal file
91
assignment3/src/consumer.c
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
#include "buffer.h"
|
||||
#include <fcntl.h>
|
||||
#include <semaphore.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/shm.h>
|
||||
#include <unistd.h>
|
||||
|
||||
shared_buffer_t *buffer = NULL;
|
||||
sem_t *mutex = NULL;
|
||||
sem_t *empty = NULL;
|
||||
sem_t *full = NULL;
|
||||
int shm_id = -1;
|
||||
|
||||
void cleanup() {
|
||||
// Detach shared memory
|
||||
if (buffer != NULL) {
|
||||
shmdt(buffer);
|
||||
}
|
||||
|
||||
// Close semaphores (don't unlink - other processes may be using)
|
||||
if (mutex != SEM_FAILED)
|
||||
sem_close(mutex);
|
||||
if (empty != SEM_FAILED)
|
||||
sem_close(empty);
|
||||
if (full != SEM_FAILED)
|
||||
sem_close(full);
|
||||
}
|
||||
|
||||
void signal_handler(int sig) {
|
||||
printf("\nProducer: Caught signal %d, cleaning up...\n", sig);
|
||||
cleanup();
|
||||
exit(2);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Argument parsing
|
||||
if (argc != 3) {
|
||||
printf("Usage: %s <id> <number of msgs>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
int id = atoi(argv[1]);
|
||||
int num_items = atoi(argv[2]);
|
||||
|
||||
// Setup shared memory
|
||||
shm_id = shmget(SHM_KEY, sizeof(shared_buffer_t), IPC_CREAT | 0666);
|
||||
if (shm_id < 0) {
|
||||
perror("shmget-failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
buffer = (shared_buffer_t *)shmat(shm_id, NULL, 0);
|
||||
if (buffer == (void *)-1) {
|
||||
perror("shmat-failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Open semaphores
|
||||
mutex = sem_open(SEM_MUTEX, 0, 0644, 1);
|
||||
empty = sem_open(SEM_EMPTY, 0, 0644, BUFFER_SIZE);
|
||||
full = sem_open(SEM_FULL, 0, 0644, 0);
|
||||
|
||||
if (mutex == SEM_FAILED || empty == SEM_FAILED || full == SEM_FAILED) {
|
||||
perror("sem_open-failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Primary logic
|
||||
for (int i = 0; i < num_items; i++) {
|
||||
sem_wait(full); // Wait for item
|
||||
sem_wait(mutex); // Enter critical section
|
||||
|
||||
// Remove from buffer
|
||||
item_t item = buffer->buffer[buffer->tail];
|
||||
buffer->tail = (buffer->tail + 1) % BUFFER_SIZE;
|
||||
buffer->count--;
|
||||
|
||||
printf("Consumer %d: Consumed value %d from Producer %d\n", id, item.value,
|
||||
item.producer_id);
|
||||
|
||||
sem_post(mutex); // Exit critical section
|
||||
sem_post(empty); // Signal slot available
|
||||
|
||||
usleep(rand() % 100000); // Simulate work
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
printf("Consumer %d: Finished consuming %d items\n", id, num_items);
|
||||
cleanup();
|
||||
return 0;
|
||||
}
|
||||
99
assignment3/src/producer.c
Normal file
99
assignment3/src/producer.c
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
#include "buffer.h"
|
||||
#include <fcntl.h>
|
||||
#include <semaphore.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/shm.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
shared_buffer_t *buffer = NULL;
|
||||
sem_t *mutex = NULL;
|
||||
sem_t *empty = NULL;
|
||||
sem_t *full = NULL;
|
||||
int shm_id = -1;
|
||||
|
||||
void cleanup() {
|
||||
// Detach shared memory
|
||||
if (buffer != NULL) {
|
||||
shmdt(buffer);
|
||||
}
|
||||
|
||||
// Close semaphores (don't unlink - other processes may be using)
|
||||
if (mutex != SEM_FAILED)
|
||||
sem_close(mutex);
|
||||
if (empty != SEM_FAILED)
|
||||
sem_close(empty);
|
||||
if (full != SEM_FAILED)
|
||||
sem_close(full);
|
||||
}
|
||||
|
||||
void signal_handler(int sig) {
|
||||
printf("\nProducer: Caught signal %d, cleaning up...\n", sig);
|
||||
cleanup();
|
||||
exit(2);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Argument parsing
|
||||
if (argc != 3) {
|
||||
printf("Usage: %s <id> <number of msgs>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
int id = atoi(argv[1]);
|
||||
int num_items = atoi(argv[2]);
|
||||
|
||||
// Signal handlers
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
|
||||
// Setup shared memory
|
||||
shm_id = shmget(SHM_KEY, sizeof(shared_buffer_t), IPC_CREAT | 0666);
|
||||
if (shm_id < 0) {
|
||||
perror("shmget-failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
buffer = (shared_buffer_t *)shmat(shm_id, NULL, 0);
|
||||
if (buffer == (void *)-1) {
|
||||
perror("shmat-failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Open semaphores
|
||||
mutex = sem_open(SEM_MUTEX, O_CREAT, 0644, 1);
|
||||
empty = sem_open(SEM_EMPTY, O_CREAT, 0644, BUFFER_SIZE);
|
||||
full = sem_open(SEM_FULL, O_CREAT, 0644, 0);
|
||||
|
||||
if (mutex == SEM_FAILED || empty == SEM_FAILED || full == SEM_FAILED) {
|
||||
perror("sem_open-failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Primary logic
|
||||
unsigned int seed = time(NULL) + id;
|
||||
for (int i = 0; i < num_items; i++) {
|
||||
item_t item = {.producer_id = id, .value = rand_r(&seed) % 1000};
|
||||
|
||||
sem_wait(empty); // Wait for empty slot
|
||||
sem_wait(mutex); // Enter critical section
|
||||
|
||||
// Add to buffer
|
||||
buffer->buffer[buffer->head] = item;
|
||||
buffer->head = (buffer->head + 1) % BUFFER_SIZE;
|
||||
buffer->count++;
|
||||
|
||||
printf("Producer %d: Produced value %d\n", id, item.value);
|
||||
|
||||
sem_post(mutex); // Exit critical section
|
||||
sem_post(full); // Signal item available
|
||||
|
||||
usleep(rand_r(&seed) % 100000); // Simulate work
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
printf("Producer %d: Finished producing %d items\n", id, num_items);
|
||||
cleanup();
|
||||
return 0;
|
||||
}
|
||||
38
assignment3/test
Executable file
38
assignment3/test
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
echo "Building executables..."
|
||||
|
||||
cd "${FLAKE:-$(dirname $0)}"
|
||||
if command -v nix &>/dev/null; then
|
||||
producer="$(nix build --no-link --print-out-paths .#a3-producer)/bin/producer"
|
||||
consumer="$(nix build --no-link --print-out-paths .#a3-consumer)/bin/consumer"
|
||||
else
|
||||
mkdir build
|
||||
gcc src/producer.c -o build/producer -pthread -lrt
|
||||
gcc src/consumer.c -o build/consumer -pthread -lrt
|
||||
producer="./build/producer"
|
||||
consumer="./build/consumer"
|
||||
fi
|
||||
|
||||
echo -e "\nTEST - Basic Functionality"
|
||||
"${producer}" 1 5 &
|
||||
"${consumer}" 1 5 &
|
||||
wait
|
||||
|
||||
echo -e "\nTEST - Multiple Producers"
|
||||
"${producer}" 1 10 &
|
||||
"${producer}" 2 10 &
|
||||
"${producer}" 3 10 &
|
||||
"${consumer}" 1 30 &
|
||||
wait
|
||||
|
||||
echo -e "\nTEST - Multiple Consumers"
|
||||
"${producer}" 1 20 &
|
||||
"${consumer}" 1 10 &
|
||||
"${consumer}" 2 10 &
|
||||
wait
|
||||
|
||||
# Cleanup
|
||||
ipcrm -M 0x1234
|
||||
rm /dev/shm/sem*
|
||||
rm -rf build
|
||||
9
assignment3/test.nix
Normal file
9
assignment3/test.nix
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
a3 = { runCommandLocal, makeWrapper }:
|
||||
runCommandLocal "a3-test" { nativeBuildInputs = [ makeWrapper ]; }
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
install ${./test} $out/bin/a3-test
|
||||
wrapProgram "$out/bin/a3-test" --set FLAKE ${../.}
|
||||
'';
|
||||
}
|
||||
|
|
@ -11,7 +11,9 @@
|
|||
subdirs = [
|
||||
"assignment1"
|
||||
"assignment2"
|
||||
"assignment3"
|
||||
"project1"
|
||||
"project2"
|
||||
];
|
||||
|
||||
eachSystem = nixpkgs.lib.genAttrs (import systems);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
|
|
|||
31
project2/Makefile
Normal file
31
project2/Makefile
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
SRC_DIR := ./src
|
||||
BUILD_DIR := ./build
|
||||
|
||||
EXEC ?= cpu-schedulers
|
||||
INSTALL ?= $(PREFIX)/bin/
|
||||
|
||||
# Find all the C files we want to compile
|
||||
SRCS := $(shell find $(SRC_DIR) -name '*.c')
|
||||
OBJS := $(SRCS:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)
|
||||
|
||||
.PHONY: build install clean
|
||||
|
||||
build: $(BUILD_DIR)/$(EXEC)
|
||||
install: build
|
||||
mkdir -p $(INSTALL)
|
||||
cp $(BUILD_DIR)/$(EXEC) $(INSTALL)
|
||||
|
||||
|
||||
# Link
|
||||
$(BUILD_DIR)/$(EXEC): $(OBJS)
|
||||
$(CC) $^ -o $@ $(LDFLAGS)
|
||||
|
||||
# Compile
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
||||
mkdir -p $(dir $@)
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
|
||||
|
||||
|
||||
clean:
|
||||
rm -r $(BUILD_DIR)
|
||||
36
project2/README.md
Normal file
36
project2/README.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# CS3502 Project 2: CPU Scheduling Simulation
|
||||
|
||||
This directory contains a simulator for various CPU scheduling algorithms.
|
||||
|
||||
## Compilation
|
||||
|
||||
If you have [Nix](https://nixos.org/) installed:
|
||||
|
||||
```sh
|
||||
nix run .#cpu-schedulers
|
||||
```
|
||||
|
||||
Otherwise, the code may be compiled using GNU Make:
|
||||
|
||||
``` sh
|
||||
make
|
||||
./build/cpu-schedulers
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
This program supports seven scheduling algorithms:
|
||||
|
||||
- `sjf` - Shortest Job First
|
||||
- `fcfs` - First Come First Serve
|
||||
- `roundrobin` - Round Robin
|
||||
- `priority` - Priority Queue
|
||||
- `priorityaging` - Priority Queue with Aging
|
||||
- `lottery` - Lottery
|
||||
- `srtf` - Shortest Remaining Time First
|
||||
|
||||
The `compare` option can also be entered to run all schedulers at once.
|
||||
|
||||
Test data must be entered by the user; all time values must be given in
|
||||
integers. Flags include `-v` for verbose debug output, and `-m` for
|
||||
machine-readable output (prints data in CSV format).
|
||||
8
project2/default.nix
Normal file
8
project2/default.nix
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
rec {
|
||||
project2 = cpu-schedulers;
|
||||
cpu-schedulers = { stdenv }: stdenv.mkDerivation {
|
||||
name = "cpu-schedulers";
|
||||
src = ./.;
|
||||
installFlags = [ "PREFIX=$(out)" "EXEC=$(name)" ];
|
||||
};
|
||||
}
|
||||
91
project2/report/report.tex
Normal file
91
project2/report/report.tex
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
\documentclass{article}
|
||||
|
||||
\usepackage{xcolor}
|
||||
\usepackage{minted}
|
||||
\usepackage{hyperref}
|
||||
|
||||
\title{CS3502 Project 2: CPU Scheduling Simulator}
|
||||
\author{Kiana Sheibani}
|
||||
|
||||
\definecolor{lgray}{gray}{0.9}
|
||||
|
||||
\begin{document}
|
||||
\maketitle
|
||||
\newpage
|
||||
|
||||
\section{Introduction}
|
||||
|
||||
CPU scheduling, the allotment of processor execution time to running processes, is an integral part of any modern computer. There are many different strategies for doing so, designed to optimize different metrics:
|
||||
|
||||
\begin{enumerate}
|
||||
\item \emph{Waiting time}, the amount of time between a process being ready and the CPU beginning to execute it;
|
||||
\item \emph{Turnaround time}, the amount of time between a process being ready and finishing;
|
||||
\item \emph{CPU utilization}, the percentage of CPU cycles spent executing a process;
|
||||
\item \emph{Throughput}, the rate at which processes are finished executing.
|
||||
\end{enumerate}
|
||||
|
||||
CPU scheduling strategies include FCFS (First Come First Serve), Round Robin, priority scheduling, and more. To determine which of these methods is most effective, it is useful to construct test models of CPUs in order to assess them in practice.
|
||||
|
||||
\section{Methodology}
|
||||
|
||||
To run these tests, I wrote a program in C to simulate different scheduling strategies based on process statistics input by the user. The code for this program is stored in my repository for this course under the \texttt{project2} subdirectory: \url{https://git.tokinanpa.dev/toki/CS3502}.
|
||||
|
||||
The simulator supports seven different strategies:
|
||||
|
||||
\begin{itemize}
|
||||
\item \texttt{sjf} (Shortest Job First) --- Processes are run in ascending order of time to execute (\emph{burst time}). This is theoretically ideal, but impossible in practice, as it is impossible to know how long a program will take to execute.
|
||||
\item \texttt{fcfs} (First Come First Serve) --- Processes are run in the order that they arrived in the ready queue. The simplest and least sophisticated strategy.
|
||||
\item \texttt{roundrobin} (Round Robin) --- The currently running processes are cycled between at a fixed interval.
|
||||
\item \texttt{priority} (Priority Queue) --- Each process is assigned a priority, where higher priority processes are run first over lower priority ones.
|
||||
\item \texttt{priorityaging} (Priority Queue with Aging) --- Similar to \texttt{priority}, but each running process has its priority increased over time to prevent starvation, an issue where a low priority process is never run due to more important processes running first.
|
||||
\item \texttt{lottery} (Lottery) --- The next process to run is chosen at random from the ready queue.
|
||||
\item \texttt{srtf} (Shortest Remaining Time First) --- The most complex algorithm supported. This strategy is similar to \texttt{sjf}, but the burst time of each process is approximated based on the burst times of the previous processes, and the currently running process is always that which takes the least remaining time to complete.
|
||||
\end{itemize}
|
||||
|
||||
The simulator also supports a mode to compare the performance of each strategy on the same dataset.
|
||||
|
||||
\subsection{Technical Details}
|
||||
|
||||
The processes to be scheduled are passed to the scheduler as an array of structs, where each struct contains the process's properties: a process ID, its arrival time, its burst length, and a priority value if the scheduler requires one. The struct also contains fields for the scheduler's output, specifically the intervals of time in which the CPU is running the process.
|
||||
|
||||
\begin{listing}[!ht]
|
||||
\inputminted[
|
||||
firstline=11, lastline=28,
|
||||
fontsize=\footnotesize,
|
||||
linenos,
|
||||
frame=lines,
|
||||
framesep=2mm,
|
||||
baselinestretch=1.2,
|
||||
]{c}{../src/main.h}
|
||||
\vspace*{-\baselineskip}
|
||||
\caption{\texttt{main.h} --- Definition of \texttt{process}}
|
||||
\end{listing}
|
||||
|
||||
The \texttt{cpu\_time\_t} type is an alias defined to be an integer type, specifically \texttt{long}. This type is used as the time unit for specifying both timestamps and durations.
|
||||
|
||||
In order to better facilitate the execution of the scheduling algorithms, the list of processes must be sorted by arrival time. This is enforced by the program when the arrival times are input.
|
||||
|
||||
\section{Performance}
|
||||
|
||||
Unfortunately, due to various bugs and other issues with the program that I could not resolve in time, I was not able to obtain as much testing data as I hoped on the performance of each scheduler. From what data I was able to obtain, however, the \texttt{srtf} scheduler performed consistently better in both waiting time and turnaround time than all other scheduler strategies (discounting the impossible \texttt{sjf} scheduler).
|
||||
|
||||
\begin{table}[!ht]
|
||||
\centering
|
||||
\begin{tabular}{|c|c|c|c|c|}
|
||||
Scheduler & Waiting Time & Turnaround Time & CPU \% & Throughput \\
|
||||
\hline
|
||||
\texttt{sjf} & 1112.2 & 1580.7 & 100\% & 0.002134 \\
|
||||
\hline
|
||||
\texttt{fcfs} & 2570.8 & 3039.3 & 100\% & 0.002134 \\
|
||||
\texttt{roundrobin} & 2219.6 & 3015.7 & 100\% & 0.002134 \\
|
||||
\texttt{priority} & 3097.8 & 3566.3 & 100\% & 0.002134 \\
|
||||
\texttt{priorityaging} & 3097.8 & 3566.3 & 100\% & 0.002134 \\
|
||||
\texttt{lottery} & 2590 & 3058.5 & 100\% & 0.002134 \\
|
||||
\texttt{srtf} & 1541 & 2435.7 & 100\% & 0.002134 \\
|
||||
\end{tabular}
|
||||
\caption{Example output from a randomized test suite. The CPU utilization and throughput are at maximum for all schedulers because this was a high-process-density test.}
|
||||
\end{table}
|
||||
|
||||
This suggests that the SRTF scheduling strategy is the optimal method out of these options, and that this strategy should be chosen for ideal scheduling. There are still more possible strategies for implementing a scheduler, however, and other considerations to account for. For example, adding a priority system on top of SRTF may allow it to become more flexible and to perform better in all circumstances.
|
||||
|
||||
\end{document}
|
||||
24
project2/src/fcfs.c
Normal file
24
project2/src/fcfs.c
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#include "fcfs.h"
|
||||
#include "main.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
void fcfs_run(process processes[], int num_processes) {
|
||||
// Assign each to next free time
|
||||
cpu_time_t next_free = 0;
|
||||
for (int i = 0; i < num_processes; i++) {
|
||||
cpu_time_t arrival = processes[i].arrival;
|
||||
cpu_time_t burst = processes[i].burst;
|
||||
|
||||
processes[i].num_schedules = 1;
|
||||
processes[i].starts = malloc(sizeof(cpu_time_t));
|
||||
processes[i].starts[0] = arrival >= next_free ? arrival : next_free;
|
||||
processes[i].durations = malloc(sizeof(cpu_time_t));
|
||||
processes[i].durations[0] = burst;
|
||||
next_free = processes[i].starts[0] + burst;
|
||||
}
|
||||
}
|
||||
|
||||
scheduler fcfs = (scheduler){.name = "fcfs",
|
||||
.description = "First Come First Serve",
|
||||
.run = fcfs_run,
|
||||
.uses_priority = 0};
|
||||
7
project2/src/fcfs.h
Normal file
7
project2/src/fcfs.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef FCFS_H_
|
||||
#define FCFS_H_
|
||||
#include "main.h"
|
||||
|
||||
extern scheduler fcfs;
|
||||
|
||||
#endif // FCFS_H_
|
||||
52
project2/src/lottery.c
Normal file
52
project2/src/lottery.c
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#include "lottery.h"
|
||||
#include "main.h"
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
void lottery_run(process processes[], int num_processes) {
|
||||
unsigned int seed = time(NULL);
|
||||
|
||||
process *ready_queue[num_processes];
|
||||
int num_ready = 0;
|
||||
int num_processed = 0;
|
||||
int num_run = 0;
|
||||
cpu_time_t next_free = 0;
|
||||
while (num_run < num_processes) {
|
||||
// Add new arrivals to ready queue
|
||||
for (int i = num_processed;
|
||||
i < num_processes && processes[i].arrival <= next_free; i++) {
|
||||
ready_queue[num_ready] = &processes[i];
|
||||
num_ready++;
|
||||
num_processed++;
|
||||
}
|
||||
|
||||
// Idle if ready queue is empty
|
||||
if (num_ready == 0) {
|
||||
next_free = processes[num_processed].arrival;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove random process
|
||||
int next_i = rand_r(&seed) % num_ready;
|
||||
process *next = ready_queue[next_i];
|
||||
num_ready--;
|
||||
for (int i = next_i; i < num_ready; i++) {
|
||||
ready_queue[i] = ready_queue[i + 1];
|
||||
}
|
||||
|
||||
// Run process
|
||||
next->num_schedules = 1;
|
||||
next->starts = malloc(sizeof(cpu_time_t));
|
||||
next->starts[0] = next_free;
|
||||
next->durations = malloc(sizeof(cpu_time_t));
|
||||
next->durations[0] = next->burst;
|
||||
num_run++;
|
||||
|
||||
next_free = next->starts[0] + next->burst;
|
||||
}
|
||||
}
|
||||
|
||||
scheduler lottery = (scheduler){.name = "lottery",
|
||||
.description = NULL,
|
||||
.run = lottery_run,
|
||||
.uses_priority = 0};
|
||||
7
project2/src/lottery.h
Normal file
7
project2/src/lottery.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef LOTTERY_H_
|
||||
#define LOTTERY_H_
|
||||
#include "main.h"
|
||||
|
||||
extern scheduler lottery;
|
||||
|
||||
#endif // LOTTERY_H_
|
||||
263
project2/src/main.c
Normal file
263
project2/src/main.c
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
#include "main.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "fcfs.h"
|
||||
#include "lottery.h"
|
||||
#include "priority.h"
|
||||
#include "round_robin.h"
|
||||
#include "sjf.h"
|
||||
#include "srtf.h"
|
||||
|
||||
// See main.h
|
||||
struct process;
|
||||
struct scheduler;
|
||||
|
||||
scheduler *schedulers[] = {
|
||||
&sjf, &fcfs, &round_robin, &priority, &priority_aging, &lottery, &srtf};
|
||||
int num_schedulers = sizeof(schedulers) / sizeof(*schedulers);
|
||||
|
||||
int read_comma_sep(process list[], int num, size_t offset, int is_long) {
|
||||
char *buffer = malloc(num * 10);
|
||||
fgets(buffer, num * 10, stdin);
|
||||
int processed = 0;
|
||||
char *value_str = strtok(buffer, ",");
|
||||
while (value_str != NULL) {
|
||||
if (is_long)
|
||||
sscanf(value_str, "%ld", (long *)((void *)&list[processed] + offset));
|
||||
else
|
||||
sscanf(value_str, "%d", (int *)((void *)&list[processed] + offset));
|
||||
processed++;
|
||||
value_str = strtok(NULL, ",");
|
||||
}
|
||||
free(buffer);
|
||||
if (processed != num) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void run_scheduler(scheduler *scheduler, process processes[], int num_processes,
|
||||
int verbose, int machine_readable) {
|
||||
if (!machine_readable) {
|
||||
if (scheduler->description != NULL)
|
||||
printf("\nScheduler %s (%s):\n", scheduler->name, scheduler->description);
|
||||
else
|
||||
printf("\nScheduler %s:\n", scheduler->name);
|
||||
}
|
||||
|
||||
scheduler->run(processes, num_processes);
|
||||
|
||||
cpu_time_t total_time = 0;
|
||||
cpu_time_t used_time = 0;
|
||||
cpu_time_t total_waiting = 0;
|
||||
cpu_time_t total_turnaround = 0;
|
||||
|
||||
if (!machine_readable && verbose)
|
||||
printf("Schedule times: ");
|
||||
for (int i = 0; i < num_processes; i++) {
|
||||
used_time += processes[i].burst;
|
||||
|
||||
if (!machine_readable && verbose) {
|
||||
for (int j = 0; j < processes[i].num_schedules; j++) {
|
||||
printf("%ld-%ld%s", processes[i].starts[j],
|
||||
processes[i].starts[j] + processes[i].durations[j],
|
||||
i == num_processes - 1 && j == processes[i].num_schedules - 1
|
||||
? "\n"
|
||||
: j == processes[i].num_schedules - 1 ? ", "
|
||||
: " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!machine_readable && verbose)
|
||||
printf("Waiting times: ");
|
||||
for (int i = 0; i < num_processes; i++) {
|
||||
cpu_time_t first_start = processes[i].starts[0];
|
||||
for (int j = 1; j < processes[i].num_schedules; j++) {
|
||||
if (processes[i].starts[j] < first_start)
|
||||
first_start = processes[i].starts[j];
|
||||
}
|
||||
processes[i].waiting = first_start - processes[i].arrival;
|
||||
total_waiting += processes[i].waiting;
|
||||
|
||||
if (!machine_readable && verbose)
|
||||
printf("%ld%s", processes[i].waiting,
|
||||
i == num_processes - 1 ? "\n" : ", ");
|
||||
}
|
||||
|
||||
if (!machine_readable && verbose)
|
||||
printf("Turnaround times: ");
|
||||
for (int i = 0; i < num_processes; i++) {
|
||||
cpu_time_t process_end = processes[i].starts[0] + processes[i].durations[0];
|
||||
for (int j = 0; j < processes[i].num_schedules; j++) {
|
||||
int end = processes[i].starts[j] + processes[i].durations[j];
|
||||
if (process_end < end)
|
||||
process_end = end;
|
||||
if (total_time < end)
|
||||
total_time = end;
|
||||
}
|
||||
processes[i].turnaround = process_end - processes[i].arrival;
|
||||
total_turnaround += processes[i].turnaround;
|
||||
|
||||
if (!machine_readable && verbose)
|
||||
printf("%ld%s", processes[i].turnaround,
|
||||
i == num_processes - 1 ? "\n" : ", ");
|
||||
}
|
||||
|
||||
double avg_waiting = (double)total_waiting / (double)num_processes;
|
||||
double avg_turnaround = (double)total_turnaround / (double)num_processes;
|
||||
double cpu_util = (double)used_time / (double)total_time;
|
||||
double throughput = (double)num_processes / (double)total_time;
|
||||
|
||||
if (machine_readable) {
|
||||
printf("%lf,%lf,%lf,%lf\n", avg_waiting, avg_turnaround, cpu_util,
|
||||
throughput);
|
||||
} else {
|
||||
printf("Avg. Waiting Time: %.3lf\n", avg_waiting);
|
||||
printf("Avg. Turnaround Time: %.3lf\n", avg_turnaround);
|
||||
printf("CPU Utilization: %.2lf%%\n", cpu_util * 100);
|
||||
printf("Throughput: %.6lf proc/t\n", throughput);
|
||||
}
|
||||
|
||||
// Free memory
|
||||
for (int i = 0; i < num_processes; i++) {
|
||||
free(processes[i].starts);
|
||||
free(processes[i].durations);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Parse argument (-v verbose, for debug)
|
||||
char opt;
|
||||
int verbose = 0;
|
||||
int machine_readable = 0;
|
||||
while ((opt = getopt(argc, argv, "vm")) != -1) {
|
||||
switch (opt) {
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case 'm':
|
||||
machine_readable = 1;
|
||||
break;
|
||||
default:
|
||||
printf("Usage: %s [-v] [-m]\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!machine_readable) {
|
||||
printf("CPU Scheduling Simulator\n");
|
||||
printf("========================\n\n");
|
||||
}
|
||||
|
||||
// Input scheduler to run
|
||||
|
||||
char scheduler_name[32];
|
||||
|
||||
if (!machine_readable) {
|
||||
printf("Scheduler to run (");
|
||||
for (int i = 0; i < num_schedulers; i++) {
|
||||
printf("%s, ", schedulers[i]->name);
|
||||
}
|
||||
printf("compare): ");
|
||||
}
|
||||
fgets(scheduler_name, sizeof(scheduler_name), stdin);
|
||||
if (scheduler_name[strlen(scheduler_name) - 1] != '\n') {
|
||||
printf("Too many characters\n");
|
||||
return 1;
|
||||
}
|
||||
scheduler_name[strlen(scheduler_name) - 1] = '\0';
|
||||
|
||||
scheduler *scheduler = NULL;
|
||||
if (strcmp(scheduler_name, "compare") != 0) {
|
||||
for (int i = 0; i < num_schedulers; i++) {
|
||||
if (strcmp(scheduler_name, schedulers[i]->name) == 0) {
|
||||
scheduler = schedulers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (scheduler == NULL) {
|
||||
printf("Invalid scheduler name\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Number of processes
|
||||
|
||||
int num_processes = 0;
|
||||
if (!machine_readable)
|
||||
printf("Number of processes: ");
|
||||
char num_processes_str[10];
|
||||
fgets(num_processes_str, sizeof(num_processes_str), stdin);
|
||||
sscanf(num_processes_str, "%d", &num_processes);
|
||||
if (num_processes < 1) {
|
||||
printf("Invalid number of processes\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
process processes[num_processes];
|
||||
for (int i = 0; i < num_processes; i++) {
|
||||
processes[i] = (process){.pid = i};
|
||||
}
|
||||
|
||||
// Arrival times
|
||||
|
||||
if (!machine_readable)
|
||||
printf("Process arrival times (ordered, comma-separated): ");
|
||||
if (!read_comma_sep(processes, num_processes,
|
||||
(void *)&(processes->arrival) - (void *)processes, 1)) {
|
||||
printf("Invalid arrival times\n");
|
||||
return 1;
|
||||
}
|
||||
// Check ordering
|
||||
for (int i = 1; i < num_processes; i++) {
|
||||
if (processes[i].arrival < processes[i - 1].arrival) {
|
||||
printf("Arrival times not ordered\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Burst times
|
||||
|
||||
if (!machine_readable)
|
||||
printf("Process burst times (comma-separated): ");
|
||||
if (!read_comma_sep(processes, num_processes,
|
||||
(void *)&(processes->burst) - (void *)processes, 1)) {
|
||||
printf("Invalid burst times\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Priorities
|
||||
|
||||
if (scheduler == NULL || scheduler->uses_priority) {
|
||||
if (!machine_readable)
|
||||
printf("Process priorities (comma-separated): ");
|
||||
if (!read_comma_sep(processes, num_processes,
|
||||
(void *)&(processes->priority) - (void *)processes,
|
||||
0)) {
|
||||
printf("Invalid priorities\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Run scheduler(s)
|
||||
|
||||
if (scheduler != NULL) {
|
||||
run_scheduler(scheduler, processes, num_processes, verbose,
|
||||
machine_readable);
|
||||
} else {
|
||||
for (int i = 0; i < num_schedulers; i++) {
|
||||
// Reset process fields
|
||||
for (int i = 0; i < num_processes; i++) {
|
||||
processes[i].num_schedules = 0;
|
||||
processes[i].executed = 0;
|
||||
}
|
||||
|
||||
run_scheduler(schedulers[i], processes, num_processes, verbose,
|
||||
machine_readable);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
project2/src/main.h
Normal file
37
project2/src/main.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef MAIN_H_
|
||||
#define MAIN_H_
|
||||
#include <stddef.h>
|
||||
|
||||
typedef long cpu_time_t;
|
||||
|
||||
// Average CPU burst length
|
||||
// This is used for Round Robin and SRTF scheduling
|
||||
#define AVG_BURST 1000
|
||||
|
||||
typedef struct process {
|
||||
// Input values
|
||||
int pid;
|
||||
cpu_time_t arrival;
|
||||
cpu_time_t burst;
|
||||
int priority;
|
||||
|
||||
// Used internally by some schedulers
|
||||
cpu_time_t executed;
|
||||
|
||||
// Scheduler output value
|
||||
size_t num_schedules;
|
||||
cpu_time_t *starts; // Dynamically allocated arrays; free after read
|
||||
cpu_time_t *durations;
|
||||
// Derived output values
|
||||
cpu_time_t waiting;
|
||||
cpu_time_t turnaround;
|
||||
} process;
|
||||
|
||||
typedef struct scheduler {
|
||||
char *name;
|
||||
char *description;
|
||||
void (*run)(process *processes, int num_processes);
|
||||
int uses_priority;
|
||||
} scheduler;
|
||||
|
||||
#endif // MAIN_H_
|
||||
75
project2/src/priority.c
Normal file
75
project2/src/priority.c
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#include "priority.h"
|
||||
#include "main.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
void priority_run(process processes[], int num_processes, int aging) {
|
||||
process *ready_queue[num_processes];
|
||||
int num_ready = 0;
|
||||
int num_processed = 0;
|
||||
int num_run = 0;
|
||||
cpu_time_t next_free = 0;
|
||||
while (num_run < num_processes) {
|
||||
// Add new arrivals to ready queue
|
||||
for (int i = num_processed;
|
||||
i < num_processes && processes[i].arrival <= next_free; i++) {
|
||||
ready_queue[num_ready] = &processes[i];
|
||||
num_ready++;
|
||||
num_processed++;
|
||||
}
|
||||
|
||||
// Idle if ready queue is empty
|
||||
if (num_ready == 0) {
|
||||
next_free = processes[num_processed].arrival;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove process with highest priority (FCFS if same priority)
|
||||
int next_i = 0;
|
||||
for (int i = 0; i < num_ready; i++) {
|
||||
if (ready_queue[i]->priority > ready_queue[next_i]->priority ||
|
||||
ready_queue[i]->priority == ready_queue[next_i]->priority) {
|
||||
next_i = i;
|
||||
}
|
||||
}
|
||||
process *next = ready_queue[next_i];
|
||||
num_ready--;
|
||||
for (int i = next_i; i < num_ready; i++) {
|
||||
ready_queue[i] = ready_queue[i + 1];
|
||||
}
|
||||
|
||||
// Age processes (increase priority by 1)
|
||||
if (aging) {
|
||||
for (int i = 0; i < num_ready; i++) {
|
||||
ready_queue[i]->priority++;
|
||||
}
|
||||
}
|
||||
|
||||
// Run process
|
||||
next->num_schedules = 1;
|
||||
next->starts = malloc(sizeof(cpu_time_t));
|
||||
next->starts[0] = next_free;
|
||||
next->durations = malloc(sizeof(cpu_time_t));
|
||||
next->durations[0] = next->burst;
|
||||
num_run++;
|
||||
|
||||
next_free = next->starts[0] + next->burst;
|
||||
}
|
||||
}
|
||||
|
||||
void priority_run_no_aging(process processes[], int num_processes) {
|
||||
priority_run(processes, num_processes, 0);
|
||||
}
|
||||
|
||||
void priority_run_aging(process processes[], int num_processes) {
|
||||
priority_run(processes, num_processes, 1);
|
||||
}
|
||||
|
||||
scheduler priority = (scheduler){.name = "priority",
|
||||
.description = NULL,
|
||||
.run = priority_run_no_aging,
|
||||
.uses_priority = 1};
|
||||
|
||||
scheduler priority_aging = (scheduler){.name = "priorityaging",
|
||||
.description = "Priority - With Aging",
|
||||
.run = priority_run_aging,
|
||||
.uses_priority = 1};
|
||||
8
project2/src/priority.h
Normal file
8
project2/src/priority.h
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef PRIORITY_H_
|
||||
#define PRIORITY_H_
|
||||
#include "main.h"
|
||||
|
||||
extern scheduler priority;
|
||||
extern scheduler priority_aging;
|
||||
|
||||
#endif // PRIORITY_H_
|
||||
66
project2/src/round_robin.c
Normal file
66
project2/src/round_robin.c
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#include "round_robin.h"
|
||||
#include "main.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
// Exact quantum does not matter much for sandbox testing;
|
||||
// arbitrarily set to 100 time units
|
||||
cpu_time_t quantum = AVG_BURST;
|
||||
|
||||
void round_robin_run(process processes[], int num_processes) {
|
||||
// Initialize memory
|
||||
for (int i = 0; i < num_processes; i++) {
|
||||
size_t memory_heuristic = processes[i].burst / quantum + 1;
|
||||
processes[i].starts = malloc(memory_heuristic * sizeof(cpu_time_t));
|
||||
processes[i].durations = malloc(memory_heuristic * sizeof(cpu_time_t));
|
||||
}
|
||||
|
||||
process *ready_queue[num_processes];
|
||||
int num_ready = 0;
|
||||
int num_processed = 0;
|
||||
int num_run = 0;
|
||||
cpu_time_t next_free = 0;
|
||||
while (num_run < num_processes) {
|
||||
// Add new arrivals to ready queue
|
||||
for (int i = num_processed;
|
||||
i < num_processes && processes[i].arrival <= next_free; i++) {
|
||||
ready_queue[num_ready] = &processes[i];
|
||||
num_ready++;
|
||||
num_processed++;
|
||||
}
|
||||
|
||||
// Idle if ready queue is empty
|
||||
if (num_ready == 0) {
|
||||
next_free = processes[num_processed].arrival;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Run next process (potentially partially)
|
||||
process *next = ready_queue[0];
|
||||
num_ready--;
|
||||
for (int i = 0; i < num_ready; i++) {
|
||||
ready_queue[i] = ready_queue[i + 1];
|
||||
}
|
||||
|
||||
size_t num_sch = next->num_schedules;
|
||||
next->starts[num_sch] = next_free;
|
||||
if (next->executed + quantum < next->burst) {
|
||||
// Partial execution
|
||||
next->durations[num_sch] = quantum;
|
||||
next->executed += quantum;
|
||||
|
||||
// Place back into execution
|
||||
ready_queue[num_ready] = next;
|
||||
num_ready++;
|
||||
} else {
|
||||
next->durations[num_sch] = next->burst - next->executed;
|
||||
num_run++;
|
||||
}
|
||||
next_free = next->starts[num_sch] + next->durations[num_sch];
|
||||
next->num_schedules++;
|
||||
}
|
||||
}
|
||||
|
||||
scheduler round_robin = (scheduler){.name = "roundrobin",
|
||||
.description = NULL,
|
||||
.run = round_robin_run,
|
||||
.uses_priority = 0};
|
||||
7
project2/src/round_robin.h
Normal file
7
project2/src/round_robin.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef ROUND_ROBIN_H_
|
||||
#define ROUND_ROBIN_H_
|
||||
#include "main.h"
|
||||
|
||||
extern scheduler round_robin;
|
||||
|
||||
#endif // ROUND_ROBIN_H_
|
||||
54
project2/src/sjf.c
Normal file
54
project2/src/sjf.c
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#include "sjf.h"
|
||||
#include "main.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
void sjf_run(process processes[], int num_processes) {
|
||||
process *ready_queue[num_processes];
|
||||
int num_ready = 0;
|
||||
int num_processed = 0;
|
||||
int num_run = 0;
|
||||
cpu_time_t next_free = 0;
|
||||
while (num_run < num_processes) {
|
||||
// Add new arrivals to ready queue
|
||||
for (int i = num_processed;
|
||||
i < num_processes && processes[i].arrival <= next_free; i++) {
|
||||
ready_queue[num_ready] = &processes[i];
|
||||
num_ready++;
|
||||
num_processed++;
|
||||
}
|
||||
|
||||
// Idle if ready queue is empty
|
||||
if (num_ready == 0) {
|
||||
next_free = processes[num_processed].arrival;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove process with shortest burst
|
||||
int shortest = 0;
|
||||
for (int i = 0; i < num_ready; i++) {
|
||||
if (ready_queue[i]->burst < ready_queue[shortest]->burst) {
|
||||
shortest = i;
|
||||
}
|
||||
}
|
||||
process *next = ready_queue[shortest];
|
||||
num_ready--;
|
||||
for (int i = shortest; i < num_ready; i++) {
|
||||
ready_queue[i] = ready_queue[i + 1];
|
||||
}
|
||||
|
||||
// Run process
|
||||
next->num_schedules = 1;
|
||||
next->starts = malloc(sizeof(cpu_time_t));
|
||||
next->starts[0] = next_free;
|
||||
next->durations = malloc(sizeof(cpu_time_t));
|
||||
next->durations[0] = next->burst;
|
||||
num_run++;
|
||||
|
||||
next_free = next->starts[0] + next->burst;
|
||||
}
|
||||
}
|
||||
|
||||
scheduler sjf = (scheduler){.name = "sjf",
|
||||
.description = "Shortest Job First",
|
||||
.run = sjf_run,
|
||||
.uses_priority = 0};
|
||||
7
project2/src/sjf.h
Normal file
7
project2/src/sjf.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef SJF_H_
|
||||
#define SJF_H_
|
||||
#include "main.h"
|
||||
|
||||
extern scheduler sjf;
|
||||
|
||||
#endif // SJF_H_
|
||||
94
project2/src/srtf.c
Normal file
94
project2/src/srtf.c
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
#include "srtf.h"
|
||||
#include "main.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
double alpha = 0.5;
|
||||
|
||||
void srtf_run(process processes[], int num_processes) {
|
||||
// Compute predicted burst times based on exponential averaging
|
||||
cpu_time_t predicted_bursts[num_processes];
|
||||
predicted_bursts[0] = AVG_BURST;
|
||||
for (int i = 1; i < num_processes; i++) {
|
||||
predicted_bursts[i] = (cpu_time_t)(alpha * processes[i - 1].burst +
|
||||
(1 - alpha) * predicted_bursts[i - 1]);
|
||||
}
|
||||
|
||||
// Allocate memory
|
||||
for (int i = 0; i < num_processes; i++) {
|
||||
processes[i].starts = malloc(5 * sizeof(cpu_time_t));
|
||||
processes[i].durations = malloc(5 * sizeof(cpu_time_t));
|
||||
}
|
||||
|
||||
process *ready_queue[num_processes];
|
||||
int num_ready = 0;
|
||||
int num_processed = 0;
|
||||
int num_run = 0;
|
||||
cpu_time_t next_free = 0;
|
||||
while (num_run < num_processes) {
|
||||
// Add new arrivals to ready queue
|
||||
for (int i = num_processed;
|
||||
i < num_processes && processes[i].arrival <= next_free; i++) {
|
||||
ready_queue[num_ready] = &processes[i];
|
||||
num_ready++;
|
||||
num_processed++;
|
||||
}
|
||||
|
||||
// Idle if ready queue is empty
|
||||
if (num_ready == 0) {
|
||||
next_free = processes[num_processed].arrival;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove process with shortest predicted burst remaining
|
||||
int next_i = 0;
|
||||
for (int i = 0; i < num_ready; i++) {
|
||||
int next_remaining = predicted_bursts[ready_queue[next_i]->pid] -
|
||||
ready_queue[next_i]->executed;
|
||||
int i_remaining =
|
||||
predicted_bursts[ready_queue[i]->pid] - ready_queue[i]->executed;
|
||||
if (i_remaining < next_remaining) {
|
||||
next_i = i;
|
||||
}
|
||||
}
|
||||
process *next = ready_queue[next_i];
|
||||
num_ready--;
|
||||
for (int i = next_i; i < num_ready; i++) {
|
||||
ready_queue[i] = ready_queue[i + 1];
|
||||
}
|
||||
|
||||
// Grow memory if not big enough
|
||||
if (next->num_schedules % 5 == 0) {
|
||||
if (!realloc(next->starts, next->num_schedules + 5) ||
|
||||
!realloc(next->durations, next->num_schedules + 5)) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Run process preemptively (only until next one arrives)
|
||||
size_t num_sch = next->num_schedules;
|
||||
next->starts[num_sch] = next_free;
|
||||
if (num_processed != num_processes &&
|
||||
next_free + next->burst - next->executed >
|
||||
processes[num_processed].arrival) {
|
||||
// Partial execution
|
||||
next->durations[num_sch] = processes[num_processed].arrival - next_free;
|
||||
next->executed += next->durations[num_sch];
|
||||
|
||||
// Place back into execution
|
||||
ready_queue[num_ready] = next;
|
||||
num_ready++;
|
||||
} else {
|
||||
next->durations[num_sch] = next->burst - next->executed;
|
||||
num_run++;
|
||||
}
|
||||
next->num_schedules++;
|
||||
|
||||
next_free = next->starts[num_sch] + next->durations[num_sch];
|
||||
}
|
||||
}
|
||||
|
||||
scheduler srtf = (scheduler){.name = "srtf",
|
||||
.description = "Shortest Remaining Time First",
|
||||
.run = srtf_run,
|
||||
.uses_priority = 0};
|
||||
7
project2/src/srtf.h
Normal file
7
project2/src/srtf.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef SRTF_H_
|
||||
#define SRTF_H_
|
||||
#include "main.h"
|
||||
|
||||
extern scheduler srtf;
|
||||
|
||||
#endif // SRTF_H_
|
||||
1
project2/test.nix
Normal file
1
project2/test.nix
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
5
project2/test/input.txt
Normal file
5
project2/test/input.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
compare
|
||||
10
|
||||
0,10,73,78,78,80,90,137,211,237
|
||||
853,1,1470,654,108,6,1074,508,1,10
|
||||
7,6,15,12,8,1,14,1,4,6
|
||||
34
project2/test/random
Executable file
34
project2/test/random
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
echo "Building executable..."
|
||||
|
||||
cd "${FLAKE:-$(dirname $0)}"
|
||||
if command -v nix &>/dev/null; then
|
||||
cpu_schedulers="$(nix build --no-link --print-out-paths .#cpu-schedulers)/bin/cpu-schedulers"
|
||||
else
|
||||
make
|
||||
cpu_schedulers="build/cpu-schedulers"
|
||||
fi
|
||||
|
||||
num_processes=10
|
||||
|
||||
printf "compare\n$num_processes\n" >input.txt
|
||||
|
||||
proc_arrival=0
|
||||
for i in $(seq 2 "$num_processes"); do
|
||||
echo -n "$proc_arrival," >>input.txt
|
||||
proc_arrival=$(($RANDOM % 100 + $proc_arrival))
|
||||
done
|
||||
echo "$proc_arrival" >>input.txt
|
||||
|
||||
for i in $(seq 2 "$num_processes"); do
|
||||
echo -n $((($RANDOM % 1250) ** 4 / 1000000000 + 1))"," >>input.txt
|
||||
done
|
||||
echo $((($RANDOM % 1250) ** 4 / 1000000000 + 1)) >>input.txt
|
||||
|
||||
for i in $(seq 2 "$num_processes"); do
|
||||
echo -n $(($RANDOM % 20))"," >>input.txt
|
||||
done
|
||||
echo $(($RANDOM % 20)) >>input.txt
|
||||
|
||||
"$cpu_schedulers" -m <input.txt
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
{ mkShell, llvmPackages_21 }:
|
||||
{ mkShell,
|
||||
llvmPackages_21,
|
||||
dotnet-sdk
|
||||
}:
|
||||
|
||||
mkShell {
|
||||
packages = [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue