Ubuntu 下 nginx-1.24.0 源码分析 - ngx_atomic_cmp_set 函数

目录

修正 


执行 

./configure

 命令时,输出:

 checking for OS
 + Linux 6.8.0-52-generic x86_64
checking for C compiler ... found
 + using GNU C compiler
 + gcc version: 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04) 

 所以当前环境是 x86_64

于是在 src\os\unix\ngx_atomic.h 中

#include "ngx_gcc_atomic_x86.h"

被包含 

src/os/unix/ngx_gcc_atomic_x86.h 中:

static ngx_inline ngx_atomic_uint_t
ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,ngx_atomic_uint_t set)
{u_char  res;__asm__ volatile (NGX_SMP_LOCK"    cmpxchgl  %3, %1;   ""    sete      %0;       ": "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "cc", "memory");return res;
}

函数功能:

原子地比较 *lock 的值是否等于 old

若相等:将 *lock 设置为 set,并返回 1(成功)

若不相等:不修改 *lock,返回 0(失败)


内联汇编实现

NGX_SMP_LOCK

宏定义,在多核(SMP)系统中扩展为 lock 前缀,确保指令的原子性;单核系统中可能为空

cmpxchgl %3, %1

x86 的原子比较并交换指令:

  • 比较 %1(即 *lock)与 eax 寄存器(隐含使用,值为 old
  • 若相等,将 %3(即 set)写入 *lock;否则,将 *lock 的值加载到 eax

sete %0

根据 cmpxchgl 结果(ZF 标志位)设置 res 为 0 或 1

输入输出约束

输出 "=a" (res):结果 res 通过 eax 寄存器返回

输入 "m" (*lock), "a" (old), "r" (set)

*lock 作为内存操作数

old 存入 eax(隐含用于 cmpxchgl 的比较)

set 存入通用寄存器

"cc", "memory":告知编译器条件寄存器和内存可能被修改


内联汇编语法

操作数占位符

  • %0, %1, %2, %3:按操作数出现顺序编号:
    • %0 → "=a" (res)(输出)
    • %1 → "m" (*lock)(输入)
    • %2 → "a" (old)(输入)
    • %3 → "r" (set)(输入)

操作数约束(Constraints)

输出操作数 "=a" (res)

  • =:表示只写(输出)。
  • a:使用 eax 寄存器。
  • res:C变量,接收结果(0或1)

"m" (*lock) 

 m:内存操作数,直接操作 *lock 的内存地址

"a" (old)

 a:将 old 的值存入 eax 寄存器(cmpxchgl 隐式使用 eax 进行比较)。

"r" (set)

 r:将 set 的值存入任意通用寄存器(如 ebxecx 等)。

Clobber列表

  • "cc":表示指令修改了标志寄存器(如 ZF、CF)。
  • "memory":表示指令可能修改内存,强制编译器刷新内存缓存。

cmpxchgl 源操作数, 目标操作数

比较 目标操作数(即 *lock)与 eax 的值(old)。

若相等:

将 源操作数(即 set)写入 目标操作数*lock)。

设置 ZF(Zero Flag)为 1。

若不相等:

将 目标操作数*lock)的值加载到 eax

设置 ZF 为 0。

sete %0

  • sete:若 ZF=1(即比较成功),将目标(%0,即 res)设为 1,否则设为 0。
  • 由于 %0 约束为 "=a",结果通过 eax 写入 res

执行流程

将 old 加载到 eax

原子比较 *lock 与 eax

相等 → 将 set 写入 *lock,ZF=1。

不等 → 将 *lock 值加载到 eax,ZF=0。

根据 ZF 设置 res(1 或 0)。

返回 res


意图

  1. 实现原子操作
    通过 cmpxchgl 指令和 lock 前缀,确保在多核环境下的原子性,避免竞态条件。

  2. 跨平台兼容性

        使用 NGX_SMP_LOCK 宏适配不同平台(如单核无需 lock 前缀)

NGX_SMP_LOCK

在 src\os\unix\ngx_gcc_atomic_x86.h

#if (NGX_SMP)
#define NGX_SMP_LOCK  "lock;"
#else
#define NGX_SMP_LOCK
#endif

objs/ngx_auto_config.h 中

#ifndef NGX_SMP
#define NGX_SMP  1
#endif

所以 NGX_SMP_LOCK 是 "lock;"

  • 无 lockcmpxchgl 本身是原子的,但仅限单核环境。
  • 有 lock:确保多核环境下的原子性,完整执行“比较-交换”操作。

修正 

以上部分 可能是错误的

在 src\os\unix\ngx_atomic.h 中:

#define ngx_trylock(lock)  (*(lock) == 0 && ngx_atomic_cmp_set(lock, 0, 1))

这样定义了 ngx_trylock

但对于 ngx_atomic_cmp_set 的定义可能判断错了

在 我的Ubuntu 环境中 

objs/ngx_auto_config.h:9:#define NGX_HAVE_GCC_ATOMIC  1

也就是在 objs/ngx_auto_config.h 中

定义了 

#ifndef NGX_HAVE_GCC_ATOMIC
#define NGX_HAVE_GCC_ATOMIC  1
#endif

在 ngx_atomic.h 中

#if (NGX_HAVE_LIBATOMIC)
省略
#elif (NGX_HAVE_GCC_ATOMIC)
省略
#define ngx_atomic_cmp_set(lock, old, set)                                    \__sync_bool_compare_and_swap(lock, old, set)

#elif (NGX_HAVE_GCC_ATOMIC) 条件成立

所以 ngx_atomic_cmp_set 的定义应该是:

#define ngx_atomic_cmp_set(lock, old, set)                                    \__sync_bool_compare_and_swap(lock, old, set)

在Ubuntu的x86_64架构下,NGX_HAVE_GCC_ATOMIC会被定义

ngx_atomic_cmp_set 在支持GCC原子内置函数的情况下,这个函数应该会被定义为上述情况

__sync_bool_compare_and_swap 

是 GCC 提供的一个内置原子操作函数,用于实现多线程环境下的无锁同步。其作用是在原子操作中比较并交换(Compare-and-Swap, CAS)一个值,常用于实现线程安全的操作。

函数原型

bool __sync_bool_compare_and_swap(type *ptr, type oldval, type newval);
  • 参数
    • ptr:指向需要操作的内存地址的指针。
    • oldval:期望的旧值。
    • newval:要设置的新值。
  • 返回值
    • 如果 *ptr 的当前值等于 oldval,则将 *ptr 设置为 newval,并返回 true
    • 否则不修改内存,返回 false

底层原理

该函数依赖硬件级别的原子指令(如 x86 的 CMPXCHG 指令)实现,确保多线程环境下操作的原子性

函数由 GCC 编译器直接提供,无需像标准库函数(如 printf)那样通过 #include 引入头文件。

直接在代码中调用即可

gcc -E

鉴于有时预编译指令较多且嵌套,难以判断具体使用的哪一个定义

于是改用 gcc -E 的方法

  • 作用:运行 GCC 的 预处理阶段,处理以下内容:
    • 展开 #include 引入的头文件。
    • 替换 #define 定义的宏。
    • 处理 #ifdef/#if 等条件编译指令。
    • 删除注释。
  • 输出:预处理后的纯 C 代码(未编译)。
gcc -E src/core/ngx_times.c \-I src/core \-I src/event \-I src/event/modules \-I src/os/unix \-I objs \> ngx_times_preprocessed.c

-I 添加头文件搜索路径

> ngx_times_preprocessed.c

  • 作用:将预处理结果重定向到文件 ngx_times_preprocessed.c
  • 文件内容:展开后的完整代码

找到原本 ngx_times.c中

void
ngx_time_update(void)
{u_char          *p0, *p1, *p2, *p3, *p4;ngx_tm_t         tm, gmt;time_t           sec;ngx_uint_t       msec;ngx_time_t      *tp;struct timeval   tv;if (!ngx_trylock(&ngx_time_lock)) {return;}

ngx_time_update 函数中,struct timeval   tv; 后,ngx_trylock 调用的地方

在 ngx_times_preprocessed.c 中的位置

 if (!(*(&ngx_time_lock) == 0 && __sync_bool_compare_and_swap(&ngx_time_lock, 0, 1))) {return;}

这里是展开后的样子

所以 确认是 调用了

__sync_bool_compare_and_swap

 

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

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

相关文章

解读 Flink Source 接口重构后的 KafkaSource

前言 Apache Kafka 和 Apache Flink 的结合,为构建实时流处理应用提供了一套强大的解决方案[1]。Kafka 作为高吞吐量、低延迟的分布式消息队列,负责数据的采集、缓冲和分发;而 Flink 则是功能强大的流处理引擎,负责对数据进行实时…

【推理llm论文精读】DeepSeek V3技术论文_精工见效果

先附上原始论文和效果对比https://arxiv.org/pdf/2412.19437 摘要 (Abstract) DeepSeek-V3是DeepSeek-AI团队推出的最新力作,一个强大的混合专家(Mixture-of-Experts,MoE)语言模型。它拥有671B的总参数量,但每个tok…

如何使用Java语言在Idea和Android中分别建立服务端和客户端实现局域网聊天

手把手教你用Java语言在Idea和Android中分别建立服务端和客户端实现局域网聊天 目录 文章目录 手把手教你用**Java**语言在**Idea**和**Android**中分别建立**服务端**和**客户端**实现局域网聊天**目录**[toc]**基本实现****问题分析****服务端**Idea:结构预览Server类代码解…

java韩顺平最新教程,Java工程师进阶

简介 HikariCP 是用于创建和管理连接,利用“池”的方式复用连接减少资源开销,和其他数据源一样,也具有连接数控制、连接可靠性测试、连接泄露控制、缓存语句等功能,另外,和 druid 一样,HikariCP 也支持监控…

如何在 IDE 里使用 DeepSeek?

近期,阿里云百炼平台重磅推出 DeepSeek-V3、DeepSeek-R1、DeepSeek-R1-Distill-Qwen-32B 等 6 款模型,进一步丰富其 AI 模型矩阵。与此同时,通义灵码也紧跟步伐,全新上线模型选择功能,支持基于百炼的 DeepSeek-V3 和 D…

网络安全技术复习总结

1|0第一章 概论 1.网络安全发展阶段包括四个阶段:通信安全、计算机安全、网络安全、网络空间安全。 2.2017年6月1日,我国第一部全面规范网络空间安全的基础性法律《中华人民共和国网络安全法》正式实施。 3.2021年 6月10日,《中华人民共和…

DedeBIZ系统审计小结

之前简单审计过DedeBIZ系统,网上还没有对这个系统的漏洞有过详尽的分析,于是重新审计并总结文章,记录下自己审计的过程。 https://github.com/DedeBIZ/DedeV6/archive/refs/tags/6.2.10.zip 📌DedeBIZ 系统并非基于 MVC 框架&…

业务开发 | 基础知识 | Maven 快速入门

Maven 快速入门 1.Maven 全面概述 Apache Maven 是一种软件项目管理和理解工具。基于项目对象模型的概念(POM),Maven 可以从中央信息中管理项目的构建,报告和文档。 2.Maven 基本功能 因此实际上 Maven 的基本功能就是作为 Ja…

2.11 sqlite3数据库【数据库的相关操作指令、函数】

练习: 将 epoll 服务器 客户端拿来用 客户端:写一个界面,里面有注册登录 服务器:处理注册和登录逻辑,注册的话将注册的账号密码写入数据库,登录的话查询数据库中是否存在账号,并验证密码是否正确…

Python(十九)实现各大跨境船公司物流查询数据处理优化

一、前言 之前已经实现了常用 跨境物流船司 基础信息查询功能,如下所示 实现各大跨境船公司[COSCO/ZIM/MSK/MSC/ONE/PIL]的物流信息查询:https://blog.csdn.net/Makasa/article/details/145484999?spm1001.2014.3001.5501 然后本章在其基础上做了一些…

基于微信小程序的博物馆预约系统的设计与实现

hello hello~ ,这里是 code袁~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹 🦁作者简介:一名喜欢分享和记录学习的在校大学生…

深度学习框架TensorFlow怎么用?

大家好呀,以下是使用 TensorFlow 的详细步骤,从安装到构建和训练模型: 一、安装 TensorFlow 安装 Python:TensorFlow 基于 Python,确保已安装 Python(推荐 Python 3.8 及以上版本)。可通过 Pyt…

如何在华为harmonyOS上调试软件

1、设置-》关于手机-》HarmonyOS 版本连按多下,输入锁屏密码。显示开发者模式已打开。 2、设置-》搜索“开发人员选项”-》开启“开发人员选项”选项。 3、在 开发者选项 中找到 “USB 调试” 并开启。 4、开启 “仅充电时允许 ADB 调试”。 5、设置中开启 &quo…

fpga系列 HDL:Quartus II JTAG 间接配置文件 Indirect Configuration File (.jic) AS模式烧录

先编译生成pof文件 File->Convert Programming Files 转换文件 Tools->Programer 烧录

Python:凯撒密码

题目内容: 凯撒密码是古罗马恺撒大帝用来对军事情报进行加密的算法,它采用了替换方法对信息中的每一个英文字符循环替换为字母表序列该字符后面第三个字符,对应关系如下: 原文:A B C D E F G H I J K L M N O P Q R …

【大模型知识点】什么是KV Cache?为什么要使用KV Cache?使用KV Cache会带来什么问题?

1.什么是KV Cache?为什么要使用KV Cache? 理解此问题,首先需理解自注意机制的计算和掩码自注意力机制,在Decoder架构的模型中,每生成一个新的token,便需要重新执行一次自注意力计算,这个过程中…

【STM32】HAL库Host MSC读写外部U盘及FatFS文件系统的USB Disk模式

【STM32】HAL库Host MSC读写外部U盘及FatFS文件系统的USB Disk模式 在先前 分别介绍了FatFS文件系统和USB虚拟U盘MSC配置 前者通过MCU读写Flash建立文件系统 后者通过MSC连接电脑使其能够被操作 这两者可以合起来 就能够实现同时在MCU、USB中操作Flash的文件系统 【STM32】通过…

本地生活服务平台开发进入发展热潮

本地生活服务平台:当下的发展热潮 本地生活服务平台开发模式 在当今数字化时代,本地生活服务平台开发已成为人们日常生活中不可或缺的一部分。只需动动手指,打开手机上的 APP,就能轻松满足各类生活需求。像某团、饿XX这样的平台&a…

LSTM变种模型

GRU GRU简介 门控循环神经网络 (Gated Recurrent Neural Network,GRNN) 的提出,旨在更好地捕捉时间序列中时间步距离较大的依赖关系。它通过可学习的门来控制信息的流动。其中,门控循环单元 (Gated Recurrent Unit , GRU) 是…

微服务与网关

什么是网关 背景 单体项目中,前端只用访问指定的一个端口8080,就可以得到任何想要的数据 微服务项目中,ip是不断变化的,端口是多个的 解决方案:网关 网关:就是网络的关口,负责请求的路由、转发…