Nginx 原始碼完全剖析(11)ngx_spinlock
Nginx 原始碼完全剖析(11)ngx_spinlock
- 作者:鍾超
- 部落格:http://Blog.CSDN.net/Poechant
- 郵箱:zhongchao.ustc#gmail.com (#->@)
- 日期:2012年10月12日
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)標誌位。
-
轉載請註明來自鍾超的CSDN部落格:Blog.CSDN.net/Poechant
-
相關文章
- Nginx 原始碼完全剖析(10)ngx_radix_treeNginx原始碼
- Nginx原始碼完全註釋(6)core/murmurhashNginx原始碼
- Nginx原始碼完全註釋(9)nginx.c: ngx_get_optionsNginx原始碼
- Nginx原始碼完全註釋(8)ngx_errno.cNginx原始碼
- Java集合原始碼剖析——ArrayList原始碼剖析Java原始碼
- 【Java集合原始碼剖析】ArrayList原始碼剖析Java原始碼
- 【Java集合原始碼剖析】Vector原始碼剖析Java原始碼
- 【Java集合原始碼剖析】HashMap原始碼剖析Java原始碼HashMap
- 【Java集合原始碼剖析】Hashtable原始碼剖析Java原始碼
- 【Java集合原始碼剖析】TreeMap原始碼剖析Java原始碼
- 【Java集合原始碼剖析】LinkedList原始碼剖析Java原始碼
- 【Java集合原始碼剖析】LinkedHashmap原始碼剖析Java原始碼HashMap
- Nginx原始碼完全註釋(5)core/ngx_cpuinfo.cNginx原始碼UI
- epoll–原始碼剖析原始碼
- HashMap原始碼剖析HashMap原始碼
- Alamofire 原始碼剖析原始碼
- Handler原始碼剖析原始碼
- Kafka 原始碼剖析Kafka原始碼
- TreeMap原始碼剖析原始碼
- SDWebImage原始碼剖析(-)Web原始碼
- Boost原始碼剖析--原始碼
- Spring原始碼剖析9:Spring事務原始碼剖析Spring原始碼
- Nginx原始碼完全註釋(1)ngx_alloc.h / ngx_alloc.cNginx原始碼
- Nginx原始碼完全註釋(7)ngx_palloc.h/ngx_palloc.cNginx原始碼
- Nginx原始碼完全註釋(4)ngx_queue.h / ngx_queue.cNginx原始碼
- Nginx原始碼完全註釋(3)ngx_list.h / ngx_list.cNginx原始碼
- Nginx原始碼完全註釋(2)ngx_array.h / ngx_array.cNginx原始碼
- Flutter 原始碼剖析(一)Flutter原始碼
- 全面剖析 Redux 原始碼Redux原始碼
- vue原始碼剖析(一)Vue原始碼
- Kafka 原始碼剖析(一)Kafka原始碼
- Thread原始碼剖析thread原始碼
- Retrofit 原始碼剖析-深入原始碼
- SDWebImage原始碼剖析(二)Web原始碼
- iOS Aspects原始碼剖析iOS原始碼
- Apache Spark原始碼剖析ApacheSpark原始碼
- 《STL原始碼剖析》-- memory原始碼
- Nginx篇--Nginx原始碼搭建Nginx原始碼