CS444 Class 7

Handout: hw1 expected script using testio.sh, an mtip shell script

Short demo: running testio.sh to see the automated input happening.

 

Check your directory structure: The grading script will assume your tty  files are in hw1_part2, and will build testio.lnx and run it, so leave all the supplied files there too.

Check with du (disk usage utility), ignoring the numbers of blocks in the first column--

cd cs444

du

hw1_part1

hw1_part2/queue

hw1_part2

 

Note you can rename directories on UNIX with the mv command, for example “mv hw1 hw1_part2”.

 

Interrupt Handler Points

 

 

Debugging Points

 

Use kprintf generously to report what’s happening.

 

Common Problem: system “goes away”, after printing some of your kprintf messages.

 

~r gets you going again, but memory info is lost.

 

 

Use kprintf generously to report what’s happening, blow by blow.  kprintf is your spy—it runs in between any two program lines, and runs immediately, no delay like we’re putting into write(). 

 

Another idea is to use the debugger, remote gdb.   It works within interrupt handlers as well as in ttyread and ttywrite.  


 

Dataflow pictures, very rough here:  those stick things are supposed to be arrows showing data flow.

 

 

read ---> ttyread : upper half code, dequeues from input Q

                     ^

                     |                               

               input Q

                     ^

                     |

         input interrupt handler: enqueues into input Q

 

 

 

 write --> ttywrite: upper half code, enqueues to output Q

                     |

                     v

               output Q

                     |

                     v

         output interrupt handler: dequeues from output Q

 

Echoes

How about echoes?  They are generated when a char is processed in the input interrupt handler.  One copy of the char is enqueued in the input Q and another in an output echo Q, separate from the ordinary output Q.  If we put the above pictures next to each other we can draw in the echo Q next to the output Q and an arrow from the input int handler to the echo Q, and another arrow from the echo Q to the output int handler.

 

When the output int handler runs, it should first look in the echo Q, and output a char from there, or if there are no chars there, output one from the ordinary output Q.  This way users see echoes as soon as possible.

 

What if TX interrupts are off when we generate an echo—should we turn on transmitter ints then? 

Answer: Yes, there needs to be code in the receiver interrupt handler for this.

 

Note: the UART has only one IRQ for transmitter and receiver interrupts.  That means there is one interrupt handler that has to handle both input and output interrupts.  You can check for receiver data-ready, and if that’s on, do the input interrupt handling, and then, if the transmitter is ready, do the output interrupt handling.

 

Not covered in class but may be useful for hw1:

Note: don’t confuse & and &&.  When we’re working with bits we need &, the bit-wise AND, and also |, the bit-wise OR.  These operators works the same in C and Java, but are not as commonly used in Java.  Reread K&R, pp. 48-49 and 52-53 if you need review on this.

One interrupt handler: how do we tell which kind of interrupt is happening?

 

The simple-UART treatment expected in hw1 uses the LSR to discriminate between receiver and transmitter interrupts.  After all, the UART is interrupting to tell us it’s data-ready (DR on) or transmit-ready (THRE on), where DR and THRE are bits in the LSR.

 

            if (inpt(COM2_BASE + UART_LSR) & UART_LSR_DR) … receiver int case, COM2

 

Of course, in tty.c you need to use a variable for the base register.  Also note that this action does not acknowledge the interrupt, so you need to read the char in (DR on), or if THRE is on, transmit another char or turn off transmit interrupts.

 

 

 

Back to Tanenbaum to finish Chap 1.

 

UNIX system calls—we looked at write and exit earlier, now look at

fork(), the amazing parameterless UNIX process-creation system call.

 

Here is a trivial fork program, using the return value from fork to tell the parent and child apart:  The parent gets the child pid (always !=0) from fork, while the child gets back 0.

 

int main()

{

     if (fork())

printf(“hello from parent\n”);

     else printf(“hello from child\n”);

}

When you run this program, you will see one message and then the other, in either order.  Each terminates by executing the exit syscall in the startup module.

 

<Drawing of one address space, then copied second address space for the child process.>

 

After fork, while both processes exist, there is a parent-child relationship between them.  If the parent exits first, the child is an orphan, and gets assigned to process 1 as a child.  Being an orphan doesn’t affect how it runs, only how it gets cleaned up at the end.

 

Fork clones the whole process image, even the stack, so we can put in local variables and they will be copied:

 

int x_ext = 2;  /* external var, in data segment */

int main()

{

    int x_loc=6;  /* local (automatic) var, on stack */

 

   if (fork()) {

       x_loc++;

       x_ext++;

 printf(“hello from parent, x_loc=%d, x_ext = %d\n”, x_loc, x_ext);

}

else printf(“hello from child, x_loc=%d, x_ext = %d\n”, x_loc, x_ext); 

}

 

<Drawing of one address space, then copied second address space for the child process.>

<x_ext is in the data segment, x_loc is on the stack>

 

Here x_loc, a local or automatic variable, will be on the stack, = 6 in the original program image, and thus =6 in the copied image as well.  In the parent, x_loc will be incremented to 7, but this has no effect on the child. 

 

Similarly, x_ext is an external variable = 2 at the fork, so still 2 in the child’s report.

 

So you will see “hello from parent, x_loc=7, x_ext=3\n” and “hello from child, x_loc=6, x_ext=2\n” output, in either order.

 

Next time: exec system call, UNIX vs Windows system calls. Start on Chap. 2