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

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.kler.cn/a/542723.html

相关文章:

  • 活动预告 |【Part1】Microsoft Azure 在线技术公开课:AI 基础知识
  • C++智能指针的使用
  • 华中科技大学软件学院专硕怎样?
  • 从零到一:基于Rook构建云原生Ceph存储的全面指南(上)
  • DeepSeek做赛车游戏
  • 推荐系统Day1笔记
  • 网络工程师 (31)VLAN
  • 什么是WebSocket?在Python中如何应用?
  • 性格测评小程序03搭建用户管理
  • ES6~ES11新特性全解析
  • Untiy3d 铰链、弹簧,特殊的物理关节
  • 在 Navicat 17 中扩展 PostgreSQL 数据类型 - 枚举类型
  • 信息安全之网络安全
  • CSS 表单 实现响应式布局
  • DeepSeek影响网络安全行业?
  • UWB功耗大数据插桩调研
  • 深度学习的图像生成
  • redo和binlog区别
  • SQLite 约束
  • Nat Python:用于自然语言处理的Python库
  • 【网络安全】无线安全协议的演变
  • VeryReport和FastReport两款报表软件深度分析对比
  • DeepSeek做赛车游戏
  • C++智能指针的使用
  • C语言————快速幂
  • java项目之直销模式下家具工厂自建网站源码(ssm+mysql)