Nginx 源码完全剖析 nginx 自旋锁 ngx_spinlock
2015-11-11 14:15
1056 查看
Nginx 是多进程模式的,一个 master 与多个 workers,一般工作在多核 CPU 上,所以自旋锁就是必须用到的。Nginx 中的自旋锁的定义,位于 ngx_spinlock.c 中,如下:
其中用 lock 这个整形变量表示锁,在笔者的机器(Darwin 12.0)上,是如下定义的:
再回到上面 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 上面是如下的宏定义:
在我一位朋友的 Linux 环境(具体忘记了,但是 x86),如下。其中的内联汇编可以参考本博客内的 GCC 内联汇编的两篇博文。其中的 SMP 为总线锁。
这里输出为 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)标志位。
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)标志位。
相关文章推荐
- 小测试 php代理,nginx代理,直接访问对比
- web服务器【apache/nginx] 关闭目录的浏览权限
- 打杂程序员之nginx服务配置
- Linux(CentOS) - 源码编译安装Nginx
- Nginx多线程原理
- nginx 服务启动脚本 chkconfig service
- 源码安装一般用途nginx+php
- CentOS7 重启gitlab nginx
- nginx优化
- 配置nginx 高并发 php
- 分布式文件系统FastDFS部署
- nginx: if逻辑运算 (与或非) 实现
- Nginx实战之让用户通过用户名密码认证访问web站点
- Linux下nginx安装笔记
- nginx之location详解
- mac 安装 nginx 环境
- LNMP平滑升级nginx并安装ngx_lua模块教程
- nginx的luajit安装luarocks并安装luafilesystem
- 使用Nginx后获取用户IP,以及防止伪造IP
- 搭建nginx+ffmpeg服务