#include #include #include #include #include #include #include /************************************************************************** * Authors: Gary Fleming, Amber McCloughan * Class: CIS452 - Operating Systems Concepts * Professor: Dr. Greg Wolffe * Date: 2/14/2019 * * Program that starts out with a dispatcher thread. This thread * waits on user input. When it receives a filename string from * the user, it spawns a worker thread that goes out to * "retrieve" the filename from the "file server". On an exit, * the program prints statistics about files serviced. * * To end the program, do a Ctrl + C. ***************************************************************************/ // Input limit. #define CHAR_LIMIT 1024 // See function implementations for information. void *file_request(void *arg); void sigHandler(int); // File counter. int counter; // File access time counter. int timeCounter; // Mutex. pthread_mutex_t lock; // Input buffer. char *input; /** * The main function for driving the program. */ int main() { // Seeding randomness. srand(time(NULL)); /* * Initializing the mutex. * I figured out how to do this using the following article: * https://www.geeksforgeeks.org/mutex-lock-for-linux-thread-synchronization/ */ pthread_mutex_init(&lock, NULL); // Thread ID holder. pthread_t thread1; // Status variable for checking if pthread_create failed. int status; // Initializing file counter. counter = 0; // Allocating memory for input buffer. input = malloc(CHAR_LIMIT * sizeof(char)); // Binding signal handler for keyboard interrupt. signal(SIGINT, sigHandler); // The dispatching loop. while (1) { // User input for filename. fprintf(stderr, "Enter file name: "); input = fgets(input, CHAR_LIMIT, stdin); // Removing newline character from input. strtok(input, "\n"); // Spawning a thread to deal with the "file" requested. if ((status = pthread_create(&thread1, NULL, file_request, strdup(input))) != 0) { fprintf(stderr, "Thread creation error %d: %s.\n", status, strerror(status)); exit(1); } } return 0; } /** * Function executed by worked threads. Randomly decides * whether the "file" can be accessed from the disk cache * (80% chance) or if it has to be fetched from the hard * drive (20% chance). If the "file" is accessed from cache, * the thread sleeps for a second. If the "file" is accessed * from the hard drive, the thread sleeps for 7-10 seconds. * A mutex is used to ensure mutual exclusion for writes. * * @param arg, the name of the "file" to be handled by the * worker thread. */ void *file_request(void *arg) { // Probability roll for HDD or cache. int val = (rand() % 5) + 1; // Variable used for sleep time. int val2; // File accessed from disk. if (val == 5) { // 7-10 seconds. val2 = (rand() % 4) + 7; } // File accessed from cache. else { // 1 second. val2 = 1; } // Sleep for seconds decided on above. sleep(val2); // Serving the file. fprintf(stderr, "\nServing file \"%s\" after %i seconds.\n", (char *)arg, val2); // Writing to global variables. Mutex lock! pthread_mutex_lock(&lock); counter++; timeCounter += val2; pthread_mutex_unlock(&lock); // Freeing allocated memory. free(arg); // Goodbye (no need to return anything). return NULL; } /** * Handler for signals. Bound for the one signal handled * in this application: SIGINT (program interrupt signal ^C). * * @param sigNum, the identifying number of the signal * sent and now being handled. According to the * man page for signal, SIGINT = 2. */ void sigHandler(int sigNum) { if (sigNum == SIGINT) { // Exit message. printf(" received. That's it, I'm shutting you down...\n"); printf("Files serviced: %i.\n", counter); printf("Avg. file access time: %lf seconds.\n", (double)timeCounter / (double)counter); // Destroying mutex. pthread_mutex_destroy(&lock); // Freeing user input pointer. free(input); // Exiting program. exit(0); } }