CS644 Midterm Exam Solution            for practice, S10                 

Open books, handouts, notes. Write on these pages where appropriate. Points: 1 (40), 2 (20), 3 (20), 4 (20)

1.        Short answer.

a.        What 32-bit x86 CPU register is saved by both call (the ordinary function-call instruction) and int $n (the special system-call instruction)?  EIP Which 32-bit register is saved by int $n but not call? EFLAGS

Note: CS (where user vs. kernel mode is held) is also saved by int $n but not by call, but it’s only 16 bits.

b.       Suppose two devices send in interrupt signals at the same moment.  But the x86 CPU sees only one at a time.  What device makes this happen?  The P.I.C. What action in the interrupt handler enables the second interrupt to be sent to the CPU? The sending of the EOI command (end-of-interrupt) to the PIC (via outb to i/o port 0x20.)

c.        For x86 Linux, where in memory is the clock tick interrupt vector, i.e., where in what table?

In IDT[0x20]. 

d.       In x86 Linux, how does execution get to function system_call?

This is a code address in the kernel where syscall execution in the kernel starts. The syscall instruction, itself in user code, causes a trap cycle, during which the CPU execution mode is changed to kernel and the EIP is loaded with this address.

e.        Suppose the CPU is running at user level, and then, a few CPU cycles later, in kernel mode.  List the two most commonly occurring mechanisms that cause this transition.  

System call, interrupt.

f. In which of the following UNIX/Linux execution environments are interrupts always allowed, sometimes allowed, never allowed:  circle the right answer

      --running in process context, user-level: always/sometimes/never

      --running in process context, kernel-level: always/sometimes/never

g.       Suppose a Xinu semaphore was created with count 1 but now has count -2.  What can you say about the number of processes waiting for this semaphore?

There are 2 processes waiting (blocked) on this semaphore.

 

2.        Suppose you need a fast way to receive a byte from either of two COM ports, whichever gets an input byte first.  Devise and implement this in a function that accepts two base ports (usually 0x2f8 and 0x3f8, but leave it general), and returns the first byte that arrives at either port, and the base port of the port that received the byte.  Clearly at least one return value has to be delivered via a pointer argument.  Write it with inpt() calls directly to the hardware, using polling.  Use the back of a page or an additional page.

void get2(int baseport1, baseport2, char *ch, int *baseport)

{

while (1) {

if (inpt(baseport1 + UART_LSR) & UART_LSR_DR) {

*ch = inpt(baseport1 + UART_RX);

*baseport = baseport1;

return;

}

          if (inpt(baseport2 + UART_LSR) & UART_LSR_DR) {

*ch = inpt(baseport2 + UART_RX);

*baseport = baseport2;

return;

}

}
}

CS644 Midterm Exam, pg. 2 of 3

3.        Consider the following UNIX program.  Note that getpid() is a system call that returns the process pid.

#include <stdio.h>

int x = 2;

int main()

{

     int z = 4;

 

     x = 3;

     if (fork()) {

x++; z++;

     }

     printf(“%d: %d %d\n”, getpid(), x , z);  /* corrected by removing y */

}

 

a.        Here are some abbreviations for various data areas in memory:

UC user code, including library user code

UD user data, including library user data

US  user stack

 Consider the process image just before the fork.  Where (UD, US, etc.) are the following memory objects located:

x UD

z US

main UC

write system call instruction executed by printf UC

exit system call instruction UC

 

b.       Just after the fork is executed, how many copies of x are there? 2  Of z? 2

c.        Given parent pid of 3000 and child pid of 3002, what is printed out?  If more than one output is possible, show it or discuss. For simplicity, assume each individual printf string comes out intact.

 There is a race here between the parent and the child:

Either:

3000: 4 5

3002: 3 4

or

3002: 3 4

3000: 4 5

CS644 Midterm Exam, pg. 3 of 3

1.      Consider the solution to hw2, using UNIX/Linux POSIX semaphores, running in test1 after the forks with 2 worker processes.

a.       List the (UNIX/Linux) system calls in use.

 

from reading lock.c: shmget, shmat, sem_init, sem_wait, sem_post, sem_destroy, shmdt, shmctl

called from printf, perror: write

from reading test1.c/test2.c: fork, wait

sleep: not really a syscall itself, but uses syscalls (see “man 3 sleep” on users.cs.umb.edu: “man 3” means C library sleep)

exit: called by C startup code after return from main

 

b.      Here is a picture of the VA space layout of the parent:

 

    

 

Being careful to align areas of memory if appropriate, make parallel drawings of the VA spaces of the two workers:

 

 

 

 

    

    

 

 

 

The child code, data, C DLL, and stack all show up at exactly the same addresses as in the parent.

 

 

 

 

a.       Add in a representation of the shared memory in use, and show the location of the struct locker that holds the pointer to the shared memory in each process. Draw an arrow from the struct locker to the shared memory to show the pointer.

 

The small amount of shared memory in use here could show up anywhere not already occupied by the older memory objects. Since it is created before the forks, it will show up at the same place in all three processes.

 

The struct locker was originally returned from lckcreate to its caller in main, where it gives value to a local variable name lock. Since this is a local variable of main, its lifetime will be the same as the program’s. Like any local variable, it is stored on the stack, before the forks occur. The forks copy the stack including this variable to the worker processes. So there should be an arrow from the same stack location in the three processes to the little block of shared memory, and there should be some indication that this shared memory is actually shared between the three processes.