x86 汇编中的 “lock“ 指令详解

在深入理解 “lock” 指令之前,我们先来看一下 Qt 源代码中的一段 x86 汇编代码:

q_atomic_increment:movl 4(%esp), %ecxlock incl (%ecx)mov $0,%eaxsetne %alret.align 4,0x90.type q_atomic_increment,@function.size   q_atomic_increment,.-q_atomic_increment

通过 Google 搜索,我知道 “lock” 指令会导致 CPU 锁住总线,但我不清楚 CPU 什么时候释放总线?另外,我不明白这段代码是如何实现“加法”的?


什么是 “lock” 指令?

“lock” 并不是一条独立的指令,而是一个指令前缀,它作用于随后的那条指令。被作用的指令通常是对内存进行读-修改-写操作的指令,比如 INC、XCHG、CMPXCHG 等。在我们示例代码中,它作用于 incl (%ecx) 指令,该指令会原子性地将 ECX 寄存器所指向内存中的值加 1。

使用 “lock” 前缀的原因

当我们使用 “lock” 前缀时,CPU 会确保该操作期间对相关缓存线的独占所有权,并提供某些额外的顺序保证。CPU 会尽量避免锁住整个总线,如果不得已需要锁住总线,那么会在该指令执行期间内完成。

在上述代码中,首先将要增加的变量地址从栈中拷贝到 ECX 寄存器,接下来用 lock incl (%ecx) 来原子性地增加该变量。随后的两条指令设置 EAX 寄存器(函数的返回值)为 0,如果该变量的新值为 0,则设置 AL 寄存器为 1,即返回值为 1,这是一种增量操作而不是加法。

深入解析代码逻辑

让我们逐行解析代码:

movl 4(%esp), %ecx  ; 将要增加的变量地址从栈中加载到 ECX 寄存器
lock                ; "lock" 前缀,确保以下操作的原子性
incl (%ecx)         ; 将 ECX 寄存器指向的内存变量加 1
mov $0,%eax         ; 将 EAX 寄存器设置为 0
setne %al           ; 如果加法结果不等于 0,将 AL 寄存器(EAX 的低字节)设置为 1
ret                 ; 返回

为什么需要 mov $0,%eax 指令?

mov $0,%eax 不是冗余指令,它将整个 EAX 寄存器设置为 0,而随后的 setne %al 只会修改 EAX 的低字节(即 AL)。如果没有 mov $0,%eax,EAX 的高三字节可能还包含以前操作的随机值,从而导致返回值不正确。

“lock” 前缀的实际作用

尽管某些文献(例如 “Assembler for DOS, Windows и Linux, 2000. Sergei Zukkov”)指出 “lock” 前缀会锁定数据总线,现代 CPU 通常使用更高效的方法。如果数据不跨越缓存行,CPU 核心可以内部锁定缓存行,从而避免阻塞所有其他核的读/写访问。这种机制利用了 MESI 缓存一致性协议。

一个完整示例

以下是一个使用 C++ 和内嵌汇编的例子,演示了 “lock” 前缀的实际作用:

#include <atomic>
#include <cassert>
#include <iostream>
#include <thread>
#include <vector>std::atomic_ulong my_atomic_ulong(0);
unsigned long my_non_atomic_ulong = 0;
unsigned long my_arch_atomic_ulong = 0;
unsigned long my_arch_non_atomic_ulong = 0;
size_t niters;void threadMain() {for (size_t i = 0; i < niters; ++i) {my_atomic_ulong++;my_non_atomic_ulong++;__asm__ __volatile__ ("incq %0;": "+m" (my_arch_non_atomic_ulong)::);__asm__ __volatile__ ("lock;""incq %0;": "+m" (my_arch_atomic_ulong)::);}
}int main(int argc, char **argv) {size_t nthreads;if (argc > 1) {nthreads = std::stoull(argv[1], NULL, 0);} else {nthreads = 2;}if (argc > 2) {niters = std::stoull(argv[2], NULL, 0);} else {niters = 10000;}std::vector<std::thread> threads(nthreads);for (size_t i = 0; i < nthreads; ++i)threads[i] = std::thread(threadMain);for (size_t i = 0; i < nthreads; ++i)threads[i].join();assert(my_atomic_ulong.load() == nthreads * niters);assert(my_atomic_ulong == my_atomic_ulong.load());std::cout << "my_non_atomic_ulong " << my_non_atomic_ulong << std::endl;assert(my_arch_atomic_ulong == nthreads * niters);std::cout << "my_arch_non_atomic_ulong " << my_arch_non_atomic_ulong << std::endl;
}

在 Ubuntu 19.04 amd64 上编译并运行:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp -pthread
./main.out 2 10000

可能输出结果:

my_non_atomic_ulong 15264
my_arch_non_atomic_ulong 15267

从输出结果可以看出,加了 “lock” 前缀后,增加操作是原子性的:否则,我们会在许多加法操作上出现竞争条件,最终计数结果会小于预期的 20000。

“lock” 前缀被广泛用于实现 C++11 中的 std::atomic 和 C11 中的 atomic_int,确保线程安全的增量和其他修改操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/857480.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网工常见面试题

1-10题 1.介绍TCP/IP四层、五层模型作用及每层包含的协议 TCP/IP四层模型 应用层&#xff1a; 作用&#xff1a;直接与应用程序交互&#xff0c;定义了应用程序如何通过网络发送数据。包含协议&#xff1a;HTTP&#xff08;网页浏览&#xff09;、FTP&#xff08;文件传输&…

Linux学习笔记:前言与操作系统的初识【1】

前言 为什么学习Linux 作为当下最流行的操作系统之一&#xff0c;学会如何使用和操作Linux操作系统也就是每位计算机学者的看家必备技能了。其次呢&#xff0c;本人受Linux的创始人林纳斯的影响太深了&#xff0c;觉得这个人太了不起了&#xff0c;而且人家大学里就自研开发出…

javascript的toFixed()以及使用

toFixed() 是 JavaScript 中数字类型&#xff08;Number&#xff09;的一个方法&#xff0c;用来将数字转换为指定小数位数的字符串表示形式。 使用方式和示例&#xff1a; let num 123.45678; let fixedNum num.toFixed(2); console.log(fixedNum); // 输出 "123.46&qu…

【Gradio】构建自定义多模态聊天机器人

这是我们构建自定义多模态聊天机器人组件两部分系列的第一部分。在第一部分中&#xff0c;我们将修改 Gradio 聊天机器人组件&#xff0c;使其能够在同一消息中显示文本和媒体文件&#xff08;视频、音频、图片&#xff09;。在第二部分中&#xff0c;我们将构建一个自定义的文…

深度解析RocketMq源码-持久化组件(一) MappedFile

1. 绪论 rocketmq之所以能够有如此大的吞吐量&#xff0c;离不开两个组件&#xff0c;一个是利用netty实现的高性能网络通信组件&#xff1b;另一个就是利用mmap技术实现的存储组件。而在rocketmq的存储组件中主要有三个组件&#xff0c;分别是持久化文件commitLog&#xff0c…

音樂大模型的崛起:技術革新與行業變革

音樂大模型的崛起&#xff1a;技術革新與行業變革 在過去的一個月中&#xff0c;隨著多個音樂大模型的輪番上線&#xff0c;音樂創作的門檻驟然降低&#xff0c;使得普通人也能輕鬆創作出高質量的音樂作品。這一技術進步引發了廣泛的討論&#xff0c;尤其是在音樂圈內&#xf…

多模态MLLM都是怎么实现的(10)-Chameleon和Florence-2如果你想玩多模态就不能不了解

这个也是一个补充文&#xff0c;前9章基本把该讲的讲了&#xff0c;今天这个内容主要是因为Meta出了一个Chameleon&#xff0c;这个以后可能会成为LLaMA的一个很好的补充&#xff0c;或者说都有可能统一起来&#xff0c;叫LLaMA或者Chamleon或者什么别的&#xff0c;另外我司把…

【图解IO与Netty系列】Netty源码解析——事件循环

Netty源码解析——事件循环 Netty事件循环源码解析select()processSelectedKeys()NioMessageUnsafe#read()NioByteUnsafe#read() runAllTasks() Netty事件循环 当Netty服务端启动起来以后&#xff0c;就可以接受客户端发送的请求&#xff0c;接收到客户端发来的请求后就会有事…

计算机网络 交换机的VLAN配置

一、理论知识 1.VLAN的定义 ①VLAN虚拟局域网&#xff0c;是一种通过将局域网内的设备逻辑地而不是物理地划分成一个个网段从而实现虚拟工作组的技术。 ②IEEE于1999年颁布了用以标准化VLAN实现方案的802.1Q协议标准草案。 ③VLAN技术允许网络管理者将一个物理的LAN逻辑地划…

【Ruby简单脚本02】双色球系统

# frozen_string_literal: true require date # 生成中奖号码的工具 # 红球 1-32 篮球 1-15 def create_num nums [] 6.times do while true num rand(1..32) unless nums.include?(num) nums << num break end end end blue rand(1..15) nums…

MySQL存储管理(一):删数据

从表中删除数据 从表中删除数据&#xff0c;也即是delete过程。 什么是表空间 表空间可以看做是InnoDB存储引擎逻辑结构的最高层&#xff0c;所有的数据都存放在表空间中。默认情况下&#xff0c;InnoDB存储引擎有一个共享表空间idbdata1&#xff0c;即所有数据都存放在这个表…

无限滚动表格

纵向无限滚动 单元格内部横向滚动 <!--* Description: 横向、纵向滚动表格* Author: liyanfeng liyanfenghopewind.com* Date: 2024-06-15 16:06:57* LastEditors: liyanfeng liyanfenghopewind.com* LastEditTime: 2024-06-20 17:15:37* FilePath: \plus-ui\src\componen…

Gone框架介绍33 - HTTP 注入说明

gone是可以高效开发Web服务的Golang依赖注入框架 github地址&#xff1a;https://github.com/gone-io/gone 文档地址&#xff1a;https://goner.fun/zh/ 文章目录 HTTP 注入说明HTTP 依赖注入标签的格式支持注入的类型和响应标签Query参数注入属性类型为简单类型[1]属性类型为简…

SEO是什么?SEO相关发展历史

一、SEO是什么意思&#xff1f; SEO&#xff08;Search Engine Optimization&#xff09;&#xff0c;翻译成中文就是“搜索引擎优化”。简单来讲&#xff0c;seo是指自然搜索结果下获得的网站流量的技术&#xff0c;是可以不用花钱就可以让自己的网站有好的排名&#xff0c;也…

关于Redis知识的理解

系列文章 关于时间复杂度o(1), o(n), o(logn), o(nlogn)的理解 关于HashMap的哈希碰撞、拉链法和key的哈希函数设计 关于JVM内存模型和堆内存模型的理解 关于代理模式的理解 关于Mysql基本概念的理解 关于软件设计模式的理解 文章目录 Redis的由来一、Redis数据类型的发展…

SpringTask Cron表达式

Cron表达式格式 1.Cron表达式格式 Cron表达式是一个字符串&#xff0c;字符串以5或6个空格隔开&#xff0c;分为6或7个域&#xff0c;每一个域代表一个含义&#xff0c;Cron有如下两种语法格式&#xff1a; 秒 分 时 一个月第几天 月 一个星期第几天 年 &…

fegin返回参数统一处理

相关版本: <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.3</version></parent><properties><spring-cloud.version>2023.0.0</spri…

C语言:生命周期和作用域,static和extern

关键字static与extern 1.作用域&#xff08;scope&#xff09;&#xff1a;代码中能够访问到变量的范围&#xff08;变量可以被使用的文本区间&#xff09;。&#xff08;分为全局作用域和局部作用域&#xff09; ☺全局作用域&#xff1a;在整个程序中都能访问的变量。通常…

C语言入门系列:数据类型转换

文章目录 一&#xff0c;自动类型转换1&#xff0c;赋值运算1.1&#xff0c;浮点数赋值给整型变量-不安全1.2&#xff0c;整数赋值给浮点数变量-安全1.3&#xff0c;窄类型赋值给宽类型-安全1.4&#xff0c;宽类型赋值给窄类型-不安全 2&#xff0c;混合类型的运算2.1&#xff…

Python 连接clickhouse常用的三种方式

1. 概述 ClickHouse是一个开源的分布式列式数据库管理系统&#xff0c;它被设计用于存储和分析大规模数据。Python是一种流行的编程语言&#xff0c;凭借其简洁的语法和丰富的生态系统&#xff0c;成为了数据处理和分析的首选语言之一。在Python中&#xff0c;我们可以使用多种…