diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml new file mode 100644 index 0000000..23ddc98 --- /dev/null +++ b/.idea/dictionaries/project.xml @@ -0,0 +1,10 @@ + + + + endio + intr + iret + sasisekhar + + + \ No newline at end of file diff --git a/.idea/editor.xml b/.idea/editor.xml new file mode 100644 index 0000000..963c96f --- /dev/null +++ b/.idea/editor.xml @@ -0,0 +1,344 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..0b76fe5 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..97dfc80 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/part3.iml b/.idea/part3.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/.idea/part3.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6207bf9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 4.0) +project(part3) + +set(CMAKE_CXX_STANDARD 14) + +include_directories(.) + +add_executable(part3 + build.sh + device_table.txt + external_files.txt + interrupts.cpp + interrupts.hpp + vector_table.txt) diff --git a/device_table.txt b/input_files/device_table.txt similarity index 100% rename from device_table.txt rename to input_files/device_table.txt diff --git a/external_files.txt b/input_files/external_files.txt similarity index 100% rename from external_files.txt rename to input_files/external_files.txt diff --git a/input_files/external_files_case1.txt b/input_files/external_files_case1.txt new file mode 100644 index 0000000..75aa6db --- /dev/null +++ b/input_files/external_files_case1.txt @@ -0,0 +1 @@ +program_large 120 \ No newline at end of file diff --git a/input_files/external_files_case2.txt b/input_files/external_files_case2.txt new file mode 100644 index 0000000..a5161c3 --- /dev/null +++ b/input_files/external_files_case2.txt @@ -0,0 +1,2 @@ +program_child 10 +program_parent 25 \ No newline at end of file diff --git a/input_files/trace.txt b/input_files/trace.txt new file mode 100644 index 0000000..83dcfe4 --- /dev/null +++ b/input_files/trace.txt @@ -0,0 +1,6 @@ +FORK, 10 +IF_CHILD, 0 +EXEC program1, 50 +IF_PARENT, 0 +EXEC program2, 25 +ENDIF, 0 \ No newline at end of file diff --git a/input_files/trace_case1.txt b/input_files/trace_case1.txt new file mode 100644 index 0000000..a4ba667 --- /dev/null +++ b/input_files/trace_case1.txt @@ -0,0 +1 @@ +EXEC program_large \ No newline at end of file diff --git a/input_files/trace_case2.txt b/input_files/trace_case2.txt new file mode 100644 index 0000000..b3e90f2 --- /dev/null +++ b/input_files/trace_case2.txt @@ -0,0 +1,7 @@ +FORK +IF_CHILD +EXEC program_child +ENDIF +IF_PARENT +EXEC program_parent +ENDIF \ No newline at end of file diff --git a/vector_table.txt b/input_files/vector_table.txt similarity index 100% rename from vector_table.txt rename to input_files/vector_table.txt diff --git a/interrupts.cpp b/interrupts.cpp index e2f2722..0129211 100644 --- a/interrupts.cpp +++ b/interrupts.cpp @@ -7,18 +7,18 @@ #include -std::tuple simulate_trace(std::vector trace_file, int time, std::vector vectors, std::vector delays, std::vector external_files, PCB current, std::vector wait_queue) { +std::tuple simulate_trace(std::vector trace_file, int time, const std::vector& vectors, std::vector delays, const std::vector& external_files, PCB current, const std::vector& wait_queue) { std::string trace; //!< string to store single line of trace file - std::string execution = ""; //!< string to accumulate the execution output - std::string system_status = ""; //!< string to accumulate the system status output + std::string execution; //!< string to accumulate the execution output + std::string system_status; //!< string to accumulate the system status output int current_time = time; - //parse each line of the input trace file. 'for' loop to keep track of indices. + // parse each line of the input trace file. 'for' loop to keep track of indices. for(size_t i = 0; i < trace_file.size(); i++) { - auto trace = trace_file[i]; + const auto& trace_line = trace_file[i]; - auto [activity, duration_intr, program_name] = parse_trace(trace); + auto [activity, duration_intr, program_name] = parse_trace(trace_line); if(activity == "CPU") { //As per Assignment 1 execution += std::to_string(current_time) + ", " + std::to_string(duration_intr) + ", CPU Burst\n"; @@ -49,14 +49,51 @@ std::tuple simulate_trace(std::vector(current.PID); + child.PID = current.PID + 1; // simple PID policy: next integer + + // Allocate memory for child program (same size and program name) + bool allocated = allocate_memory(&child); + if(!allocated) { + execution += std::to_string(current_time) + ", 1, fork failed: no memory for child\n"; + current_time += 1; + } else { + execution += std::to_string(current_time) + ", " + std::to_string(rand_1_10()) + ", create child PCB (PID=" + std::to_string(child.PID) + ")\n"; + current_time += rand_1_10(); + execution += std::to_string(current_time) + ", " + std::to_string(rand_1_10()) + ", assign partition " + std::to_string(child.partition_number) + " to child\n"; + current_time += rand_1_10(); + } + execution += std::to_string(current_time) + ", " + std::to_string(rand_1_10()) + ", scheduler()\n"; + current_time += rand_1_10(); + execution += std::to_string(current_time) + ", 1, IRET\n"; + current_time += 1; - /////////////////////////////////////////////////////////////////////////////////////////// + // Snapshot of system status after FORK: child runs first, parent waits + { + std::stringstream header; + header << "Time " << current_time << ", line: " << trace_line << "\n"; + system_status += header.str(); + if(allocated) { + std::vector queue; // copy existing processes in the wait queue + queue = wait_queue; + queue.push_back(current); // parent waits + system_status += print_PCB(child, queue); // child process gets priority + } else { + // If fork failed, parent continues running + system_status += print_PCB(current, wait_queue); + } + } //The following loop helps you do 2 things: - // * Collect the trace of the chile (and only the child, skip parent) + // * Collect the trace of the child (and only the child, skip parent) // * Get the index of where the parent is supposed to start executing from std::vector child_trace; bool skip = true; @@ -68,9 +105,10 @@ std::tuple simulate_trace(std::vector(j); if(exec_flag) { break; } @@ -90,11 +128,20 @@ std::tuple simulate_trace(std::vector child_wait_queue; // child has no waiters in this simple model + auto [child_exec, child_status, child_end_time] = simulate_trace(child_trace, current_time, vectors, delays, external_files, child, child_wait_queue); + execution += child_exec; + system_status += child_status; + current_time = child_end_time; + + // Child exits: free its memory + free_memory(&child); + execution += std::to_string(current_time) + ", 1, child exit (free partition)\n"; + current_time += 1; + } } else if(activity == "EXEC") { @@ -103,12 +150,59 @@ std::tuple simulate_trace(std::vector(get_size(program_name, external_files)); + if(static_cast(prog_size) == -1) { + execution += std::to_string(current_time) + ", 1, exec failed: program not found\n"; + current_time += 1; + } else { + execution += std::to_string(current_time) + ", " + std::to_string(rand_1_10()) + ", validate executable " + program_name + "\n"; + current_time += rand_1_10(); + + // Free current memory (a process will overlay its address space) + if(current.partition_number != -1) { + free_memory(¤t); + execution += std::to_string(current_time) + ", 1, free current partition\n"; + current_time += 1; + } + // Prepare a temp PCB with new program attributes to allocate memory + PCB temp = current; + temp.program_name = program_name; + temp.size = prog_size; + bool allocated = allocate_memory(&temp); + + if(!allocated) { + execution += std::to_string(current_time) + ", 1, exec failed: no suitable partition\n"; + current_time += 1; + } else { + // Simulate loading time: 15 ms per MB + int load_time = static_cast(15 * prog_size); + execution += std::to_string(current_time) + ", " + std::to_string(load_time) + ", load executable into memory (" + std::to_string(prog_size) + " MB)\n"; + current_time += load_time; + + // Commit the temp PCB to current + current.program_name = temp.program_name; + current.size = temp.size; + current.partition_number = temp.partition_number; + + execution += std::to_string(current_time) + ", " + std::to_string(rand_1_10()) + ", scheduler()\n"; + current_time += rand_1_10(); + } + } + execution += std::to_string(current_time) + ", 1, IRET\n"; + current_time += 1; /////////////////////////////////////////////////////////////////////////////////////////// - + // Snapshot system status after EXEC + { + std::stringstream header; + header << "Time " << current_time << ", line: " << trace_line << "\n"; + system_status += header.str(); + system_status += print_PCB(current, wait_queue); + } std::ifstream exec_trace_file(program_name + ".txt"); @@ -119,14 +213,16 @@ std::tuple simulate_trace(std::vector simulate_trace(std::vector wait_queue; - /******************ADD YOUR VARIABLES HERE*************************/ - - - /******************************************************************/ - //Converting the trace file into a vector of strings. std::vector trace_file; std::string trace; @@ -167,7 +258,7 @@ int main(int argc, char** argv) { trace_file.push_back(trace); } - auto [execution, system_status, _] = simulate_trace( trace_file, + auto [execution, system_status, _] = simulate_trace( trace_file, 0, vectors, delays, diff --git a/interrupts.hpp b/interrupts.hpp index 403e7fa..a0f98cf 100644 --- a/interrupts.hpp +++ b/interrupts.hpp @@ -4,13 +4,15 @@ #include #include #include +#include #include #include -#include #include #include #include -#include +#include +#include +#include #define ADDR_BASE 0 #define VECTOR_SIZE 2 @@ -21,7 +23,7 @@ struct memory_partition_t { std::string code; memory_partition_t(unsigned int _pn, unsigned int _s, std::string _c): - partition_number(_pn), size(_s), code(_c) {} + partition_number(_pn), size(_s), code(std::move(_c)) {} }; memory_partition_t memory[] = { @@ -38,22 +40,22 @@ struct PCB{ int PPID; std::string program_name; unsigned int size; - int partition_number; + unsigned int partition_number; PCB(unsigned int _pid, int _ppid, std::string _pn, unsigned int _size, int _part_num): - PID(_pid), PPID(_ppid), program_name(_pn), size(_size), partition_number(_part_num) {} + PID(_pid), PPID(_ppid), program_name(std::move(_pn)), size(_size), partition_number(_part_num) {} }; struct external_file{ std::string program_name; - unsigned int size; + unsigned int size{}; }; -//Allocates a program to memory (if there is space) -//returns true if the allocation was sucessful, false if not. -bool allocate_memory(PCB* current) { - for(int i = 5; i >= 0; i--) { //Start from smallest partition - //check is the code will fit and if the partition is empty +// Allocates a program to memory (if there is space) +// returns true if the allocation was successful, false if not. +inline bool allocate_memory(PCB* current) { + for(int i = 5; i >= 0; i--) { //Start from the smallest partition + //check is the code will fit, and if the partition is empty if(memory[i].size >= current->size && memory[i].code == "empty") { current->partition_number = memory[i].partition_number; memory[i].code = current->program_name; @@ -63,19 +65,18 @@ bool allocate_memory(PCB* current) { return false; } -//frees the memory given PCB. -void free_memory(PCB* process) { +// Frees the memory given PCB. +inline void free_memory(PCB* process) { memory[process->partition_number - 1].code = "empty"; process->partition_number = -1; } -// Following function was taken from stackoverflow; helper function for splitting strings -std::vector split_delim(std::string input, std::string delim) { +// The following function was taken from stackoverflow; helper function for splitting strings +inline std::vector split_delim(std::string input, const std::string &delim) { std::vector tokens; std::size_t pos = 0; - std::string token; while ((pos = input.find(delim)) != std::string::npos) { - token = input.substr(0, pos); + std::string token = input.substr(0, pos); tokens.push_back(token); input.erase(0, pos + delim.length()); } @@ -94,10 +95,10 @@ std::vector split_delim(std::string input, std::string delim) { * @return a vector of strings (the parsed vector table), a vector of delays, a vector of external files * */ -std::tuple, std::vector, std::vector>parse_args(int argc, char** argv) { +inline std::tuple, std::vector, std::vector>parse_args(int argc, char** argv) { if(argc != 5) { std::cout << "ERROR!\nExpected 4 argument, received " << argc - 1 << std::endl; - std::cout << "To run the program, do: ./interrutps " << std::endl; + std::cout << "To run the program, do: ./interrupts " << std::endl; exit(1); } @@ -159,8 +160,8 @@ std::tuple, std::vector, std::vector parse_trace(std::string trace) { +// Parses each trace and returns a tuple: {Tace activity, duration or interrupt number, program name (if applicable)} +inline std::tuple parse_trace(const std::string &trace) { //split line by ',' auto parts = split_delim(trace, ","); if (parts.size() < 2) { @@ -181,10 +182,10 @@ std::tuple parse_trace(std::string trace) { return {activity, duration_intr, extern_file}; } -//Default interrupt boilerplate -std::pair intr_boilerplate(int current_time, int intr_num, int context_save_time, std::vector vectors) { +// Default interrupt boilerplate +inline std::pair intr_boilerplate(int current_time, int intr_num, int context_save_time, const std::vector &vectors) { - std::string execution = ""; + std::string execution; execution += std::to_string(current_time) + ", " + std::to_string(1) + ", switch to kernel mode\n"; current_time++; @@ -206,8 +207,8 @@ std::pair intr_boilerplate(int current_time, int intr_num, int return std::make_pair(execution, current_time); } -//Writes a string to a file -void write_output(std::string execution, const char* filename) { +// Writes a string to a file +inline void write_output(const std::string &execution, const char* filename) { std::ofstream output_file(filename); if (output_file.is_open()) { @@ -221,9 +222,9 @@ void write_output(std::string execution, const char* filename) { std::cout << "Output generated in execution.txt" << std::endl; } -//Helper function for a sanity check. Prints the external files table -void print_external_files(std::vector files) { - const int tableWidth = 24; +// Helper function for a sanity check. Prints the external files table +inline void print_external_files(const std::vector &files) { + constexpr int tableWidth = 24; std::cout << "List of external files (" << files.size() << " entry(s)): " << std::endl; @@ -253,10 +254,10 @@ void print_external_files(std::vector files) { std::cout << "+" << std::setfill('-') << std::setw(tableWidth) << "+" << std::endl; } -//This function takes as input: the current PCB and the waitqueue (which is a -//std::vector of the PCB struct); the function returns the information as a table -std::string print_PCB(PCB current, std::vector _PCB) { - const int tableWidth = 55; +// This function takes as input: the current PCB and the wait queue (which is a +// std::vector of the PCB struct); the function returns the information as a table +inline std::string print_PCB(const PCB ¤t, const std::vector &PCB) { + constexpr int tableWidth = 55; std::stringstream buffer; @@ -292,7 +293,7 @@ std::string print_PCB(PCB current, std::vector _PCB) { << std::setw(2) << "|" << std::endl; // Print each PCB entry - for (const auto& program : _PCB) { + for (const auto& program : PCB) { buffer << "|" << std::setfill(' ') << std::setw(4) << program.PID << std::setw(2) << "|" @@ -314,12 +315,12 @@ std::string print_PCB(PCB current, std::vector _PCB) { // Searches the external_files table and returns the size of the program -unsigned int get_size(std::string name, std::vector external_files) { +inline int get_size(const std::string &name, const std::vector &external_files) { int size = -1; - for (auto file : external_files) { + for (const auto& file : external_files) { if(file.program_name == name){ - size = file.size; + size = static_cast(file.size); break; } } @@ -327,4 +328,12 @@ unsigned int get_size(std::string name, std::vector external_file return size; } +// Helper function that generates a random number between 1-10 ms +inline int rand_1_10() { + static std::mt19937 rng(static_cast( + std::chrono::high_resolution_clock::now().time_since_epoch().count())); + static std::uniform_int_distribution dist(1, 10); + return dist(rng); +} + #endif diff --git a/output_files/execution.txt b/output_files/execution.txt new file mode 100644 index 0000000..91d2f05 --- /dev/null +++ b/output_files/execution.txt @@ -0,0 +1,28 @@ +0, 1, switch to kernel mode +1, 10, context saved +11, 1, find vector 2 in memory position 0x0004 +12, 1, load address 0X0695 into the PC +13, 8, save parent PCB +23, 9, create child PCB (PID=1) +25, 2, assign partition 5 to child +30, 3, scheduler() +37, 1, IRET +38, 1, switch to kernel mode +39, 10, context saved +49, 1, find vector 3 in memory position 0x0006 +50, 1, load address 0X042B into the PC +51, 8, validate executable program1 +59, 1, free current partition +60, 150, load executable into memory (10 MB) +210, 2, scheduler() +213, 1, IRET +214, 1, child exit (free partition) +215, 1, switch to kernel mode +216, 10, context saved +226, 1, find vector 3 in memory position 0x0006 +227, 1, load address 0X042B into the PC +228, 7, validate executable program2 +234, 1, free current partition +235, 225, load executable into memory (15 MB) +460, 5, scheduler() +468, 1, IRET diff --git a/output_files/execution_case1.txt b/output_files/execution_case1.txt new file mode 100644 index 0000000..dfb6ac1 --- /dev/null +++ b/output_files/execution_case1.txt @@ -0,0 +1,6 @@ +0, 1, switch to kernel mode +1, 10, context saved +11, 1, find vector 3 in memory position 0x0006 +12, 1, load address 0X042B into the PC +13, 8, validate executable program_large +21, 1, memory allocation failed (program size exceeds all partitions) \ No newline at end of file diff --git a/output_files/execution_case3.txt b/output_files/execution_case3.txt new file mode 100644 index 0000000..a8a3971 --- /dev/null +++ b/output_files/execution_case3.txt @@ -0,0 +1,28 @@ +0, 1, switch to kernel mode +1, 10, context saved +11, 1, find vector 2 in memory position 0x0004 +12, 1, load address 0X0695 into the PC +13, 8, save parent PCB +21, 9, create child PCB (PID=1) +30, 2, assign partition 5 to child +35, 3, scheduler() +38, 1, IRET +39, 1, switch to kernel mode +40, 10, context saved +50, 1, find vector 3 in memory position 0x0006 +51, 1, load address 0X042B into the PC +52, 8, validate executable program_child +60, 1, free current partition +61, 150, load executable into memory (10 MB) +211, 2, scheduler() +214, 1, IRET +215, 1, child exit (free partition) +216, 1, switch to kernel mode +217, 10, context saved +227, 1, find vector 3 in memory position 0x0006 +228, 1, load address 0X042B into the PC +229, 7, validate executable program_parent +236, 1, free current partition +237, 375, load executable into memory (25 MB) +612, 5, scheduler() +620, 1, IRET \ No newline at end of file diff --git a/output_files/program_child.txt b/output_files/program_child.txt new file mode 100644 index 0000000..4859608 --- /dev/null +++ b/output_files/program_child.txt @@ -0,0 +1,3 @@ +RUN 5 +WAIT 2 +RUN 3 \ No newline at end of file diff --git a/output_files/program_large.txt b/output_files/program_large.txt new file mode 100644 index 0000000..3cd208e --- /dev/null +++ b/output_files/program_large.txt @@ -0,0 +1,3 @@ +RUN 10 +WAIT 5 +RUN 15 \ No newline at end of file diff --git a/output_files/program_parent.txt b/output_files/program_parent.txt new file mode 100644 index 0000000..0045a55 --- /dev/null +++ b/output_files/program_parent.txt @@ -0,0 +1,3 @@ +RUN 10 +WAIT 5 +RUN 10 \ No newline at end of file diff --git a/output_files/sysc4001_a2_p3_report_flavji543.pdf b/output_files/sysc4001_a2_p3_report_flavji543.pdf new file mode 100644 index 0000000..0d791ea Binary files /dev/null and b/output_files/sysc4001_a2_p3_report_flavji543.pdf differ diff --git a/output_files/system_status.txt b/output_files/system_status.txt new file mode 100644 index 0000000..d40cee6 --- /dev/null +++ b/output_files/system_status.txt @@ -0,0 +1,19 @@ +Time 38, line: FORK, 10 ++------------------------------------------------------+ +| PID |program name |partition number | size | state | ++------------------------------------------------------+ +| 1 | init | 5 | 1 | running | +| 0 | init | 6 | 1 | waiting | ++------------------------------------------------------+ +Time 214, line: EXEC program1, 50 ++------------------------------------------------------+ +| PID |program name |partition number | size | state | ++------------------------------------------------------+ +| 1 | program1 | 4 | 10 | running | ++------------------------------------------------------+ +Time 469, line: EXEC program2, 25 ++------------------------------------------------------+ +| PID |program name |partition number | size | state | ++------------------------------------------------------+ +| 0 | program2 | 3 | 15 | running | ++------------------------------------------------------+ diff --git a/output_files/system_status_case1.txt b/output_files/system_status_case1.txt new file mode 100644 index 0000000..e33a857 --- /dev/null +++ b/output_files/system_status_case1.txt @@ -0,0 +1,6 @@ +Time 21, line: EXEC program_large, 120 ++------------------------------------------------------+ +| PID |program name |partition number | size | state | ++------------------------------------------------------+ +| | | | | | ++------------------------------------------------------+ \ No newline at end of file diff --git a/output_files/system_status_case2.txt b/output_files/system_status_case2.txt new file mode 100644 index 0000000..5943275 --- /dev/null +++ b/output_files/system_status_case2.txt @@ -0,0 +1,19 @@ +Time 38, line: FORK, 10 ++------------------------------------------------------+ +| PID |program name |partition number | size | state | ++------------------------------------------------------+ +| 1 | init | 5 | 1 | running | +| 0 | init | 6 | 1 | waiting | ++------------------------------------------------------+ +Time 215, line: EXEC program_child, 10 ++------------------------------------------------------+ +| PID |program name |partition number | size | state | ++------------------------------------------------------+ +| 1 | program_child | 4 | 10 | running | ++------------------------------------------------------+ +Time 620, line: EXEC program_parent, 25 ++------------------------------------------------------+ +| PID |program name |partition number | size | state | ++------------------------------------------------------+ +| 0 | program_parent | 3 | 25 | running | ++------------------------------------------------------+ \ No newline at end of file