CS360 Midterm Exam #1. March 28, 2019. James S. Plank

Answers and Grading

Each question was worth 25 points.

Question 1

Part A: You are making 2n system calls in this program. In other words, for each value of i, you are calling write() twice. Suppose n is 1,000,000, and each system call is 10 microseconds. That's 20 seconds!

Part B: The approach is to have a buffer to reduce the number of system calls. Suppose its size is b, and that b is a multiple of 12. Then, you can write b/3 ints and doubles with exactly one write() call. What you'll do is fill in the buffer with ids[i] and vals[i] alternatively. When the buffer is full, you write() it.

Part C: Here's some code that does the trick. You need to use memcpy() rather than, for example, pointers to ints and doubles, because the doubles will be unaligned in the buffer.

int write_double_array(int fd, int *ids, double *vals, int n)
{
  int index, i;
  char buffer[12000];

  index = 0;
  for (i = 0; i < n; i++) {
    memcpy(buffer+index*12, ids+i, sizeof(int));
    memcpy(buffer+index*12+4, vals+i, sizeof(double));
    index++;
    if (index % 1000 == 0) {
      if (write(fd, buffer, 12000) != 12000) return -1;
      index = 0;
    }
  }
  if (index > 0) {
    if (write(fd, buffer, index*12) != index*12) return -1;
  }
  return 0;
}

Part D: This happens when write() fails. You can focus on each of the arguments to write():

A lot of you said things like "too many open files" or "not writable file." Although it may not seem irrelevant to you, it is, because this would be an error in an open() call, not in a write() call. Write() cares about what the file descriptor is, not how it was created.

Grading


Question 2

This is a garden-variety stat() program. The approach is this:

#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>

int hleqf(char *f, char *d)
{
  char *fn;                    /* The constructed file name for each file. */
  int l;                       /* The length of f, so I don't have to call strlen a lot. */
  int total;                   /* The total number of hard links. */
  DIR *dir;                    /* The opened directory. */
  struct dirent *de;           /* Each directory entry. */
  struct stat fbuf;            /* The stat buf for f.  I'll check each inode against its inode. */
  struct stat buf;             /* The stat buf for each file in the directory. */
  
  /* Store the stat buf for f. */

  if (stat(f, &fbuf) != 0) return -1;

  /* Prepare the file name for each file in the directory by 
     allocating it and copying the directory there. */

  l = strlen(d);
  fn = (char *) malloc(sizeof(char) * (strlen(d)+257));
  if (fn == NULL) return -1;
  strcpy(fn, d);

  /* Open the directory and set the total to zero. */

  dir = opendir(d);
  if (dir == NULL) return -1;
  total = 0;

  /* Traverse the directory, construct each file name, and then call stat() to check the inode. */

  for (de = readdir(dir); de != NULL; de = readdir(dir)) {
    sprintf(fn+l, "/%s", de->d_name);
    if (stat(fn, &buf) != 0) return -1;
    if (buf.st_ino == fbuf.st_ino) total++;
  }

  /* Clean up and return. */

  closedir(dir);
  free(fn);
  return total;
}

int main(int argc, char **argv)
{
  printf("%d\n", hleqf(argv[1], argv[2]));
  return 0;
}

Grading


Question 3

Here are the important chunks of memory that you can derive from the text that says, "we know the following things": That allows us to answer the questions in part A: Part B: When you call free(0x5208), it will put the chunk starting at 0x5200 onto the head of the free list. That will make the following changes:

Grading


Question 4

a:
  ld [fp+16] -> %r0       / This is b
  mov #4 -> %r1
  mul %r0, %r1 -> %r0     / This is 4*b
  ld [fp+12] -> %r1       / This is p
  add %r0, %r1 -> %r0     / This is &(p[b])
  ld [r0] -> %r0          / This is p[b]
  ret

b:
  push #4                 / Allocate d
  st %r2 -> [sp]--        / Spill r2, because you'll need it
  mov #4 -> %r0           / Push the argument 4 on the stack
  st %r0 -> [sp]--        
  jsr e                   / Call e
  pop #4                  / Pop the argument off the stack
  mov %r0 -> %r2          / Store r0 to r2, so that f() won't mess with it
  mov #6 -> %r0           / Push 5 and 6 on the stack in reverse order
  st %r0 -> [sp]--        
  mov #5 -> %r0           
  st %r0 -> [sp]--        
  jsr f                   / Call f
  pop #8                  / Pop the two arguments off the stack
  add %r0, %r2 -> %r0     / Add the two return values
  st %r0 -> [fp]          / Store in d
  ld ++[sp] -> %r2        / Unspill
  ret                     

g:
  push #80                / Allocate h
  mov #5 -> %r0           / The compiler knows exactly where h[10] is: [fp-36]
  st %r0 -> [fp-36]       
  mov #-36 -> %r0         / It has to calculate &h[10] as (fp-36)
  add %fp, %r0 -> %r0     
  ret

Grading