在多处理器系统上给出以下伪代码:
class SpinLock {
private:
int value = 0; // 0 = FREE; 1 = BUSY
public:
void acquire() {
while (test_and_set(&value)) // while BUSY
; // spin
}
void release() {
value = 0;
memory_barrier();
}
}
其中,测试设置指令从内存中原子读取一个值到寄存器,并将值1写入该内存位置。
现在,它们通过以下方式实现Locks:
class Lock {
private:
int value = FREE;
SpinLock spinLock;
Queue waiting;
public:
void acquire();
void release();
}
Lock::acquire() {
spinLock.acquire();
if (value != FREE) {
waiting.add(runningThread);
scheduler.suspend(&spinLock);
// scheduler releases spinLock
} else {
value = BUSY;
spinLock.release();
}
}
void Lock::release() {
TCB *next;
spinLock.acquire();
if (waiting.notEmpty()) {
next = waiting.remove();
scheduler.makeReady(next);
} else {
value = FREE;
}
spinLock.release();
}
class Scheduler {
private:
Queue readyList;
SpinLock schedulerSpinLock;
public:
void suspend(SpinLock *lock);”
void makeReady(Thread *thread);
}
void
Scheduler::suspend(SpinLock *lock) {
TCB *chosenTCB;
disableInterrupts();
schedulerSpinLock.acquire();
lock->release();
runningThread->state = WAITING;
chosenTCB = readyList.getNextThread();
thread_switch(runningThread,
chosenTCB);
runningThread->state = RUNNING;
schedulerSpinLock.release();
enableInterrupts();
}
void
Scheduler::makeReady(TCB *thread) {
disableInterrupts();
schedulerSpinLock.acquire();
readyList.add(thread);
thread->state = READY;
schedulerSpinLock.release();
enableInterrupts();
}
我不知道这是如何工作的。假设当我们调用acquire()时,我们位于线程A中,而锁由其他线程B拥有。
我们获取Lock对象的自旋锁,将线程添加到等待列表中,并以Lock的自旋锁作为参数调用scheduler.suspend。 在suspend方法中,我们将获取调度程序的自旋锁,假设这是免费的,然后释放Lock的自旋锁,将运行线程A的状态更改为waiting,从就绪队列中获取线程C,然后执行上下文切换。
我不知道现在如何释放调度程序的自旋锁。在上下文切换中,将堆栈指针更改为新线程C的堆栈指针,还交换了寄存器,特别是指令指针,因此在重新分配旧线程C的CPU时间之前,不会执行thread_switch之后的语句,对吗?因此,假设调度程序的自旋锁不是免费的,而是由线程A保持的。
假定线程B释放了它的锁。它获取了Lock的自旋锁,该锁是免费的,并且等待队列中至少有一个线程,即A。假设选择A作为下一个线程,因此我们以线程A作为参数调用scheduler.makeReady。但是现在在makeReady内部有一个调用来获取调度程序的自旋锁,该调用未释放,因为在执行线程A时,我们在Scheduler :: suspend()内部调用schedulerSpinlock.release()之前执行了上下文切换。如果我们不能再次运行它,线程A立即释放调度程序的自旋锁吗?
答案 0 :(得分:1)
[caveat:我还没看过安德森]。
Thread_switch()停止一个线程的执行,并继续执行另一个线程。在调用它使其停止的thread_switch()调用之后,另一个将立即在其自己的堆栈上返回指令。
如果我们假设每个未运行的线程由于调用suspend而变为未运行,那么刚唤醒的线程将释放由挂起自身的线程获取的调度程序自旋锁。如果我们不能做这个假设,但是可以假设对thread_switch的每次调用都采用以下形式:
schedulerSpinLock.acquire();
...
thread_switch(cur, new);
...
schedulerSpinLock.release();
那么就足以确保不会发生您所设想的场景-C会因为C正在退出挂起或其他某些重复挂起模式的功能而将schedulerSpinLock释放。
这种设计的优点可能是有争议的。是否应该在分配给另一个线程的一个线程中释放spinlock()可能是某些领域的热门话题。放心,大多数内核都有一些杂凑的纯语义,可以在技术暂停但仍在执行或技术运行但仍暂停的情况下处理过渡。