CS360 Midterm Exam: October 8, 2003. Answers and Grading

Question 1: 12 Points -- Answer


Question 1: Grading


Question 2: 12 points -- Answer

Part A

  1. 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.

  2. 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:

  3. 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



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

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).