#include "simulator.h" /* Name: accessMemory Process: checks that the memory is between the offset and the size. checks that the memory is ??? Function Input/Parameters: source PCB (ProcessControlBlock *), offset (int), size (int) Function Output/Parameters: none Function Output/Returned: boolean to indicate success or failure of accessed memory Device Input/Device: none Device Output/Device: none Dependencies: ??? */ bool accessMemory(ProcessControlBlock *singlePCB, int offset, int size) { // return true (temp) return true; } /* Name: addProcess Process: allocates the memory for the pcb, grabs the data from the created PCB, dumps the data so that the node can be linked correctly. Function Input/Parameters: source PCB (ProcessControlBlock) Function Output/Parameters: none Function Output/Returned: filled PCB (ProcessControlBlock *) Device Input/Device: none Device Output/Device: none Dependencies: malloc, sizeof, ??? */ ProcessControlBlock *addProcess(const ProcessControlBlock source) { // declare a pointer to working node ProcessControlBlock *workingNode; // make some space that the pointer is pointing to workingNode = malloc(sizeof(ProcessControlBlock)); // get each value from the source, and set their values workingNode->uniquePid = source.uniquePid; workingNode->cpuCycles = source.cpuCycles; workingNode->ioCycles = source.ioCycles; workingNode->nextOpCode = source.nextOpCode; workingNode->process_state = source.process_state; workingNode->timeLeft = source.timeLeft; workingNode->totalCycles = source.totalCycles; workingNode->allocatedMemOffsets = (int *) malloc(sizeof(int) * source.memArraySize); workingNode->allocatedMemSizes = (int *) malloc(sizeof(int) * source.memArraySize); workingNode->memArraySize = source.memArraySize; // make sure to set the next pointer to NULL! workingNode->nextPtr = NULL; // return workingNode return workingNode; } /* Name: allocMemory Process: 'allocates' memory within the PCB, stores offset and size within it's PCB Function Input/Parameters: source PCB (ProcessControlBlock *), offset (int), size (int) Function Output/Parameters: none Function Output/Returned: boolean to indicate success or failure of allocated memory Device Input/Device: none Device Output/Device: none Dependencies: ??? */ bool allocMemory(ProcessControlBlock *singlePCB, int offset, int size, int arraySize) { int allocateIterator = 0; // while until a '-1' is hit, then store the value. while(singlePCB->allocatedMemSizes[allocateIterator] != -1 && allocateIterator < arraySize) { // singlePCB->allocatedMemOffsets[allocateIterator] = offset; singlePCB->allocatedMemSizes[allocateIterator] = size; allocateIterator = allocateIterator + 1; // printf("Alloc Iter: %d\n", allocateIterator); } // return true (temp) return true; } /* Name: createProcessList Process: reads through the opcode struct to fill out the linked list of PCBs Function Input/Parameters: none Function Output/Parameters: none Function Output/Returned: head pointer to the PCBs Device Input/Device: none Device Output/Device: none Dependencies: malloc, sizeof, ??? */ ProcessControlBlockLL *createProcessList() { // allocate and create the ProcessControlBlockLL using malloc and size of ProcessControlBlockLL *headBlock; headBlock = malloc(sizeof(ProcessControlBlockLL)); // set the head pointer to NULL headBlock->headPtr = NULL; // return headBlock return headBlock; } /* Name: deallocMemory Process: 'deallocates' memory within the PCB, removes every offset and size within it's PCB Function Input/Parameters: source PCB (ProcessControlBlock *) Function Output/Parameters: none Function Output/Returned: boolean to indicate success or failure of allocated memory Device Input/Device: none Device Output/Device: none Dependencies: ??? */ bool deallocMemory(ProcessControlBlock *singlePCB, int arraySize) { int deallocateIterator; // while until a '-1' is hit, then store the value. for (deallocateIterator = 0; deallocateIterator < arraySize; deallocateIterator++) { // singlePCB->allocatedMemOffsets[deallocateIterator] = -1; singlePCB->allocatedMemSizes[deallocateIterator] = -1; } // return true (temp) return true; } /* Name: destroyProcessList Process: deallocates memory used for the linked list struct Function Input/Parameters: head pointer to linked list (ProcessControlBlockLL *) Function Output/Parameters: none Function Output/Returned: NULL Device Input/Device: none Device Output/Device: none Dependencies: free, destroyProcessListHelper, ??? */ ProcessControlBlockLL *destroyProcessList(ProcessControlBlockLL *linkedListPtr) { // call the first destroyProcessListHelper destroyProcessListHelper(linkedListPtr->headPtr); // free the rest of the struct free(linkedListPtr); // return null return NULL; } /* Name: destroyProcessListHelper Process: recursively clears the PCB linked list struct, freeing the struct Function Input/Parameters: head pointer to linked list (ProcessControlBlockLL *) Function Output/Parameters: none Function Output/Returned: NULL Device Input/Device: none Device Output/Device: none Dependencies: free, destroyProcessListHelper(recursively), ??? */ ProcessControlBlock *destroyProcessListHelper(ProcessControlBlock *nodePtr) { // check to see if the pointer is NOT null if(nodePtr != NULL) { free(nodePtr->allocatedMemOffsets); free(nodePtr->allocatedMemSizes); // if not null, move to the next pointer (recurse) nodePtr->nextPtr = destroyProcessListHelper(nodePtr->nextPtr); } // free the 'parent' node free(nodePtr); // return null return NULL; } /* Name: fillProcessList Process: iterates through the metadata LL, fills out each process as needed. Function Input/Parameters: metadata (OpCodeType *), headPointer (ProcessControlBlockLL *) Function Output/Parameters: none Function Output/Returned: pointer to filled PCB (ProcessControlBlockLL *) Device Input/Device: none Device Output/Device: none Dependencies: ??? */ ProcessControlBlockLL *fillProcessList(ConfigDataType *configPtr, OpCodeType *metadataPtr, ProcessControlBlockLL *headPointer) { // initialize function/variables // need a working pointer for the metadata OpCodeType *wkgMetaPtr; ProcessControlBlock currentPCB; ProcessControlBlock *wkgPCBPtr; OpCodeType *wkgInsPtr; // need a variable to count how many IO cycles are in the process int ioCycleCount = 0; // need a variable to count how many CPU cycles are in the process int cpuCycleCount = 0; // need a variable to calculate the time left in ms for a process double timeLeftCount = 0.0; // need a variable that fills PIDs, starting at 0 int processId = 0; // need a variable that checks how many allocate statements there are. int memAllocation = 0; // process variables? char *command; char *argument; char *inOutArg; int integerArgumentOne; // unused as of current //int integerArgumentTwo; // set the working pointer to the first part of the metadata pointer wkgMetaPtr = metadataPtr; wkgInsPtr = metadataPtr; // set another working pointer to the first part of the PCB list wkgPCBPtr = headPointer->headPtr; // grab the first command and argument command = wkgMetaPtr->command; argument = wkgMetaPtr->strArg1; inOutArg = wkgMetaPtr->inOutArg; // integer argument two is unused atm //integerArgumentTwo = wkgMetaPtr->intArg3; // loop through the process list, until you hit a sys end. while(wkgMetaPtr != NULL && !(compareString(argument, "end") == EQUIV && compareString(command, "sys") == EQUIV)) { currentPCB.nextOpCode = wkgInsPtr->nextNode; integerArgumentOne = wkgMetaPtr->intArg2; // if app start, if (compareString(command, "app") && compareString(argument, "start")) { // set the unique ID for both PCB and Metadata wkgMetaPtr->pid = processId; currentPCB.uniquePid = processId; // set the process state to PROCESS_STATE_NEW currentPCB.process_state = PROCESS_STATE_NEW; } // count number of io cycles if(compareString(command, "dev") == EQUIV) { integerArgumentOne = wkgMetaPtr->intArg2; if(compareString(inOutArg, "in") == EQUIV) { // add up the IO cycle ioCycleCount = ioCycleCount + integerArgumentOne; } else { // add up the IO cycle ioCycleCount = ioCycleCount + integerArgumentOne; } } // count number of cpu cycles if ((compareString(argument, "process") == EQUIV && compareString(command, "cpu") == EQUIV)) { integerArgumentOne = wkgMetaPtr->intArg2; cpuCycleCount = cpuCycleCount + integerArgumentOne; } // count number of times memory is done, sub checks for how many allocates, accesses, etc. if ((compareString(argument, "allocate") == EQUIV && compareString(command, "mem") == EQUIV)) { // allocate the memory memAllocation = memAllocation + 1; } if ((compareString(argument, "end") == EQUIV && compareString(command, "app") == EQUIV)) { // set the ioCycles from variable currentPCB.ioCycles = ioCycleCount; // set the cpuCycles from variable currentPCB.cpuCycles = cpuCycleCount; // set totalCycles from adding both IO/CPU currentPCB.totalCycles = ioCycleCount + cpuCycleCount; // set number of allocations from variable, then reset. currentPCB.memArraySize = memAllocation; memAllocation = 0; // currentPCB must have the starting instruction from wkgInsPtr wkgInsPtr = wkgMetaPtr; // set the time left from (ioCycles * ioCycleRate) + (cpuCycles * procCyclesRate) // this gets the number of ms in the process. timeLeftCount = (ioCycleCount * configPtr->ioCycleRate) + (cpuCycleCount * configPtr->procCycleRate); currentPCB.timeLeft = timeLeftCount; // increment the PID counter processId = processId + 1; ioCycleCount = 0; cpuCycleCount = 0; timeLeftCount = 0.0; // needs to add nodes to the end and all data once parsed // do both the metadata and PCBs if(headPointer->headPtr == NULL) { headPointer->headPtr = addProcess(currentPCB); wkgPCBPtr = headPointer->headPtr; } else { wkgPCBPtr->nextPtr = addProcess(currentPCB); wkgPCBPtr = wkgPCBPtr->nextPtr; } } // move to the next bit of data wkgMetaPtr = wkgMetaPtr->nextNode; command = wkgMetaPtr->command; argument = wkgMetaPtr->strArg1; inOutArg = wkgMetaPtr->inOutArg; } // return headPointer return headPointer; } /* Name: findMin Process: compares two values, returns the smaller of the two. Function Input/Parameters: two integer values (int) Function Output/Parameters: none Function Output/Returned: minimum of the two input integer values (int) Device Input/Device: none Device Output/Device: none Dependencies: none */ int findMin(int one, int another) { int minimumVal = one; if(another < minimumVal) { minimumVal = another; } // return zero(temp) return minimumVal; } /* Name: interruptManager Process: does the managing of interrupts for the program Function Input/Parameters: ??? Function Output/Parameters: none Function Output/Returned: ??? Device Input/Device: none Device Output/Device: none Dependencies: none */ bool interruptManager(ConfigDataType *configPtr, ProcessControlBlock *singlePCB, INTERRUPT_ROUTINE interruptRequest) { // need to check which interrupt service routine that is selected from the list switch (interruptRequest) { case IO_RETURN: // unset the PCB's state from blocked to ready. singlePCB->process_state = PROCESS_STATE_READY; return true; break; case QUANTUM_TIMEOUT: // set something? // this may need to stop the current process from running, and force a new one... not sure. return true; break; case CPU_SWITCH: // needs to switch to the next process, schedule to the next process? return true; break; // case FOUR: // /* code */ // break; case CLEAR_INTERRUPT: // COMMENT HERE return false; break; } // return false; } /* Name: logOutput Process: Takes in a string, and then outputs it to monitor AND/OR file Function Input/Parameters: string (char *), file pointer (FILE *), configuration data (ConfigDataType *), end of simulator (bool) Function Output/Parameters: none Function Output/Returned: none Device Input/Device: none Device Output/Device: none Dependencies: fprintf, ??? */ void logOutput(ConfigDataType *configPtr, char *logStr, bool isEnd) { if(configPtr->logToCode == LOGTO_BOTH_CODE) { // FILE *fileWritePtr; fileWritePtr = fopen(configPtr->logToFileName, "w+"); if (fileWritePtr != NULL) { fprintf(fileWritePtr, "%s", logStr); fprintf(stdout, "%s", logStr); } // no return, void // close the file, if at the end. if (isEnd) { fclose(fileWritePtr); } // fclose(fileWritePtr); } else if(configPtr->logToCode == LOGTO_FILE_CODE) { // printf("\nSimulator will output to a file.\n"); FILE *fileWritePtr; fileWritePtr = fopen(configPtr->logToFileName, "w+"); if (fileWritePtr != NULL) { fprintf(fileWritePtr, "%s", logStr); } // no return, void // close the file, if at the end. if (isEnd) { fclose(fileWritePtr); } // fclose(fileWritePtr); } else { fprintf(stdout, "%s", logStr); } } /* Name: memManager Process: checks to make sure an allocate will be successful, makes sure that an access will not segfault (or return segfault if it does), and initializes memory and deallocates memory Function Input/Parameters: PCBList (ProcessControlBlockLL *), memory code (MEMORY_SIGNALS), offset (int), size (int), configuration data (ConfigDataType *) Function Output/Parameters: none Function Output/Returned: boolean for success or failure of memory management Device Input/Device: none Device Output/Device: none Dependencies: ??? */ bool memManager(ProcessControlBlockLL *pcbList, MEMORY_SIGNALS memCode, int offset, int size, ConfigDataType *configPtr, ProcessControlBlock *singlePCB) { bool memDisplay = configPtr->memDisplay; ProcessControlBlock *wkgPtr; int availableMem = configPtr->memAvailable; int usedMem = 0; int currentOffset = 0; int currentSize = 0; int memIterator; int *memorySizeArray; int totalArraySize = 0; bool subFunctionSuccess = true; wkgPtr = pcbList->headPtr; // grab how much memory is being used total, then count how big the total memory array should be while(wkgPtr != NULL) { // count up the total used memory within each process control block. for(memIterator = 0; memIterator < wkgPtr->memArraySize; memIterator++) { if(wkgPtr->allocatedMemSizes[memIterator] != -1) { // grab how much memory is used by that process usedMem = usedMem + wkgPtr->allocatedMemSizes[memIterator]; } totalArraySize = totalArraySize + 1; } // move to the next pointer wkgPtr = wkgPtr->nextPtr; } // reset the working pointer wkgPtr = pcbList->headPtr; // declare the array for the memory memorySizeArray = malloc(sizeof(int) * totalArraySize); // grab the entire memory structure, so that the memory can be contiguous while (wkgPtr != NULL) { // count up the total used memory within each process control block. for (memIterator = 0; memIterator < totalArraySize; memIterator++) { // grab how much memory is used by that process memorySizeArray[memIterator] = wkgPtr->allocatedMemSizes[memIterator]; } // move to the next pointer wkgPtr = wkgPtr->nextPtr; } // grab the size of the // while() // { // // // } // switch case for each memory code switch (memCode) { case MEMORY_INITIALIZE: for(memIterator = 0; memIterator < singlePCB->memArraySize; memIterator++) { // initialize the arrays with all '-1' to 'nullify' the memory, make it easier to iterate through later singlePCB->allocatedMemOffsets[memIterator] = -1; singlePCB->allocatedMemSizes[memIterator] = -1; } if(memDisplay) { // printf("--------------------------------------------------\n"); printf("After memory initialization\n"); printf("%d [ Open, P#: x, %d-%d ] %d\n", usedMem, currentOffset, currentSize, availableMem - 1); printf("--------------------------------------------------\n"); } break; case MEMORY_ALLOCATE: // check if the memory allocation can happen before storing it. if(size <= availableMem - usedMem + 1) { // grab the offset and size, store it, using the allocate memory function. subFunctionSuccess = allocMemory(singlePCB, offset, size, singlePCB->memArraySize); usedMem = usedMem + size; } else { // set the subfunction success to false. subFunctionSuccess = false; } // if the function returns true, then it successfully did the operation, display as such. if (memDisplay) { if(subFunctionSuccess) { // printf("--------------------------------------------------\n"); printf("After memory allocation\n"); printf("%d [ Used, P#: %d, %d-%d ] %d\n", offset - offset, singlePCB->uniquePid, offset, offset + size - 1, size - 1); printf("%d [ Open, P#: x, %d-%d ] %d\n", usedMem, currentOffset, currentSize, availableMem - 1); printf("--------------------------------------------------\n"); } else { // printf("--------------------------------------------------\n"); printf("After memory allocation failure\n"); printf("%d [ Open, P#: x, %d-%d ] %d\n", usedMem, currentOffset, currentSize, availableMem - 1); printf("--------------------------------------------------\n"); } } break; case MEMORY_ACCESS: // printf("Used memory at current: %d\n", usedMem); subFunctionSuccess = accessMemory(singlePCB, offset, size); if (memDisplay) { if (subFunctionSuccess) { // printf("--------------------------------------------------\n"); printf("After access success\n"); printf("%d [ Used, P#: %d, %d-%d ] %d\n", offset - offset, singlePCB->uniquePid, offset, offset + size - 1, size - 1); printf("%d [ Open, P#: x, %d-%d ] %d\n", usedMem, currentOffset, currentSize, availableMem - 1); printf("--------------------------------------------------\n"); } else { // printf("--------------------------------------------------\n"); printf("After access failure\n"); printf("%d [ Used, P#: %d, %d-%d ] %d\n", offset - offset, singlePCB->uniquePid, offset, offset + size - 1, size - 1); printf("%d [ Open, P#: x, %d-%d ] %d\n", usedMem, currentOffset, currentSize, availableMem - 1); printf("--------------------------------------------------\n"); } } break; case MEMORY_DEALLOCATE: subFunctionSuccess = deallocMemory(singlePCB, singlePCB->memArraySize); if (memDisplay) { if (subFunctionSuccess) { // THIS SHOULD ALWAYS HAPPEN. printf("--------------------------------------------------\n"); printf("After clear process %d success\n", singlePCB->uniquePid); printf("%d [ Open, P#: x, %d-%d ] %d\n", usedMem - usedMem, currentOffset, currentSize, availableMem - 1); printf("--------------------------------------------------\n"); } else { // THIS SHOULD NEVER HAPPEN. printf("--------------------------------------------------\n"); printf("After clear process %d failure\n", singlePCB->uniquePid); printf("%d [ Open, P#: x, %d-%d ] %d\n", usedMem - usedMem, currentOffset, currentSize, availableMem - 1); printf("--------------------------------------------------\n"); } } break; case MEMORY_CLEAR: if (memDisplay) { // this will happen regardless. Will always be cleaning up memory with dealloc. printf("--------------------------------------------------\n"); printf("After memory clear\n"); printf("No memory configured\n"); printf("--------------------------------------------------\n"); } break; default: break; } // clear out the dynamic array free(memorySizeArray); // return if the subfunction succeeded, if applicable return subFunctionSuccess; } /* Name: runCPU Process: runs the CPU operations for however long it takes Function Input/Parameters: ??? Function Output/Parameters: none Function Output/Returned: none Device Input/Device: none Device Output/Device: none Dependencies: pthreads, sprintf, logOutput, ??? */ void runCPU(char *command, char *argument, int intArg, ConfigDataType *configPtr, int processId, ProcessControlBlock *singlePCB, ConfigDataCodes schedMethod) { double timeDigits; char timeStringy[MAX_STR_LEN]; // char *command = workingMeta->command; // char *argument = workingMeta->strArg1; // int intArg = workingMeta->intArg2; char cpuOutput[MAX_STR_LEN * 4]; int cpuTime = 0; int *cpuTimePtr = &cpuTime; pthread_t cpuThread; switch (schedMethod) { case NON_PREEMPTIVE_CODE: cpuTime = intArg * configPtr->procCycleRate; timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(cpuOutput, "%f, Process: %d, %s %s operation start\n", timeDigits, processId, command, argument); logOutput(configPtr, cpuOutput, false); // run timer for intArg * procCycleRate pthread_create(&cpuThread, NULL, runTimer, (void *)cpuTimePtr); pthread_join(cpuThread, NULL); timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(cpuOutput, "%f, Process: %d, %s %s operation end\n", timeDigits, processId, command, argument); logOutput(configPtr, cpuOutput, false); // pthread_exit(NULL); break; case PREEMPTIVE_CODE: cpuTime = intArg * configPtr->procCycleRate; timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(cpuOutput, "%f, Process: %d, %s %s operation start\n", timeDigits, processId, command, argument); logOutput(configPtr, cpuOutput, false); // run timer for intArg * procCycleRate pthread_create(&cpuThread, NULL, runTimer, (void *)cpuTimePtr); pthread_join(cpuThread, NULL); timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(cpuOutput, "%f, Process: %d, %s %s operation end\n", timeDigits, processId, command, argument); logOutput(configPtr, cpuOutput, false); break; default: // this should never happen... if it does, you'll know. break; } } /* Name: runIO Process: runs the IO operations for however long it takes Function Input/Parameters: ??? Function Output/Parameters: none Function Output/Returned: none Device Input/Device: none Device Output/Device: none Dependencies: pthreads, sprintf, logOutput, ??? */ void runIO(char *command, char *argument, int intArg, ConfigDataType *configPtr, int processId, ProcessControlBlock *singlePCB, ConfigDataCodes schedMethod) { double timeDigits; char timeStringy[MAX_STR_LEN]; char ioOutput[MAX_STR_LEN * 4]; int ioTime = 0; int *ioTimePtr = &ioTime; pthread_t ioThread; switch (schedMethod) { case NON_PREEMPTIVE_CODE: ioTime = intArg * configPtr->ioCycleRate; timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(ioOutput, "%f, Process: %d, %s %s operation start\n", timeDigits, processId, command, argument); logOutput(configPtr, ioOutput, false); pthread_create(&ioThread, NULL, runTimer, (void *)ioTimePtr); pthread_join(ioThread, NULL); timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(ioOutput, "%f, Process: %d, %s %s operation end\n", timeDigits, processId, command, argument); logOutput(configPtr, ioOutput, false); break; case PREEMPTIVE_CODE: // need to set this to 'blocked' state here. singlePCB->process_state = PROCESS_STATE_BLOCKED; ioTime = intArg * configPtr->ioCycleRate; timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(ioOutput, "%f, Process: %d, %s %s operation start\n", timeDigits, processId, command, argument); logOutput(configPtr, ioOutput, false); pthread_create(&ioThread, NULL, runTimer, (void *)ioTimePtr); timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(ioOutput, "%f, Process: %d, %s %s operation end\n", timeDigits, processId, command, argument); logOutput(configPtr, ioOutput, false); // pthread_join(ioThread, NULL); // not sure if this will be needed... // pthread_exit(NULL); // this may be needed if pthread_join doesn't produce expected results. // call the interruptManager here, with the IO Return code. interruptManager(configPtr, singlePCB, IO_RETURN); break; default: // this shouldn't happen, but if it does, you'll know... break; } } /* Name: runProcess Process: runs the process that was selected Function Input/Parameters: ??? Function Output/Parameters: none Function Output/Returned: none Device Input/Device: none Device Output/Device: none Dependencies: accessTimer, compareString, runCPU, runIO, ??? */ void runProcess(ProcessControlBlock *singlePCB, ConfigDataType *configPtr, ProcessControlBlockLL *pcbList, ConfigDataCodes schedMethod) { double timeDigits; // run over the entire process, running the CPU when a CPU cycle comes up // or a run IO when an IO cycle comes up char timeStringy[MAX_STR_LEN]; char processOutput[HUGE_STR_LEN * 4]; // grab the next operation code for the OpCodeType *workingMeta = singlePCB->nextOpCode; char *command; char *argument; int intArg; int intArgTwo; bool memManagerSuccess = true; // char *inOutArg; command = workingMeta->command; argument = workingMeta->strArg1; intArg = workingMeta->intArg2; intArgTwo = workingMeta->intArg3; // inOutArg = workingMeta->inOutArg; while (!(compareString(command, "app") == EQUIV && compareString(argument, "end") == EQUIV) && !(compareString(command, "sys") == EQUIV && compareString(argument, "end") == EQUIV) && memManagerSuccess) { // check to see what operation will be performed if (compareString(command, "cpu") == EQUIV && compareString(argument, "process") == EQUIV) { // run the CPU for the required amount of time. runCPU(workingMeta->command, workingMeta->strArg1, workingMeta->intArg2, configPtr, singlePCB->uniquePid, singlePCB, schedMethod); } if(compareString(command, "dev") == EQUIV) { // run IO operations for the required amount of time. runIO(workingMeta->command, workingMeta->strArg1, workingMeta->intArg2, configPtr, singlePCB->uniquePid, singlePCB, schedMethod); } if(compareString(command, "mem") == EQUIV) { // run memory operations with the memory manager if(compareString(argument, "allocate") == EQUIV) { timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(processOutput, "%f, Process: %d, %s %s request (%d, %d)\n", timeDigits, singlePCB->uniquePid, command, argument, intArg, intArgTwo); logOutput(configPtr, processOutput, false); // run the memory manager in allocate mode memManagerSuccess = memManager(pcbList, MEMORY_ALLOCATE, intArg, intArgTwo, configPtr, singlePCB); if(!memManagerSuccess) { // timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(processOutput, "%f, Process: %d, failed %s %s request (%d, %d)\n", timeDigits, singlePCB->uniquePid, command, argument, intArg, intArgTwo); logOutput(configPtr, processOutput, false); } } else if(compareString(argument, "access") == EQUIV) { timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(processOutput, "%f, Process: %d, %s %s request (%d, %d)\n", timeDigits, singlePCB->uniquePid, command, argument, intArg, intArgTwo); logOutput(configPtr, processOutput, false); // run the memory manager in access mode memManagerSuccess = memManager(pcbList, MEMORY_ACCESS, intArg, intArgTwo, configPtr, singlePCB); if (!memManagerSuccess) { // timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(processOutput, "%f, Process: %d, failed %s %s request (%d, %d)\n", timeDigits, singlePCB->uniquePid, command, argument, intArg, intArgTwo); logOutput(configPtr, processOutput, false); } } } workingMeta = workingMeta->nextNode; command = workingMeta->command; argument = workingMeta->strArg1; intArg = workingMeta->intArg2; intArgTwo = workingMeta->intArg3; switch (schedMethod) { case PREEMPTIVE_CODE: interruptManager(configPtr, singlePCB, CLEAR_INTERRUPT); break; default: // this will only happen when NON_PREEMPTIVE is in progress, but // warnings will result without having this here, so... break; } } // display the process exited, if not segfaulted. if(memManagerSuccess) { // timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(processOutput, "\n%f, OS: Process %d ended\n", timeDigits, singlePCB->uniquePid); logOutput(configPtr, processOutput, false); } else { // timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(processOutput, "\n%f, OS: Segmentation Fault, Process %d ended\n", timeDigits, singlePCB->uniquePid); logOutput(configPtr, processOutput, false); } // deallocate the memory for the process memManager(pcbList, MEMORY_DEALLOCATE, intArg, intArgTwo, configPtr, singlePCB); } /* Name: runSim Process: primary simulation driver Function Input/Parameters: configuration data (ConfigDataType *), metadata (OpCodeType *) Function Output/Parameters: none Function Output/Returned: none Device Input/Device: none Device Output/Device: none Dependencies: pthreads, accessTimer, sprintf, logOutput, runProcess, destroyProcessList, ??? */ void runSim(ConfigDataType *configPtr, OpCodeType *metaDataMstrPtr) { // initialize function/variables // need two working pointers, one for master loop, one for internal loop // OpCodeType *wkgPtrMaster; // currentTime variable that stores the time. [now unsure if needed] // need a variable for headPointer to point ProcessControlBlockLL *pcbList; ProcessControlBlock *pcbPtr; // string variable for nextCode, temp unused // char *nextCode; // string variable for the timer stuff int schedulingMethod = configPtr->cpuSchedCode; char simulatorOutput[MAX_STR_LEN * 4]; double timeDigits; char timeStringy[MAX_STR_LEN]; int processCount = 0; int processIterator; int memoryOffset = 0; int memorySize = 1; if (schedulingMethod == CPU_SCHED_FCFS_P_CODE || schedulingMethod == CPU_SCHED_RR_P_CODE || schedulingMethod == CPU_SCHED_SRTF_P_CODE) { // if in a preemptive scheduling method, set to PREEMPTIVE MODE schedulingMethod = PREEMPTIVE_CODE; } else { // if in a non-preemptive scheduling method, set to NON_PREEMPTIVE MODE schedulingMethod = NON_PREEMPTIVE_CODE; } accessTimer(ZERO_TIMER, timeStringy); // Operating System start timer goes here, log it. timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(simulatorOutput, "%f, OS: Simulator start\n", timeDigits); logOutput(configPtr, simulatorOutput, false); // int variable for process to run int currentPid; // create the process list with createProcessList, store the pointed to address pcbList = createProcessList(); // fill the process list given the metadata pcbList = fillProcessList(configPtr, metaDataMstrPtr, pcbList); pcbPtr = pcbList->headPtr; // calculate pcb total time, number of processes int totalPCBTime = 0; while(pcbPtr != NULL) { totalPCBTime = totalPCBTime + pcbPtr->timeLeft; pcbPtr = pcbPtr->nextPtr; processCount = processCount + 1; } pcbPtr = pcbList->headPtr; int *totalPCBTimePtr = &totalPCBTime; pthread_t cpuThread; pthread_create(&cpuThread, NULL, runTimer, (void *)totalPCBTimePtr); // int processTimeLeft = pcbPtr->timeLeft; int *processTimeLeftPtr = &processTimeLeft; // set the current process id to the first scheduled process currentPid = scheduleProcess(pcbList, configPtr); // initialize memory with the memManager memManager(pcbList, MEMORY_INITIALIZE, memoryOffset, memorySize, configPtr, pcbList->headPtr); // loop until current process is hit for(processIterator = 0; processIterator < processCount; processIterator++) { while (pcbPtr != NULL) { // printf("Current Process Number is: %d\n", pcbPtr->uniquePid); // printf("Process Status is: %d\n", pcbPtr->process_state); // printf("Scheduled Process is: %d\n", currentPid); if (pcbPtr->process_state != PROCESS_STATE_TERMINATED && pcbPtr->uniquePid == currentPid) { // needs to set from READY to RUNNING pcbPtr->process_state = PROCESS_STATE_RUNNING; timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(simulatorOutput, "%f, OS: Process %d set to RUNNING state from READY state\n\n", timeDigits, pcbPtr->uniquePid); logOutput(configPtr, simulatorOutput, false); pthread_t runThread; pthread_create(&runThread, NULL, runTimer, (void *)processTimeLeftPtr); runProcess(pcbPtr, configPtr, pcbList, schedulingMethod); switch (schedulingMethod) { case NON_PREEMPTIVE_CODE: // when finished, set to terminated timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(simulatorOutput, "%f, OS: Process %d set to TERMINATED state from RUNNING state\n\n", timeDigits, pcbPtr->uniquePid); logOutput(configPtr, simulatorOutput, false); // this assumes that the processes finish executing all the way though. pcbPtr->process_state = PROCESS_STATE_TERMINATED; break; case PREEMPTIVE_CODE: // will need to instead, set the next process up? interruptManager(configPtr, pcbPtr, CPU_SWITCH); break; } pthread_join(runThread, NULL); } processTimeLeft = pcbPtr->timeLeft; processTimeLeftPtr = &processTimeLeft; pcbPtr = pcbPtr->nextPtr; } currentPid = scheduleProcess(pcbList, configPtr); pcbPtr = pcbList->headPtr; } pthread_join(cpuThread, NULL); // deallocate all the memory for each process. memManager(pcbList, MEMORY_CLEAR, memoryOffset, memorySize, configPtr, pcbList->headPtr); timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(simulatorOutput, "%f, OS: System Stop\n", timeDigits); logOutput(configPtr, simulatorOutput, false); timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(simulatorOutput, "%f, OS: Simulation End\n", timeDigits); logOutput(configPtr, simulatorOutput, true); // deallocate the process list with destroyProcessList destroyProcessList(pcbList); // once complete, log the output to FILE if required logOutput(configPtr, "", true); // pthread_exit(NULL); // this line seems to make the program execution stop short... no warning either // void function, no return. } /* Name: scheduleProcess Process: takes in the scheduling data, and then returns which process is to be ran Function Input/Parameters: head pointer of PCB List (ProcessControlBlockLL *), configuration data (ConfigDataType *) Function Output/Parameters: none Function Output/Returned: none Device Input/Device: none Device Output/Device: none Dependencies: sprintf, logOutput, ??? */ int scheduleProcess(ProcessControlBlockLL *headPointer, ConfigDataType *configPtr) { double timeDigits; ProcessControlBlock *wkgPtr; int returnPid = -1; wkgPtr = headPointer->headPtr; char loggingStr[MAX_STR_LEN]; char timeStringy[MAX_STR_LEN]; int processCount = 0; int shortestJob = 0; int shortestJobProcNum = 0; while (wkgPtr != NULL) { if(wkgPtr->process_state != PROCESS_STATE_TERMINATED) { // processCount = processCount + 1; } wkgPtr = wkgPtr->nextPtr; } wkgPtr = headPointer->headPtr; // switch case for each scheduling algorithm switch (configPtr->cpuSchedCode) { case CPU_SCHED_FCFS_N_CODE: // First Come, First Served, Non-preemptive Scheduling // need to find whichever process is first, AND the state is NEW. // will set the state to READY, return the pid of the process which // was set to ready. // set every process in the list to READY from NEW while(wkgPtr != NULL) { // if(wkgPtr->process_state == PROCESS_STATE_NEW) { // set the process within the PCB to the ready state, report in ready state. wkgPtr->process_state = PROCESS_STATE_READY; // printf("Process %d set to READY from NEW\n", wkgPtr->uniquePid); timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(loggingStr, "%f, OS: Process %d set to READY state from NEW state\n", timeDigits, wkgPtr->uniquePid); logOutput(configPtr, loggingStr, false); } // move to the next pointer wkgPtr = wkgPtr->nextPtr; } // reset the pointer to get ready for selection of process wkgPtr = headPointer->headPtr; while(wkgPtr != NULL && (returnPid == -1)) { // check if the process is NOT terminated if (wkgPtr->process_state != PROCESS_STATE_RUNNING && wkgPtr->process_state != PROCESS_STATE_TERMINATED) { //printf("Process %d Selected with %.0f ms remaining\n", wkgPtr->uniquePid, wkgPtr->timeLeft); timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(loggingStr, "%f, OS: Process %d selected with %.0f ms remaining\n", timeDigits, wkgPtr->uniquePid, wkgPtr->timeLeft); logOutput(configPtr, loggingStr, false); returnPid = wkgPtr->uniquePid; } // move to next pointer wkgPtr = wkgPtr->nextPtr; } break; case CPU_SCHED_FCFS_P_CODE: // TODO: First Come, First Served, Preemptive Scheduling // try copying the previous code, making sure that if it is in a // blocked state, it cannot be scheduled, // if it is in a waiting state, then it can be scheduled. // can also be scheduled or not based on the previous section of // code as well. while (wkgPtr != NULL) { // if (wkgPtr->process_state == PROCESS_STATE_NEW) { // set the process within the PCB to the ready state, report in ready state. wkgPtr->process_state = PROCESS_STATE_READY; // printf("Process %d set to READY from NEW\n", wkgPtr->uniquePid); timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(loggingStr, "%f, OS: Process %d set to READY state from NEW state\n", timeDigits, wkgPtr->uniquePid); logOutput(configPtr, loggingStr, false); } // move to the next pointer wkgPtr = wkgPtr->nextPtr; } // reset the pointer to get ready for selection of process wkgPtr = headPointer->headPtr; while (wkgPtr != NULL && (returnPid == -1)) { // check if the process is NOT running, terminated OR blocked. if (wkgPtr->process_state != PROCESS_STATE_RUNNING && wkgPtr->process_state != PROCESS_STATE_TERMINATED && wkgPtr->process_state != PROCESS_STATE_BLOCKED) { // printf("Process %d Selected with %.0f ms remaining\n", wkgPtr->uniquePid, wkgPtr->timeLeft); timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(loggingStr, "%f, OS: Process %d selected with %.0f ms remaining\n", timeDigits, wkgPtr->uniquePid, wkgPtr->timeLeft); logOutput(configPtr, loggingStr, false); returnPid = wkgPtr->uniquePid; } // move to next pointer wkgPtr = wkgPtr->nextPtr; } break; case CPU_SCHED_RR_P_CODE: // TODO: Round Robin Scheduling // need to FCFS first. however, just rotate through each process. // maybe keep track via an array or previously used process? not sure how exactly I want this. // maybe an extern??? not sure how to essentially keep track even when scope is lost here. // actually, I think a static variable may be the key to solving this. // alternatively, I could use a circularly linked list, given that proper scheduling is done. break; case CPU_SCHED_SJF_N_CODE: // Shortest Job First Scheduling // need to find shortest job, maybe using a find min function? // reuse the previous code to set each process to READY from NEW // will select the process that is in the READY state, with the shortest time. while (wkgPtr != NULL) { // if (wkgPtr->process_state == PROCESS_STATE_NEW) { // set the process within the PCB to the ready state, report in ready state. wkgPtr->process_state = PROCESS_STATE_READY; // printf("Process %d set to READY from NEW\n", wkgPtr->uniquePid); timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(loggingStr, "%f, OS: Process %d set to READY state from NEW state\n", timeDigits, wkgPtr->uniquePid); logOutput(configPtr, loggingStr, false); } // move to the next pointer wkgPtr = wkgPtr->nextPtr; } // reset the pointer to get ready for selection of process wkgPtr = headPointer->headPtr; // load in the first process time shortestJob = wkgPtr->timeLeft; // move to the next pointer. wkgPtr = wkgPtr->nextPtr; while (wkgPtr != NULL && (returnPid == -1)) { // check if the process is NOT terminated // now, ALSO needs to check which process takes the least time. if (wkgPtr->process_state != PROCESS_STATE_RUNNING && wkgPtr->process_state != PROCESS_STATE_TERMINATED) { // check to see if the working pointer job is shorter than the current shortest job shortestJob = findMin(shortestJob, wkgPtr->timeLeft); // check to see if the shortest job time changed, if it did, change the process number if(shortestJob == wkgPtr->timeLeft) { // change process number, or leave it unchanged. shortestJobProcNum = wkgPtr->uniquePid; } } // move to next pointer wkgPtr = wkgPtr->nextPtr; } // check the number of processes, if there is one left, then wkgPtr = headPointer->headPtr; if(processCount == 1) { // find the last process that is READY but not TERMINATED, set it's PID as the return. while(wkgPtr != NULL) { // if (wkgPtr->process_state != PROCESS_STATE_TERMINATED) { // shortestJobProcNum = wkgPtr->uniquePid; shortestJob = wkgPtr->timeLeft; } wkgPtr = wkgPtr->nextPtr; } } if(processCount != 0) { // printf("Process %d Selected with %.0f ms remaining\n", wkgPtr->uniquePid, wkgPtr->timeLeft); timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(loggingStr, "%f, OS: Process %d selected with %.0f ms remaining\n", timeDigits, shortestJobProcNum, (double)shortestJob); logOutput(configPtr, loggingStr, false); } returnPid = shortestJobProcNum; break; case CPU_SCHED_SRTF_P_CODE: // TODO: Shortest Run-time First Scheduling // may be able to copy the same code that I've used above, just // with more restrictive rules similarly to the FCFS described above. while (wkgPtr != NULL) { // if (wkgPtr->process_state == PROCESS_STATE_NEW) { // set the process within the PCB to the ready state, report in ready state. wkgPtr->process_state = PROCESS_STATE_READY; // printf("Process %d set to READY from NEW\n", wkgPtr->uniquePid); timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(loggingStr, "%f, OS: Process %d set to READY state from NEW state\n", timeDigits, wkgPtr->uniquePid); logOutput(configPtr, loggingStr, false); } // move to the next pointer wkgPtr = wkgPtr->nextPtr; } // reset the pointer to get ready for selection of process wkgPtr = headPointer->headPtr; // load in the first process time shortestJob = wkgPtr->timeLeft; // move to the next pointer. wkgPtr = wkgPtr->nextPtr; while (wkgPtr != NULL && (returnPid == -1)) { // check if the process is NOT terminated and NOT blocked. (can only be waiting) // now, ALSO needs to check which process takes the least time. if (wkgPtr->process_state != PROCESS_STATE_RUNNING && wkgPtr->process_state != PROCESS_STATE_TERMINATED && wkgPtr->process_state != PROCESS_STATE_BLOCKED) { // check to see if the working pointer job is shorter than the current shortest job shortestJob = findMin(shortestJob, wkgPtr->timeLeft); // check to see if the shortest job time changed, if it did, change the process number if (shortestJob == wkgPtr->timeLeft) { // change process number, or leave it unchanged. shortestJobProcNum = wkgPtr->uniquePid; } } // move to next pointer wkgPtr = wkgPtr->nextPtr; } // check the number of processes, if there is one left, then wkgPtr = headPointer->headPtr; if (processCount == 1) { // find the last process that is READY but not TERMINATED, set it's PID as the return. while (wkgPtr != NULL) { // if (wkgPtr->process_state != PROCESS_STATE_TERMINATED) { // shortestJobProcNum = wkgPtr->uniquePid; shortestJob = wkgPtr->timeLeft; } wkgPtr = wkgPtr->nextPtr; } } if (processCount != 0) { // printf("Process %d Selected with %.0f ms remaining\n", wkgPtr->uniquePid, wkgPtr->timeLeft); timeDigits = accessTimer(LAP_TIMER, timeStringy); sprintf(loggingStr, "%f, OS: Process %d selected with %.0f ms remaining\n", timeDigits, shortestJobProcNum, (double)shortestJob); logOutput(configPtr, loggingStr, false); } returnPid = shortestJobProcNum; break; } // return returnPid return returnPid; }