Week 09 Tutorial Questions

  1. Write a C program, print_diary.c, which print the contents of the file $HOME/.diary to stdout
  2. Assume we have 6 virtual memory pages and 4 physical memory pages and are using a least-recently-used (LRU) replacement strategy.

    What will happen if these virtualmemory pages were accessed?

    5 3 5 3 0 1 2 2 3 5
    
  3. Discuss code supplied for the lru lab exercise.
    // Simulate LRU replacement of page frames
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <assert.h>
    
    
    // represent an entry in a simple inverted page table
    
    typedef struct ipt_entry {
        int virtual_page;        // == -1 if physical page free
        int last_access_time;
    } ipt_entry_t;
    
    
    void lru(int n_physical_pages, int n_virtual_pages);
    void access_page(int virtual_page, int access_time, int n_physical_pages, struct ipt_entry *ipt);
    
    int main(int argc, char *argv[]) {
        if (argc != 3) {
            fprintf(stderr, "Usage: %s <n-physical-pages> <n-virtual-pages>\n", argv[0]);
            return 1;
        }
        lru(atoi(argv[1]), atoi(argv[2]));
        return 0;
    }
    
    
    void lru(int n_physical_pages, int n_virtual_pages) {
        printf("Simulating %d pages of physical memory, %d pages of virtual memory\n",
              n_physical_pages, n_virtual_pages);
        struct ipt_entry *ipt = malloc(n_physical_pages * sizeof *ipt);
        assert(ipt);
    
        for (int i = 0; i < n_physical_pages; i++) {
            ipt[i].virtual_page = -1;
            ipt[i].last_access_time = -1;
        }
    
        int virtual_page;
        for (int access_time = 0; scanf("%d", &virtual_page) == 1; access_time++) {
            assert(virtual_page >= 0 && virtual_page < n_virtual_pages);
            access_page(virtual_page, access_time, n_physical_pages, ipt);
        }
    }
    
    
    // if virtual_page is not in ipt, the first free page is used
    // if there is no free page, the least-recently-used page is evicted
    //
    // a single line of output describing the page access is always printed
    // the last_access_time in ipt is always updated
    
    void access_page(int virtual_page, int access_time, int n_physical_pages, struct ipt_entry *ipt) {
    
        // PUT YOUR CODE HERE TO HANDLE THE 3 cases
        //
        // 1) The virtual page is already in a physical page
        //
        // 2) The virtual page is not in a physical page,
        //    and there is free physical page
        //
        // 3) The virtual page is not in a physical page,
        //    and there is no free physical page
        //
        // don't forgot to update the last_access_time of the virtual_page
    
        printf("Time %d: virtual page %d accessed\n", access_time, virtual_page);
    }
    
  4. The assignment specification doesn't fully explain the assignment. What can I do?

  5. Why are we using gitlab.cse.unsw.edu.au for assignment 2?

  6. What can I change in the supplied code for assignment 2?

  7. Can I add my own functions and my own .c and .h files for assignment 2?
  8. The supplied code for assignment 2 includes this function:
    //
    // Split a string 's' into pieces by any one of a set of separators.
    //
    // Returns an array of strings, with the last element being `NULL';
    // The array itself, and the strings, are allocated with `malloc(3)';
    // the provided `free_token' function can deallocate this.
    //
    static char **tokenize(char *s, char *separators, char *special_chars) {
        size_t n_tokens = 0;
        // malloc array guaranteed to be big enough
        char **tokens = malloc((strlen(s) + 1) * sizeof *tokens);
    
    
        while (*s != '\0') {
            // We are pointing at zero or more of any of the separators.
            // Skip leading instances of the separators.
            s += strspn(s, separators);
    
            // Now, `s' points at one or more characters we want to keep.
            // The number of non-separator characters is the token length.
            //
            // Trailing separators after the last token mean that, at this
            // point, we are looking at the end of the string, so:
            if (*s == '\0') {
                break;
            }
    
            size_t token_length = strcspn(s, separators);
            size_t token_length_without_special_chars = strcspn(s, special_chars);
            if (token_length_without_special_chars == 0) {
                token_length_without_special_chars = 1;
            }
            if (token_length_without_special_chars < token_length) {
                token_length = token_length_without_special_chars;
            }
            char *token = strndup(s, token_length);
            assert(token != NULL);
            s += token_length;
    
            // Add this token.
            tokens[n_tokens] = token;
            n_tokens++;
        }
    
        tokens[n_tokens] = NULL;
        // shrink array to correct size
        tokens = realloc(tokens, (n_tokens + 1) * sizeof *tokens);
    
        return tokens;
    }
    
    What does
    tokenize("<cowrie.c   wc -l > line_count.txt", " ", "<")
    
    return?
  9. The supplied code for assignment 2 includes this function:
    //
    // Check whether this process can execute a file.
    // Use this function when searching through the directories
    // in the path for an executable file
    //
    static int is_executable(char *pathname) {
        struct stat s;
        return
            // does the file exist?
            stat(pathname, &s) == 0 &&
            // is the file a regular file?
            S_ISREG(s.st_mode) &&
            // can we execute it?
            faccessat(AT_FDCWD, pathname, X_OK, AT_EACCESS) == 0;
    }
    
    What do these calls return?
    is_executable("/bin/ls")
    is_executable("/usr/bin/ls")
    is_executable("ls")
    
    return?
  10. What is the differences between builtin commands and non-builtin commands.

    What commands are builtin to cowrie?

    What commands are not builtin to cowrie?

  11. Each new process in a computer system will have a new address space. Which parts of the address space contain initial values at the point when the process starts running? Code? Data? Heap? Stack? Which parts of the address space can be modified as the process executes?

  12. One possible (and quite old) approach to loading programs into memory is to load the entire program address space into a single contiguous chunk of RAM, but not necessarily at location 0. For example:

    Doing this requires all of the addresses in the program to be rewritten relative to the new base address.

    Consider the following piece of MIPS code, where loop1 is located at 0x1000, end_loop1 is located at 0x1028, and array is located at 0x2000. If the program containing this code is loaded starting at address A = 0x8000, which instructions need to be rewritten, and what addresses are in the relocated code?

       li  $t0, 0
       li  $t1, 0
       li  $t2, 20  # elements in array
    loop1:
       bge $t1, $t2, end_loop1
       mul $t3, $t1, 4
       la  $t4, array
       add $t3, $t3, $t4
       lw  $t3, ($t3)
       add $t1, $t1, $t3
       add $t1, $t1, 1
       j   loop1
    end_loop1:
    
  13. What is the difference between a virtual address and a physical address?

  14. Consider a process whose address space is partitioned into 4KB pages and the pages are distributed across the memory as shown in the diagram below:

    The low byte address in the process is 0 (in Code1) and the top byte address in the process is 28671 (max address in page containing Stack2).

    For each of the following process addresses (in decimal notation), determine what physical address it maps to.

    1. jal func, where the label func is at 5096
    2. lw $s0,($sp), where $sp contains 28668
    3. la $t0, msg, where the label msg is at 10192
  15. The working set of a process could be defined as the set of pages being referenced by the process over a small window of time. This would naturally include the pages containing the code being executed, and the pages holding the data being accessed by this code.

    Consider the following code, which computes the sum of all values in a very large array:

    int bigArray[100000];
    // ...
    int sum = 0;
    for (int i = 0; i < 100000; i++)
    	sum += bigArray[i];
    

    Answer the questions below under the assumptions that pages are 4 KiB (4096 bytes), all of the above code fits in a single page, the sum and i variables are implemented in registers, and there is just one process running in the system.

    1. How large is the working set of this piece of code?

    2. Assuming that the code is already loaded in memory, but that none of bigArray is loaded, and that only the working set is held in memory, how many page faults are likely to be generated during the execution of this code?

  16. Consider a (very small) virtual memory system with the following properties:

    • a process with 5 pages
    • a memory with 4 frames
    • page table entries containing (Status, MemoryFrameNo, LastAccessTime)
    • pages status is one of NotLoaded, Loaded, Modified (where Modified implies Loaded)

    Page table:

    If all of the memory frames are initially empty, and the page table entries are flagged as NotLoaded, show how the page table for this process changes as the following operations occur:

    read page0,  read page4,  read page0,  write page4,  read page1,
    read page3,  read page2,  write page2,  read page1,  read page0,
    

    Assume that a LRU page replacement policy is used, and unmodified pages are considered for replacement before modified pages. Assume also that access times are clock ticks, and each of the above operations takes one clock tick.

  17. The kill(1) command (run from the shell command-line) and the kill() system call can be used to send any of the defined signals to a specified process. For each of the following signals, explain the circumstances under which it might be generated (apart from kill(1)), and what is the default effect on the process receiving the signal:

    1. SIGHUP

    2. SIGINT

    3. SIGQUIT

    4. SIGABRT

    5. SIGFPE

    6. SIGSEGV

    7. SIGPIPE

    8. SIGTSTP

    9. SIGCONT

  18. The sigaction(2) function for defining signal handlers takes three arguments:

    • int signum ... the signal whose handler is being defined
    • struct sigaction *act ... pointer to a record describing how to handle the signal
    • struct sigaction *oldact ... pointer to a record describing how the signal was handled (set by sigaction(3) if not NULL)

    The struct sigaction record includes a field of type void (*sa_handler)(int).

    Describe precisely what this field is, and what its type signature means.

  19. Consider the following program:

    // assume a bunch of #include's
    
    static void handler (int sig)
    {
    	printf ("Quitting...\n");
    	exit (0);
    }
    
    int main (int argc, char *argv[])
    {
    	struct sigaction act;
    	memset (&act, 0, sizeof (act));
    	act.sa_handler = &handler;
    	sigaction (SIGHUP, &act, NULL);
    	sigaction (SIGINT, &act, NULL);
    	sigaction (SIGKILL, &act, NULL);
    	while (1)
    		sleep (5);
    	return 0;
    }
    

    What does this program do if it receives

    1. a SIGHUP signal?

    2. a SIGINT signal?

    3. a SIGTSTP signal?

    4. a SIGKILL signal?

  20. Some commands on Unix allow you to name the files that they operate on, e.g.,
    cat file
    

    Commands that read from their standard input allow you to specify which file they read their input from by redirecting their standard input, e.g.,

    cat < file
    

    Describe how each of these cases might be implemented. Assume that once the file is made accessible, it is scanned and copied to the standard output (file descriptor 1, or the #define'd constant STDOUT_FILENO) as follows:

    int infd;               // input file descriptor
    char buffer[BUFSIZ];    // input file buffer
    int nread;              // # characters read
    
    while ((nread = read (infd, buffer, BUFSIZ)) > 0)
        write (STDOUT_FILENO, buffer, nread);