OSdev:在键盘中断处理程序中使用自旋锁导致死锁

问题描述 投票:0回答:1

我正在用 C++ 编写操作系统,并为键盘设置了旋转锁。

_Use_decl_annotations_
bool
HandleKeyboard(
    _In_ const BYTE Index,
    _In_ const PINTERRUPT_STACK Stack,
    _In_ const BYTE HasError,
    _In_ const PINTERRUPT_CPU_STATE Cpu
)
{
    (void)Index;
    (void)Stack;
    (void)HasError;
    (void)Cpu;

    if (const auto key = Pic::PicManager::keyboard.GetKeyPress(); key.hasValue)
    {
        __cli();
        {
            auto& [capitalize, keyLock, currentKey, wakeUp] = Pic::PicManager::keyboard;
            Sync::Guard guard(keyLock);

            currentKey = key.inner;
            wakeUp = true;
        }
        __sti();
    }


    return true;
}

它禁用中断,获取锁并将密钥放入全局变量中,然后设置一个标志以便主循环可以处理它:

    while (true)
    {
        auto& [capitalize, keyLock, currentKey, wakeUp] = Pic::PicManager::keyboard;
        Sync::Guard guard(keyLock);

        if (!wakeUp || currentKey == 0)
        {
            continue;
        }
        
        // do something with the key
        
        currentKey = 0;
        wakeUp = false;
    }

有时我可以写 1-3 个键,然后就卡住了。 Bochs 说键盘缓冲区已满,所以我猜它以某种方式卡在中断处理程序中。

自旋锁的实现:

__spinlock_lock:
    lock bts word [rcx], 0
    jc .wait
    ret
.wait:
    test word [rcx], 0
    jnz .wait
    jmp __spinlock_lock

__spinlock_unlock:
    lock btr word [rcx], 0
    ret

RCX 中的参数是一个不稳定的词。

Sync::Spin::Lock
只调用这些函数,
Sync::Guard
在构造函数中调用
Lock
,在析构函数中调用
Unlock
。我尝试了多种自旋锁的实现(带有标志的基本 while 循环,
<atomic>
),但效果是一样的。如果我从任何一个可以工作的地方取下锁。

我不明白它是如何卡住的,因为 while 循环在每次迭代时都会解锁自旋锁,因此中断自旋锁有机会被获取。

c++ operating-system osdev
1个回答
0
投票

如果主循环已经获取了锁,并且中断在临界区期间到达同一个核心,则处理程序将旋转,但锁永远不会变得可用,因为主循环无法完成临界区,直到处理程序返回。所以是的,当然会陷入僵局。

自旋锁对于在不同核心上并发运行的线程之间的互斥很有用。您不能将它们用于同一核心上的主线程和中断处理程序之间的互斥,因为它们不会同时运行;主线程被挂起,直到中断处理程序返回。

这里的一种方法是使用处理程序的无锁原子标志来指示数据可用。当主线程观察到此标志被设置时,它可以在从缓冲区检索数据时禁用中断。

或者,使用完全无锁的队列算法 - 最好是现成的算法,因为它们很难正确使用。

© www.soinside.com 2019 - 2024. All rights reserved.