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.
The value of 0x248 at address 0x7200 feels problematic - it can't be a legal size, so it should be data. That doesn't change our answer -- if the first chunk is 24 bytes, and then the rest are 16 bytes, then 0x7200 is data, and its value of 0x248 doesn't matter.
(2 points)
Each of these address-value pairs is worth three points.
AAB BAA |
ABC AC BAC BC C |
CAB CBA |
DABC DACB |
ABC ACB |
ACBAC ACBCA CABAC CABCA |
However, there's a chance that the "other" thread will get the mutex before thread B, in which case it signals both cv's, which at this point have no blocked threads. Thread B will finally get the mutex, print, and block on cv2, never to wake up. The outputs will be either "ACB" or "CAB". So the answer is:
ABCB ACB CAB CBAB |
So the answer needs to have the parent's sleep time to be greater than at least one child.
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: