Search code examples
assemblyx86operating-systeminterruptxinu

Modify clock interrupt handler xinu


I have an operating systems project due tonight, and needed clarification on a topic. We are working with the operating system XINU. I am trying to change the default OS scheduler to account for some processes being starved. I was directed towards the clkint.S file, which I think is the clock interrupt handler. My question is, how do I know how often it interrupts? How do I set the time to lets say make a function call every 5 seconds? I have looked at the intel x86 manuals, but they have an absurd amount of information to sift through, and I am short on time If someone can point me in the right direction, I'd be grateful.

Here are a couple of files I think necessary:

clkint.S:

    /* clkint.s - _clkint */

#include <icu.s>
        .text
count1000:  .word   1000
        .globl  sltop
        .globl  clkint
clkint:
        pushal
        cli
        movb    $EOI,%al
        outb    %al,$OCW1_2

        incl    ctr1000
        subw    $1,count1000
        ja  cl1
        incl    clktime
        movw    $1000,count1000
cl1:
        cmpl    $0,slnonempty   # if no sleeping processes,
        je  clpreem     #   skip to preemption check
        movl    sltop,%eax  # decrement key of first
        decl    (%eax)      #   sleeping process
        jg  clpreem     # must use jg for signed int
        call    wakeup      # if zero, call wakeup
clpreem:    decl    preempt     # decrement preemption counter
        jg  clret       # must use jg for signed int
        call    resched     # if preemption, call resched
clret:                  # return from interrupt
        sti
        popal
        iret

clkinit.c:

/* clkinit.c - clkinit */

#include <xinu.h>
#include <interrupt.h>
#include <clock.h>

uint32  clktime;        /* seconds since boot           */
uint32  ctr1000 = 0;        /* milliseconds since boot      */
qid16   sleepq;         /* queue of sleeping processes      */
int32   slnempty;       /* zero if the sleep queue is empty;    */
                /*   non-zero otherwise         */
int32   *sltop;         /* ptr to key in first entry of sleepq  */
                /*   if sleepq is not empty     */
uint32  preempt;        /* preemption counter           */
   
/*------------------------------------------------------------------------
 * clkinit - initialize the clock and sleep queue at startup
 *------------------------------------------------------------------------
 */
void    clkinit(void)
{
    uint16  intv;           /* clock rate in KHz        */

    /* Set interrupt vector for clock to invoke clkint */

    set_evec(IRQBASE, (uint32)clkint);

    /* clock rate is 1.190 Mhz; this is 10ms interrupt rate */
    intv = 1190;

    sleepq = newqueue();    /* allocate a queue to hold the delta   */
                /* list of sleeping processes       */
    preempt = QUANTUM;  /* initial time quantum         */

    /* Specify that seepq is initially empty */

    slnonempty = FALSE;

    clktime = 0;        /* start counting seconds       */

    /*  set to: timer 0, 16-bit counter, rate generator mode,
        counter is binary */
    outb(CLKCNTL, 0x34);

    /* must write LSB first, then MSB */
    outb(CLOCK0, (char)intv);
    outb(CLOCK0, intv>>8);
    return;
}

clkhandler.c:

/* clkhandler.c - clkhandler */

#include <xinu.h>

/*------------------------------------------------------------------------
 *  clkhandler - handle clock interrupt and process preemption events
 *          as well as awakening sleeping processes
 *------------------------------------------------------------------------
 */
interrupt clkhandler(void)
{
    clkupdate(CLKCYCS_PER_TICK);

    /* record clock ticks */

    clkticks++;

    /* update global counter for seconds */

    if (clkticks == CLKTICKS_PER_SEC) {
        clktime++;
        clkticks = 0;
    }

    /* If sleep queue is nonempty, decrement first key; when the    */
    /* key reaches zero, awaken a sleeping process          */

    if (nonempty(sleepq) && (--firstkey(sleepq) <= 0)) {
        wakeup();
    }

    /* Check to see if this proc should be preempted */

    if (--preempt <= 0) {
        preempt = QUANTUM;
        resched();
    }
    return;
}

I can add all of the starvation functionality on my own, I just need to figure out how to make the clkint.s handler call my function lets say A every 5 seconds...I don't understand how the clock works or the assembly entirely. I am not asking for someone to give me a solution, I just need some guidance.


Solution

  • intv is the time slicing granularity (in Khz) for the interrupt timer for scheduling (at least in this code). Every 5 seconds sounds very slow and is likely to make your system barely responsive, try on the order of milliseconds.

    To calculate a new interval you need to change this value and do a little math with the frequency. You can probably figure this step out from the calculation done on intv to get 10ms.

    In x86 the timer value can be established by writing the timer interrupt value in KHz to the port 0x40 using the outb assembly instruction. As shown in the code you have to write the least significant byte followed by the most significant (outb writes a byte at a time). To enable the timer you write 0x36 to the same port and you must bind an IRQ handler to IRQ0 in order to receive the interrupt. I tried condensing the info for you a bit but this is x86 specific.