Зачем нам нужно pushcli () перед xchg ()? - PullRequest
1 голос
/ 03 марта 2020

Я смотрел на эту реализацию спин-блокировок и, в частности, функции получения и выпуска:

void
acquire(struct spinlock *lk)
{
  pushcli(); // disable interrupts to avoid deadlock.
  if(holding(lk))
    panic("acquire");

  // The xchg is atomic.
  // It also serializes, so that reads after acquire are not
  // reordered before it. 
  while(xchg(&lk->locked, 1) != 0)
    ;

  lk->cpu = cpu;
  getcallerpcs(&lk, lk->pcs);
}

// Release the lock.
void
release(struct spinlock *lk)
{
  if(!holding(lk))
    panic("release");

  lk->pcs[0] = 0;
  lk->cpu = 0;

  xchg(&lk->locked, 0);

  popcli();
}

Какова цель pushcli() и popcli() если функция xchg это атоми c? Разве атомарность не гарантирует, что только один поток может изменить значение блокировки в любом случае и не будет прерван? Почему мы должны явно отключать прерывания, чтобы предотвратить взаимные блокировки?

1 Ответ

2 голосов
/ 03 марта 2020

Разве атомарность не гарантирует, что только один поток может изменить значение блокировки в любом случае и не быть прерван?

Да, это не проблема. Операция обеспечивает атомарность между потоками, но обработчики прерываний все еще являются проблемой.


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

Когда ЦП перехватывает прерывание, любой выполняющийся код «приостанавливается» и вводится обработчик прерывания. Только после обработчик прерываний завершил свою работу, все остальное может продолжаться как обычно.

Теперь представьте, что вашему коду требуется acquire() спин-блокировка, и функция определяется следующим образом:

void acquire(struct spinlock *lk) {
    while(xchg(&lk->locked, 1) != 0)
        ;

    lk->cpu = cpu;
}

Если прерывание происходит в любое время после while выше (и до release()), и обработчик прерывания должен получить такой же спин-блокировку, то происходит то, что он пытается сделать то же самое: снова набрать acquire(), ввести while l oop. Однако, поскольку спин-блокировка уже удерживается, l oop никогда не выйдет. Обработчик прерываний будет продолжать вращаться, пытаясь получить блокировку, которая никогда не будет снята, потому что по определению обработчик прерываний должен выполняться до его конца, прежде чем ЦП сможет продолжить делать что-либо еще. Это вызывает тупик. Больше ничего не остается, кроме как выключить и перезагрузить процессор.

Вот почему в коде, который вы показываете, необходимо отключение прерываний.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...