Oneos Lite code learning: task scheduling and interrupt switch

Task scheduling switch

The following codes are in /oneos-kernel/source/os_sched.c

Initialization of scheduling:
k_ sched_ init->_ k_ readq_ bmap_ Init: the priority bitmap is 0 and the priority linked list array is initialized here.

Start kernel scheduling:
k_start->os_first_task_start-

Turn off task scheduling:

void os_schedule_lock(void)
{
    register unsigned long    kernel_irq_save;
    kernel_irq_save = os_irq_lock();
    g_os_sched_lock_cnt++;
    os_irq_unlock(kernel_irq_save);
}

Enable task scheduling:

void os_schedule_unlock(void)
{
    register unsigned long    kernel_irq_save;
    kernel_irq_save = os_irq_lock();
    g_os_sched_lock_cnt--;
    os_irq_unlock(kernel_irq_save);

    if (0 == g_os_sched_lock_cnt)
    {
        kernel_irq_save = os_irq_lock();

        if ((OS_NULL == g_os_current_task) || (0 != g_os_sched_lock_cnt))
        {
            os_irq_unlock(kernel_irq_save);
        }
        else
        {
            g_os_next_task = g_os_high_task;
            if (g_os_current_task != g_os_next_task)
            {
                os_task_switch();
            }
            os_irq_unlock(kernel_irq_save);
        }
    }
}

Note here:

g_ os_ sched_ lock_ The initial value of CNT is 0. When task scheduling is turned off, it is + 1. When it is turned on, it is - 1. If the value is 0, it indicates that scheduling is turned on. If the value is negative, it indicates an error! There's a problem!
When the value is 0, start scheduling to find the task with the highest priority. If the task with the highest priority is different from the current priority, switch the task.

It can be seen that the key to determining whether task scheduling is enabled is the global variable: g_os_sched_lock_cnt. So we should be very careful with his operation. When operating it, the interrupt is turned off to prevent interference.

Interrupt switch

Again, the switch is interrupted:
The following code uses inline assembly in /arch/arm/armv7march_interrupt.c Medium:

os_ubase_t os_irq_lock(void)
{
    os_ubase_t primask;
    __asm__ __volatile__(
        "MRS     %0, PRIMASK\n"
        "CPSID   I"
        : "=r"(primask)
        : 
        : "memory");
    return primask;
}
void os_irq_unlock(os_ubase_t irq_save)
{
    __asm__ __volatile__(
        "MSR     PRIMASK, %0"
        : 
        : "r"(irq_save)
        : "memory");
    return;
}
void os_irq_disable(void)
{
    __asm__ __volatile__(
        "CPSID   I"
        :
        : 
        :);
    return;
}
void os_irq_enable(void)
{
    __asm__ __volatile__(
        "CPSIE   I"
        :
        : 
        :);
    return;
}

There are several points to note here: MRS,PRIMASK,CPSID I.

  1. PRIMASK: interrupt mask register. This register has only one bit. After setting 1, all exceptions that can mask interrupts will be closed, leaving only NMI and hard fault. The default value is 0;
  2. MRS: transfer instruction from status register to general register.
  3. MSR: transfer instruction from general register to status register.

Therefore:

  1. "Mrs% 0, primask \ n" is to save the value of primask interrupt mask register to primask
  2. "MSR primask,% 0 \ n" is to write the value saved in primask back to primask interrupt mask register
  3. The full name of CPS is Change Processor State, which changes the processor state.
  4. IE: Interrupt or abort enable. Enable interrupt
  5. ID: Interrupt or abort disable. Close interrupt

Therefore:

  1. Cpsid I is to change the state of the processor and turn off irq interrupt
  2. CPSIE I changes the state of the processor and starts irq interrupt

Visible: os_irq_lock and os_irq_unlock is a set of interfaces, os_irq_disable and os_irq_enable is another set, which can be used.

Follow & & contact

Open source lightweight operating system: https://gitee.com/cmcc-oneos/OneOS-Lite

docs document center: https://oneos-lite.com/

Keywords: Embedded system

Added by jbalanski on Fri, 24 Dec 2021 16:39:10 +0200