Почему планировщик xv6 вызывает sti () в начале каждого цикла?

В сопутствующей книге говорится

Причина для периодического включения прерываний на простаивающем процессоре заключается в том, что может не быть RUNNABLE процесса, потому что процессы (например, оболочка) ожидают ввода-вывода; если планировщик оставлял прерывания отключенными все время, ввод-вывод никогда не поступал бы.

Но я думаю, нам просто нужно один раз вызвать sti () перед выходным циклом for, поскольку каждый раз, когда мы освобождаем ptable.lock, прерывания снова включаются.


person delphifirst    schedule 07.05.2015    source источник
comment
Может быть, вам стоит попробовать удалить повторяющийся sti и посмотреть, зависает ли планировщик или продолжает нормально.   -  person smac89    schedule 26.11.2016


Ответы (2)


Возможно, что schedule() вызывается с отключенными прерываниями, и в этом случае освобождение спин-блокировки ptable не приведет к их повторному включению.

person Benjamin Peterson    schedule 07.05.2015
comment
Да все верно. Но я думаю, нам нужно разрешить прерывания только в начале scheduler (). Почему нужно делать это неоднократно? Можете ли вы дать мне сценарий, когда он понадобится? Большое спасибо. - person delphifirst; 08.05.2015

Если вы посмотрите на код для освобождения блокировки a>, вы увидите, что он не разрешает прерывания явно. Вместо этого он использует функцию popcli.

void release ( struct spinlock* lk )
{
    ...
    popcli();  // enable interrupts
}

Функция popcli не всегда разрешает прерывания. Он используется вместе с pushcli для отслеживания уровня вложенности. «Pushcli / popcli похожи на cli / sti, за исключением того, что они совпадают: требуется два popcli, чтобы отменить два pushcli». 1

void popcli ( void )
{
    // If interrupts are enabled, panic...
    if ( readeflags() & FL_IF )
    {
        panic( "popcli: interruptible" );
    }

    // Track depth of cli nesting
    mycpu()->ncli -= 1;

    // Popped more than were pushed...
    if ( mycpu()->ncli < 0 )
    {
        panic( "popcli" );
    }

    // Reached outermost, so restore interrupt state
    if ( mycpu()->ncli == 0 && mycpu()->intena )
    {
        sti();  // enable interrupts
    }
}

В то время как popcli иногда разрешает прерывания, pushcli всегда запрещает прерывания.

void pushcli ( void )
{
    int eflags;

    eflags = readeflags();

    // Disable interrupts
    cli();

    // Save interrupt state at start of outermost
    if ( mycpu()->ncli == 0 )
    {
        mycpu()->intena = eflags & FL_IF;
    }

    // Track depth of cli nesting
    mycpu()->ncli += 1;
}

Явным вызовом sti планировщик отменяет текущее состояние push / popcli. Я думаю, что это дает краткое окно, необходимое для прерывания ввода-вывода. Т.е. период времени между вызовом sti и вызовом cli (через acquire -> pushcli -> cli).

void scheduler ( void )
{
    ...
    for ( ;; )
    {
        // Enable interrupts on this processor.
        sti();

        // Acquire process table lock
        acquire( &ptable.lock );

        // Loop over process table looking for process to run.
        for ( p = ptable.proc; p < &ptable.proc[ NPROC ]; p += 1 )
        {
            ...
        }

        // Release process table lock
        release( &ptable.lock );
    }
}
person Jet Blue    schedule 16.06.2020