#include "simulator.h" /* 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; // make sure to set the next pointer to NULL! workingNode->nextPtr = NULL; // return workingNode return workingNode; } /* 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: 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) { // 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; // 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; } 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; // 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: 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: 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) { char timeStringy[MAX_STR_LEN]; // char *command = workingMeta->command; // char *argument = workingMeta->strArg1; // int intArg = workingMeta->intArg2; char cpuOutput[MAX_STR_LEN]; int cpuTime = 0; cpuTime = intArg * configPtr->procCycleRate; sprintf(cpuOutput, "%f, Process: %d, %s %s operation start\n", accessTimer(LAP_TIMER, timeStringy), processId, command, argument); logOutput(configPtr, cpuOutput, false); // run timer for intArg * procCycleRate pthread_t cpuThread; pthread_create(&cpuThread, NULL, (void *)&simThread, (void *)cpuTime); pthread_join(cpuThread, NULL); sprintf(cpuOutput, "%f, Process: %d, %s %s operation end\n", accessTimer(LAP_TIMER, timeStringy), processId, command, argument); logOutput(configPtr, cpuOutput, false); // pthread_exit(NULL); } /* 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) { char timeStringy[MAX_STR_LEN]; char ioOutput[MAX_STR_LEN]; int ioTime = 0; ioTime = intArg * configPtr->ioCycleRate; sprintf(ioOutput, "%f, Process: %d, %s %s operation start\n", accessTimer(LAP_TIMER, timeStringy), processId, command, argument); logOutput(configPtr, ioOutput, false); pthread_t ioThread; pthread_create(&ioThread, NULL, (void *)&simThread, (void *)ioTime); pthread_join(ioThread, NULL); sprintf(ioOutput, "%f, Process: %d, %s %s operation end\n", accessTimer(LAP_TIMER, timeStringy), processId, command, argument); logOutput(configPtr, ioOutput, false); } /* 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) { // 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]; // grab the next operation code for the OpCodeType *workingMeta = singlePCB->nextOpCode; char *command; char *argument; int intArg; char *inOutArg; command = workingMeta->command; argument = workingMeta->strArg1; intArg = workingMeta->intArg2; inOutArg = workingMeta->inOutArg; while (!(compareString(command, "app") == EQUIV && compareString(argument, "end") == EQUIV) && !(compareString(command, "sys") == EQUIV && compareString(argument, "end") == EQUIV)) { // 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); } if(compareString(command, "dev") == EQUIV) { // run IO operations for the required amount of time. runIO(workingMeta->command, workingMeta->strArg1, workingMeta->intArg2, configPtr, singlePCB->uniquePid); } workingMeta = workingMeta->nextNode; command = workingMeta->command; argument = workingMeta->strArg1; intArg = workingMeta->intArg2; } } /* 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 char simulatorOutput[MAX_STR_LEN]; char timeStringy[MAX_STR_LEN]; accessTimer(ZERO_TIMER, timeStringy); // Operating System start timer goes here, log it. sprintf(simulatorOutput, "%f, OS: Simulator start\n", accessTimer(LAP_TIMER, timeStringy)); 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 int totalPCBTime = 0; while(pcbPtr != NULL) { totalPCBTime = totalPCBTime + pcbPtr->timeLeft; pcbPtr = pcbPtr->nextPtr; } pcbPtr = pcbList->headPtr; pthread_t cpuThread; pthread_create(&cpuThread, NULL, (void *)&simThread, (void *)totalPCBTime); // int processTimeLeft = pcbPtr->timeLeft; // set the current process id to the first scheduled process currentPid = scheduleProcess(pcbList, configPtr); // loop until current process is hit while(pcbPtr != NULL) { if(pcbPtr->uniquePid == currentPid) { // needs to set from READY to RUNNING pcbPtr->process_state = PROCESS_STATE_READY; sprintf(simulatorOutput, "%f, OS: Process %d set to RUNNING state from READY state\n\n", accessTimer(LAP_TIMER, timeStringy), pcbPtr->uniquePid); logOutput(configPtr, simulatorOutput, false); pthread_t runThread; pthread_create(&runThread, NULL, (void *)&simThread, (void *)processTimeLeft); runProcess(pcbPtr, configPtr); // when finished, set to terminated sprintf(simulatorOutput, "%f, OS: Process %d set to TERMINATED state from RUNNING state\n\n", accessTimer(LAP_TIMER, timeStringy), pcbPtr->uniquePid); logOutput(configPtr, simulatorOutput, false); pcbPtr->process_state = PROCESS_STATE_TERMINATED; pthread_join(runThread, NULL); } processTimeLeft = pcbPtr->timeLeft; pcbPtr = pcbPtr->nextPtr; currentPid = scheduleProcess(pcbList, configPtr); } pthread_join(cpuThread, NULL); sprintf(simulatorOutput, "%f, OS: System Stop\n", accessTimer(LAP_TIMER, timeStringy)); logOutput(configPtr, simulatorOutput, false); sprintf(simulatorOutput, "%f, OS: Simulation End\n", accessTimer(LAP_TIMER, timeStringy)); 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) { ProcessControlBlock *wkgPtr; int returnPid = -1; wkgPtr = headPointer->headPtr; char loggingStr[MAX_STR_LEN]; char timeStringy[MAX_STR_LEN]; // switch case for each scheduling algorithm switch (configPtr->cpuSchedCode) { case CPU_SCHED_FCFS_N_CODE: // First Come, First Served, Non-preemptive Scheduling // only one implemented for now. returns first process without fail. // 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_READY && wkgPtr->process_state != PROCESS_STATE_TERMINATED) { // 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); sprintf(loggingStr, "%f, OS: Process %d set to READY state from NEW state\n", accessTimer(LAP_TIMER, timeStringy), 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_READY) { //printf("Process %d Selected with %.0f ms remaining\n", wkgPtr->uniquePid, wkgPtr->timeLeft); sprintf(loggingStr, "%f, OS: Process %d selected with %.0f ms remaining\n", accessTimer(LAP_TIMER, timeStringy), 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 break; case CPU_SCHED_RR_P_CODE: // TODO: Round Robin Scheduling break; case CPU_SCHED_SJF_N_CODE: // TODO: Shortest Job First Scheduling break; case CPU_SCHED_SRTF_P_CODE: // TODO: Shortest Run-time First Scheduling break; } // return returnPid return returnPid; } /* Name: simThread Process: wrapper functions for threading in the simulator Function Input/Parameters: milliseconds (int) Function Output/Parameters: none Function Output/Returned: none Device Input/Device: none Device Output/Device: none Dependencies: pthreads, runTimer */ void *simThread(int milSec) { runTimer(milSec); pthread_exit(NULL); }