CS360 Midterm Exam: October 8, 2003. Answers and Grading
Question 1: 12 Points -- Answer
- Procedure 1: B
- Procedure 2: P
- Procedure 3: J
- Procedure 4: D
Question 1: Grading
- Procedure 1: 3 points for B, 1 for
E, 1 for F, 1 for H.
- Procedure 2: 3 points for P,
1 for M, 1 for G, 0.5 for A.
- Procedure 3: 3 points for J,
1.5 for K, 1 for L, 0.5 for O.
- Procedure 4: 3 points for D,
1 for C, 0.5 for I, 0.5 for N.
Question 2: 12 points -- Answer
Part A
- Begin_atomic_action() should freeze all other processes from
the system, so that the calling process can perform actions without
interruption.
End_atomic_action() should unfreeze the other processes.
- This would be useful because it would allow programmers to
have guarantees that the state of the system does not change between
successive system calls. There are many examples:
- Seeking to the end of a file and then writing to the end of the file
(e.g. the functionality provided when a file is opened with the
O_SYNC and O_APPEND flags). This was discussed in class.
- Creating a file if and only if it does not already exist
(e.g. the functionality provided when a file is opened with the
O_CREAT and O_EXCL flags). This was discussed in class.
- Being guaranteed that if a readdir() call returns a file, that
the file actually exists with you open or stat() it.
- No operating system would ever export such calls as they would
compromise the performance and even safety of the system, because
processes would be able to shut other processes out for significant
(and perhaps unending) periods of time.
Part B
An inode is "metadata" for a file. In other words, the inode contains
information about a file. This information is: the file's owner, size,
protections, access/modification times, location on disk, and unique
inode number. Directories contain bindings of names to inodes, and this
binding is how users gain access to files. Such a binding is called
a link, and the Unix file system allows multiple links to the same file.
Finally, the stat system call returns the information contained
in an inode to the calling process.
Part C
The statement is true. When a system call, such as read() or
write() has a choice of operating on a small or large amount
of data, it is a fact of operating systems that operating many times
on small amounts of data will perform much more poorly than operating
few times on large amounts of data. This is because the overhead of
a single system call is significant (typically hundreds of microseconds).
With buffering, you use a piece of memory, called a buffer, to perform
system calls on large amounts of data. If the use desires to work on
small amounts of data, that can come from or go to the buffer, instead
of directly from or to the operating system.
An example is fread() from the standard I/O library. See
the Cat lecture for details.
Question 2: Grading
- Part A: 1 point for the definition, 1 point for stating why
it's useful, 1 point for the example, 1 point for why an OS wouldn't
actually implement it.
- Part B: 1 point for the definition and what it contains
1 point for the relationship to directories,
1 point for the relationship to links,
1 point for the relationship to the stat call.
- Part C: 1 point for saying it's true,
2 points for the justification,
1 point for the example.
Question 3: 16 points - Answer
This one is a little involved. You need to maintain a red-black tree
that has all the first
lines. You'll insert a line into that tree whenever you get a file that
ends with ".txt". You can figure that out in a bunch of ways -- my preferred
way is to use strcmp() -- you'll see it in the code.
Ignoring the includes, here is the answer (also in q3.c
if you want to compile and run it for yourself).
/* The code must be recursive, since this is a directory traversal */
void find_txt_files(char *directory, JRB lines)
{
DIR *d; /* Directory pointer */
struct dirent *de; /* Directory entry */
char *path; /* Path name that must be constructed for each file */
Dllist dirs, tmp; /* List of directories to traverse */
struct stat buf; /* Stat buffer to check for directories */
IS is; /* Inputstruct to read the first line of a file */
/* First preallocate space for the constructed pathname */
path = (char *) malloc(sizeof(char)*(strlen(directory)+258));
if (path == NULL) { perror("malloc"); exit(1); }
/* Open the directory, and create the list of subdirectories */
d = opendir(directory);
if (d == NULL) { perror(directory); exit(1); }
dirs = new_dllist();
/* Traverse the directory */
for (de = readdir(d); de != NULL; de = readdir(d)) {
/* Construct the file name, then test to see if it is a
subdirectory that must be traversed -- remember to
ignore . and .. */
sprintf(path, "%s/%s", directory, de->d_name);
if (lstat(path, &buf) == 0 && strcmp(de->d_name, ".") != 0
&& strcmp(de->d_name, "..") != 0) {
if (S_ISDIR(buf.st_mode)) {
dll_append(dirs, new_jval_s(strdup(path)));
}
}
/* If the file ends with .txt, then read its first line and put
it into the red black tree. */
if (strlen(path) >= 4 && strcmp(".txt", path+(strlen(path)-4)) == 0) {
is = new_inputstruct(path);
if (is != NULL) {
if (get_line(is) >= 0) {
jrb_insert_str(lines, strdup(is->text1), new_jval_v(NULL));
}
jettison_inputstruct(is);
}
}
}
/* Close the directory and traverse subdirectories */
closedir(d);
dll_traverse(tmp, dirs) find_txt_files(tmp->val.s, lines);
/* Free up memory -- the directory names, the list, path */
dll_traverse(tmp, dirs) free(tmp->val.s);
free_dllist(dirs);
free(path);
return;
}
/* The main routine -- create the red black tree, make the first
recursive call to find_txt_files, traverse the tree and print out
the first lines */
main()
{
JRB lines, tmp;
lines = make_jrb();
find_txt_files(".", lines);
jrb_traverse(tmp, lines) printf("%s", tmp->key.s);
}
Question 3: Grading
- Overall Structure (recursive code): 2 points
- Main: Allocating a tree for the lines: 1 point
- Main: Recursive call: 1 point
- Main: Traversing and printing: 1 point
- Rec: Allocating a string to hold the path: 1 point
- Rec: Opendir/test return/readdir: 1 point
- Rec: Construct the right pathname: 1 point
- Rec: Stat error check, test for ./..: 1 point
- Rec: Putting directories into dllist: 1 point
- Rec: Making recursive call for directories: 1 point
- Rec: Finding the .txt at the end of the file: 1 point
- Rec: Opening the file, reading first line, error checking: 1 point
- Rec: Putting first line into the tree: 1 point
- Rec: Closing the files (directory/inputstruct): 1 point
- Rec: Making the requisite free calls: 1 point
I took off points for garbage, or the random use of the right calls.
I also took off half points (e.g. you call opendir and don't test the
return value).