#include "simulator.h" /* * 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];// = { "NEW", "READY", "RUNNING", "WAITING", "EXIT" }; // TODO clean up to be more elegant possibleStates[ 0 ] = "NEW"; possibleStates[ 1 ] = "READY"; possibleStates[ 2 ] = "RUNNING"; possibleStates[ 3 ] = "WAITING"; possibleStates[ 4 ] = "EXIT"; writeStr = malloc( sizeof( char ) * MAX_STR_LEN ); // temporary solution due to intToChar( pid, writeStr ); char *firstState = possibleStates[PCB->state]; // sprintf log file corruption char *nextState = possibleStates[state]; sprintf( writeStr, "%d", pid ); 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 != -1 ) { 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 //printWriteList( fileWriteList ); // 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 FCFS_N_Choice( &PCBLinkedListPtr, configPtr, fileWriteList ); break; case CPU_SCHED_SRTF_P_CODE: return FCFS_N_Choice( &PCBLinkedListPtr, configPtr, fileWriteList ); break; case CPU_SCHED_FCFS_P_CODE: return FCFS_N_Choice( &PCBLinkedListPtr, configPtr, fileWriteList ); break; case CPU_SCHED_RR_P_CODE: return FCFS_N_Choice( &PCBLinkedListPtr, configPtr, fileWriteList ); break; case CPU_SCHED_FCFS_N_CODE: return FCFS_N_Choice( &PCBLinkedListPtr, configPtr, fileWriteList ); break; } return NULL; } /* * Function Name: clearPCBList * Algorithm: frees memory used in Linked List structure * Precondition: None * Postcondition: Linked List is freed and NULL returned * Exceptions: None * Notes: None */ PCBLinkedList *clearPCBList( PCBLinkedList *pcbList ) { // initialize function variables // check if pcbList is null if( pcbList->PCB->pid != -1 ) { // recurse until empty PCB clearPCBList( pcbList->nextPCB ); } // free PCB and PCBLinkedList node free( pcbList->PCB ); free( pcbList ); // if it is, return null return NULL; } /* * 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 = -1; // 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; // step PCBLL pointer forward to nextPCB (empty) *PCBLLIterPtr = ( *PCBLLIterPtr )->nextPCB; } /* * 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 = false; // 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 = true; } // 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); } /* * 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 ); //printPCBList( &pcbListPtr ); while ( pcbListPtr != NULL && pcbListPtr->PCB->pid != -1 ) { // 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; } 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: 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 ); } return 0; // if neither for now 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 != -1 ) { 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 != -1 ) { // 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: 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 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 ) { // int to keep track of number of op codes // TODO: could cause issues with preemption, may need to add to PCB struct int count = 0; // 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]; // while op code is not app end while( compareString(opCodePtr->command, "app" ) != STR_EQ || compareString(opCodePtr->strArg1, "end" ) != STR_EQ ) { // get run time of op code // function: getTimeFromOpCode opTime = getTimeFromOpCode( configPtr, opCodePtr ); // output op code start sprintf( buffer, "Process: %d %s %s operation start\n", PCB->pid, opCodePtr->command, opCodePtr->strArg1 ); copyString( displayStr, buffer ); // 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++; } // start timer thread // function: runTimerFor runTimerFor( opTime ); // output op code end sprintf( buffer, "Process: %d %s %s operation end\n", PCB->pid, opCodePtr->command, opCodePtr->strArg1 ); copyString( displayStr, buffer ); displayHandler( configPtr, fileWriteList, displayStr, false, false ); // set next op code opCodePtr = opCodePtr->nextNode; // set pcb op code PCB->programCounter = opCodePtr; } // 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 ); PCB->timeRemaining = 0; 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; PCB *currentProcess; // initialize PCBLinkedList node and PCB PCBLinkedList *PCBLinkedListPtr = ( PCBLinkedList * )malloc( sizeof( PCBLinkedList ) ); PCBLinkedListPtr->PCB = ( PCB * )malloc( sizeof( PCB ) ); // 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; // TODO: check if -dm or -dc flags are used and output to file is selected // if so add information to output file // need to add boilerplate for beginning/ending of file as well // create all PCBs using helper function // function: scanForNewPCBs scanForNewPCBs( &metaDataMstrPtr, &PCBLinkedListPtr, configPtr ); // start timer accessTimer( ZERO_TIMER, 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 ); // start main loop programEnding = checkProgramEnd( &PCBLinkedListPtr ); // while all processes are not in exit state while( !programEnding ) { // find next process TODO: more thorough state checking // function: chooseNextProcess currentProcess = chooseNextProcess( configPtr, &toFileList, PCBLinkedListPtr ); if( currentProcess != NULL ) { // run process // function: runProcess runProcess( configPtr, &toFileList, PCBLinkedListPtr, currentProcess ); // TODO check for input (interrupts) // TODO check for output (interrupts) // TODO check for idle (no processes found / all processes are done) // TODO check for memory // TODO check for CPU // TODO check for and resolve interrupts ( sim04 ) // check for app end ( all processes in exit state ) programEnding = checkProgramEnd( &PCBLinkedListPtr ); } else { programEnding = true; } } // end main loop displayHandler( configPtr, &toFileList, "OS: System stop\n", false, false ); // free PCB struct memory // function: clearPCBList clearPCBList( PCBLinkedListPtr ); 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( timeStr ); } /* * 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 ); } /* * 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; }