void
sema_down (struct semaphore *sema)
{
old_level = intr_disable ();
while (sema->value == 0)
{
list_push_back (&sema->waiters, &thread_current ()->elem);
thread_block ();
}
sema->value--;
intr_set_level (old_level);
}
上面的代码是PintOS中的一个机械锁定互斥量。PintOS是针对单处理器系统的。因此,仅禁用中断就足够了。另一方将不可能使用互斥体。
因此,让我们考虑一个多处理器设计:
void
sema_down (struct semaphore *sema)
{
old_level = intr_disable ();
while (!lock cmpxchg(1,0)) // it is just pseudocode-idea
{
list_push_back (&sema->waiters, &thread_current ()->elem);
thread_block ();
}
intr_set_level (old_level);
}
old_level = intr_disable ();
。它关闭了中断,但仅在该CPU的上下文中才至关重要。
它可以作为MP体系结构中功能获取互斥量的原型。但是,出现了问题list_push_back
。它也必须是安全多线程的。但是,我们不能使用互斥锁来确保它的安全,因为我们现在才实现它!
主要问题是:是否有可能两个(或多个)CPU在Ring 0级别(内核)上执行代码?
并且,取决于第一个答案的子问题:
如果没有,我上面没有问题。但是,如何实施呢?
如果是的话(似乎不可能或很难实现),那么我的上述考虑又如何呢(这仅仅是潜在问题的示例)。我们必须使用spinlocks
还是lock-free structures
?
是的,即使在环0,在SMP中,多个CPU仍可以执行相同的代码。
每个CPU都是对称的,因此它可以执行与其他CPU相同的代码路径(包括内核代码),除非该软件实现某种同步。
Linux内核也面临这个问题,并最初实现了一个不太好的解决方案:在进入和退出内核时获取并释放了Big Kernel Lock。
这不是一个好的解决方案,因为一次只能有一个CPU可以执行内核代码,但是它实现起来很快,并且与列表中的第一个项目等效。
更好的解决方案是在整个内核中使用更精细的锁。
由于是实现如示例中所示的互斥锁或信号灯之类的睡眠锁的内核,因此它不能依靠这些原语本身1,而必须使用自旋锁或其他更简单的锁形式。
幸运的是,这不是问题,自旋锁(及其变体)在争用较少或关键路径确实很短(例如更新列表)时实际上比互斥锁更好。
您可以查看Linux上的mutex_init,以了解自旋锁用于同步等待任务的队列。
49 void
50 __mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key)
51 {
52 atomic_set(&lock->count, 1);
53 spin_lock_init(&lock->wait_lock);
54 INIT_LIST_HEAD(&lock->wait_list);
55 mutex_clear_owner(lock);
56 #ifdef CONFIG_MUTEX_SPIN_ON_OWNER
57 osq_lock_init(&lock->osq);
58 #endif
59
60 debug_mutex_init(lock, name, key);
61 }
因此,第二项的答案是肯定的。
1等待锁时无法入睡。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句