锁的种类

自旋锁(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. 暑假算法练习Day6

    最近开始了实验室的生活,并且学习了bullet journal.希望接下来的每一天都能完成所有的任务. 1012 数字分类 (20 分) 给定一系列正整数,请按要求对数字进行分类,并输出以下 5 个数 ...

  2. 算法学习->递归典例N皇后问题

    00 问题 在NN(这个N==N皇后的N)的方格棋盘上放置n个皇后,要求:1.每个皇后在不同行不同列:2.每个皇后在不同左右对角线 输出要求:输出符合条件的所有解,解以皇后的坐标的形式. 01 思路 ...

  3. 猿猿有责,维持整洁的 Git 提交记录,三个锦囊送给你

    背景 大家都有学习如何规范简洁的编写代码,但却很少学习如何规范简洁的提交代码.现在大家基本上都用 Git 作为源码管理的工具,Git 提供了极大的灵活性,我们按照各种 workflow 来提交/合并 ...

  4. 搜索系统核心技术概述【1.5w字长文】

    前排提示:本文为综述性文章,梳理搜索相关技术,如寻求前沿应用可简读或略过 搜索引擎介绍 搜索引擎(Search Engine),狭义来讲是基于软件技术开发的互联网数据查询系统,用户通过搜索引擎查询所需 ...

  5. [atAGC034F]RNG and XOR

    令$N=2^{n}$先将$\forall 0\le i<N,a_{i}$除以$\sum_{i=0}^{N-1}a_{i}$,即变为概率 令$f_{i}$表示$i$的答案(第一次变成$i$的期望步 ...

  6. 力扣 - 剑指 Offer 55 - I. 二叉树的深度

    题目 剑指 Offer 55 - I. 二叉树的深度 思路1(DFS) 后续遍历吧,先遍历到最深(递归到末尾返回0),然后从后面一步一步比较取大的值返回,每次返回层数都加1, 执行流程是怎样的:比如其 ...

  7. Vue 中使用 TypeScript 详细总结

    VUE 项目中使用 Typescript 第一节:项目起步 Vue 中使用 TypeScript 项目中主要使用到的第三方依赖 vue2 vue-class-component vue-propert ...

  8. 记一次VS2010和VS2015自定义颜色的过程

    首先,是遇到的问题: 一天,使用VS2010看新项目代码时候,发现选中某个变量后,其它位置高亮显示的变量颜色太淡,不利于阅读代码,如下图.所以想修改这个颜色. 后来网上找了一遍,可以这样设置:工具-- ...

  9. FESTUNG — 3. 采用 HDG 方法求解对流问题

    FESTUNG - 3. 采用 HDG 方法求解对流问题[1] 1. 控制方程 线性对流问题控制方程为 \[\begin{array}{ll} \partial_t c + \nabla \cdot ...

  10. EXCEL excel中运用ctrl+D、ctrl+enter、ctrl+E批量填充数据

    在excel中,利用鼠标拖动可以快速向下或者向右填充序列或者复制单元格.但是利用快捷键也可以实现多种填充方式,本文就为大家介绍一些ctrl系列快捷键的填充,一起来看看吧. 封面tu 一,ctrl+D/ ...