操作系统基础 锁的实现

锁的实现

操作系统会给用户程序提供开锁,闭锁的原语操作,那么锁在操作系统中是怎么实现的呢?

1.使用中断启用与禁止来实现锁

中断禁止:就是禁止打断,可以将一系列操作变为原子操作
中断启用:就是从这里开始,可以被打断,允许操作系统进行调度

上锁操作
lock() {
    /**中断禁止*/
    while(value != FREE) {
        /**中断启用*/
        //之所以在这里允许被调度,是因为在单核机器上运行时,
        //如果调用lock()的线程因为中断禁止而一直获得CPU时间的话,
        //那么获得锁的线程根本没有机会运行并释放锁,所以必须给其他线程插入的机会
        /**中断禁止*/
    }
    value = BUSY;
    /**中断启用*/
}
解锁操作
unlock() {
    /**中断禁止*/
    value = FREE;//因为这并不是原子操作,所以需要中断禁止的保护
    /**中断启用*/
}

缺点:繁忙等待,不可重入(指该实现)

2.使用测试与设置指令来实现锁

测试与设置(test & set)指令:以不可分割的方式执行如下两个步骤:
1)设置操作:将1写入指定内存单元。
2)读取操作:返回指定内存单元里原来的值(写入1之前的值)。

指令若以程序来实现,则是这样的:

test_and_set(value) {
    tmp = value;
    value = 1;
    return(tmp);
}
上锁操作
lock() {
    while(test_and_set(value) != 0) {}
}

如果返回值不为0(即不能将锁从0变为1),则会一直循环,繁忙等待锁的释放(0)再获取锁(1)。当能够将value从0设置为1时,跳出循环,调用lock()的线程进入临界区。
如果同一个线程调用两次lock(),因为value已经为1,所以返回值会一直返回1,会无法退出循环,导致死锁。

解锁操作
unlock() {
    value = 0;
}

缺点:繁忙等待,不可重入(指该实现)

3.以非繁忙等待、中断启用与禁止来实现锁

锁的实现思路如下:
1)使用中断禁止,但不进行繁忙等待。
2)如果拿不到锁,进程放弃CPU并进入睡眠状态,以便持有锁的进程可以更好地运行。
3)当锁释放的时候将睡觉进程叫醒。

错误示例 上锁操作
lock() {
    /**中断禁止*/
    if(value == FREE) {
        value = BUSY;
    } else {
        /**将该线程放进等待队列,准备进入睡眠*/
        /**调用yield放弃CPU切换到其他线程*/
    }
    /**中断启用*/
}
错误示例 解锁操作
unlock() {
    /**中断禁止*/
    value = FREE;
    if(等待队列不为空) {
        /**将等待队列的其中一个线程移至就绪队列*/
        value = BUSY;
    }
    /**中断启用*/
}

上述锁的实现程序乍一看似乎很有道理,可仔细一想却发现有问题。
问题是,在上锁操作中,我们调用yield切换到别的线程的语句在启用中断指令前执行,由于切换到另一个线程后,该线程就无法再执行,那么后面的中断启用指令自然就不能执行了。
而因为我们是在中断处于禁止状态下切换到别的线程的,如果别的线程没有执行中断启用或者自动放弃CPU给另一个线程,那么该线程将一直占用CPU,其他的线程(包括调用lock()的线程)将无法得到执行。

解决方法:
分析后可以发现,“将自己放在等待队列”和“切换到别的线程”这两个操作应该是一组原子操作,不能在中间中断。
这也就是说,启用中断操作只能在执行这两个操作之后,而不能在中间。
那么剩下的唯一可能就是闭锁操作不启用中断,而是留给别的线程去启用中断。

也就是说,我们要求所有线程遵守下列约定:
•所有线程承诺在调用线程切换时将中断留在禁止状态。
•所有线程承诺在从切换返回时(回到自己线程时)将中断重新启用。

实现过程

因此整个过程如下:

线程A(没有锁,调用lock()) 线程B(初始获得锁)
CPU时间片结束
禁止中断
切换到A
启用中断
(用户代码)
请求资源,调用lock()
禁止中断
检查锁的情况(锁被B持有)
进入等待队列
切换至线程B
启用中断
(用户代码)
释放资源,调用unlock()
从等待队列中拿出A
将A移到就绪队列
CPU时间片结束
禁止中断
切换到A
启用中断
lock()方法返回
A获得锁
进入临界区

缺点:依赖操作系统。

4.以最少繁忙等待、测试与设置来实现锁

思路:只用繁忙等待来执行闭锁的操作。如果不能获得就放弃CPU。

上锁操作
lock() {
    while(test_and_set(guard) == 1) {}
    if(value == FREE) {
        value = BUSY;
        guard = 0;
} else {
    guard = 0;
    /**将自己加入该锁的等待队列,准备进入睡眠*/
    /**切换到其他线程*/
}
解锁操作
unlock() {
    while(test_and_set(guard) == 1){}
    value = FREE;
    if(锁的等待队列不为空) {
        /**将等待队列中一个线程移至就绪队列*/
        value = BUSY;
    }
    guard = 0;
}

为什么繁忙等待时间缩短了?
这是因为我们将等待对象从value改成了guard。
guard要防止的只是多个线程同时拿锁而已,一旦拿锁的动作完成(不管是否拿到锁),guard都将被设置为0。
而这个范围很小。在拿到锁的情况下, guard保护的只有if(value == FREE)的条件判断和value=BUSY的赋值语句。在没有拿到锁的情况下,guard保护的语句只有将自己这个线程加到等待队列和切换线程的一段代码。

该实现中存在一个问题。假如在将自己放在等待队列后,执行切换操作前突然发生线程切换,那么本线程将同时处于等待和就绪两个队列。这个问题是怎么解决的呢?
解决办法也很简单,就是将执行lock这个系统调用的进程的优先权提高,以使这种情况发生的概率降低。当然,完全避免是不可能的。

参考资料:
《操作系统之哲学原理》 邹恒明著

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容