Разве атомарность не гарантирует, что только один поток может изменить значение блокировки в любом случае и не быть прерван?
Да, это не проблема. Операция обеспечивает атомарность между потоками, но обработчики прерываний все еще являются проблемой.
Прерывания (особенно аппаратные прерывания) являются асинхронными и могут происходить в любое время , что означает, что код может делать что-либо в то время, когда генерируется прерывание.
Когда ЦП перехватывает прерывание, любой выполняющийся код «приостанавливается» и вводится обработчик прерывания. Только после обработчик прерываний завершил свою работу, все остальное может продолжаться как обычно.
Теперь представьте, что вашему коду требуется acquire()
спин-блокировка, и функция определяется следующим образом:
void acquire(struct spinlock *lk) {
while(xchg(&lk->locked, 1) != 0)
;
lk->cpu = cpu;
}
Если прерывание происходит в любое время после while
выше (и до release()
), и обработчик прерывания должен получить такой же спин-блокировку, то происходит то, что он пытается сделать то же самое: снова набрать acquire()
, ввести while
l oop. Однако, поскольку спин-блокировка уже удерживается, l oop никогда не выйдет. Обработчик прерываний будет продолжать вращаться, пытаясь получить блокировку, которая никогда не будет снята, потому что по определению обработчик прерываний должен выполняться до его конца, прежде чем ЦП сможет продолжить делать что-либо еще. Это вызывает тупик. Больше ничего не остается, кроме как выключить и перезагрузить процессор.
Вот почему в коде, который вы показываете, необходимо отключение прерываний.