锁的种类

自旋锁(spinlock):无法获得锁,就一直循环获取,适合短时间的加锁

睡眠锁(sleeplock):为了防止长时间的循环等待,在获取不到锁时,进程陷入睡眠,当锁释放时对睡眠进程进行唤醒

自旋锁的实现

其实自旋锁的实现很简单,不过是一个状态量置1或者置0的操作

为了防止中断产生死锁以及编译器将临界区的指令重排到锁操作外,使用一些特殊指令

在修改状态量时,使用原子操作确保不会出现操作过程中,其他操作发生。

// 自旋锁的数据结构
// Mutual exclusion lock.
struct spinlock {
uint locked; // Is the lock held? // For debugging:
char *name; // Name of lock.
struct cpu *cpu; // The cpu holding the lock.
};
// 辅助函数
// Check whether this cpu is holding the lock.
// Interrupts must be off.
int
holding(struct spinlock *lk)
{
int r;
// 锁处于被持有状态并且持有cpu为当前cpu 返回 1(ture)
r = (lk->locked && lk->cpu == mycpu());
return r;
} // 加锁操作
// Acquire the lock.
// Loops (spins) until the lock is acquired.
void
acquire(struct spinlock *lk)
{
// 关中断
// 加解锁的流程中需要关闭中断
// 防止使用锁的过程中中断,而中断处理程序又需要锁,造成死锁
push_off(); // disable interrupts to avoid deadlock. // 判读当前cpu是否正在持有该锁
// 如果当前CPU在其他位置持有了这个锁,那么当前的加锁操作将永远无法完成
// 程序会阻塞在当前位置,并且其他位置持有锁的位置也无法释放
// 程序将进入死锁状态
if(holding(lk))
panic("acquire"); // __sync_lock_test_and_set(&lk->locked, 1)
// 加锁的原子操作,确保括号中的操作为原子操作,一般是使用CPU的特殊硬件指令实现
// 效果:如果lk->locked等于0,我们调用test-and-set将1写入locked字段,并且返回locked字段之前的数值0。
// 如果lk->locked等于1,则返回1 // 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)
; // __sync_synchronize()
// 从当前位置到下一个__sync_synchronize指令之间禁止指令重排
// 加锁和critical section的代码执行通常完全相互独立,它们之间没有任何关联
// 因此CPU和编译器极有可能将critical section代码置于锁之外
// 对于synchronize指令,任何在它之前的load/store指令,都不能移动到它之后
// Tell the C compiler and the processor to not move loads or stores
// past this point, to ensure that the critical section's memory
// references happen strictly after the lock is acquired.
// On RISC-V, this emits a fence instruction.
__sync_synchronize(); // Record info about lock acquisition for holding() and debugging.
lk->cpu = mycpu();
} // 释放锁的操作
// Release the lock.
void
release(struct spinlock *lk)
{
if(!holding(lk))
panic("release"); lk->cpu = 0; // Tell the C compiler and the CPU to not move loads or stores
// past this point, to ensure that all the stores in the critical
// section are visible to other CPUs before the lock is released,
// and that loads in the critical section occur strictly before
// the lock is released.
// On RISC-V, this emits a fence instruction.
__sync_synchronize(); // __sync_lock_release()
// 原子性的将lk->locked置0
// Release the lock, equivalent to lk->locked = 0.
// This code doesn't use a C assignment, since the C standard
// implies that an assignment might be implemented with
// multiple store instructions.
// On RISC-V, sync_lock_release turns into an atomic swap:
// s1 = &lk->locked
// amoswap.w zero, zero, (s1)
__sync_lock_release(&lk->locked); pop_off();
}
睡眠锁的实现
// 睡眠锁的数据结构
struct sleeplock {
uint locked; // Is the lock held?
struct spinlock lk; // spinlock protecting this sleep lock // For debugging:
char *name; // Name of lock.
int pid; // Process holding lock
};
void
acquiresleep(struct sleeplock *lk)
{
acquire(&lk->lk);
while (lk->locked) {
sleep(lk, &lk->lk);
}
lk->locked = 1;
lk->pid = myproc()->pid;
release(&lk->lk);
} void
releasesleep(struct sleeplock *lk)
{
acquire(&lk->lk);
lk->locked = 0;
lk->pid = 0;
wakeup(lk);
release(&lk->lk);
} // Atomically release lock and sleep on chan.
// Reacquires lock when awakened.
// sleep函数设置当前进程的chan(表示等待该chan的信息),
// 然后将进程设置为SLEEPING状态,放弃CPU,进入进程调度
// 被唤醒后,将继续执行sched()函数后面指令,将chan置空
void
sleep(void *chan, struct spinlock *lk)
{
struct proc *p = myproc(); // Must acquire p->lock in order to
// change p->state and then call sched.
// Once we hold p->lock, we can be
// guaranteed that we won't miss any wakeup
// (wakeup locks p->lock),
// so it's okay to release lk. acquire(&p->lock); //DOC: sleeplock1
release(lk); // Go to sleep.
p->chan = chan;
p->state = SLEEPING; sched(); // Tidy up.
p->chan = 0; // Reacquire original lock.
release(&p->lock);
acquire(lk);
} // Wake up all processes sleeping on chan.
// Must be called without any p->lock.
// wakeup函数将所有在当前chan等待的进程状态全部设置成RUNNABLE
void
wakeup(void *chan)
{
struct proc *p; for(p = proc; p < &proc[NPROC]; p++) {
if(p != myproc()){
acquire(&p->lock);
if(p->state == SLEEPING && p->chan == chan) {
p->state = RUNNABLE;
}
release(&p->lock);
}
}
}

为什么睡眠锁的实现需要使用自旋锁?

待续

Lock 锁的实现的更多相关文章

  1. Lock锁的使用示例

    Lock锁是java5用来代替synchronized的一种面向对象的锁的方案 public class LockDemo { /** * Lock是用来替换synchronized, 优点是Lock ...

  2. Android(java)学习笔记69:JDK5之后的Lock锁的概述和使用

    1. Lock锁的概述: java.util.concurrent.locks,接口Lock 首先Lock是一个接口,Lock实现提供了比使用synchronized方法 和 同步代码块更为广泛的锁定 ...

  3. python多线程threading.Lock锁用法实例

    本文实例讲述了python多线程threading.Lock锁的用法实例,分享给大家供大家参考.具体分析如下: python的锁可以独立提取出来 mutex = threading.Lock() #锁 ...

  4. Lock锁_线程_线程域

    using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...

  5. (删)Java线程同步实现二:Lock锁和Condition

    在上篇文章(3.Java多线程总结系列:Java的线程同步实现)中,我们介绍了用synchronized关键字实现线程同步.但在Java中还有一种方式可以实现线程同步,那就是Lock锁. 一.同步锁 ...

  6. 转: 【Java并发编程】之二十:并发新特性—Lock锁和条件变量(含代码)

    简单使用Lock锁 Java5中引入了新的锁机制--Java.util.concurrent.locks中的显式的互斥锁:Lock接口,它提供了比synchronized更加广泛的锁定操作.Lock接 ...

  7. 使用Lock锁生产者消费者模式

    package com.java.concurrent; import java.util.concurrent.locks.Condition; import java.util.concurren ...

  8. JAVA基础再回首(二十五)——Lock锁的使用、死锁问题、多线程生产者和消费者、线程池、匿名内部类使用多线程、定时器、面试题

    JAVA基础再回首(二十五)--Lock锁的使用.死锁问题.多线程生产者和消费者.线程池.匿名内部类使用多线程.定时器.面试题 版权声明:转载必须注明本文转自程序猿杜鹏程的博客:http://blog ...

  9. java并发编程的艺术——第五章总结(Lock锁与队列同步器)

    Lock锁 锁是用来控制多个线程访问共享资源的方式. 一般来说一个锁可以防止多个线程同时访问共享资源(但有些锁可以允许多个线程访问共享资源,如读写锁). 在Lock接口出现前,java使用synchr ...

  10. Java中的Lock锁

    Lock锁介绍: 在java中可以使用 synchronized 来实现多线程下对象的同步访问,为了获得更加灵活使用场景.高效的性能,java还提供了Lock接口及其实现类ReentrantLock和 ...

随机推荐

  1. 在同级路径下,SpringBoot两种类型的配置文件(.properties/.yml)同时存在时,配置优先级如何处理?

    两类配置文件如果同时存在,若 key 相同则 properties 优先级高,若key不同则合并加载:

  2. 大爽Python入门教程 2-3 字符串,列表,字典

    大爽Python入门公开课教案 点击查看教程总目录 除了通用的序列方法, 列表和字符串还有些自己的专属方法. 后面介绍有些是英中文对照介绍(英文来自官方文档), 便于大家更深入的去理解其意思. 灵活的 ...

  3. ECharts 点击事件

    一个问题 ECharts 点击出现多个弹窗

  4. php 图像和水印

    生成图像 $img = imagecreate(400,400); imagecolorallocate($img,255,255,255); imageellipse($img,200,200,50 ...

  5. [hdu7012]Miserable Faith

    类似于[NOI2021]轻重边的逆过程,操作1即为对$u$​执行access(根为1),$dist(u,v)$​即为$u$​到$v$​的虚边数 对前者用LCT维护,并记录轻重边的切换,显然切换总量为$ ...

  6. WebRTC与CSS滤镜(CSS filter)

    我们知道了如何使用WebRTC打开摄像头,可以截取视频帧并且用canvas显示出来. 本文将滤镜与视频结合.给视频加上一层滤镜.主要使用到的是filter属性. canvas与滤镜 先来看filter ...

  7. Node.js实现前后端交互——用户注册

    我之前写过一篇关于使用Node.js作为后端实现用户登陆的功能,现在再写一下node.js做后端实现简单的用户注册实例吧.另外需要说的是,上次有大佬提醒需要加密数据传输,不应该使用明文传输用户信息.在 ...

  8. 洛谷 P6570 - [NOI Online #3 提高组] 优秀子序列(集合幂级数+多项式)

    洛谷题面传送门 首先 \(3^n\) 的做法就不多说了,相信对于会状压 dp+会枚举子集的同学来说不算困难(暴论),因此这篇博客将着重讲解 \(2^nn^2\) 的做法. 首先如果我们把每个 \(a_ ...

  9. 关于 n 个 [0,1] 的随机变量第 k 小的期望值

    今天做到一道题,感觉里面一个结论有点意思,就到网上扒了篇证明(bushi)下来了. 知乎回答习惯,先抛结论,再给证明(大雾 结论:对于 \(n\) 个取值范围为 \([0,1]\) 的随机变量 \(x ...

  10. python——关变量下划线叙述

    _xx:前置单下划线,私有化属性或方法,一般来讲,变量名_xx被看作是"私有 的",在模块或类外不可以使用.当变量是私有的时候,用_xx 来表示变量是很好的习惯.类对象和子类可以访 ...