Nginx is a multi-process mode. A master and multiple workers generally work on multi-core CPUs, so spin locks are required. The definition of the spin lock in nginx is located in ngx_spinlock.c, as follows:
Voidngx_spinlock (ngx_atomic_t * Lock, negative value, ngx_uint_t spin) {# If (ngx_have_atomic_ops) ngx_uint_t I, n; For (;) {// lock is the lock, is an integer // ngx_atomic_cmp_set is platform-related and generally involves inline assembly if (* Lock = 0 & ngx_atomic_cmp_set (lock, 0, value) {return ;} // multi-core if (ngx_ncpu> 1) {// wait and retry policy, see the description below 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 definnot ed! # Endif}
Here, the integer variable lock is used to represent the lock. On the author's machine (Darwin 12.0), it is defined as follows:
typedef volatile ngx_atomic_uint_t ngx_atomic_t;
Return to the source code analysis of the above spinlock. If ngx_ncpu (indicating the number of CPU cores) exceeds 1, that is, multi-core CPU, you have to wait/retry. For example, if spin is 80, wait for one ngx_cpu_pause () operation for the first time and check whether the lock is available again. Next, wait for 2, 4, 8, 16, 32, and 64 ngx_cpu_pause () operations in each round and try again. In this intermediate process, if the lock is released and can be used, the loop will be aborted, And the spinlock function will return a value. If the Retry is still unsuccessful, run ngx_sched_yield and then repeat the preceding operation.
In addition, the ngx_atomic_cmp_set function is also very valuable. In Darwin 12.0, the macro definition is as follows:
#define ngx_atomic_cmp_set(lock, old, new) OSAtomicCompareAndSwap64Barrier(old, new, (int64_t *) lock)
In a friend of mine's Linux environment (I forgot it, But x86), as follows. For inline assembly, refer to the two blog posts on GCC inline assembly in this blog. SMP is a bus lock.
static ngx_inline ngx_atomic_uint_tngx_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;}
Output here is res, Which is saved in the eax register. The input is * Lock (in memory), old (in eax), and set (r indicates general registers ). In this way, % 0 is res, % 1 is * Lock, % 2 is old, and % 3 is set.
If * Lock and old are equal, then the difference or (cmpxchgl) is 0, ZF is 1, sete sets the res (% 0) value to 1 and returns it. If * Lock and old are not equal, the fire value is non-zero. Therefore, if ZF is non-zero, sete will not execute the action, that is, the res value is 0, that is, the ngx_atomic_cmp_set call fails.
Cmpxchgl affects the ZF (zero flag) flag.
Nginx source code full annotation (11) ngx_spinlock