#include "simulator.h" /* * Function Name: accessMemory * Algorithm: given a list of MemoryNodes, checks if the logical address of a * process has been allocated and can be used * Precondition: MemoryNode list allocated and pointer passed to function * Postcondition: bool returned of whether access was successful or not * Exceptions: None * Notes: None */ bool accessMemory( MemoryNode *memNodePtr, int startIndex, int endIndex, int pid ) { // initialize function variables int difference = 0; MemoryNode *tempPtr = memNodePtr; bool found = false; while( tempPtr->logicalStartIndex != END_OF_MEM_LIST ) { difference = startIndex - tempPtr->logicalStartIndex; // if logical address fits within node logical address bounds if( startIndex >= tempPtr->logicalStartIndex && difference <= ( tempPtr->logicalEndIndex - ( tempPtr->logicalStartIndex + difference ) ) ) { found = true; } tempPtr = tempPtr->nextNode; } return found; } /* * Function Name: allocateMemory * Algorithm: checks whether memory allocation is valid * if so, adds allocation to MemoryList, otherwise 'segfaults' * Precondition: pointer to memory list / base and offset values provided to function * Postcondition: allocated memory accounted for in list structure * Exceptions: if invalid allocation, terminate the program * Notes: None */ void allocateMemory( ConfigDataType *configPtr, fileWriteList **fileWriteListPtr, PCBLinkedList **pcbLLPtr, MemoryNode *memNodePtr, MemoryNode **headPtr, PCB *pcbPtr, int base, int offset ) { memNodePtr = ( *headPtr ); // initialize function variables int logicalEndIndex; bool collision = false; MemoryNode *tempPtr = memNodePtr; MemoryNode *openPtr; // convert base and offset values to start / end index (offset += base - 1) logicalEndIndex = base + offset - 1; // check that end index is not greater than maximum memory value specified in config file if( logicalEndIndex >= configPtr->memAvailable ) { // output allocation failed and perform necessary operations to end program // function: segfault segfault( configPtr, fileWriteListPtr, pcbLLPtr, memNodePtr, headPtr, pcbPtr ); } // check that memory allocation does not clash with all other current allocations while( tempPtr->logicalStartIndex != END_OF_MEM_LIST ) { // verify proposed allocation is valid // funtion: verifyLogicalAllocation verifyLogicalAllocation( tempPtr, base, logicalEndIndex, &collision ); tempPtr = tempPtr->nextNode; } if( collision ) { segfault( configPtr, fileWriteListPtr, pcbLLPtr, memNodePtr, headPtr, pcbPtr ); } // made to end of list, so logical allocation must be valid // test for enough space in physical address // function: findOpenMemory openPtr = findOpenMemory( base, logicalEndIndex, memNodePtr ); // assuming for now openPtr is at the end if( openPtr == NULL ) { segfault( configPtr, fileWriteListPtr, pcbLLPtr, memNodePtr, headPtr, pcbPtr ); } // create new MemoryNode prior to openPtr, then change openPtr indices accordingly else { tempPtr = memNodePtr; // if open node is the only one in the list if( tempPtr == openPtr ) { tempPtr = ( MemoryNode * )malloc( sizeof( MemoryNode ) ); tempPtr->nextNode = openPtr; memNodePtr = tempPtr; *headPtr = tempPtr; } else { // loop until just before open allocation while( tempPtr->nextNode != openPtr ) { tempPtr = tempPtr->nextNode; } tempPtr->nextNode = ( MemoryNode * )malloc( sizeof( MemoryNode ) ); tempPtr = tempPtr->nextNode; tempPtr->nextNode = openPtr; } // regardless set to the same values tempPtr->pid = pcbPtr->pid; tempPtr->status = USED; tempPtr->logicalStartIndex = base; tempPtr->logicalEndIndex = logicalEndIndex; tempPtr->physicalStartIndex = openPtr->physicalStartIndex; tempPtr->physicalEndIndex = tempPtr->physicalStartIndex + ( logicalEndIndex - base ); openPtr->physicalStartIndex = tempPtr->physicalEndIndex + 1; } if( pcbPtr->state != EXIT ) { if( configPtr->logToCode != LOGTO_FILE_CODE ) { printMemoryList( *headPtr, configPtr, fileWriteListPtr, SUCCESS ); } } } /* * Function Name: changeProcessState * Algorithm: changes state in PCB struct and displays results as needed * Precondition: PCB passed into function * Postcondition: Process state changed and results shown * Exceptions: if the state parameter is the same as in the PCB do nothing * Notes: None */ void changeProcessState( ConfigDataType *configPtr, fileWriteList **fileWriteList, PCB *PCB, int state ) { char *writeStr; int pid = 0; char buffer[MAX_STR_LEN]; if( PCB->state != state ) { pid = PCB->pid; char *possibleStates[5]; possibleStates[ 0 ] = "NEW"; possibleStates[ 1 ] = "READY"; possibleStates[ 2 ] = "RUNNING"; possibleStates[ 3 ] = "WAITING"; possibleStates[ 4 ] = "EXIT"; writeStr = malloc( sizeof( char ) * MAX_STR_LEN ); char *firstState = possibleStates[PCB->state]; char *nextState = possibleStates[state]; if( state == RUNNING ) { sprintf( buffer, "OS: Process %d set from %s -to-> %s\n\n", pid, firstState, nextState ); } else { sprintf( buffer, "OS: Process %d set from %s -to-> %s\n", pid, firstState, nextState ); } copyString( writeStr, buffer ); displayHandler( configPtr, fileWriteList, writeStr, false, false ); PCB->state = state; free( writeStr ); } } /* * Function Name: checkProgramEnd * Algorithm: checks if all PCBs are in exit state * Precondition: PCBs are initialized in PCBLinkedList * PostCondition: bool result returned * Exceptions: None * Notes: None */ bool checkProgramEnd( PCBLinkedList **PCBLinkedListPtr ) { // initialize function variables PCBLinkedList *tempPtr = *PCBLinkedListPtr; bool result = true; // while not at the end of PCB Linked List structure while( tempPtr->PCB->pid != END_OF_PCB_LIST ) { if( tempPtr->PCB->state != EXIT ) { result = false; } tempPtr = tempPtr->nextPCB; } return result; } /* * Function Name: chooseNextProcess * Algorithm: depending on the scheduling type, * picks the next process based on PCB info * Precondition: All processes are not currently executing * (since we are assuming a single CPU), * and scheduling type is valid and selected * Postcondition: the next process to be run is selected and pointer to PCB is returned * Exceptions: If all processes are waiting or in exit state cannot choose a process * Notes: None */ PCB *chooseNextProcess( ConfigDataType *configPtr, fileWriteList **fileWriteList, PCBLinkedList *PCBLinkedListPtr ) { // check which scheduler is chosen // FCFS-N, SJF-N, SRTF-P, FCFS-P, RR-P // switch statement to select based on scheduler switch( configPtr->cpuSchedCode ) { // For now all cases use FCFS_N_Choice case CPU_SCHED_SJF_N_CODE: return SJF_N_Choice( &PCBLinkedListPtr, configPtr, fileWriteList ); break; case CPU_SCHED_SRTF_P_CODE: return SRTF_P_Choice( &PCBLinkedListPtr, configPtr, fileWriteList ); break; case CPU_SCHED_FCFS_P_CODE: return FCFS_P_Choice( &PCBLinkedListPtr, configPtr, fileWriteList ); break; case CPU_SCHED_RR_P_CODE: return RR_P_Choice( &PCBLinkedListPtr, configPtr, fileWriteList ); break; case CPU_SCHED_FCFS_N_CODE: return FCFS_N_Choice( &PCBLinkedListPtr, configPtr, fileWriteList ); break; } return NULL; } /* * Function Name: clearMemoryList * Algorithm: recursively frees memory used in linked list structure for memory operations * Precondition: None * Postcondition: Linked List is freed and NULL is returned * Exceptions: None * Notes: None */ MemoryNode *clearMemoryList( MemoryNode *memoryList ) { if( memoryList->logicalStartIndex != END_OF_MEM_LIST ) { clearMemoryList( memoryList->nextNode ); } free( memoryList ); // all allocations are freed, return null return NULL; } /* * Function Name: clearPCBList * Algorithm: frees memory used in Linked List structure used for process operations * Precondition: None * Postcondition: Linked List is freed and NULL returned * Exceptions: None * Notes: None */ PCBLinkedList *clearPCBList( PCBLinkedList *pcbList ) { // check if pcbList is null if( pcbList->PCB->pid != END_OF_PCB_LIST ) { // recurse until empty PCB clearPCBList( pcbList->nextPCB ); } // free PCB and PCBLinkedList node free( pcbList->PCB ); free( pcbList ); // structs are all freed return NULL; } /* * Function Name: clearProcessMemory * Algorithm: frees memory used by a specific process in the memoryList * Precondition: Process has been moved to exit state * Postcondition: MemoryNode list of memory used by exited process is freed * Exceptions: None * Notes: None */ MemoryNode *clearProcessMemory( MemoryNode *memNodePtr, MemoryNode **headPtr, int pid ) { // initialize function variables MemoryNode *tempPtrOne = memNodePtr; MemoryNode *tempPtrTwo = tempPtrOne->nextNode; MemoryNode *nodeToDelete; // remove all nodes in the beginning of the same pid // for now just go until end of list while( memNodePtr->pid == pid || memNodePtr->logicalStartIndex != END_OF_MEM_LIST ) { tempPtrOne = memNodePtr; memNodePtr = memNodePtr->nextNode; free( tempPtrOne ); *headPtr = memNodePtr; } // set head pointer to new first node tempPtrOne = memNodePtr; if( memNodePtr->logicalStartIndex == END_OF_MEM_LIST ) { // sets last node in MemoryNode list back to zero // TODO: for preemption, need to track freed memory and give to open allocations memNodePtr->physicalStartIndex = 0; // for now loop headPtr to end while( ( *headPtr )->logicalStartIndex != END_OF_MEM_LIST ) { *headPtr = ( *headPtr )->nextNode; } return memNodePtr; } // this section is for later use when there is more than one open segment tempPtrTwo = memNodePtr->nextNode; while( tempPtrTwo->logicalStartIndex != END_OF_MEM_LIST ) { if( tempPtrTwo->pid == pid ) { nodeToDelete = tempPtrTwo; tempPtrTwo = tempPtrTwo->nextNode; tempPtrOne->nextNode = tempPtrTwo; free( nodeToDelete ); } } if( tempPtrOne->pid != pid ) { memNodePtr = tempPtrOne; } else { free( tempPtrOne ); memNodePtr = tempPtrTwo; } // sets last node in MemoryNode list back to zero // TODO: for preemption, need to track freed memory and give to open allocations memNodePtr->physicalStartIndex = 0; return memNodePtr; } /* * Function Name: clearWriteList * Algorithm: Recursive method to free memory of data structure used to * write to file * Precondition: None * Postcondition: Memory allocated for file write operations freed * Exceptions: None * Notes: None */ fileWriteList *clearWriteList( fileWriteList **fileWriteListPtr ) { fileWriteList *fileWriteList = *fileWriteListPtr; if( compareString( fileWriteList->strToWrite, END_OF_STRLIST ) != STR_EQ ) { clearWriteList( &( fileWriteList->nextStr ) ); } free( fileWriteList ); return NULL; } /* * Function Name: createPCB * Algorithm: helper function for scanForNewPCBs, makes new PCB struct * Precondition: given pointer to start op code * Postcondition: new PCB created, pointer referencing now contains PCB struct * Exceptions: None * Notes: None */ void createPCB( OpCodeType *startOpCode, PCBLinkedList **PCBLLIterPtr, int cycleTime ) { // allocate memory for next node in Linked List ( *PCBLLIterPtr )->nextPCB = ( PCBLinkedList * )malloc( sizeof( PCBLinkedList ) ); // set pid to -1 to mark end of list ( *PCBLLIterPtr )->nextPCB->PCB = ( PCB * )malloc( sizeof( PCB ) ); ( *PCBLLIterPtr )->nextPCB->PCB->pid = END_OF_PCB_LIST; ( *PCBLLIterPtr )->nextPCB->PCB->state = EXIT; // keep as exit? see FCFS_P_Choice // set various values for PCB ( *PCBLLIterPtr )->PCB->pid = getNextPID(); ( *PCBLLIterPtr )->PCB->state = NEW; ( *PCBLLIterPtr )->PCB->programCounter = startOpCode; ( *PCBLLIterPtr )->PCB->timeRemaining = cycleTime; ( *PCBLLIterPtr )->PCB->interruptable = true; ( *PCBLLIterPtr )->PCB->ranForCycle = false; ( *PCBLLIterPtr )->PCB->opCodeComplete = false; ( *PCBLLIterPtr )->PCB->startedOp = false; ( *PCBLLIterPtr )->PCB->cycleRemainder = 0; // step PCBLL pointer forward to nextPCB (empty) *PCBLLIterPtr = ( *PCBLLIterPtr )->nextPCB; } /* * Function Name: decrementCycles * Algorithm: given an op code and a WAITING PCB, find out if they are both the same * op code type. If they aren't, keep track of the remainder by storing in the PCB. Additionally, subtract from the timeRemaining in the WAITING PCB * Precondition: op code is currently running and PCB given is 'running' in the background * Postcondition: integer returned of the amount of cycles to decrement. occasionally will be * greater than one to make up for differences in op codes. * Exceptions: None * Notes: None */ int decrementCycles( ConfigDataType *configPtr, OpCodeType *opCodePtr, PCB *PCB ) { int timeDec = 0; int opCycleTime = 0; int remainder = 0; int cyclesToDec = 1; // set op code cycle time if( compareString( opCodePtr->command, "dev" ) == STR_EQ ) { opCycleTime = configPtr->ioCycleRate; } else if( compareString( opCodePtr->command, "cpu" ) == STR_EQ ) { opCycleTime = configPtr->procCycleRate; } // set pcb cycle time / time to deduct from timeRemaining // PCB op code will always be I/O if waiting but oh well if( compareString( PCB->programCounter->command, "dev" ) == STR_EQ ) { timeDec = configPtr->ioCycleRate; } else if( compareString( PCB->programCounter->command, "cpu" ) == STR_EQ ) { timeDec = configPtr->procCycleRate; } PCB->timeRemaining = PCB->timeRemaining - timeDec; remainder = opCycleTime - timeDec; if( remainder != 0 ) { if( remainder > 0 ) { PCB->cycleRemainder = PCB->cycleRemainder + remainder; } else { cyclesToDec--; PCB->cycleRemainder = PCB->cycleRemainder + opCycleTime; } } // "cash in" remainder to add another cycle to waiting process if( PCB->cycleRemainder >= timeDec ) { cyclesToDec++; PCB->cycleRemainder = PCB->cycleRemainder - timeDec; } return cyclesToDec; } /* * Function Name: displayHandler * Algorithm: different operations depending on if the output is to be * directed to a file, to std output, or both * Precondition: Config file specifies type of output * ( which must already be working at this point in the program ) * Postcondition: program operations are properly displayed, can be during * simulator run as well as all at once to a file at termination * Exceptions: None * Notes: None */ void displayHandler( ConfigDataType *configPtr, fileWriteList **fileWriteListPtr, char *toWrite, bool writeOnly, bool newLine ) { // initialize variable for time string and string for output char *timeStr = malloc( sizeof(char) * MAX_STR_LEN ); char *tempStr = malloc( sizeof(char) * MAX_STR_LEN ); timeStr[0] = '\0'; tempStr[0] = '\0'; // get time from start of simulation accessTimer( LAP_TIMER, timeStr ); // TODO: handle spacing of string so that the length of time string doesn't // affect indendation // for readability of output, separate certain lines if( newLine ) { sprintf( tempStr, "\n%s, %s", timeStr, toWrite ); } else { sprintf( tempStr, "%s, %s", timeStr, toWrite ); } // shortcut if need to write to monitor but config specifies to file if( !writeOnly ) { // initialize function variables fileWriteList *ptrToIterNode = *fileWriteListPtr; // if only to file, write out to notify user that program is still working static bool firstStatement = true; // if we are writing to file (and using the data structure) if( configPtr->logToCode != LOGTO_MONITOR_CODE ) { // make sure iter pointer is at end node and fileStrPtr points to the penultimate node while( compareString( ( ptrToIterNode->strToWrite), (END_OF_STRLIST) ) != STR_EQ ) { ptrToIterNode = ptrToIterNode->nextStr; } } switch( configPtr->logToCode ) { case LOGTO_MONITOR_CODE: fprintf( stderr, tempStr ); break; case LOGTO_FILE_CODE: if( firstStatement ) { fprintf( stderr, "Simulator Running - outputting to file only...\n" ); firstStatement = false; } // create next node after last ptrToIterNode->nextStr = ( fileWriteList * )malloc( sizeof( fileWriteList ) ); // swap values ptrToIterNode->nextStr->strToWrite = END_OF_STRLIST; ptrToIterNode->strToWrite = tempStr; break; case LOGTO_BOTH_CODE: fprintf( stderr, tempStr ); // create next node after last ptrToIterNode->nextStr = ( fileWriteList * )malloc( sizeof( fileWriteList ) ); // swap values ptrToIterNode->nextStr->strToWrite = END_OF_STRLIST; ptrToIterNode->strToWrite = tempStr; break; } } else { fprintf( stderr, toWrite ); } free(timeStr); // TODO: Free memory without corrupting write to file // when running in valgrind and freeing tempStr memory, there is no file corruption // free(tempStr); } /* * Function Name: findOpenMemory * Algorithm: given a linked list and start & end indices, find an 'Open' memory address. * returns pointer to Open memory node. * Precondition: MemoryNode list and indices provided * Postcondition: Open node with adequate space provided * Exceptions: If none large enough found, returns NULL * Notes: None */ MemoryNode *findOpenMemory( int logicalStartIndex, int logicalEndIndex, MemoryNode *memNodePtr ) { int totalNeeded = logicalEndIndex - logicalStartIndex + 1; MemoryNode *returnPtr = memNodePtr; MemoryNode *tempPtr = memNodePtr; // currently finds last open segment // TODO: perhaps implement other memory segment choice algorithms while( tempPtr != NULL )//|| tempPtr->logicalStartIndex != END_OF_MEM_LIST ) { if( tempPtr->status == OPEN ) { // if open memory spot is large enough if( ( tempPtr->physicalEndIndex - tempPtr->physicalStartIndex + 1 ) >= totalNeeded ) { returnPtr = tempPtr; } } tempPtr = tempPtr->nextNode; } if( returnPtr->status != OPEN ) { returnPtr = NULL; } return returnPtr; } /* * Function Name: FCFS_N_Choice * Algorithm: first come first-served. chooses lowest pid process, non-preemptive * Precondition: Processes with PCBs created exist * Postcondition: PCB with lowest pid is chosen and returned * Exceptions: None * Notes: None */ PCB *FCFS_N_Choice( PCBLinkedList **pcbList, ConfigDataType *configPtr, fileWriteList **fileWriteListPtr ) { // initialize function variables PCBLinkedList *pcbListPtr = *pcbList; char *outputStr = malloc( sizeof( char ) * MAX_STR_LEN ); int pid = 0; int timeRemaining; // set return pointer to first pcb PCB *returnPtr = ( pcbListPtr->PCB ); while ( pcbListPtr != NULL && pcbListPtr->PCB->pid != END_OF_PCB_LIST ) { // if pid is less than returnPtr pid set returnPtr to current PCB if( ( ( pcbListPtr->PCB->pid ) < ( returnPtr->pid ) && pcbListPtr->PCB->state == READY ) || ( returnPtr->state ) == EXIT ) { returnPtr = ( pcbListPtr->PCB ); } pcbListPtr = pcbListPtr->nextPCB; } // perform output operations if( returnPtr->state == READY ) { pid = returnPtr->pid; timeRemaining = returnPtr->timeRemaining; sprintf( outputStr, "OS: Process %d selected with %d ms remaining\n", pid, timeRemaining ); displayHandler( configPtr, fileWriteListPtr, outputStr, false, false ); } free( outputStr ); return returnPtr; } /* * Function Name: FCFS_P_Choice * Algorithm: first come first-served. chooses lowest pid process in ready queue, preemptive * Precondition: Processes with PCBs created exist * Postcondition: PCB with lowest pid is chosen and returned * Exceptions: None * Notes: None */ PCB *FCFS_P_Choice( PCBLinkedList **pcbList, ConfigDataType *configPtr, fileWriteList **fileWriteListPtr ) { // initialize function variables PCBLinkedList *pcbListPtr = *pcbList; char *outputStr = malloc( sizeof( char ) * MAX_STR_LEN ); int pid = 0; int timeRemaining; static int lastPid = -1; // set return pointer to first pcb PCB *returnPtr = ( pcbListPtr->PCB ); while ( pcbListPtr != NULL && pcbListPtr->PCB->pid != END_OF_PCB_LIST ) { // if pid is less than returnPtr pid set returnPtr to current PCB if( ( ( pcbListPtr->PCB->pid ) < ( returnPtr->pid ) && pcbListPtr->PCB->state == READY ) || ( returnPtr->state ) == EXIT || ( ( returnPtr->state ) == WAITING && pcbListPtr->PCB->state != EXIT ) ) { returnPtr = ( pcbListPtr->PCB ); } pcbListPtr = pcbListPtr->nextPCB; } // perform output operations pid = returnPtr->pid; // due to preemptive nature, only output when process actually changes if( lastPid != pid ) { timeRemaining = returnPtr->timeRemaining; if( returnPtr->state != WAITING ) { sprintf( outputStr, "OS: Process %d selected with %d ms remaining\n", pid, timeRemaining ); displayHandler( configPtr, fileWriteListPtr, outputStr, false, false ); } lastPid = pid; } free( outputStr ); return returnPtr; } /* * Function Name: SRTF_P_Choice * Algorithm: shortest remaining time first. chooses the process with the * lowest timeRemaining attribute in ready queue, preemptive * Precondition: Processes with PCBs created exist * Postcondition: PCB with lowest pid is chosen and returned * Exceptions: None * Notes: None */ PCB *SRTF_P_Choice( PCBLinkedList **pcbList, ConfigDataType *configPtr, fileWriteList **fileWriteListPtr ) { // initialize function variables PCBLinkedList *pcbListPtr = *pcbList; char *outputStr = malloc( sizeof( char ) * MAX_STR_LEN ); int pid = 0; int timeRemaining; static int lastPid = -1; // set return pointer to first pcb PCB *returnPtr = ( pcbListPtr->PCB ); while ( pcbListPtr != NULL && pcbListPtr->PCB->pid != END_OF_PCB_LIST ) { // if pid is less than returnPtr pid set returnPtr to current PCB if( ( ( pcbListPtr->PCB->timeRemaining ) < ( returnPtr->timeRemaining ) && pcbListPtr->PCB->state == READY ) || ( returnPtr->state ) == EXIT || ( ( returnPtr->state ) == WAITING && pcbListPtr->PCB->state != EXIT ) ) { returnPtr = ( pcbListPtr->PCB ); } pcbListPtr = pcbListPtr->nextPCB; } // perform output operations pid = returnPtr->pid; // due to preemptive nature, only output when process actually changes if( lastPid != pid ) { timeRemaining = returnPtr->timeRemaining; if( returnPtr->state != WAITING ) { sprintf( outputStr, "OS: Process %d selected with %d ms remaining\n", pid, timeRemaining ); displayHandler( configPtr, fileWriteListPtr, outputStr, false, false ); } lastPid = pid; } free( outputStr ); return returnPtr; } /* * Function Name: RR_P_Choice * Algorithm: Round Robin Preemptive. Evenly distributes the given quanta of time (from the config file). * Precondition: Processes with PCBs created exist * Postcondition: PCB with lowest pid is chosen and returned * Exceptions: None * Notes: None */ PCB *RR_P_Choice( PCBLinkedList **pcbList, ConfigDataType *configPtr, fileWriteList **fileWriteListPtr ) { // initialize function variables PCBLinkedList *pcbListPtr = *pcbList; char *outputStr = malloc( sizeof( char ) * MAX_STR_LEN ); int pid = 0; int PCBListLength = getPCBListLength( pcbList ); int timeRemaining; int quantumLim = configPtr->quantumCycles; static int lastPid = 0; static int printDifPid = -1; static int quantumCycles = 0; // reset lastPid to cycle through linked list if( quantumCycles >= quantumLim ) { lastPid++; quantumCycles = 0; if( lastPid > PCBListLength ) { lastPid = 0; } } // set return pointer to first pcb PCB *returnPtr = ( pcbListPtr->PCB ); while ( pcbListPtr != NULL && pcbListPtr->PCB->pid != END_OF_PCB_LIST ) { if( ( ( pcbListPtr->PCB->pid ) == lastPid && quantumCycles < quantumLim && pcbListPtr->PCB->state == READY ) || ( returnPtr->state ) == EXIT || ( ( returnPtr->state ) == WAITING && pcbListPtr->PCB->state != EXIT ) ) { returnPtr = ( pcbListPtr->PCB ); } pcbListPtr = pcbListPtr->nextPCB; } // perform output operations pid = returnPtr->pid; // due to preemptive nature, only output when process actually changes if( printDifPid != pid ) { timeRemaining = returnPtr->timeRemaining; if( returnPtr->state != WAITING ) { sprintf( outputStr, "OS: Process %d selected with %d ms remaining\n", pid, timeRemaining ); displayHandler( configPtr, fileWriteListPtr, outputStr, false, false ); } printDifPid = pid; } free( outputStr ); if( returnPtr->state == READY ) { quantumCycles++; } return returnPtr; } /* * Function Name: SJF_N_Choice * Algorithm: shortest job first non-preemptive. * chooses the process with the least amount of time remaining * Precondition: Processes with PCBs created exist in the linked list structure * Postcondition: PCB with the shortest time remaining chosen and returned * Exceptions: None * Notes: None */ PCB *SJF_N_Choice( PCBLinkedList **pcbList, ConfigDataType *configPtr, fileWriteList **fileWriteListPtr ) { // initialize function variables PCBLinkedList *pcbListPtr = *pcbList; char *outputStr = malloc( sizeof( char ) * MAX_STR_LEN ); int pid = 0; int timeRemaining; // set return pointer to first pcb PCB *returnPtr = ( pcbListPtr->PCB ); while ( pcbListPtr != NULL && pcbListPtr->PCB->pid != END_OF_PCB_LIST ) { // if timeRemaining is less than returnPtr set to current PCB. // else will naturally default to FCFS when the timeRemaining is equal, // as processes are in order of pid in the structure if( ( ( pcbListPtr->PCB->timeRemaining ) < ( returnPtr->timeRemaining ) && pcbListPtr->PCB->state == READY ) || ( returnPtr->state ) == EXIT ) { returnPtr = ( pcbListPtr->PCB ); } pcbListPtr = pcbListPtr->nextPCB; } // perform output operations pid = returnPtr->pid; timeRemaining = returnPtr->timeRemaining; sprintf( outputStr, "OS: Process %d selected with %d ms remaining\n", pid, timeRemaining ); displayHandler( configPtr, fileWriteListPtr, outputStr, false, false ); free( outputStr ); return returnPtr; } /* * Function Name: getNextPID * Algorithm: keeps track of the pid by using a static variable to ensure * the process id of each process is always one more than the last * Precondition: None * Postcondition: PCB structs will be created with strictly linearly increasing pids * Exceptions: None * Notes: None */ int getNextPID() { static int pidCount = -1; pidCount++; return pidCount; } /* * Function Name: getPCBListLength * Algorithm: returns an integer of the length (zero-indexed) of the PCBLinkedList. If empty * returns -1 * Precondition: PCBLinkedList passed to function * Postcondition: Integer signifying list length returned * Notes: None */ int getPCBListLength( PCBLinkedList **PCBList ) { int length = -1; // if non-empty if( ( *PCBList )->PCB->pid != END_OF_PCB_LIST ) { PCBLinkedList *iterPtr = *PCBList; while( iterPtr->PCB->pid != END_OF_PCB_LIST ) { length++; iterPtr = iterPtr->nextPCB; } } return length; } /* * Function Name: getTimeFromOpCode * Algorithm: Given an op code, returns the time it will take in ms * Precondition: Valid op code * Postcondition: time in ms (int) returned * Exceptions: None * Notes: None */ int getTimeFromOpCode( ConfigDataType *configPtr, OpCodeType *opCodePtr ) { // initialize function variables int multiplier = 1; char *cmd = opCodePtr->command; if( compareString( cmd, "dev" )== STR_EQ ) { multiplier = configPtr->ioCycleRate; return ( long )( opCodePtr->intArg2 * multiplier ); } else if( compareString( cmd, "cpu" ) == STR_EQ ) { multiplier = configPtr->procCycleRate; return ( long )( opCodePtr->intArg2 * multiplier ); } // if neither for now return 0 return 0; } /* * Function Name: initializeProcesses * Algorithm: Sets all PCBs to ready state and displays accordingly * Precondition: PCBs initialized * Postcondition: All processes in ready queue * Exceptions: None * Notes: None */ void initializeProcesses( ConfigDataType *configPtr, fileWriteList **fileWriteListPtr, PCBLinkedList **PCBLinkedListPtr ) { PCBLinkedList *pcbListPtr = *PCBLinkedListPtr; while( pcbListPtr != NULL && pcbListPtr->PCB->pid != END_OF_PCB_LIST ) { changeProcessState( configPtr, fileWriteListPtr, pcbListPtr->PCB, READY ); pcbListPtr = ( pcbListPtr )->nextPCB; } } /* * Function Name: printPCBList * Algorithm: prints out information about PCBs for testing purposes * Precondition: PCBLinkedList passed is not empty * Postcondition: Data about each PCB is shown * Exceptions: None * Notes: None */ void printPCBList( PCBLinkedList **pcbLLPtr ) { PCBLinkedList *tempPtr = *pcbLLPtr; while( tempPtr != NULL ) { if( tempPtr->PCB->pid != END_OF_PCB_LIST ) { // not used during simulation, only used for testing purposes fprintf( stderr, "Iter pointer location:\t%p\n", (void*)pcbLLPtr ); printf( "Process:\t%d\n", (tempPtr)->PCB->pid ); printf( "State:\t%d\n", (tempPtr)->PCB->state ); printf( "Time Remaining:\t%d\n\n", (tempPtr)->PCB->timeRemaining ); // step linked list pointer forward tempPtr = ( tempPtr )->nextPCB; } else { tempPtr = NULL; } } } /* * Function Name: printMemoryList * Algorithm: prints out information about MemoryNode list for testing purposes * Precondition: MemoryNode list passed is not empty * Postcondition: Data about each MemoryNode is shown * Exceptions: None * Notes: None */ void printMemoryList( MemoryNode *memNodePtr, ConfigDataType *configPtr, fileWriteList **fileWriteListPtr, int displayType ) { MemoryNode *tempPtr = memNodePtr; char *writeStr = ( char * )malloc( sizeof( char ) * MAX_STR_LEN ); switch( displayType ) { case INIT: writeStr = "After memory initialization\n"; break; case SUCCESS: writeStr = "After allocate success\n"; break; case CLEARONE: writeStr = "After clear one process success\n"; break; case FAIL: writeStr = "After allocate overlap failure\n"; break; case CLEARALL: writeStr = "After clear all process success\nNo memory configured\n"; break; case ACCESS_SUCCESS: writeStr = "After access success\n"; break; case ACCESS_FAIL: writeStr = "After access failure\n"; break; } displayHandler( configPtr, fileWriteListPtr, STR_NEWLINE, true, false ); displayHandler( configPtr, fileWriteListPtr, writeStr, true, false ); if( displayType != CLEARALL ) { while( tempPtr->logicalStartIndex != END_OF_MEM_LIST ) { // not used during simulation, only used for testing purposes if( tempPtr->status == OPEN ) { fprintf( stderr, "%d [ Open, P#: x, 0-0 ] %d\n", tempPtr->physicalStartIndex, tempPtr->physicalEndIndex ); } else { fprintf( stderr, "%d [ Used, P#: %d, %d-%d ] %d\n", tempPtr->physicalStartIndex, tempPtr->pid, tempPtr->logicalStartIndex, tempPtr->logicalEndIndex, tempPtr->physicalEndIndex ); } // step linked list pointer forward tempPtr = tempPtr->nextNode; } if( tempPtr->status == OPEN ) { fprintf( stderr, "%d [ Open, P#: x, 0-0 ] %d\n", tempPtr->physicalStartIndex, tempPtr->physicalEndIndex ); } else { fprintf( stderr, "%d [ Used, P#: %d, %d-%d ] %d\n", tempPtr->physicalStartIndex, tempPtr->pid, tempPtr->logicalStartIndex, tempPtr->logicalEndIndex, tempPtr->physicalEndIndex ); } } displayHandler( configPtr, fileWriteListPtr, STR_NEWLINE, true, false ); // causes segfault //free( writeStr ); } /* * Function Name: printWriteList * Algorithm: displays fileWriteList for testing purposes * Precondition: List has at least one node ( with END_OF_STRLIST as strToWrite ) * Postcondition: Linked list of file display structs shown * Exceptions: None * Notes: None */ void printWriteList( fileWriteList **fileWriteListPtr ) { // not used during simulation, only used for testing purposes fileWriteList *fileWriteList = *fileWriteListPtr; fprintf( stderr, "\nPrinting fileWriteList:\n" ); while( compareString( fileWriteList->strToWrite, END_OF_STRLIST ) != STR_EQ ) { fprintf( stderr, fileWriteList->strToWrite ); fileWriteList = fileWriteList->nextStr; } fprintf( stderr, fileWriteList->strToWrite ); fprintf( stderr, "\n" ); } /* * Function Name: scanForNewPCBs * Algorithm: looks through metadata file and creates a PCB struct for each app start * Precondition: given pointer to metadata file * Postcondition: all new PCBs have been created and initialized * Exceptions: None * Notes: None */ void scanForNewPCBs( OpCodeType **metaDataMstrPtr, PCBLinkedList **PCBLinkedListPtr, ConfigDataType *configPtr ) { // initialize function variables PCBLinkedList *PCBLinkedListIterPtr = *PCBLinkedListPtr; // create iter pointer to PCBLL OpCodeType *tempCode = *metaDataMstrPtr; OpCodeType *appStartCode = *metaDataMstrPtr; int timeRemaining = 0; // loop through metadata while( tempCode != NULL ) { // if start is found if( compareString(tempCode->command, "app" ) == STR_EQ ) { if( compareString(tempCode->strArg1, "start" ) == STR_EQ ) { appStartCode = tempCode; } } // if not app end // do the math, add the time for a given op code create PCB after totaled timeRemaining? if( compareString( tempCode->command, "app" ) != STR_EQ || compareString(tempCode->strArg1, "end" ) != STR_EQ) { timeRemaining = timeRemaining + getTimeFromOpCode( configPtr, tempCode ); } // else app end else if( compareString( tempCode->command, "app" ) == STR_EQ && compareString(tempCode->strArg1, "end") == STR_EQ ) { // create new PCB node in linked list // function: createPCB createPCB( appStartCode, &PCBLinkedListIterPtr, timeRemaining ); timeRemaining = 0; } // step program counter to next op code tempCode = tempCode->nextNode; } // end loop through metadata } /* * Function Name: runProcess * Algorithm: runs through op code operations by taking in PCB struct and * utilizing the program counter * Precondition: Process is in ready mode * Postcondition: Process has executed until app end or an interrupt * Exceptions: None * Notes: None */ PCB *runProcess( ConfigDataType *configPtr, fileWriteList **fileWriteList, PCBLinkedList *PCBLinkedListPtr, PCB *PCB, MemoryNode *memNodePtr, MemoryNode **headPtr ) { // int to keep track of number of op codes // TODO: could cause issues with preemption, may need to add to PCB struct static int count = 0; PCB->ranForCycle = false; // for use with preemptive scheduling, exit loop after single cycle PCB->opCodeComplete = false; // when opcode is fully finished, ( cycles left is 0 ) for use with display if( PCB->state == READY ) { // set process to running changeProcessState( configPtr, fileWriteList, PCB, RUNNING ); } // initialize function variables OpCodeType *opCodePtr = PCB->programCounter; // long opTime = 0; char *displayStr = ( char * ) malloc ( sizeof(char) * MAX_STR_LEN ); char buffer[MAX_STR_LEN]; // keep track of first allocation for output message static bool firstAlloc = true; bool accessSuccess = false; // set to next op code as to not display app start as an operation if( compareString( opCodePtr->command, "app" ) == STR_EQ && compareString( opCodePtr->strArg1, "start" ) == STR_EQ ) { opCodePtr = opCodePtr->nextNode; PCB->programCounter = opCodePtr; } // while op code is not app end TODO: exit while loop after a single cycle while( ( compareString(opCodePtr->command, "app" ) != STR_EQ && compareString(opCodePtr->strArg1, "end" ) != STR_EQ ) && PCB->state != EXIT // may not need this conditional && !PCB->ranForCycle ) { // BEGIN OP SECTION // if haven't outputted beginning of operation yet if( !PCB->startedOp ) { // output op code start // if dev, specify input or output if( compareString( opCodePtr->command, "dev" ) == STR_EQ ) { sprintf( buffer, "Process: %d, %s %s operation start\n", PCB->pid, opCodePtr->strArg1, opCodePtr->inOutArg ); copyString( displayStr, buffer ); } // if mem, try to allocate memory else if( compareString( opCodePtr->command, "mem" ) == STR_EQ ) { if( compareString( opCodePtr->strArg1, "allocate" ) == STR_EQ ) { sprintf( buffer, "Process: %d, %s allocate request (%d, %d)\n", PCB->pid, opCodePtr->command, opCodePtr->intArg2, opCodePtr->intArg3 ); copyString( displayStr, buffer ); displayHandler( configPtr, fileWriteList, displayStr, false, false ); // if first allocation display memory map if( firstAlloc ) { if( configPtr->logToCode != LOGTO_FILE_CODE ) { printMemoryList( *headPtr, configPtr, fileWriteList, INIT ); } firstAlloc = false; } allocateMemory( configPtr, fileWriteList, &PCBLinkedListPtr, memNodePtr, headPtr, PCB, opCodePtr->intArg2, opCodePtr->intArg3 ); // TODO: memory alloc functions may affect tracking if start of operation has been printed // if state = exit startedOp = false? } else if( compareString( opCodePtr->strArg1, "access" ) == STR_EQ ) { sprintf( buffer, "Process: %d, %s access request (%d, %d)\n", PCB->pid, opCodePtr->command, opCodePtr->intArg2, opCodePtr->intArg3 ); accessSuccess = accessMemory( memNodePtr, opCodePtr->intArg2, opCodePtr->intArg3, PCB->pid ); copyString( displayStr, buffer ); displayHandler( configPtr, fileWriteList, displayStr, false, false ); if( accessSuccess ) { if( configPtr->logToCode != LOGTO_FILE_CODE ) { printMemoryList( *headPtr, configPtr, fileWriteList, ACCESS_SUCCESS ); } } else { if( configPtr->logToCode != LOGTO_FILE_CODE ) { printMemoryList( *headPtr, configPtr, fileWriteList, ACCESS_FAIL ); } } } } else { sprintf( buffer, "Process: %d, %s %s operation start\n", PCB->pid, opCodePtr->command, opCodePtr->strArg1 ); copyString( displayStr, buffer ); } // test if in exit state to have memory operation output in order if( PCB->state != EXIT && compareString( opCodePtr->command, "mem" ) != STR_EQ ) { // if first op code of process, write newline if( count == 0 ) { displayHandler( configPtr, fileWriteList, displayStr, false, true ); count++; } else { displayHandler( configPtr, fileWriteList, displayStr, false, false ); count++; } } PCB->startedOp = true; } // TIMER SECTION // // exit loop after running individual cycle, and keep track of if OpCode has started // don't allow 'mem' op code as runTimerForCycle will not decrement form intArg2 resulting in infinite loop while( opCodePtr->intArg2 > 0 && !PCB->ranForCycle && compareString( opCodePtr->command, "mem" ) != STR_EQ ) { runTimerForCycle( configPtr, opCodePtr, PCB, &PCBLinkedListPtr, fileWriteList ); // if preemptive, only run for a single cycle and exit if( configPtr->cpuSchedCode == CPU_SCHED_FCFS_P_CODE || configPtr->cpuSchedCode == CPU_SCHED_SRTF_P_CODE || configPtr->cpuSchedCode == CPU_SCHED_RR_P_CODE ) { PCB->ranForCycle = true; } } // after loop, check if op code can be finished / displayed if( compareString( opCodePtr->command, "mem" ) != STR_EQ && opCodePtr->intArg2 <= 0 ) { PCB->opCodeComplete = true; } // mem op code does not take time, so mark as finished else if( compareString( opCodePtr->command, "mem" ) == STR_EQ ) { PCB->opCodeComplete = true; // need to mark that ran for cycle, otherwise will continue in loop and only run one cycle in next op code as opCodeComplete is now true if( configPtr->cpuSchedCode == CPU_SCHED_FCFS_P_CODE || configPtr->cpuSchedCode == CPU_SCHED_SRTF_P_CODE || configPtr->cpuSchedCode == CPU_SCHED_RR_P_CODE ) { PCB->ranForCycle = true; } } // END OP SECTION // // if complete, output op code finished if( PCB->opCodeComplete ) { //PCB->startedOp = false; PCB->opCodeComplete = false; // output op code end // if dev, specify input or output if( compareString( opCodePtr->command, "dev" ) == STR_EQ ) { sprintf( buffer, "Process: %d, %s %s operation end\n", PCB->pid, opCodePtr->strArg1, opCodePtr->inOutArg ); copyString( displayStr, buffer ); } else if( compareString( opCodePtr->command, "mem" ) == STR_EQ ) { if( compareString( opCodePtr->strArg1, "allocate" ) == STR_EQ ) { if( PCB->state != EXIT ) { // assume successful allocation since program has not segfaulted sprintf( buffer, "Process: %d, succesful mem allocate request\n", PCB->pid); copyString( displayStr, buffer ); } // failed, so process will segfault. reset count to print newline before next op code else { count = 0; } } else if( compareString( opCodePtr->strArg1, "access" ) == STR_EQ ) { if( accessSuccess ) { sprintf( buffer, "Process: %d, succesful mem access request\n", PCB->pid); copyString( displayStr, buffer ); } else { sprintf( buffer, "Process: %d, failed mem access request\n", PCB->pid); copyString( displayStr, buffer ); } } } else { sprintf( buffer, "Process: %d, %s %s operation end\n", PCB->pid, opCodePtr->command, opCodePtr->strArg1 ); copyString( displayStr, buffer ); } if( PCB->state != EXIT ) { displayHandler( configPtr, fileWriteList, displayStr, false, false ); } // set next op code opCodePtr = opCodePtr->nextNode; // set pcb op code PCB->programCounter = opCodePtr; PCB->startedOp = false; } } // END PROCESS SECTION // // if preemptive && last op code, or if not preemptive if( ( ( configPtr->cpuSchedCode == CPU_SCHED_RR_P_CODE || configPtr->cpuSchedCode == CPU_SCHED_SRTF_P_CODE || configPtr->cpuSchedCode == CPU_SCHED_FCFS_P_CODE ) && ( compareString(opCodePtr->command, "app" ) == STR_EQ && compareString(opCodePtr->strArg1, "end" ) == STR_EQ ) ) || configPtr->cpuSchedCode == CPU_SCHED_FCFS_N_CODE || configPtr->cpuSchedCode == CPU_SCHED_SJF_N_CODE ) { // reset first op code counter count = 0; // write that process has ended sprintf( displayStr, "OS: Process %d ended\n", PCB->pid ); displayHandler( configPtr, fileWriteList, displayStr, false, true ); changeProcessState( configPtr, fileWriteList, PCB, EXIT ); clearProcessMemory( memNodePtr, headPtr, PCB->pid ); if( configPtr->logToCode != LOGTO_FILE_CODE ) { printMemoryList( *headPtr, configPtr, fileWriteList, CLEARONE ); } free( displayStr ); } return PCB; } /* * Function Name: runSim * Algorithm: master driver for simulator operations; conducts OS simulation * with varying scheduling strategies and varying numbers * of processes * Precondition: given head pointer to config data and meta data * Postcondition: simulation is provided, file output is provided as configured * Exceptions: None * Notes: None */ void runSim( ConfigDataType *configPtr, OpCodeType *metaDataMstrPtr ) { // initialize function variables bool programEnding; // bool to keep track of if 'waiting for all processes' has been displayed yet or not bool displayedWaitingForProcess = false; int quantumCounter = 0; int quantumLim = configPtr->quantumCycles; char buffer[ MAX_STR_LEN ]; char *displayStr = malloc( sizeof( char ) * MAX_STR_LEN ); PCB *currentProcess; PCB *lastProcess; // initialize PCBLinkedList node and PCB PCBLinkedList *PCBLinkedListPtr = ( PCBLinkedList * )malloc( sizeof( PCBLinkedList ) ); PCBLinkedListPtr->PCB = ( PCB * )malloc( sizeof( PCB ) ); // initialize beginning of memory list MemoryNode *memoryListPtr = ( MemoryNode * )malloc( sizeof( MemoryNode ) ); memoryListPtr->logicalStartIndex = END_OF_MEM_LIST; memoryListPtr->logicalEndIndex = 0; memoryListPtr->pid = END_OF_PCB_LIST; memoryListPtr->status = OPEN; memoryListPtr->physicalStartIndex = 0; memoryListPtr->physicalEndIndex = configPtr->memAvailable - 1; MemoryNode **memoryHeadPtr = &memoryListPtr; // initialize timeStr to zero timer char *timeStr = ( char * )malloc( sizeof( char ) * MAX_STR_LEN ); // initialize fileWriteList if logging to file fileWriteList *toFileList = ( fileWriteList * )malloc( sizeof( fileWriteList ) ); // set end of list str for first node toFileList->strToWrite = END_OF_STRLIST; // create all PCBs using helper function // function: scanForNewPCBs scanForNewPCBs( &metaDataMstrPtr, &PCBLinkedListPtr, configPtr ); // start timer accessTimer( ZERO_TIMER, timeStr ); free( timeStr ); displayHandler( configPtr, &toFileList, "OS: Simulator start\n", false, false ); // search for processes in new and set them to ready // function: initializeProcesses initializeProcesses( configPtr, &toFileList, &PCBLinkedListPtr ); // initialize 'lastProcess' for quantum time use lastProcess = chooseNextProcess( configPtr, &toFileList, PCBLinkedListPtr ); // start main loop programEnding = checkProgramEnd( &PCBLinkedListPtr ); // while all processes are not in exit state while( !programEnding ) { // find next process // function: chooseNextProcess currentProcess = chooseNextProcess( configPtr, &toFileList, PCBLinkedListPtr ); if( currentProcess != NULL && currentProcess->state != EXIT ) { // run process // function: runProcess if( currentProcess->state != WAITING ) { if( configPtr->cpuSchedCode == CPU_SCHED_FCFS_P_CODE || configPtr->cpuSchedCode == CPU_SCHED_SRTF_P_CODE || configPtr->cpuSchedCode == CPU_SCHED_RR_P_CODE ) { // keep track of time quanta to intermittently interrupt processes that take longer if( currentProcess->pid != lastProcess->pid || !currentProcess->startedOp ) { quantumCounter = 0; } // display quantum timeout operations if( quantumCounter == quantumLim ) { sprintf( buffer, "OS: Process %d quantum time out, %s %s operation end\n", currentProcess->pid, currentProcess->programCounter->command, currentProcess->programCounter->strArg1 ); copyString( displayStr, buffer ); displayHandler( configPtr, &toFileList, displayStr, false, true ); sprintf( buffer, "Process: %d, %s %s operation start\n", currentProcess->pid, currentProcess->programCounter->command, currentProcess->programCounter->strArg1 ); copyString( displayStr, buffer ); displayHandler( configPtr, &toFileList, displayStr, false, true ); quantumCounter = 0; } } lastProcess = currentProcess; displayedWaitingForProcess = false; runProcess( configPtr, &toFileList, PCBLinkedListPtr, currentProcess, memoryListPtr, memoryHeadPtr ); quantumCounter++; } else { if( !displayedWaitingForProcess ) { displayHandler( configPtr, &toFileList, "OS: all processes waiting\n", false, false ); displayedWaitingForProcess = true; } // decrement from cycles from waiting processes so eventually one will be ready runTimerForCycle( configPtr, currentProcess->programCounter, currentProcess, &PCBLinkedListPtr, &toFileList ); } // TODO check for process allocation still in MemoryList of exited processes // check for app end ( all processes in exit state ) programEnding = checkProgramEnd( &PCBLinkedListPtr ); } else { // fprintf( stderr, "I don't know what I'm doing aaa\n"); currentProcess = chooseNextProcess( configPtr, &toFileList, PCBLinkedListPtr ); // programEnding = true; } } // end main loop displayHandler( configPtr, &toFileList, "OS: System stop\n", false, false ); // display that all memory has been cleared if( configPtr->logToCode != LOGTO_FILE_CODE ) { printMemoryList( memoryListPtr, configPtr, &toFileList, CLEARALL ); } // free PCB struct memory // function: clearPCBList clearPCBList( PCBLinkedListPtr ); // free Memory List of allocations // function: clearMemoryList clearMemoryList( memoryListPtr ); displayHandler( configPtr, &toFileList, "OS: Simulation end\n", false, false ); // if config specifies writing to file if ( ( configPtr->logToCode == LOGTO_FILE_CODE ) || ( configPtr->logToCode == LOGTO_BOTH_CODE ) ) { // write to file and free fileWriteList memory // function: writeToFile writeToFile( configPtr, &toFileList ); } free( displayStr ); } /* * Function Name: runTimerFor * Algorithm: creates a thread and sets limit for inputted milliseconds * Precondition: None * Postcondition: None * Exceptions: None * Notes: None */ void runTimerFor( long milliseconds ) { // initialize threadInfo struct threadInfo *timerThreadDataPtr = ( threadInfo * )malloc( sizeof( threadInfo ) ); timerThreadDataPtr->milliseconds = milliseconds; timerThreadDataPtr->currentTime = 0; timerThreadDataPtr->timeString = malloc( 17 * sizeof( char ) ); timerThreadDataPtr->outputTime = false; timerThreadDataPtr->keepGoing = true; void *threadExitStatus; pthread_t timerThread; // create pthread pthread_create( &timerThread, NULL, &runTimerThread, (void * )timerThreadDataPtr ); // join pthread pthread_join( timerThread, &threadExitStatus ); // free threadInfo struct free( timerThreadDataPtr->timeString ); free( timerThreadDataPtr ); } /* * Function Name: runTimerForCycle * Algorithm: creates a thread and runs timer for however many ms a CPU cycle takes. * Additionally handles decrementing from cycles of waiting processes. * Precondition: ConfigDataType pointer passed with cpu cycle information * Postcondition: program delayed for a CPU cycle's worth of ms, waiting processes' op codes are decremented. * Exceptions: None * Notes: None */ void runTimerForCycle( ConfigDataType *configPtr, OpCodeType *opCodePtr, PCB *PCB, PCBLinkedList **PCBLinkedListPtr, fileWriteList **writeList ) { // initializing ms to 0 allows for function to be called with memory // allocation/access op code without issue long milliseconds = 0; char *cmd = opCodePtr->command; int toDecrement = 0; // char buffer[ MAX_STR_LEN ]; // char *outputStr; PCBLinkedList *tempPtr = *PCBLinkedListPtr; if( compareString( cmd, "dev" ) == STR_EQ ) { if( ( configPtr->cpuSchedCode == CPU_SCHED_FCFS_P_CODE || configPtr->cpuSchedCode == CPU_SCHED_SRTF_P_CODE || configPtr->cpuSchedCode == CPU_SCHED_RR_P_CODE ) && opCodePtr->intArg2 > 0 ) { if( PCB->state != WAITING ) { // TODO: fix sprintf bugginess/corruption //outputStr = malloc( sizeof(char) * MAX_STR_LEN ); //if( compareString( opCodePtr->strArg1, "in" ) == STR_EQ ) // { // sprintf( buffer, "OS: Process %d blocked for input operation\n", PCB->pid ); // } //else if( compareString( opCodePtr->strArg1, "out" ) == STR_EQ ) // { // sprintf( buffer, "OS: Process %d blocked for output operation\n", PCB->pid ); // } //copyString( outputStr, buffer ); //displayHandler( configPtr, writeList, outputStr, false, true ); //free( outputStr ); // change process to WAITING as is an I/O operation changeProcessState( configPtr, writeList, PCB, WAITING ); } } milliseconds = ( long ) configPtr->ioCycleRate; // decrement from op code cycles remaining opCodePtr->intArg2 = opCodePtr->intArg2 - 1; } else if( compareString( cmd, "cpu" ) == STR_EQ ) { milliseconds = ( long ) configPtr->procCycleRate; // cannot decrement outside of if statement as could affect mem op codes opCodePtr->intArg2 = opCodePtr->intArg2 - 1; } runTimerFor( milliseconds ); PCB->timeRemaining = PCB->timeRemaining - milliseconds; // loop through list of op codes and subtract a cycle off / account for remainders // TODO: subtract timeRemaining from different PCBs while( tempPtr->PCB->pid != END_OF_PCB_LIST && compareString( opCodePtr->command, "mem" ) != STR_EQ ) { // TODO: account for timings of different op code types if( tempPtr->PCB->state == WAITING ) { // Already decremented intArg2 in current PCB if( tempPtr->PCB->pid != PCB->pid ) { // check how many cycles to decrement ( due to differences in I/O / CPU cycle time ) // function decrementCycles toDecrement = decrementCycles( configPtr, opCodePtr, tempPtr->PCB ); tempPtr->PCB->programCounter->intArg2 = tempPtr->PCB->programCounter->intArg2 - toDecrement; } if( tempPtr->PCB->programCounter->intArg2 <= 0 ) { changeProcessState( configPtr, writeList, tempPtr->PCB, READY ); } //fprintf( stderr, "pcb: %d\t cycles: %d\n", tempPtr->PCB->pid, tempPtr->PCB->programCounter->intArg2 ); } tempPtr = tempPtr->nextPCB; } } /* * Function Name: writeToFile * Algorithm: Destructive method that simultaneously writes from linked list * structure to file and frees memory from structures. Intended for * use at the end of the program * Precondition: Config file must specify log to file or both * Postcondition: File specified written to and data structure will be cleared, * returns NULL * Exceptions: None * Notes: None */ fileWriteList *writeToFile( ConfigDataType *configPtr, fileWriteList **printList ) { // initialize function variables char *filename = ( char * )configPtr->logToFileName; fileWriteList *fileWriteListPtr = *printList; // open file FILE *filePtr = fopen( filename, "w" ); // if opening file fails, exit with error message if( filePtr == NULL ) { displayHandler( configPtr, NULL, "File creation has failed, aborting operation\n", true, false ); exit( -1 ); } // write linked list to file while( compareString( fileWriteListPtr->strToWrite, END_OF_STRLIST ) != STR_EQ ) { // output to file fprintf( filePtr, "%s", fileWriteListPtr->strToWrite ); // keep track of next location fileWriteListPtr = fileWriteListPtr->nextStr; } // close the file fclose( filePtr ); // free linked list with recursive helper method // function: clearWriteList clearWriteList( printList ); // return empty fileWriteList return NULL; } /* * Function Name: segfault * Algorithm: Performs necessary operations after a memory allocation is found to be invalid * Precondition: None * Postcondition: None * Exceptions: None * Notes: None */ void segfault( ConfigDataType *configPtr, fileWriteList **printList, PCBLinkedList **pcbLLPtr, MemoryNode *memNodePtr, MemoryNode **headPtr, PCB *pcbPtr ) { if( configPtr->logToCode != LOGTO_FILE_CODE ) { printMemoryList( *headPtr, configPtr, printList, FAIL ); } // initialize function variables char *outputStr = ( char * )malloc( sizeof(char) * MAX_STR_LEN ); // output allocation failed sprintf( outputStr, "Process %d, failed mem allocate request\n", pcbPtr->pid ); displayHandler( configPtr, printList, outputStr, false, false ); // set process to exit // need pcb to call function: changeProcessState changeProcessState( configPtr, printList, pcbPtr, EXIT ); pcbPtr->opCodeComplete = true; pcbPtr->ranForCycle = true; clearProcessMemory( memNodePtr, headPtr, pcbPtr->pid ); free( outputStr ); } /* * Function Name: verifyLogicalAllocation * Algorithm: Given the start and end index in memory, check if invalid allocation * Do nothing if valid; if invalid, 'segfault' by setting boolean value to true * Precondition: None * Postcondition: None * Exceptions: None * Notes: None */ void verifyLogicalAllocation( MemoryNode *memNodePtr, int logicalStartIndex, int logicalEndIndex, bool *collision ) { // TODO: Ensure only verifies between logical addresses of the same pid if( ( memNodePtr->logicalStartIndex >= logicalStartIndex && memNodePtr->logicalStartIndex <= logicalEndIndex ) || ( ( memNodePtr->logicalEndIndex >= logicalStartIndex && memNodePtr->logicalEndIndex <= logicalEndIndex ) ) ) { *collision = true; } }