Page 2 of 2

Posted: Sun Feb 25, 2007 1:51 pm
by Dex
Have you seen the code from "AnonymOS" a 32bit pmode, pascal OS written in freepascal, it has many functions.
Let me know if you want me to post the code to "AnonymOS".

Posted: Sun Feb 25, 2007 2:52 pm
by Brendan
Hi,
Tyler wrote:I recently implemented a delay function in my native library... for the sake of it. But it causes serious problems the way i am doing it. Does anyone know how it is done by an operatin system like linux? Do they use the PIT and time the system with APIC or do they simply check the RTC during kernel mode access?
Not sure exactly how Linux does things, but I'd have a scheduler function that runs other tasks until the delay expires for long and efficient but not-too-accurate delays (e.g. "sleep()") and another function for short accurate delays that waste CPU time (e.g. "udelay()").

Then, for long and accurate delays I'd use the first function followed by the second function (e.g. an exact 100023 ms delay might use the first function for 100000 ms, then the second function for the remaining 23 ms). Call this one "delay()".

I wouldn't let anything outside the kernel use the second function ("udelay()") directly - they can use the third function ("delay()") instead. That way if you improve the accuracy of "sleep()", code that uses "delay()" will automatically waste less CPU time. Most code would just use "sleep()" because accuracy isn't needed, but device drivers would use "delay()".

The timing for "sleep()" could use anything - RTC, PIT, local APIC timer, etc. What you use will probably depend on what the kernel uses to keep track of time and/or scheduling.

For "udelay()" you could use RDTSC, the local APIC count, a PIT channel count or a calibrated "LOOP $". Because this is meant to be accurate you'd need to be carefull here - different things are effected in different way. For example, RDTSC isn't suitable for systems that use Intel's SpeedStep for power management. Also, you might not be able to use the local APIC on some systems, and can't use RDTSC on 80486 or older.

The "LOOP $" option is usually inaccurate - it's messed up by IRQs, SMI/SMM, hyper-threading, and power managment. There are ways to improve the accuracy though (like doing an "IN al,<dummy_port>" in the loop, so that the time delay depends much more on fixed hardware timing).

This means to get the most accuracy possible for "udelay()" you need to detect what is supported and then use that.

Because the acccuracy of the "sleep()" code can also change (either due to dynamic timer frequency selection during boot, deciding if local APIC is present or not, or due to kernel changes) it's also a good idea to provide kernel functions that code can use to find out how accurate "sleep()" and "delay()" actually are.

Lastly, I'd have "sleep()" and "sleepUntil()", and "delay() and "delayUntil()". Consider the following 2 pieces of code:

Code: Select all

    for(;;) {
        do_something();
        sleep(100);
    }

Code: Select all

    next_tick = get_current_tick() + 100;
    for(;;) {
        do_something();
        sleepUntil(next_tick);
        next_tick += 100;
    }
For the first piece of code, how often is "do_something()" called? If "do_something()" takes 1 ms then it'd be called every 101 ms, but if "do_something()" takes 100 ms it'd be called every 200 ms.

For the second piece of code, "do_something()" is called every 100 ms (unless "do_something()" always takes longer than 100 ms).


Cheers,

Brendan