手工打造一把锁

简介: 接上篇(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,然额还不知道这是缓存一致性导致的问题,还是乱序执行导致的问题。

目录
相关文章
|
缓存 JavaScript 前端开发
JavaScript中DOM操作:新手常犯错误与避免策略
【4月更文挑战第1天】本文介绍了JavaScript中DOM操作的基础和新手常犯错误,包括频繁查询DOM、不恰当的遍历、滥用innerHTML、忽视异步与DOM状态以及过度同步更新。建议包括缓存DOM引用、注意文本节点、慎用innerHTML以防止XSS、正确处理异步和批量更新。遵循最佳实践,开发者能提升代码质量和应用性能。
582 2
|
存储 缓存 JavaScript
RxJS中四种Subject的用法和区别
RxJS中四种Subject的用法和区别
453 2
RxJS中四种Subject的用法和区别
|
数据采集
检验代理ip是否有用的小办法
检验代理ip是否有用的小办法
225 0
|
机器学习/深度学习 人工智能 自动驾驶
【AI 现况分析】AI 大模型在自动驾驶中的应用分析
【1月更文挑战第27天】【AI 现况分析】AI 大模型在自动驾驶中的应用分析
|
人工智能 Linux 云计算
【专访阿里云】云智融合转型期,国产服务器操作系统路在何方?
在云智融合转型趋势引领服务器操作系统迭代升级的时期,国产服务器操作系统社区该如何应对?
|
JavaScript 前端开发 数据安全/隐私保护
🔒 一文带你了解多文件混淆加密
JavaScript 代码多文件混淆加密可以有效保护源代码不被他人轻易盗取。虽然前端的 JS 无法做到纯粹的加密,但通过一系列的混淆操作,可以让源码变得难以阅读,增加他人复制的难度。强烈推荐您试一试 ipaguard代码加密工具,它能够为您的代码提供更强的保护。
|
存储 中间件 Go
用Go实现一个简单的Web服务器
用Go实现一个简单的Web服务器
239 0
|
运维 Java 开发工具
Java后端学习路线6大维度详细总结(编程基础+开发工具+应用框架+运维知识+成神之路+平稳降落)【可作为知识点梳理列表】【点击可查看高清原图】
Java后端学习路线6大维度详细总结(编程基础+开发工具+应用框架+运维知识+成神之路+平稳降落)【可作为知识点梳理列表】【点击可查看高清原图】
233 0
|
数据采集 人工智能 搜索推荐
OpenAI公布「官方爬虫」:GPT-5靠它训练,有需要可以屏蔽
OpenAI公布「官方爬虫」:GPT-5靠它训练,有需要可以屏蔽
708 1
|
新零售 存储 人工智能
阿里云携手河南伙伴,服务本地客户
2023年2月7日,阿里云河南合作伙伴新春团拜会·郑州站顺利召开,超过150位合作伙伴到场参会。