锁的种类

自旋锁(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. 菜鸡的Java笔记 日期操作类

    日期操作类        Date 类与 long 数据类型的转换        SimpleDateFormat 类的使用        Calendar 类的使用                如 ...

  2. jpg与jpeg的区别在哪

    JPG文件的优点是体积小巧,并且兼容性好,因为大部分的程序都能读取这种文件,这是因为JPG格式不仅是一个工业标准格式,而且更是web的标准文件格式.JPG文件如此拥有如此便利的条件,难怪得到了业余玩家 ...

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

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

  4. 第05章 MySQL排序与分页

    第05章 MySQL排序与分页 1. 排序数据 1.1 排序规则 使用 ORDER BY 子句排序 ASC(ascend): 升序 DESC(descend):降序 ORDER BY 子句在SELEC ...

  5. 【原创】【自制系列】自制stack类型(泛型)

    前言 自制类型的第三篇,stack类型.stack是指栈,其实我个人认为stack是最好写的类型,没有之一.关于queue类型需要涉及到循环队列避免浪费内存,但是stack的插入删除都是对于栈顶而言, ...

  6. 联盛德 HLK-W806 (四): 软件SPI和硬件SPI驱动ST7735液晶LCD

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  7. os.path.join()函数

    连接两个或更多的路径名组件 import os p1 = '/date' p2 = 'mage' p3 = 'img' all = os.path.join(p1,p2,p3) print(all) ...

  8. nginx——网站显示问题

    一般来说修改3个位置,一个是nginx.h.另一个是ngx_http_header_filter_module.c.还有一个ngx_http_special_response.c. 提示:一般修改都是 ...

  9. DRF请求流程及主要模块分析

    目录 Django中CBV请求生命周期 drf前期准备 1. 在views.py中视图类继承drf的APIView类 2. drf的as_view()方法 drf主要模块分析 1. 请求模块 2. 渲 ...

  10. 基于 芯片 nordic 52832 rtt 调试(Mac 电脑)

    代码配置 // <e> NRF_LOG_BACKEND_SERIAL_USES_UART - If enabled data is printed over UART //======== ...