CS360 Final Exam - Answers and Grading

Spring, 2023
James S. Plank

The exam was given on Canvas with banks used for some questions. These explanations apply to the example exam, plus I give answers to the other bank questions.


Question 3 (25 points)

Man, that value of 0x258 at address 0x7200 was a typo. It is actually immaterial (see below), but I apologize if it confused you.


Question 4 (25 points)

Grading

Four points for the each of the first four, and three points for the other three. The way I graded your answer is that I sorted all of your outputs and then put dashes between them. I then graded the entire string.


Question 5 (25 points)


Question 6 (25 points)

Part 1: Here's start_ga():

int start_ga(int seed)
{
  int pid;
  int p[2];
  int fd;
  char buf[20];

  /* You can call pipe() before fork(), and have the parent write the
     seed to the child, but it's easier to have the child write to the pipe. */

  pid = fork();

  if (pid == 0) {
    pipe(p);                        /* Only call pipe() in the child */
    sprintf(buf, "%d\n", seed);     /* Write the seed -- it will go to the buffer in the OS. */
    write(p[1], buf, strlen(buf));
    close(p[1]);
  
    sprintf(buf, "f%03d.txt", seed);                   /* Create the output file and have it */
    fd = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0666);/* go to stdout */
    dup2(fd, 1);
    close(fd);

    dup2(p[0], 0);                  /* Stdin comes from the pipe */
    close(p[0]);
    execlp("./ga", NULL);           /* Execute the program. */
    perror("execlp");
    exit(1);
  }
  return pid;
}

And here's kill_slow()

void kill_slow(JRB pids)
{
  JRB tmp;
  long t0;

  /* Traverse the tree and kill old processes. */

  t0 = time(0);
  jrb_traverse(tmp, pids) {
    if (tmp->val.l + TIMEOUT <= t0) {
      kill(tmp->key.i, SIGKILL);
    }
  }
}

Part 2: There are two answers. The easy one is to call alarm(3600) in the child process right before it calls execlp(). If the child doesn't handle the alarm, it will kill the process at the right time.

The truly proper way to do this is to have kill_slow() calculate when the next process should be terminated, and call alarm() for that time. Each alarm() call cancels the previous one, which is why you have to figure out the soonest process to kill each time you call alarm(). Have the alarm handler call kill_slow(). You don't need to have the code, but here it is:

void kill_slow(JRB pids)
{
  JRB tmp;
  long t0;
  int min;

  min = TIMEOUT+1;
  t0 = time(0);
  jrb_traverse(tmp, pids) {
    if (tmp->val.l + TIMEOUT <= t0) {
      kill(tmp->key.i, SIGKILL);
    } else {
      if (tmp->val.l + TIMEOUT - t0 < min) min = tmp->val.l + TIMEOUT - t0;
    }
  }
  if (min == TIMEOUT+1) min = 0;
  alarm(min);
  signal(SIGALRM, handler);
}

void handler(int dummy)
{
  kill_slow();
}

Grading:

In the grading file, I tell you the points for each part, and then I identify "problems" that led to deductions. If you see "Please see the answer" with some of those categories above omited, it means that your program didn't really make sense, and didn't fit with the grading methodology.