操作系统导论习题解答(28. Locks)

本文深入探讨了Locks在操作系统中的作用,从基本概念到各种实现方式,包括Pthread Locks、自旋锁、公平性、性能评估及避免自旋的策略,如使用条件变量进行睡眠等待,同时指出了不同实现的优缺点和适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Locks

为了解决在执行一系列指令时中间发生中断事情,引入了lock

1. Locks: The Basic Idea

在这里插入图片描述
使用lock,关键部分为balance = balance + 1

2. Pthread Locks

在这里插入图片描述
传递了一个变量来锁定和解锁,因为可能使用不用的锁来保护不同的变量(提高并发性);还可以保护不同的数据和具有不同锁的数据结构,从而允许更多线程进入一次锁定代码。

3. Building A Lock

构造一个Lock,我们需要OS和硬件支持。

4. Evaluating Locks

评估一个锁是否有效,效率好坏的指标:

  1. mutual exclusion(一个锁的基本任务)
  2. fairness(线程获得锁的机会平等)
  3. performance(使用锁的时间消耗)

5. Controlling Interrupts

防止关键部分被打断的一种早期的解决方案就是在关键部分执行时停止中断程序。代码如下所示:在这里插入图片描述
如果我们运行在一个单核处理器系统,这是非常简单的。
但是,也有很多缺点,如下所示:

  1. 线程调用必须执行优先操作,而且要相信其不会被乱用
  2. 该方法不能用于多处理器系统
  3. 长时间关闭中断会导致中断丢失
  4. 该方法效率可能低下

6. A Failed Attempt: Just Using Loads/Stores

在这里插入图片描述
上述代码也是非常简单能看懂,但是有两处问题:

  1. correctness
  2. performance

首先看一下正确性,如果有如下代码:在这里插入图片描述
上述的情况就破坏了锁的基本需求:mutual exclusion
第二个性能问题就是无止尽的检查flag的值,浪费很多时间周期(自旋锁)。

7. Building Working Spin Locks with Test-And-Set

int TestAndSet(int *old_ptr, int new) {
	int old = *old_ptr;		// fetch old value at old_ptr
	*old_ptr = new;			// store 'new' into old_ptr
	return old;
}

typedef struct __lock_t {
	int flag;
} lock_t;

void init(lock_t *lock) {
	// 0: lock is available, 1: lock is held
	lock->flag = 0;
}

void lock(lock_t *lock) {
	while (TestAndSet(&lock->flag, 1) == 1)
		;	// spin-wait(do nothing)
}

void unlock(lock_t *lock) {
	lock->flag = 0;
}

这种方法比上一种更好,但是还是有spin lcok(自旋锁)的问题存在。

8. Evaluating Spin Locks

自旋锁保证了mutual exclusion,但是没有保证fairness
对于performance,单处理器性能极差,但是多处理器(线程数理等于CPU数量)性能还不错。

9. Compare-And-Swap

在这里插入图片描述
能够取代lock()程序如下所示:在这里插入图片描述

10. Load-Linked and Store-Conditional

在这里插入图片描述在这里插入图片描述
上述关于lock()能更进一步简洁:

void lock(lock_t *lock) {
	while (LoadLinked(&lock->flag) || !StoreConditional(&lock->flag, 1))
		;	// spin
}

11. Fetch-And-Add

int FetchAndAdd(int *ptr) {
	int old = *ptr;
	*ptr = old + 1;
	return old;
}

使用fetch-and-add构造ticket lock在这里插入图片描述

12. Too Much Spinning: What Now?

上述的方法对于单处理器多线程性能是非常低下的:N个线程竞争一个锁,N - 1个时间片会浪费掉。
故为了提高性能,我们要避免spinning

13. A Simple Approach: Just Yield, Baby

第一种解决上述问题的方法非常简单,代码如下所示:在这里插入图片描述
相比于spinning,性能有了改进,但是时间开销还是非常大(上下文切换的成本可能很高)。
更糟糕的是,我们根本没有解决starvation。一个线程可能陷入无限的让出CPU循环状态,而其他线程则反复进入和退出关键部分(critical section)。

14. Using Queues: Sleeping Instead Of Spinning

在这里插入图片描述
该方法可能出现的问题:wakeup/waiting race
为了解决该问题:对lock()函数做了一些修改。

在这里插入图片描述
最后还要谈及一点:对于不同的OS有不同的硬件支持

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值