From 29775b66b538979570c7064e8858de335a8acf41 Mon Sep 17 00:00:00 2001 From: bgraansm Date: Sat, 26 Aug 2017 08:47:15 -0400 Subject: [PATCH] first commit --- README.txt | 27 +++++ input.txt | 15 +++ main.c | 13 +++ makefile | 8 ++ mode.c | 89 ++++++++++++++++ mode.h | 34 +++++++ schedule.c | 281 +++++++++++++++++++++++++++++++++++++++++++++++++++ schedule.h | 132 ++++++++++++++++++++++++ sim2.txt | 15 +++ simulation.c | 219 +++++++++++++++++++++++++++++++++++++++ simulation.h | 11 ++ 11 files changed, 844 insertions(+) create mode 100644 README.txt create mode 100644 input.txt create mode 100644 main.c create mode 100644 makefile create mode 100644 mode.c create mode 100644 mode.h create mode 100644 schedule.c create mode 100644 schedule.h create mode 100644 sim2.txt create mode 100644 simulation.c create mode 100644 simulation.h diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..d0935fd --- /dev/null +++ b/README.txt @@ -0,0 +1,27 @@ +CIS3110 Assignment 2 +author: Bronson Graansma, 0872249 +date: Sunday, March 13th, 2016 +contact: bgraansm@mail.uoguelph.ca +course: CIS*3110 Operating Systems I +__________________________________________________________________ + +For documentation on any of the function's functionality, +view function headers in *.h or *.c. +__________________________________________________________________ + +**Assumptions, Limitations, and Testing** +__________________________________________________________________ +Assumes input file is redirected to stdin. +Assumes input file contains only integers, spaces or tabs +Assumes input file is in proper format + If any of the above assumptions are violated, program + receives a short message to stderr before terminating + +Assumes command line arguments (if any exist) are among: + -v, -d, (-r x) in any order. -r must be followed directly + by some integer value (x). Any unrecognized flags will cause + the program to terminate with a message on stderr. + +Assumes system has sufficient memory in order to run the program. + Memory allocations are checked with assert. Program will + terminate if an assertion fails and NDEBUG isn't defined. diff --git a/input.txt b/input.txt new file mode 100644 index 0000000..d41cfd5 --- /dev/null +++ b/input.txt @@ -0,0 +1,15 @@ +2 3 7 +1 2 +1 0 3 +1 10 20 +2 10 20 +3 10 +2 5 2 +1 50 10 +2 50 +2 2 +1 0 1 +1 100 +2 50 2 +1 100 20 +2 100 diff --git a/main.c b/main.c new file mode 100644 index 0000000..d92a121 --- /dev/null +++ b/main.c @@ -0,0 +1,13 @@ +#include "mode.h" +#include "schedule.h" +#include "simulation.h" + +int main(int argc, char** argv) { + Mode mode = newMode(argv, argc); + Schedule schedule = readSchedule(stdin); + + simulate(schedule, mode); + freeSchedule(schedule); + + return EXIT_SUCCESS; +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..55b2708 --- /dev/null +++ b/makefile @@ -0,0 +1,8 @@ +CC = gcc +CFLAGS = -Wall -pedantic -std=c99 -g + +simcpu: main.c schedule.c mode.c simulation.c + $(CC) $(CFLAGS) $^ -o $@ + +clean: + rm -f *.o simcpu diff --git a/mode.c b/mode.c new file mode 100644 index 0000000..690462f --- /dev/null +++ b/mode.c @@ -0,0 +1,89 @@ +#include "mode.h" + +/* Creates a new mode to run the simulation in. Handles any errors + * with a useful message to stderr, and terminated the program. + * args: command line arguments to parse into the mode + * returns: a mode that signifies such things as: + * RR, quantum, verbose, detailed */ +Mode newMode(char** argv, int argc) { + Mode new; + + new.detailed = false; + new.verbose = false; + new.roundRobin = false; + new.quantum = 0; + + for(int i=1; i +#include +#include +#include +#include + +typedef struct Mode { + bool detailed; + bool verbose; + bool roundRobin; + int quantum; +} Mode; + +/* Creates a new mode to run the simulation in. Handles any errors + * with a useful message to stderr, and terminated the program. + * args: command line arguments to parse into the mode + * returns: a mode that signifies such things as: + * RR, quantum, verbose, detailed */ +Mode newMode(char** argv, int argc); + +/* Checks to see if a string can be converted to an integer. + * args: the string to check + * returns: true if string is an int, false otherwise */ +bool isInt(char* str); + +/* Checks if a string is empty or not + * args: the string to check + * returns: true if all characters are whitespace, false otherwise */ +bool isEmpty(char* str); + +#endif diff --git a/schedule.c b/schedule.c new file mode 100644 index 0000000..282b1f3 --- /dev/null +++ b/schedule.c @@ -0,0 +1,281 @@ +#include "schedule.h" + +static bool readBurst(FILE* fp, Burst* burst) { + char buffer[BUFF_LEN]; + + if(!burst) { + return false; + } + + if(fgets(buffer, BUFF_LEN-1, fp)) { buffer[strlen(buffer)-1] = '\0'; + char* token = strtok(buffer, " \t"); + int ntokens = 0; + + while(token) { + if(isEmpty(token)) { + token = strtok(NULL, " \t"); + continue; + } else if(isInt(token)) { + switch(ntokens) { + case 0: burst->burstNum = atol(token); break; + case 1: burst->cpuTime = atol(token); break; + case 2: burst->ioTime = atol(token); break; + default: return false; + } + } else { + return false; + } + ntokens++; + token = strtok(NULL, " \t"); + } + if(ntokens == 2) { + burst->ioTime = 0; + } else if(ntokens != 3) { + return false; + } + } else { + return false; + } + + return true; +} + +static bool readThread(FILE* fp, Thread* thread) { + char buffer[BUFF_LEN]; + Burst* brst = NULL; + Burst* prev = NULL; + + if(!thread) { + return false; + } + + thread->burst = malloc(sizeof(Burst)); + assert(thread->burst); + brst = thread->burst; + + if(fgets(buffer, BUFF_LEN-1, fp)) { buffer[strlen(buffer)-1] = '\0'; + char* token = strtok(buffer, " \t"); + int ntokens = 0; + + while(token) { + if(isEmpty(token)) { + token = strtok(NULL, " \t"); + continue; + } else if(isInt(token)) { + switch(ntokens) { + case 0: thread->threadNum = atol(token); break; + case 1: thread->arriveTime = atol(token); break; + case 2: thread->nbursts = atol(token); break; + default: return false; + } + } else { + return false; + } + ntokens++; + token = strtok(NULL, " \t"); + } + if(ntokens != 3) { + return false; + } + } else { + return false; + } + + for(int i=0; inbursts; i++) { + if(readBurst(fp, brst)) { + brst->next = malloc(sizeof(Burst)); + assert(brst->next); + prev = brst; + brst = brst->next; + } else { + return false; + } + } + if(thread->nbursts == 0) { + free(thread->burst); + thread->burst = NULL; + } else { + free(brst); + prev->next = NULL; + } + + thread->data.serviceTime = 0; + thread->data.ioTime = 0; + thread->data.turnAroundTime = 0; + thread->data.finishTime = 0; + + return true; +} + +static bool readProcess(FILE* fp, Process* process) { + char buffer[BUFF_LEN]; + Thread* thrd = NULL; + Thread* prev = NULL; + + if(!process) { + return false; + } + + process->thread = malloc(sizeof(Thread)); + assert(process->thread); + thrd = process->thread; + + if(fgets(buffer, BUFF_LEN-1, fp)) { buffer[strlen(buffer)-1] = '\0'; + char* token = strtok(buffer, " \t"); + int ntokens = 0; + + while(token) { + if(isEmpty(token)) { + token = strtok(NULL, " \t"); + continue; + } else if(isInt(token)) { + switch(ntokens) { + case 0: process->processNum = atol(token); break; + case 1: process->nthreads = atol(token); break; + default: return false; + } + } else { + return false; + } + ntokens++; + token = strtok(NULL, " \t"); + } + if(ntokens != 2) { + return false; + } + } else { + return false; + } + + for(int i=0; inthreads; i++) { + if(readThread(fp, thrd)) { + thrd->next = malloc(sizeof(Thread)); + assert(thrd->next); + prev = thrd; + thrd = thrd->next; + } else { + return false; + } + } + if(process->nthreads == 0) { + free(process->thread); + process->thread = NULL; + } else { + free(thrd); + prev->next = NULL; + } + + return true; +} + +/* reads input from a stream to create a schedule + * args: the stream to read from + * returns: a schedule with specs from file */ +Schedule readSchedule(FILE* fp) { + Schedule schedule; + char buffer[BUFF_LEN]; + Process* proc = NULL; + Process* prev = NULL; + + schedule.nprocesses = 0; + schedule.switchSame = 0; + schedule.switchNew = 0; + schedule.process = NULL; + + if(fp == NULL) { + fprintf(stderr, "Unable to read from file.\n"); + exit(EXIT_FAILURE); + } + + schedule.process = malloc(sizeof(Process)); + assert(schedule.process); + proc = schedule.process; + + while(fgets(buffer, BUFF_LEN-1, fp)) { buffer[strlen(buffer)-1] = '\0'; + char* token = strtok(buffer, " \t"); + int ntokens = 0; + + while(token) { + if(isEmpty(token)) { + token = strtok(NULL, " \t"); + continue; + } else if(isInt(token)) { + switch(ntokens) { + case 0: schedule.nprocesses = atol(token); break; + case 1: schedule.switchSame = atol(token); break; + case 2: schedule.switchNew = atol(token); break; + default: + fprintf(stderr, "Unexpected file contents.\n"); + freeSchedule(schedule); + exit(EXIT_FAILURE); + } + } else { + fprintf(stderr, "Unexpected file contents.\n"); + freeSchedule(schedule); + exit(EXIT_FAILURE); + } + ntokens++; + token = strtok(NULL, " \t"); + } + + if(ntokens != 3) { + fprintf(stderr, "Unexpected file contents.\n"); + freeSchedule(schedule); + exit(EXIT_FAILURE); + } + + for(int i=0; inext = malloc(sizeof(Process)); + assert(proc->next); + prev = proc; + proc = proc->next; + } else { + fprintf(stderr, "Unexpected file contents.\n"); + freeSchedule(schedule); + exit(EXIT_FAILURE); + } + } + } + + if(schedule.nprocesses == 0) { + fprintf(stderr, "Unexpected file contents.\n"); + freeSchedule(schedule); + exit(EXIT_FAILURE); + } else { + free(proc); + prev->next = NULL; + } + + return schedule; +} + +static void freeBurst(Burst* burst) { + if(!burst) return; + + freeBurst(burst->next); + free(burst); +} + +static void freeThread(Thread* thread) { + if(!thread) return; + + freeThread(thread->next); + freeBurst(thread->burst); + free(thread); +} + +static void freeProcess(Process* process) { + if(!process) return; + + freeProcess(process->next); + freeThread(process->thread); + free(process); +} + + +/* frees all memory allocated for the schedule + * args: the schedule to deallocate */ +void freeSchedule(Schedule schedule) { + freeProcess(schedule.process); +} diff --git a/schedule.h b/schedule.h new file mode 100644 index 0000000..16928a9 --- /dev/null +++ b/schedule.h @@ -0,0 +1,132 @@ +#ifndef __SCHEDULE_H_ +#define __SCHEDULE_H_ + +#include "mode.h" +#include +#include +#include +#include +#include + +#define BUFF_LEN 1024 + +/* + +Schedule schedule: + + int nprocesses + int switchSame + int switchNew + Process* process: + + int processNum + int nthreads + Process* next + Thread* thread: + + int threadNum + int arriveTime + int nbursts + Thread* next + Burst* burst: + + int burstNum + int cpuTime + int ioTime + Burst* next + + Data data: + + int serviceTime + int ioTime + int turnAroundTime + int finishTime + +Queue queue: + + int size + Event* event: + + int ofProcess + int ready + Thread* thread: + + int threadNum + int arriveTime + int nbursts + Thread* next + Burst* burst: + + int burstNum + int cpuTime + int ioTime + Burst* next + + Data data: + + int serviceTime + int ioTime + int turnAroundTime + int finishTime + + +*/ + +typedef struct Data { + int serviceTime; + int ioTime; + int turnAroundTime; + int finishTime; +} Data; + +typedef struct Burst { + int burstNum; + int cpuTime; + int ioTime; + struct Burst* next; +} Burst; + +typedef struct Thread { + int threadNum; + int arriveTime; + int nbursts; + Burst* burst; + Data data; + struct Thread* next; +} Thread; + +typedef struct Process { + int processNum; + int nthreads; + Thread* thread; + struct Process* next; +} Process; + +typedef struct Schedule { + int nprocesses; + int switchSame; + int switchNew; + Process* process; +} Schedule; + +typedef struct Event { + Thread* thread; + int ofProcess; + int ready; +} Event; + +typedef struct Queue { + int size; + Event* event; +} Queue; + +/* reads input from a stream to create a schedule + * args: the stream to read from + * returns: a schedule with specs from file */ +Schedule readSchedule(FILE* fp); + +/* frees all memory allocated for the schedule + * args: the schedule to deallocate */ +void freeSchedule(Schedule schedule); + +#endif diff --git a/sim2.txt b/sim2.txt new file mode 100644 index 0000000..d23c17d --- /dev/null +++ b/sim2.txt @@ -0,0 +1,15 @@ +3 3 7 +1 2 +1 0 2 +1 10 20 +2 10 +2 5 2 +1 50 10 +2 50 +2 1 +1 0 1 +1 100 +3 1 +1 25 2 +1 100 20 +2 100 diff --git a/simulation.c b/simulation.c new file mode 100644 index 0000000..0f95da3 --- /dev/null +++ b/simulation.c @@ -0,0 +1,219 @@ +#include "simulation.h" + +/* compare function for qsort. sort queue on arrival times */ +static int compareEvent(const void* event1, const void* event2) { + Event e1 = *(Event*)event1; + Event e2 = *(Event*)event2; + + return (e1.ready > e2.ready) - (e1.ready < e2.ready); +} + +/* create an initial queue from a fresh schedule */ +static Queue newQueue(Schedule schedule) { + Queue queue = {.size = 0, .event = NULL}; + Process* proc = schedule.process; + + for(int i=0; ithread; + + for(int j=0; jnthreads; j++) { + queue.event = realloc(queue.event, (queue.size+1)*sizeof(Event)); + assert(queue.event); + queue.event[queue.size].thread = thread; + queue.event[queue.size].ofProcess = proc->processNum; + queue.event[queue.size].ready = thread->arriveTime; + queue.size++; + + thread = thread->next; + } + proc = proc->next; + } + qsort(queue.event, queue.size, sizeof(Event), compareEvent); + return queue; +} + +/* Simulates a cpu thread scheduler for rr or fcfs. + * args: the schedule to simulate, and the mode in which to simulate it */ +void simulate(Schedule schedule, Mode mode) { + Queue queue = newQueue(schedule); + Process* proc = schedule.process; + char* verbose = malloc(1); verbose[0] = '\0'; + int idle = 0; + int cpuTime = 0; + int timeSlice = 0; + int turnAround = 0; + int totalTime = 0; + int nthreads = 0; + + if(mode.roundRobin) { + printf("Round Robin Scheduling (quantum = %d time units)\n", mode.quantum); + } else { + printf("First Come First Serve Scheduling\n"); + } + + for(int i=0; ithreadNum, queue.event[i].ofProcess); + verbose = realloc(verbose, strlen(verbose)+strlen(buff)+1); + assert(verbose); + strcat(verbose, buff); + } + + if(i > 0) { // context switch + if(queue.event[i].ofProcess == queue.event[i-1].ofProcess) { + if(queue.event[i].thread->threadNum == queue.event[i-1].thread->threadNum) { + //no overhead + } else { + timeSlice += schedule.switchSame; + idle += schedule.switchSame; + } + } else { + timeSlice += schedule.switchNew; + idle += schedule.switchNew; + } + } + + if(timeSlice <= queue.event[i].ready) { // nothing waiting for the cpu + idle += queue.event[i].ready - timeSlice; + timeSlice = queue.event[i].ready; // advance the timer + } + + if(mode.verbose) { + char buff[BUFF_LEN]; + + sprintf(buff, "At time %d: Thread %d of Process %d moves from ready to running\n", + timeSlice, queue.event[i].thread->threadNum, queue.event[i].ofProcess); + verbose = realloc(verbose, strlen(verbose)+strlen(buff)+1); + assert(verbose); + strcat(verbose, buff); + } + + // run threads in queue's burst. then kick it out for io or quantum + if(mode.roundRobin) { + if(queue.event[i].thread->burst->cpuTime > mode.quantum) { + timeSlice += mode.quantum; + cpuTime += mode.quantum; + queue.event[i].thread->burst->cpuTime -= mode.quantum; + queue.event[i].thread->data.serviceTime += mode.quantum; + } else { + timeSlice += queue.event[i].thread->burst->cpuTime; + cpuTime += queue.event[i].thread->burst->cpuTime; + queue.event[i].thread->data.serviceTime += queue.event[i].thread->burst->cpuTime; + queue.event[i].thread->data.ioTime += queue.event[i].thread->burst->ioTime; + queue.event[i].thread->burst->cpuTime = 0; + } + } else { + timeSlice += queue.event[i].thread->burst->cpuTime; + cpuTime += queue.event[i].thread->burst->cpuTime; + queue.event[i].thread->data.ioTime += queue.event[i].thread->burst->ioTime; + queue.event[i].thread->data.serviceTime += queue.event[i].thread->burst->cpuTime; + } + queue.event[i].thread->data.turnAroundTime = timeSlice - queue.event[i].thread->arriveTime; + queue.event[i].thread->data.finishTime = timeSlice; + + if(mode.roundRobin && queue.event[i].thread->burst->cpuTime > 0) { + queue.event = realloc(queue.event, (queue.size+1)*sizeof(Event)); + assert(queue.event); + queue.event[queue.size] = queue.event[i]; + queue.event[queue.size].ready = timeSlice; + queue.size++; + qsort(queue.event, queue.size, sizeof(Event), compareEvent); + + if(mode.verbose) { + char buff[BUFF_LEN]; + + sprintf(buff, "At time %d: Thread %d of Process %d moves from running to blocked\n", + timeSlice, queue.event[i].thread->threadNum, queue.event[i].ofProcess); + verbose = realloc(verbose, strlen(verbose)+strlen(buff)+1); + assert(verbose); + strcat(verbose, buff); + } + continue; + } + + // place unfinished threads back into the queue with their apropriate ready time + if(queue.event[i].thread->burst->next) { + Burst* temp = queue.event[i].thread->burst; + + if(mode.verbose) { + char buff[BUFF_LEN]; + + sprintf(buff, "At time %d: Thread %d of Process %d moves from running to blocked\n", + timeSlice, queue.event[i].thread->threadNum, queue.event[i].ofProcess); + verbose = realloc(verbose, strlen(verbose)+strlen(buff)+1); + assert(verbose); + strcat(verbose, buff); + } + queue.event = realloc(queue.event, (queue.size+1)*sizeof(Event)); + assert(queue.event); + queue.event[queue.size] = queue.event[i]; + queue.event[queue.size].thread->burst = queue.event[queue.size].thread->burst->next; + queue.event[queue.size].ready = timeSlice + queue.event[i].thread->burst->ioTime; + queue.size++; + qsort(queue.event, queue.size, sizeof(Event), compareEvent); + free(temp); + } else if(mode.verbose) { + char buff[BUFF_LEN]; + + sprintf(buff, "At time %d: Thread %d of Process %d moves from running to terminated\n", + timeSlice, queue.event[i].thread->threadNum, queue.event[i].ofProcess); + verbose = realloc(verbose, strlen(verbose)+strlen(buff)+1); + assert(verbose); + strcat(verbose, buff); + } + } + + // simulation is over, begin calculating and displaying stats + + totalTime = timeSlice; + printf("Total Time required is %d time units.\n", totalTime); + + for(int i=0; ithread; + + for(int j=0; jnthreads; j++) { + turnAround += thread->data.turnAroundTime; + thread = thread->next; + nthreads++; + } + proc = proc->next; + } + + printf("Average Turnaround Time is %d time units.\n", turnAround/nthreads); + printf("CPU Utilization is %.1f%%.\n", ((float)cpuTime/(float)totalTime)*100); + + if(mode.detailed) { + proc = schedule.process; + + printf("\n"); + + for(int i=0; ithread; + + for(int j=0; jnthreads; j++) { + printf("Thread %d of Process %d:\n", thread->threadNum, proc->processNum); + printf(" arrival time: %d\n", thread->arriveTime); + printf(" service time: %d units, I/O time: %d units, turnaround time: %d units\n" + , thread->data.serviceTime, thread->data.ioTime, thread->data.turnAroundTime); + printf(" finish time: %d\n", thread->data.finishTime); + thread = thread->next; + } + proc = proc->next; + } + } + + if(mode.verbose) { + printf("\n%s", verbose); + } + + free(verbose); + free(queue.event); +} diff --git a/simulation.h b/simulation.h new file mode 100644 index 0000000..ab0d56d --- /dev/null +++ b/simulation.h @@ -0,0 +1,11 @@ +#ifndef _SIMULATION_H_ +#define _SIMULATION_H_ + +#include "schedule.h" +#include "mode.h" + +/* Simulates a cpu thread scheduler for rr or fcfs. + * args: the schedule to simulate, and the mode in which to simulate it */ +void simulate(Schedule schedule, Mode mode); + +#endif