COMP 3000 Lab 4 2011

From Soma-notes
Jump to navigation Jump to search

A few guidelines:

  • Submit your solutions for both Part A and Part B via WebCT by Sunday, October 30th at 11:30 PM (note the change).
  • Please answer using a single text file. Do not submit doc, docx, pdf, or other formats. Also, please do not submit an archive (zip, tarball, rar) of multiple text files, just upload one. (Please don't just cut and paste your work into a text box on webct.)
  • Show all your work. Short answers are not sufficient; you should list any websites or individuals you consult and should document any experiments you conducted. For any question that you could just answer without any external reference or experiment, write "(no work required)" after your answer.
  • All submitted code should compile and run. Partial code fragments or explanations may not be given credit.

Part A

  1. Compile and run race-demo.c. What does the program output? Describe the pattern. Does the output vary depending upon the system load? (2 marks)
  2. What version of the glibc library is installed on the SCS lambda machines? What command did you use to figure this out? (2 marks)
  3. What does the file /etc/init.d/ssh do on the lambda machines? (1 mark)
  4. What does the file /etc/init/cron.conf do on the lambda machines? (1 mark)
  5. What does the file /etc/init.d/cron do on the lambda machines? (1 mark)

Part B

  1. Modify race-demo.c to create rigged-race.c, a program that is identical in behavior to race-demo.c except that the consumer always wins X races while the producer wins ROUNDS-X races. Synchronize the two threads using the pthread_mutex functions (init, lock, unlock, trylock). (1 mark)
  2. Modify race-demo.c to create p-c.c, a program based on race-demo where the producer only increments values in the buffer by one while the consumer only decrements the buffer values by one. Arrange it, however, such that the values in the buffer are always 0 or 1. If the consumer will decrement a 0, it should go to sleep until the producer wakes it up. Similarly, the producer should go to sleep if it finds it will increment a 1, with the consumer waking the producer at the appropriate time. (1 mark)
  3. UNIX System V init has a simple system for initializing system services by running a set of shell scripts at different "run levels." Why aren't collections of shell scripts considered sufficient for initializing modern UNIX-like systems such as MacOS X and Ubuntu? (1 mark)

Programs

/* race-demo.c */

/* compile with -pthread flag, e.g.
     gcc -pthread race-demo.c -o race-demo */

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

int value;
int offset = -1;

#define SIZE 10000
#define ROUNDS 100

void *consume_func(void *arg) {
        int i,r;
        int *buffer;
        buffer = (int *) arg;

        for (r = 1; r <= ROUNDS; r++) {                
                for (i = 0; i < SIZE; i++) {
                        if (buffer[i] < r ) {
                                buffer[i] = -1;
                        }
                }
        }
    
        return NULL;
}

void *produce_func( void * arg ) {
        int i, r, iwon;
        int *buffer;
        buffer = (int *) arg;
        
        for (r = 1; r <= ROUNDS; r++) {                
                iwon = 1;
                for( i = 0; i < SIZE; i++ ) {
                        if (buffer[i] == r-1) {
                                buffer[i] = r;
                        } else if ((buffer[i] < r) && (buffer[i] != -1)) {
                                fprintf(stderr, 
                                        "Huh?! Got value %d\n",
                                        (int) buffer[i]);
                                exit(-1);
                        } else if (buffer[i] == -1) {
                                if (iwon) {
                                        printf("Consumer got to %d first on round %d.\n", i,r);
                                        iwon = 0;
                                }
                                buffer[i] = r;
                        }
                }
                
                if (iwon) {
                        printf("Producer won on round %d!\n",r);
                } else {
                        printf("Producer lost on round %d.\n",r);
                }
        }

        return NULL;
}

int main(int argc, char *argv[]) {
        int *buffer;
        pthread_t producer_thread, consumer_thread;
        int r1, r2;

        buffer = (int *) calloc(SIZE, sizeof(int));

        if (buffer == NULL) {
                fprintf(stderr, "Couldn't allocate memory for buffer!");
                exit(-1);
        }

        r1 = pthread_create(&producer_thread, NULL, produce_func, buffer);
        r2 = pthread_create(&consumer_thread, NULL, consume_func, buffer);

        if (r1 != 0 || r2 != 0) {
                fprintf(stderr, "Couldn't create threads.\n");
        }

        pthread_join(producer_thread, NULL);
        pthread_join(consumer_thread, NULL);
        
        return 0;
}

Answer Key

Part A

  1. The program outputs the results of the two threads producer and consumer. For the first half of rounds on the lab computers which have two processors, the consumer won, followed by the producer for the second half. On my netbook the consumer won the first twelve under a high load, the first 18 on a medium load, then the first 18 plus 10 in the middle on light load. Load does have an effect on the output as shown by my experiment. The reason why is because although threads are created with much less operating system overhead, they still share the same address space within the process in which they are created. /ref{https://computing.llnl.gov/tutorials/pthreads/ (bad ssl cert, but good infoz)}
  2. The version number is: 2.11.1-0ubuntu7 and this was shown by either of these three commands: * apt-cache show libc6 * dpkg -l | grep libc6 * /lib/libc.so.6 ** Special Note: (the ldd -version command can give you the information of which library was used to compile ldd on the system, this may not correspond to the correct glibc version)
  3. The /etc/init.d folder contains a number of start / stop scripts for various services on your system. The /etc/init.d/ssh file is a script (denoted by #! /bin/sh at the beginning of the file) that is used to start and stop the OpenBSD secure shell daemon. It tests the sshd daemon executable for the correct permissions and that the file exists. This script also tests if this is to be run in a chrooted environment, if this script was invoked by init, and whether the service actually started. Upon starting and stopping this script also logs messages regarding stopping and starting of the service utilizing log_daemon_msg. /ref{man page}
  4. The /etc/init folder contains all of the configuration files for various scripts that are run by init. Init is a program that spawns all other processes on a Unix-based operating system. The program runs as a daemon and usually has the process id of 1. A daemon is a computer program that runs as a background process rather than being in direct control of an interactive user. /ref{http://en.wikipedia.org/wiki/Daemon_%28computer_software%29) /etc/init/cron.conf is the configuration file which includes a description of cron, specifies the runlevel of cron, declares potential forks and execs cron itself.
  5. The /etc/init.d/cron file is associated with the cron daemon which is a time-based job scheduler. The file also creates a symbolic link target for init scripts that have been converted to Upstart.

Part B

  1. Since the init shell scripts are run sequentially, this does not take advantage of parallel processing capabilities of multi core machines. Launchd and Upstart both enable multiple processes to run simultaneously, speeding up boot times.