CS452 - Real-Time Programming - Fall 2008

Lecture 6 - Kernel, Tasks

Questions & Comments

  1. Bill Cowan's easy method versus inline assembly.


Kernel maintains a task descriptor (TD) for each created task. That is, to create a task the kernel must allocate a TD and initialize it. The TD normall contains

  1. The task's stack pointer, which points to a private stack containing

    all ready to be reloaded whenever the task next runs.

  2. Possibly the return value for when the task is next activated
  3. The task's parent
  4. The task's state

Possible states of the task

  1. Active: running or about to run
  2. Ready: can run if scheduled
  3. Blocked: waiting for something to happen


Hardware Facts for Eos

Memory Map


Accessed by special I/O instructions

Architecture of X86

General Purpose Registers

All these registers have special names for the low 16 bits. (drop the E), and for the low 8 bits (add an L), and for the second 8 bits (add an H)

Special Purpose Registers

  1. IDTR: interrupt descriptor table
  2. GDTR: global descriptor table
    1. the GDTR is 48 bits, divided [--16--|---32---]
      1. 16 bit part is limit, the size of the table
      2. 32 bit part is base, the start of the table
    2. write using lgdt [gdtr],where idtr is a pointer a struct containing an interrupt descriptor
    3. read using sidt
  3. CS, DS, ES, FS, GS, SS: segment registers
  4. EIP: program counter

Addressing Memory

Physical address is

Helps to provide

  1. position independent (relocatable) code
  2. memory protection

Six segment registers

  1. CS
  2. DS
  3. ES
  4. FS
  5. GS
  6. SS

Each segment register is an index into a Global Descriptor Table (GDT), which contains

  1. low limit
  2. high limit
  3. etc.

Index is actually contents of register >> 3.

Setting up a Task

Every task requires at least two GDTs

The compiler assumes that DS = SS

Set DS = ES = FS = GS = SS for each task

There is no GDT in place when the kernel boots

How is this done

  1. The address of the table is kept in the special register GDTR
  2. lgdt sets GDTR; sgdt reads GDTR

Yet another register EFLAGS contains processor state such as

Setting up a task

Context Switch in the 386

int <vector>


Task to kernel

  1. int <vector> in task
  2. pushal saves all the active task's registers on the active task's stack
  3. switch to kernel stack
    1. you got CS, EIP from IDT
    2. ESP from your magic place
    3. DS from the kernel variables
  4. restore remainder of kernel state from stack

Kernel to task

  1. save kernel state on kernel stack
  2. switch to active task's stack
  3. restore general purpose registers: popal

iretl gets EFLAGS, CS, EIP from the task stack.

Getting the Kernel Started

Each task is a C function

So is the kernel

Each gets compiled and linked into an ELF executable

Then 452-post.py (/u/cs452/public/tools/bin/452-post.py) binds the ELFs together to be downloaded.


Before starting to execute the kernel needs to have

  1. A stack, on which it can place data, with the stack pointer set.
  2. Its first instruction in the PC

The boot loader does this. How?

  1. The bound executable obeys the multiboot specification
  2. The kernel is called by int main( unsigned long magic, multiboot_info_t *mbiaddr )
  3. The stack starts as mbiaddr, magic
  4. multiboot_info_t is a struct with the following interesting fields
    typedef struct multiboot_info {
      unsigned long mods_count;
      unsigned long mods_addr;
    } multiboot_info_t

    The second is a pointer to module_t, the first module record.

  5. module_t is a struct with the following interesting fields
    typedef struct module {
      unsigned long mod_start;
      unsigned long mod_end;
      unsigned long string;
    } module_t

You might find this URL has some interesting information (and links) if you are really interested in how executables are structured, or in the multiboot boot process.

Return to: