手工打造一把锁

简介: 接上篇(https://yq.aliyun.com/articles/59034 ),我们知道了lock的意义。回到之前的多线程加法操作,当然也可以通过pthread提供的互斥锁来保证结果是正确的。那互斥锁本身是如何保证原子性的呢?当然首先获得锁的操作需要是一个指令,而不能用加载-比对-存储这种类.

接上篇(https://yq.aliyun.com/articles/59034 ),我们知道了lock的意义。回到之前的多线程加法操作,你也可以通过pthread提供的互斥锁来保证结果是正确的。但互斥锁本身是如何保证原子性的呢?当然首先获得锁的操作需要是一个指令,而不能用加载-比对-存储这种类似的三条指令;其次,如果是多核系统上,这个指令本身还需要加上lock前缀。x86提供了cmpxchg指令(http://x86.renejeschke.de/html/file_module_x86_id_41.html ),这类指令有个名词,就是CAS(Compare-and-swap, https://en.wikipedia.org/wiki/Compare-and-swap )便是许多锁操作实现的基础。一个最简单的实现方案大概如下(x86-64, gcc):


typedef volatile unsigned long lock_t;

void foobar_lock(lock_t *lock)
{
    unsigned long old   = 0;
    unsigned long new = 1;
    unsigned char result;
    while (1) {
        asm volatile(
            "lock \n"
            "cmpxchgq  %3, %1 \n"
            "sete      %0\n"
            : "=a" (result) : "m" (*lock), "a" (old), "r"(new) : "cc", "memory");
        if (result) {
            return;
        }
        sched_yield();
    }
}

void foobar_unlock(lock_t *lock)
{
    *lock = 0;
}

cmpxchg会比较rax寄存器的内容,和指定内存的内容,如果相等,会设置ZF=1,并将内存的内容改写为指定的其他内容;否则,将设置ZF=0,并将指定内存位置的内容复制到rax寄存器。而这些是在一条指令内完成的。

对于互斥锁的实现而言,初始为0,已锁为1,通过比较锁所在内存和0,相等则将其设置为1,并设置ZF=1,表示加锁成功。然后我们通过sete来获得ZF的值来判断锁的结果,失败则继续尝试。

之所以需要将0和1先存储到unsigned long,而不是"a"(0)这种立即数的形式,因为发现即便在64位平台,对于这种形式,gcc分配的寄存器是eax,而不是rax。

然后写一段测试代码,代码和上篇文章类似,结果正常:

images55

如果你把lock前缀去掉,同时通过taskset设置单个核心,那么也没有问题。

但是如果去掉了lock前缀,同时又允许多个核心运行,有可能结果小于正确数字,有可能程序hang住。前者可以理解,由于没有lock前缀,会产生两个线程都认为自己加锁成功了,进而同时进入了临界区;后者的情况相对复杂,通过gdb attach到那个进程去,会发现两个线程内部,看到的锁所在的内存,都是1,然额还不知道这是缓存一致性导致的问题,还是乱序执行导致的问题。

目录
相关文章
|
9月前
|
存储 数据可视化 安全
《低代码平台的深层架构:从组件拖拽到数据闭环的逻辑》
本文深入解析低代码平台的核心架构,从组件系统、自定义扩展、数据存储到可视化编辑、权限控制、部署流程等维度展开。组件系统通过标准化与灵活性的平衡实现功能封装与交互协同;自定义组件生态在规范约束下保障开放与稳定;数据层将技术逻辑转化为业务语言,兼顾易用与安全。同时探讨编辑器体验优化、权限设计及全流程自动化部署,揭示低代码平台如何在简化开发的表象下,通过复杂架构支撑应用创建与扩展,为相关开发提供深层思路。
288 0
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的高校学生课堂考勤系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的高校学生课堂考勤系统的详细设计和实现(源码+lw+部署文档+讲解等)
259 2
|
人工智能 搜索推荐 开发者
Kiss3DGen:基于图像扩散模型的3D资产生成框架
Kiss3DGen是一个创新的3D资产生成框架,通过重新利用预训练的2D图像扩散模型,高效生成、编辑和增强3D对象,支持文本到3D、图像到3D等多种生成任务。
507 5
|
存储 Python
Python 还能播放音频,而且花样多多?
Python 还能播放音频,而且花样多多?
|
vr&ar
VR光学系统
【6月更文挑战第26天】VR光学系统
529 1
|
缓存 JavaScript 前端开发
Vue 3的事件监听缓存如何优化性能?
【5月更文挑战第31天】Vue 3的事件监听缓存如何优化性能?
720 1
|
机器学习/深度学习 存储 安全
基于YOLOv8深度学习的人脸面部口罩检测系统【python源码+Pyqt5界面+数据集+训练代码】目标检测
基于YOLOv8深度学习的人脸面部口罩检测系统【python源码+Pyqt5界面+数据集+训练代码】目标检测
|
JavaScript 前端开发 数据安全/隐私保护
🔒 一文带你了解多文件混淆加密
JavaScript 代码多文件混淆加密可以有效保护源代码不被他人轻易盗取。虽然前端的 JS 无法做到纯粹的加密,但通过一系列的混淆操作,可以让源码变得难以阅读,增加他人复制的难度。强烈推荐您试一试 ipaguard代码加密工具,它能够为您的代码提供更强的保护。
|
新零售 存储 人工智能
阿里云携手河南伙伴,服务本地客户
2023年2月7日,阿里云河南合作伙伴新春团拜会·郑州站顺利召开,超过150位合作伙伴到场参会。
|
机器学习/深度学习 存储 算法
阿里云机器学习 PAI-DSW 介绍|学习笔记
快速学习阿里云机器学习 PAI-DSW 介绍。
2536 1
阿里云机器学习 PAI-DSW 介绍|学习笔记