// **************************************************************************************************************************************** // Anna Savarin // Operating Systems Concepts (Honors) // Professor Chee Yap // Fall 2006 // HW #2 // // File: gcd.c // Given an input of a sequence of pairs of numbers, the parent process spawns one child per pair to compute the GCD of that number pair // using Euclid's algorithm. The children communicate with the parent via two-way pipes as the modulus operation is performed only by // the parent. The parent then outputs the resulting GCDs in the same order as the input (i.e. first input pair will correspond to the // first GCD in the output, etc.). All children are serviced by the parent in a round robin fashion. Reading of pipes by the parent is // performed in a non-blocking fashion. The program's operation is timed, so that every output is accompanied by the information about // how much time was taken to compute all the GCDs from the input. // // Euclid's algorithm says that, in order to compute the greatest common denominator of two integers m and n, m >= n > 0, one must take the // modulus m mod n, then take the modulus n mod previous result, etc. until the answer is 0. The second operand in the final modulus // operation is the GCD of the two numbers. By convention, GCD (0,0)=0. // // Assumptions about the input: (1) All input consists of positive integers only; (2) Number of integers in the input is strictly even. // // WARNING: The largest integer in the input should not exceed the range of a positive signed int. The behavior is undefined shoud this // constraint not be met. // **************************************************************************************************************************************** #include #include #include #include #include #include #include #include #include extern int errno; /* System error number */ void syserr ( char * ); /* For routine termination and error reporting in cases of error */ void setFlag ( int, int ); /* Routine for setting status flag for pipes; provided by Professor Yap */ void clearBuffer ( char * ); /* For emptying out the buffer */ int modViaSubtraction ( int, int ); /* Routine for computing the modulus of two positive integers using repeated subtraction */ int main ( int argc, char *argv [ ] ) { /* Start the clocks in the very beginning */ time_t t0 = time ( NULL ); /* Wall clock */ time_t t1; clock_t c0 = clock ( ); /* CPU clock */ clock_t c1; int A, B, a, b, tmp; /* For storing interim operands in GCD computations; uppercase for parent, lowercase for children */ int i, j; /* For tracking number of characters coming into the buffers; i for parent, j for children */ int counter; /* Generic counter for array traversals */ int numChildren = -1; /* To keep track of active children; child count starting with 0 */ char parentBuffer [ 256 ]; char childBuffer [ 256 ]; int numIntegers = argc - 1; int numResults = ( argc - 1 ) / 2; int inputIntegers [ numIntegers ]; int outputResults [ numResults ]; int *pipes [ numIntegers ]; /* Initialize the buffers */ for ( counter = 0; counter < 256; counter ++ ) { parentBuffer [ counter ] = 0x0; childBuffer [ counter ] = 0x0; } /* Read in the arguments from stdin */ for ( counter = 0; counter < numIntegers; counter ++ ) inputIntegers [ counter ] = atoi ( argv [ counter + 1 ] ); /* Set up the pipes such that even-numbered pipes are parent-to-child and odd-numbered ones are child-to-parent */ for ( counter = 0; counter < numIntegers; counter += 2 ) { pipes [ counter ] = ( int * ) calloc ( 2 * sizeof ( int ), sizeof ( int ) ); pipes [ counter + 1 ] = ( int * ) calloc ( 2 * sizeof ( int ), sizeof ( int ) ); if ( pipe ( pipes [ counter ] ) == -1 ) syserr ( "Error while setting up parent to child pipe... Exiting the program." ); if ( pipe ( pipes [ counter + 1 ] ) == -1 ) syserr ( "Error while setting up child to parent pipe... Exiting the program." ); /* Set up non-blocking read by parent from child */ setFlag ( *( pipes [ counter + 1 ] ), O_NONBLOCK ); } /* Begin spawning children and assigning integer pairs to them for computing GCD */ for ( counter = 0; counter < numIntegers; counter += 2 ) { numChildren = counter / 2; i = sprintf ( parentBuffer, "%d %d", inputIntegers [ counter ], inputIntegers [ counter + 1 ] ); write ( *( pipes [ counter ] + 1 ), parentBuffer, i ); clearBuffer ( parentBuffer ); switch ( fork ( ) ) { case -1: syserr ( "fork" ); case 0: execlp ( "sleep", "sleep", 1000000, NULL ); /* Let the child sleep to avoid "mysterious" behavior mentioned in class */ /* Child continues execution by retrieving the integer pair for which the GCD must be computed */ j = read ( *( pipes [ counter ] ), childBuffer, sizeof ( childBuffer ) ); j = sscanf ( childBuffer, "%d %d", &a, &b ); while ( ! ( a == 1 || b == 1 || a == b ) ) { clearBuffer ( childBuffer ); /* Maintain the convention that the first number will be larger than the second */ if ( a < b ) { tmp=a; a=b; b=tmp;} j = sprintf ( childBuffer, "%d %d", a, b ); write ( *( pipes [ counter + 1 ] + 1 ), childBuffer, j ); /* Send the request for a mod b to the parent */ clearBuffer ( childBuffer ); a = b; /* Read the response in a blocking way */ j = read ( *( pipes [ counter ] ), childBuffer, sizeof ( childBuffer ) ); j = sscanf ( childBuffer, "%d", &b ); } } } /* Continued parent execution once the children have been delegated their responsibilities */ while ( numChildren >= 0 ) { /* Read through the pipes in a nonblocking way and service the children only if something was written; sequence through the array and loop around thus achieving round robin */ for ( counter = 0; counter < numIntegers; counter += 2 ) { i = read ( *( pipes [ counter + 1 ] ), parentBuffer, sizeof ( parentBuffer ) ); if ( i != -1 ) { i = sscanf ( parentBuffer, "%d %d", &A, &B ); clearBuffer ( parentBuffer ); if ( B == 0 ) { outputResults [ counter / 2 ] = A; numChildren --; } else { i = sprintf ( parentBuffer, "%d", modViaSubtraction ( A, B ) ); write ( *( pipes [ counter ] + 1 ), parentBuffer, i ); } } } } /* Print out the results in the correct order */ for ( counter = 0; counter < numResults; counter ++ ) printf ( "%d ", outputResults [ counter ] ); printf ( "\n" ); /* Finish timing the program and report findings */ t1 = time ( NULL ); c1 = clock ( ); printf ( "Wall clock time is %ld seconds (to the nearest second).\n", ( long ) ( t1 - t0 ) ); printf ( "CPU time is %f seconds.\n", ( float ) ( c1 - c0 ) / CLOCKS_PER_SEC ); exit ( 0 ); } void syserr ( char *message ) { fprintf ( stderr, "%s: %s", strerror ( errno ), message ); abort ( ); } void setFlag ( int fd, int flags ) { // flags is a bitvector for bits to turn on int val; if ( ( val = fcntl ( fd, F_GETFL, 0 ) ) < 0 ) // get the original bits perror ( "fcntl F_GETFL error" ); // F_GETFL predefined val |= flags; // turn on the bits in flag if ( fcntl ( fd, F_SETFL, val ) < 0 ) // set the new bits perror ( "fcntl F_SETFL error" ); } void clearBuffer ( char *buffer ) { while ( *buffer ) { *buffer = 0x0; buffer ++; } } int modViaSubtraction ( int a, int b ) { int difference = a - b; if ( difference < b ) return ( difference ); while ( difference > b ) difference -=b; return ( difference ); }