当前位置: 首页 > news >正文

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

 

相关文章:

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…...

CNN-BiLSTM卷积神经网络双向长短期记忆神经网络多变量多步预测,光伏功率预测

代码地址:CNN-BiLSTM卷积神经网络双向长短期记忆神经网络多变量多步预测,光伏功率预测 CNN-BiLSTM卷积神经网络双向长短期记忆神经网络多变量多步预测 一、引言 1.1、研究背景和意义 光伏功率预测在现代电力系统中占有至关重要的地位。随着可再生能源…...

【YOLO系列】YOLOv5 NMS源码理解、更换为DIoU-NMS

代码来源:GitHub - ultralytics/yolov5: YOLOv5 🚀 in PyTorch > ONNX > CoreML > TFLite 使用的代码是YOLOv5 6.1版本 参考笔记:YOLOv5改进系列(八) 更换NMS非极大抑制DIoU-NMS、CIoU-NMS、EIoU-NMS、GIoU-NMS 、SIoU-NMS、Soft-…...

Android RenderEffect对Bitmap高斯模糊(毛玻璃),Kotlin(1)

Android RenderEffect对Bitmap高斯模糊(毛玻璃),Kotlin(1) import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.HardwareRenderer import android.graphics.PixelFormat import android.graphic…...

【linux学习指南】线程同步与互斥

文章目录 📝线程互斥🌠 库函数strncpy🌉进程线程间的互斥相关背景概念🌉互斥量mutex 🌠线程同步🌉条件变量🌉同步概念与竞态条件🌉 条件变量函数 🚩总结 📝线…...

JavaScript函数与方法详解

目录 一、函数的定义 1. 函数声明 2. 函数表达式 3. 箭头函数 二、函数的调用 1. 调用方式 2. 参数数量的灵活性 三、arguments 对象 1. 基本概念 2. 属性 3. 应用场景 4. 转换为真数组 5. 总结 四、Rest参数 1. 基本概念 2. 特点 3. 应用场景 4. 总结 五、变…...

【论文笔记】ZeroGS:扩展Spann3R+GS+pose估计

spann3r是利用dust3r做了增量式的点云重建,这里zeroGS在前者的基础上,进行了增量式的GS重建以及进行了pose的联合优化,这是一篇dust3r与GS结合的具有启发意义的工作。 abstract NeRF和3DGS是重建和渲染逼真图像的流行技术。然而,…...

AtCoder - arc058_d Iroha Loves Strings解答与注意事项

链接:Iroha Loves Strings - AtCoder arc058_d - Virtual Judge 利用bitset这一数据结构,定义bitset类型的变量dp[i]表示第i到n个字符串能拼成的字符串长度都有哪些,比如00100101,表示能拼成的长度有0,2,5,&#xff0…...

企业使用统一终端管理(UEM)工具提高端点安全性

什么是统一终端管理(UEM) 统一终端管理(UEM)是一种从单个控制台管理和保护企业中所有端点的方法,包括智能手机、平板电脑、笔记本电脑、台式机和 IoT设备。UEM 解决方案为 IT 管理员提供了一个集中式平台,用于跨所有作系统和设备类型部署、配置、管理和…...

Leetcode 算法题 9 回文数

起因, 目的: 数学法。 % 求余数, 拆开组合,组合拆开。 这个题,翻来覆去,拆开组合, 组合拆开。构建的过程。 题目来源,9 回文数: https://leetcode.cn/problems/palindrome-number…...

设计模式Python版 命令模式(上)

文章目录 前言一、命令模式二、命令模式示例 前言 GOF设计模式分三大类: 创建型模式:关注对象的创建过程,包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式和建造者模式。结构型模式:关注类和对象之间的组合&…...

C语言之循环结构:直到型循环

C语言 循环结构 直到型循环的实现 特点:先执行,后判断,不管条件是否满足,至少执行一次。典型代表:do…while,goto(已淘汰,不推荐使用) do…while 语法: d…...

细说STM32F407单片机RTC的备份寄存器原理及使用方法

目录 一、备份寄存器的功能 二、示例功能 三、项目设置 1、晶振、DEBUG、CodeGenerator、USART6 2、RTC 3、NVIC 4、GPIO 及KEYLED 四、软件设计 1、main.h 2、main.c 3、rtc.c 4、keyled.c、keyled.h 五、运行调试 本实例旨在介绍备份寄存器的作用。本实例继续使…...

MATLAB计算反映热需求和能源消耗的度数日指标(HDD+CDD)(全代码)

目录 度数日(Degree Days, DD)概述计算公式MATLAB计算代码调用函数1:计算单站点的 CDD参考度数日(Degree Days, DD)概述 度数日(Degree Days, DD)是用于衡量建筑、城市和地区的热需求和能源消耗模式的指标。它分为两部分: 加热度日(Heating Degree Days, HDD):当室…...

J6 X8B/X3C切换HDR各帧图像

1、OV手册上的切换命令 寄存器为Ox5074 各帧切换: 2、地平线control tool实现切换命令 默认HDR模式出图: HCG出图: LCG出图 SPD出图 VS出图...

09-轮转数组

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。 方法一:使用额外数组 function rotate(nums: number[], k: number): void {const n nums.length;k k % n; // 处理 k 大于数组长度的情况const newNums new A…...

用vue3写一个好看的wiki前端页面

以下是一个使用 Vue 3 Element Plus 实现的 Wiki 风格前端页面示例&#xff0c;包含现代设计、响应式布局和常用功能&#xff1a; <template><div class"wiki-container"><!-- 头部导航 --><el-header class"wiki-header"><d…...

瑞芯微烧写工具

文章目录 前言一、安装驱动二、安装烧写工具1.直接解压压缩包2. 如何使用 三、MASKROM 裸机必备四、LOADER 烧写&#xff0c;前提是搞过第三步没问题五、Update.img包的烧录六、linux下烧写总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 项目需要…...

说下JVM中一次完整的GC流程?

大家好&#xff0c;我是锋哥。今天分享关于【说下JVM中一次完整的GC流程?】面试题。希望对大家有帮助&#xff1b; 说下JVM中一次完整的GC流程? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 JVM中的一次完整的垃圾回收&#xff08;GC&#xff09;流程可以概括为…...

Open FPV VTX开源之OSD使用分类

Open FPV VTX开源之OSD使用分类 1. 源由2. 硬件2.1 【天空端】SigmaStar2.2 【天空端】Raspberry Pi2.3 【地面端】 3. 软件3.1 天空端软件3.2 地面端软件 4. 分类4.1 嵌入式OSD分类A1-嵌入式OSD&#xff1a;SigmaStar Android分类A2-嵌入式OSD&#xff1a;SigmaStar Hi3536分…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

渗透实战PortSwigger靶场:lab13存储型DOM XSS详解

进来是需要留言的&#xff0c;先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码&#xff0c;输入的<>当成字符串处理回显到页面中&#xff0c;看来只是把用户输…...

微服务通信安全:深入解析mTLS的原理与实践

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言&#xff1a;微服务时代的通信安全挑战 随着云原生和微服务架构的普及&#xff0c;服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...

Linux 下 DMA 内存映射浅析

序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存&#xff0c;但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程&#xff0c;可以参考这篇文章&#xff0c;我觉得写的非常…...

Mac flutter环境搭建

一、下载flutter sdk 制作 Android 应用 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 1、查看mac电脑处理器选择sdk 2、解压 unzip ~/Downloads/flutter_macos_arm64_3.32.2-stable.zip \ -d ~/development/ 3、添加环境变量 命令行打开配置环境变量文件 ope…...

stm32进入Infinite_Loop原因(因为有系统中断函数未自定义实现)

这是系统中断服务程序的默认处理汇编函数&#xff0c;如果我们没有定义实现某个中断函数&#xff0c;那么当stm32产生了该中断时&#xff0c;就会默认跑这里来了&#xff0c;所以我们打开了什么中断&#xff0c;一定要记得实现对应的系统中断函数&#xff0c;否则会进来一直循环…...