Nginx 是多进程模式的,一个 master 与多个 workers,一般工作在多核 CPU 上,所以自旋锁就是必须用到的。Nginx 中的自旋锁的定义,位于 ngx_spinlock.c 中,如下:

void
ngx_spinlock(ngx_atomic_t *lock, ngx_atomic_int_t value, ngx_uint_t spin)
{ #if (NGX_HAVE_ATOMIC_OPS) ngx_uint_t i, n; for ( ;; ) { // lock 即为锁,是一个整数
// ngx_atomic_cmp_set 是平台相关的,一般都涉及内联汇编
if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {
return;
} // 多核
if (ngx_ncpu > 1) { // 等待与重试策略,见下面的描述
for (n = 1; n < spin; n <<= 1) { for (i = 0; i < n; i++) {
ngx_cpu_pause();
} if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {
return;
}
}
} ngx_sched_yield();
} #else #if (NGX_THREADS) #error ngx_spinlock() or ngx_atomic_cmp_set() are not defined ! #endif #endif }

其中用 lock 这个整形变量表示锁,在笔者的机器(Darwin 12.0)上,是如下定义的:

typedef volatile ngx_atomic_uint_t  ngx_atomic_t;

再 回到上面 spinlock 的源码分析中,如果 ngx_ncpu(表示 CPU 的核心数)超过 1 个,即多核 CPU,则要等待/重试。举个例子,如果 spin 为 80,则第一次等待 1 个 ngx_cpu_pause() 操作,然后再次查看锁是否可用。接下来每轮分别等待 2个、4 个、8 个、16 个、32 个、64 个 ngx_cpu_pause() 操作后再试。这中间过程中如果出现锁被释放从而可以使用的情况,则循环会被中止,spinlock 函数会返回值。如果重试仍没有成功,则执行 ngx_sched_yield,然后再重复上面的操作。

另外其中的 ngx_atomic_cmp_set 函数也很有探讨价值。在 Darwin 12.0 上面是如下的宏定义:

#define ngx_atomic_cmp_set(lock, old, new)                                    \
OSAtomicCompareAndSwap64Barrier(old, new, (int64_t *) lock)

在我一位朋友的 Linux 环境(具体忘记了,但是 x86),如下。其中的内联汇编可以参考本博客内的 GCC 内联汇编的两篇博文。其中的 SMP 为总线锁。

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;
}

这里输出为 res,保存在 eax 寄存器中。输入为 *lock(内存中)、old(eax中)、set(r 表示通用寄存器)。这样 %0 就是 res,%1 就是 *lock,%2 就是 old,%3 就是 set。

如 果 *lock 和 old 相等,则异或(cmpxchgl)为 0,则 ZF 为 1,sete 将 res(%0)的值设置为 1 并返回它。如果 *lock 和 old 不相等,则异火值非零,所以 ZF 非零,则 sete 不会执行动作,即 res 值为 0,即调用 ngx_atomic_cmp_set 失败。

cmpxchgl 会影响 ZF(Zero Flag)标志位。

Nginx 源码完全注释(11)ngx_spinlock的更多相关文章

  1. Nginx源码完全注释(6)core/murmurhash

    下面是摘自 Google Code 的 Murmurhash 开源项目主页上的 Murmurhash2,Nginx 就是采用的这个. uint32_t MurmurHash2 ( const void ...

  2. Nginx 源码完全注释(10)ngx_radix_tree

    ngx_radix_tree.h // 未被使用的节点 #define NGX_RADIX_NO_VALUE (uintptr_t) -1 typedef struct ngx_radix_node_ ...

  3. Nginx源码完全注释(5)core/ngx_cpuinfo.c

    /* * Copyright (C) Igor Sysoev * Copyright (C) Nginx, Inc. */ #include <ngx_config.h> #include ...

  4. Nginx源码完全注释(9)nginx.c: ngx_get_options

    本文分析 ngxin.c 中的 ngx_get_options 函数,其影响: nginx.c 中的: static ngx_uint_t ngx_show_help; static ngx_uint ...

  5. Nginx源码完全注释(8)ngx_errno.c

    errno.h中的strerror(int errno)可以确定指定的errno的错误的提示信息.在 Nginx 中,将所有错误提示信息预先存储在一个数组里,而预先确定这个数组的大小,是在自动化脚本中 ...

  6. Nginx源码完全注释(2)ngx_array.h / ngx_array.c

    数组头文件 ngx_array.h #include <ngx_config.h> #include <ngx_core.h> struct ngx_array_s { voi ...

  7. nginx源码完全注释(1)ngx_alloc.h / ngx_alloc.c

    首先看 ngx_alloc.h 文件,主要声明或宏定义了 ngx_alloc,ngx_calloc,ngx_memalign,ngx_free. /* * Copyright (C) Igor Sys ...

  8. Nginx源码完全注释(7)ngx_palloc.h/ngx_palloc.c

    ngx_palloc.h /* * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86. * On Windo ...

  9. Nginx源码完全注释(4)ngx_queue.h / ngx_queue.c

    队列头文件ngx_queue.h #include <ngx_config.h> #include <ngx_core.h> #ifndef _NGX_QUEUE_H_INCL ...

随机推荐

  1. hapi lab测试框架简单使用

    1. 依赖安装 yarn init yarn add lab code 2. 基本模式 const Lab = require('lab'); const Code = require('code') ...

  2. Vim自动补全插件----YouCompleteMe安装与配置

    Vim自动补全插件----YouCompleteMe安装与配置 使用Vim编写程序少不了使用自动补全插件,在Linux下有没有类似VS中的Visual Assist X这么方便快捷的补全插件呢?以前用 ...

  3. Linux 之 hugepage 大页内存理论

    HugePages是通过使用大页内存来取代传统的4kb内存页面,使得管理虚拟地址数变少,加快了从虚拟地址到物理地址的映射以及通过摒弃内存页面的换入换出以提高内存的整体性能.尤其是对于8GB以上的内存以 ...

  4. bzoj2748(HAOI2018)音量调节

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2748 水得不想写.别忘了-1. #include<iostream> #incl ...

  5. bzoj1588[HNOI2002]营业额统计——双向链表

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1588 简单Splay.但用双向链表做.很好的思路. 1.(离线)按值排序,记下pre和nxt ...

  6. 自己定义一个tab指令

    定义一个tab切换的指令: 指令的文件结构: Js/directives/tab tab.html tab.js tab.html: <style> .my-li-style{ line- ...

  7. 笔记本设置wifi热点并抓包

    现在很多人喜欢蹭wifi热点,这里演示一下怎么利用笔记本设置wifi热点来钓鱼.本机是win10操作系统. 一.设置笔记wifi热点:右键点击右下角网络图标 -> 打开“网络和Internet设 ...

  8. JDBC查询实例

    作为Java与数据库交互最古老的.最基础的规范,JDBC提供了访问底层数据库的接口,其他ORM框架都是在JDBC这块基石上构建的.下面我们看一个基本的JDBC查询例子: package com.ins ...

  9. OpenCV在debug和release模式下选择不同的lib静态库文件

    这两天测试OpenCV显示到MFC的Picture控件上,终于测试成功了,但是换到release模式下就会imread失败.发现问题是导入的lib问题. 因为VS如果通过Property Manage ...

  10. Spring Cloud Bus 消息总线 RabbitMQ

    Spring Cloud Bus将分布式系统中各节点通过轻量级消息代理连接起来. 从而实现例如广播状态改变(例如配置改变)或其他的管理指令. 目前唯一的实现是使用AMQP代理作为传输对象. Sprin ...