Multitasking code question.

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
ExeTwezz
Member
Member
Posts: 104
Joined: Sun Sep 21, 2014 7:16 am
Libera.chat IRC: exetwezz

Multitasking code question.

Post by ExeTwezz »

Hi,

I have a code based on this http://hosted.cjmovie.net/TutMultitask.htm tutorial. It doesn't cause any errors, but it doesn't work for its purpose, switching tasks.

IRQ0 handler.

Code: Select all

global irq0
irq0:
    cli
    pusha
    push ds
    push es
    push fs
    push gs

    mov eax, 0x10               ; Data segment.
    mov ds, eax
    mov es, eax
    mov fs, eax
    mov gs, eax

    push esp                    ; Pointer to the stuff we just pushed.
    extern switch_task          ; multitasking/task.c
    call switch_task            ; Call C code.

    pop eax
    mov esp, eax                ; Replace the stack with the return value of
                                ; the C function.
    ; Send End Of Interrupt signal.
    mov al, 0x20
    out 0x20, al

    pop gs
    pop fs
    pop es
    pop ds
    popa
    iret
multitasking/task.h

Code: Select all

// Basic Operating System.
// Multitasking.

#ifndef TASK_H
#define TASK_H

#include <stdint.h>

// Task descriptor.
typedef struct task_s
{
    uint32_t esp0; // Stack for the kernel.
    uint32_t esp3; // Stack for the task.

    struct task_s *next; // The next task.
} __attribute__ ((packed)) task_t;

/* multitasking/task.c */
task_t  *create_task (void *);
uint32_t switch_task (uint32_t);

#endif /* !TASK_H */
multitasking/task.c

Code: Select all

// Basic Operating System.
// Multitasking.

#include <multitasking/task.h>
#include <memory/kmalloc.h>
#include <io/screen.h>
#include <stddef.h>

// Task queue.
task_t *task_queue;

// The current task.
task_t *current_task = NULL;

// Create a task.
task_t *create_task (void (*task_func))
{
    uint32_t *stack; // Pointer to the stack of the new task.

    // Create a new task.
    task_t *new_task = (task_t *) kmalloc (sizeof (task_t));

    // Create a stack for the task.
    stack = (uint32_t *) kmalloc (4096);
    new_task->esp0 = (uint32_t) stack;

    // Are there any tasks in the queue?
    if (task_queue == NULL)
    {
        // No. The created task will be the first one.
        task_queue = new_task;
    }
    else
    {
        // Yes. Make the task `new_task' the last one.
        task_t *last = task_queue; // The last task.
        while (1)
        {
            if (last->next != NULL)
                last = last->next;
            else
                break;
        }
        last->next = new_task;
    }

    // Fill in the task descriptor.
    // Set the registers.
    // First, the segment registers.
    *--stack = 0x10; // GS
    *--stack = 0x10; // FS
    *--stack = 0x10; // ES
    *--stack = 0x10; // DS

    // Next, the registers pushed by `pusha'.
    *--stack = 0; // EDI
    *--stack = 0; // ESI
    *--stack = 0; // EBP
    *--stack = 0; // ESP
    *--stack = 0; // EBX
    *--stack = 0; // EDX
    *--stack = 0; // ECX
    *--stack = 0; // EAX

    // Now these are the registers pushed automatically.
    *--stack = (uint32_t) task_func; // EIP
    *--stack = 0x08; // CS
    *--stack = 0x0202; // EFLAGS
    *--stack = 0; // USERESP
    *--stack = 0x10; // SS

    // Update the stack pointer.
    new_task->esp0 = (uint32_t) stack;
    new_task->next = NULL;

    return new_task;
}

// Switch between tasks.
uint32_t switch_task (uint32_t task_esp)
{
    puts ("switch_task()\n");

    // Is there the current task?
    if (current_task == NULL)
    {
        // No. Make the first task in the queue the current one.
        current_task = task_queue;
    }

    // Save the stack pointer of the task.
    current_task->esp0 = task_esp;

    // Switch to the next task.
    // Is there the next task?
    if (current_task->next == NULL)
    {
        // No. Switch to the start of the queue.
        current_task = task_queue;
    }
    else
    {
        // Yes. Switch to it.
        current_task = current_task->next;
    }

    // This is reached, and it's returned well from the function to the ISR.
    return current_task->esp0;
}
Tasks' functions

Code: Select all

// The task №1.
void task_1 (void)
{
    halt (); // The kernel will be still running, so the function is not called.
    uint8_t *vidmem = (uint8_t *) 0xB8001;
    for (;;) *vidmem++;
}

// The task №2.
void task_2 (void)
{
    uint8_t *vidmem = (uint8_t *) 0xB8003;
    for (;;) *vidmem++;
}
Tasks creating

Code: Select all

// The C kernel main.
void kernel_main (mb_info_t *mb_info, uint32_t kernel_end)
{
    init_screen ();
    clear_screen ();

    setup_idt ();
    init_keyboard_handler ();
    init_timer_handler ();
    asm volatile ("cli"); // Disable interrupts due to multitasking.

    <...>

    puts ("[kernel_main] Creating the task #1.\n");
    task_t *task1 = create_task (task_1);
    puts ("[kernel_main] Creating the task #2.\n");
    task_t *task2 = create_task (task_2);
    puts ("[kernel_main] Enabling interrupts.\n");
    asm volatile ("sti");
    puts ("[kernel_main] End of the function.\n");
}
Why aren't tasks' functions executed? Is it because it's something wrong with POPs in the IRQ0 handler?
`switch_task()' string is displayed on the screen each short (<1 sec.) period of time, but the tasks functions are not get called.

P.S. Are there spoilers on the forum?
Last edited by ExeTwezz on Wed Nov 12, 2014 10:12 pm, edited 1 time in total.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Multitasking code question.

Post by Combuster »

Do you know what the C ABI states on return values?
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
ExeTwezz
Member
Member
Posts: 104
Joined: Sun Sep 21, 2014 7:16 am
Libera.chat IRC: exetwezz

Re: Multitasking code question.

Post by ExeTwezz »

Combuster wrote:Do you know what the C ABI states on return values?
I've just removed "pop eax" instruction, and General Protection Fault occurs. What need I do to fix this? I tried some things with pushing to the stack, but it didn't help.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Multitasking code question.

Post by Combuster »

The point is that you look up the answer, not magically try things.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
milliburn
Posts: 5
Joined: Mon Jul 01, 2013 10:13 am

Re: Multitasking code question.

Post by milliburn »

You should also check your stack allocation procedure (in create_task), remembering that the stack grows downwards in memory (as the code following demonstrates) while the allocation function presumably returns the lowest address of an allocation, to avoid memory corruption further down the road.
User avatar
Kazinsal
Member
Member
Posts: 559
Joined: Wed Jul 13, 2011 7:38 pm
Libera.chat IRC: Kazinsal
Location: Vancouver
Contact:

Re: Multitasking code question.

Post by Kazinsal »

You've disabled interrupts, then expected task switching in an interrupt handler to magically work.
ExeTwezz
Member
Member
Posts: 104
Joined: Sun Sep 21, 2014 7:16 am
Libera.chat IRC: exetwezz

Re: Multitasking code question.

Post by ExeTwezz »

Kazinsal wrote:You've disabled interrupts, then expected task switching in an interrupt handler to magically work.
Don't you see? I'm enabling interrupts after creating tasks in `kernel_main()`. Or do you mean in the interrupt handler? If so, then popping EFLAGS from the stack with set IF (9th bit) will enable itnerrupts. Isn't so?
ExeTwezz
Member
Member
Posts: 104
Joined: Sun Sep 21, 2014 7:16 am
Libera.chat IRC: exetwezz

Re: Multitasking code question.

Post by ExeTwezz »

milliburn wrote:You should also check your stack allocation procedure (in create_task), remembering that the stack grows downwards in memory (as the code following demonstrates) while the allocation function presumably returns the lowest address of an allocation, to avoid memory corruption further down the road.
Yep, thank you. But it didn't help.
ExeTwezz
Member
Member
Posts: 104
Joined: Sun Sep 21, 2014 7:16 am
Libera.chat IRC: exetwezz

Re: Multitasking code question.

Post by ExeTwezz »

Please, help me. I changed the order of pushing the registers to the stack in create_task() and now I don't see any faults, but the tasks' functions are not get called. What's the problem?

Source code: https://yadi.sk/d/5k_M96cHcgvSV
ExeTwezz
Member
Member
Posts: 104
Joined: Sun Sep 21, 2014 7:16 am
Libera.chat IRC: exetwezz

Re: Multitasking code question.

Post by ExeTwezz »

I've changed

Code: Select all

// Switch between tasks.
uint32_t switch_task (uint32_t task_esp)
{
    // Is there the current task?
    if (current_task == NULL)
    {
        // No. Make the first task in the queue the current one.
        current_task = task_queue;
    }

    // Save the stack pointer of the task.
    current_task->esp0 = task_esp;

    <...>
to

Code: Select all

// Switch between tasks.
uint32_t switch_task (uint32_t task_esp)
{
    // Is there the current task?
    if (current_task == NULL)
    {
        // No. Make the first task in the queue the current one.
        current_task = task_queue;
    }
    else
    {
        // Save the stack pointer of the task.
        current_task->esp0 = task_esp;
    }

    <...>
And everything works fine.
Post Reply