CS360 Midterm 2. March 21, 2017. Answers and Grading
Question 1 - 16 Points
The code is in q1.c in case you want to test it
for yourself.
- Line 1: This shifts two hex digits to the right: 0x0084b3a0.
- Line 2: This shifts two hex digits to the left. Because the
number is already 8 hex digits, this will "shift off" the two leftmost
digits: 0xb3a03e00.
- Line 3: Since c is the last two hex digits of x, the
XOR will clear out the last two digits: 0x84b3a000.
- Line 4: c is the last byte of x: 0x3e. So,
the OR will set the first digit to f and do nothing to the last digit: 0x000000fe
- Line 5: The AND will clear the last digit, and keep the first digit
the same: 0x00000030.
- Line 6: This shifts the first digit off the left of the char: 0x000000e0.
- Line 7: pcl and pdl point to the same location. pdh is three
doubles ahead of pdl, so it is 24 bytes ahead. This line prints 24.
- Line 8: And this line prints 3.
Grading
Two points per part.
Question 2 - 16 Points
The code for the complete program is in
q2_real.c. The idea is to keep track of the first file
that you see with at least 6 links, as denoted by the st_nlink field of
the file's stat() buffer. You keep track of its name in p1 and
its inode in number. When you see a second file with the same inode number,
you print out the stored filename, and the new file's filename.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
void find_6l(char *dir, char **p1, ino_t *number)
{
DIR *d;
struct dirent *de;
struct stat buf;
char *pn;
int length;
length = strlen(dir);
pn = (char *) malloc(sizeof(char) * (length + 258));
strcpy(pn, dir);
d = opendir(dir);
if (d == NULL) { perror(dir); exit(1); }
for (de = readdir(d); de != NULL; de = readdir(d)) {
sprintf(pn+length, "/%s", de->d_name);
if (stat(pn, &buf) != 0) { perror(pn); exit(1); }
printf("pn: %s\n", pn);
if (buf.st_mode & S_IFDIR) {
if (strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..") != 0) {
find_6l(pn, p1, number);
}
} else {
if (buf.st_nlink >= 6) {
if (*p1 == NULL) {
*p1 = strdup(pn);
*number = buf.st_ino;
} else if (buf.st_ino == *number) {
printf("%s\n%s\n", *p1, pn);
exit(0);
}
}
}
}
closedir(d);
free(pn);
}
int main()
{
ino_t num;
char *firstpn;
firstpn = NULL;
find_6l(".", &firstpn, &num);
return 0;
}
|
Now the questions:
- Question A: d holds the pointer to each open directory, so that
you can traverse the directory for files. It is initialized with opendir().
- Question B: de holds information about each file in the
directory. You fill it in with readdir().
- Question C: buf holds the metadata for each file. You fill it
in with the stat() system call.
- Question D: You use three fields of buf:
- You use st_mode to test whether the file is a directory.
- You use st_inode to store the inode number of the first file you see with
6+ links, and you also use it to test whether subsequent files with 6+ links are the
same as the stored one.
- You use st_nlinks to test whether a file has 6+ links.
- Question E: The program calls malloc() and free
on pn. This is the file name that you construct with the directory and
each file name from the readdir() call. You need to call malloc
on it at the beginning of the procedure, and free at the end.
- Question F: You have to call strdup() when you store the
name of the file in *p1. You need to do this because you reuse the
memory in pn when you construct file names.
Grading
2 points per part, with the exception of part D, which was worth 6 points.
Question 3 - 10 Points
Program B makes two system calls, read() and write(), for each character
on standard input. If there are a lot of characters on standard input, this will make
Program B slow, because system calls are expensive. On the other hand, fread()
and fwrite() use buffers to mitigate the overhead of the system calls. For
example, fread() will read 8K characters at a time into a buffer,
with a single read()
call, and it will satisfy the next 8K-1 fread() calls from the buffer. Fwrite()
copies each character to a buffer, and only calls write() on the buffer when it
is full, or when the program ends.
Grading
10 points.
Question 4 - 20 Points
These are all straightforward from the "Assembler 3" lecture.
In
b(), you need to load the pointer, add 12 to it, and dereference it
into r0:
b:
ld [fp+12] -> %r0
mov #12 -> %r1
add %r0, %r1 -> %r0
ld [r0] -> %r0.
ret
In
f(), you first call "push #16" to allocate g. The first byte of
g will be (fp-12). So, you need to calculate fp-12, then add four times
i to it, and store 10 at that memory location. Here's an expression
tree:
If you process this right to left and bottom up, you don't need to spill r2:
f:
push #16
mv #4 -> %r0
ld [fp+12] -> %r1
mul %r0, %r1 -> %r0
mov #-12 -> %r1
add %r1, %fp -> %r1
add %r0, %r1 -> %r0
mov #10 - %r1
st %r1 -> [r0]
ret
In
d(), we have the following:
- e is [fp+12]
- i is [fp+16]
- e[i] is [ [fp+12] + 4*[fp+16] ]
- & (e[i][3]) is [ [fp+12] + 4*[fp+16] ] + 12
- So, calculate that, and store 5 there.
Here's an expression tree.
Again, if you process this right to left and bottom up, you don't need to spill r2:
d:
mov #4 -> %r0
ld [fp+16] -> %r1
mul %r0, %r1 -> %r0
ld [fp+12] -> %r1
add %r0, %r1 -> %r0
ld [r0] -> %r0
mov #12 -> %r1
add %r0, %r1 -> %r0
mov #5 -> %r1
st %r1 -> [r0]
ret
Grading
- 5 points for b().
- 7 points for f().
- 8 points for d().
Question 5 - 10 Points
First, the following statement forces you to spill r2:
So, the first four statements will be:
- push #8: Allocate the local variables on the stack.
- st %r2 -> [sp]--: Spill r2.
- mov #5 -> %r0: This and the next statement push the argument to f(5)
onto the stack.
- st %r0 -> [sp]--.
The last two statements are going to be to move 3 to r0 and return. You need to
unspill r2, so you should do that, either just before or just after moving 3 to r0.
The two statments previous to that are going to be the ones after the jsr
call in "l = f(k)" -- one to pop the argument off the stack, and one to store
the return value into l:
pop #4
st %r0 -> [fp]
mov #3 -> %r0
ld ++[sp] -> %r2
ret
Grading
push #8 | 1 point |
st %r2 -> [sp]-- | 1.5 points |
mov #5 -> %r0 | 1 point |
st %r0 -> [sp]-- | 1 point |
pop #4 | 1.5 points |
%r0 -> [fp] | 1 point |
mov #3 -> %r0 | 1 point |
ld ++[sp] -> %r2 | 1.5 points |
ret | 0.5 points |
Question 6
- The easiest one here is the frame pointer. The question says its
value is 0x7ff800.
- In k() the first thing that we do is call "push #8" to allocate
n and m. So the stack pointer is (fp-8) = 0x7ff7f8.
- The program counter is at the last instruction of k. k
starts at 0x1450, so add 12*4 to it: 0x1480.
- Finally, r0 holds the return value of k(): 60.
As for the rest, this is a matter of starting with the current fp
equaling 0x7ff800 and unwinding the stack to put in all the values and
labels for h(). Just a note -- we know that the jsr statement in
h is at memory location 0x161c. So, the stored pc on the stack should
be four bytes after that, at 0x1620.
Grading
- r0 = 60: 1 point
- pc = 0x1480: 1 point
- sp = 0x7ff7f8: 1 point
- fp = 0xff800: 1 point
- location of n and m in k correct: 2 point
- values of n and m in k = 38 and 22: 1 point
- location of FP and PC in H are beneath n and m in k: 2 point
- value of FP in H is 16 bytes lower than its location: 1 point
- value of PC in H is 0x1620: 1 point
- location of i and j in k below the pc/fp in h: 2 point
- values of i and j in k are 35 and 20: 1 point
- location of n in h is below i and j or if things are wrong, it's at the value of "fp in h": 1 point
- location of FP and PC in h's caller are beneath n in h: 1 point
- location of i and j in h are 8 bytes below n in h: 1 point
- values of i and j in h are 20 and 30: 1 point
- The empty labels have dashes: 1 point
- The values of n in h, and fp/pc in h's caller are blank: 1 point