commit 29775b66b538979570c7064e8858de335a8acf41 Author: bgraansm Date: Sat Aug 26 08:47:15 2017 -0400 first commit 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