[MIT 6.S081] Lec 10: Multiprocessors and locking 笔记

Lec 10: Multiprocessors and locking

为什么需要锁

在这里插入图片描述

  • 多核运行提升性能
  • 共享数据同时读写会产生竞态条件(race condition)

锁对象

  • API:
    • acquire(&lcok): 任何时间只有一个进程能获取锁
    • release(&lock): 释放锁, 其他进程需要等待至锁被释放才能获取.
  • acquire 与 release 之间的代码片段称之为临界区, 代码片段整体执行具有原子性
  • 通常操作系统会有多把锁, 使得不同系统调用使用不同的锁, 提高并行性.

使用锁的时机

如果两个进程访问了一个共享的数据结构, 并且其中一个进程会更新共享的数据结构, 那么就需要对这个共享数据结构加锁.
这一规则有时是严格的: 某些场景不加锁也可正常工作, 不加锁的程序称之为无锁编程(lock-free program).
有时又是宽松的, 对于一些非共享数据仍需要原子的输出, 如 printf() 的输出.
在这里插入图片描述

锁的作用

  • 避免丢失更新
  • 使得多步操作具有原子性
  • 维护共享数据结构的不变性
    在这里插入图片描述

死锁

  • 重入引发的死锁: 一个进程获得锁进入临界区后又尝试获取同一个锁, 锁在非重入的情况下会引发死锁.
  • deadly embrace: 多个进程持有多个锁, 彼此需要获取对方的锁
    一种解决方案: 对(共同使用的)锁排序, 按序获取锁

锁实现

一般借助硬件指令 test-and-set, 如 XV6 中的原子交换操作(Atomic Memory Swap) amoswap addr, r1, r2, 可以原子的将内存地址 addr 的值写入寄存器 r2, 将寄存器 r1 的值写入 addr.
注: store 指令不一定是原子操作.

  // On RISC-V, sync_lock_test_and_set turns into an atomic swap:
  //   a5 = 1
  //   s1 = &lk->locked
  //   amoswap.w.aq a5, a5, (s1)
  while(__sync_lock_test_and_set(&lk->locked, 1) != 0)
    ;

如上是 XV6 自旋锁中的 test-and-set 操作, 若 lk->locked==0, 那么就写入 1 并返回 0, 循环退出, 表示获得到了锁. 若 lk->locked==1, 则同样是写入 1 但返回的也是 1, 则会继续循环, 直到 lk->locked 字段为 0 后能获取锁.
__sync_synchronize() 函数会生成 fence 指令进行内存屏障, 防止指令重排.
在 acquire 和 release 操作时都需要关闭中断, 防止原子操作被打断或出现锁重入情况.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值