1. 程式人生 > >Nginx學習之四-Nginx程序同步方式-自旋鎖(spinlock)

Nginx學習之四-Nginx程序同步方式-自旋鎖(spinlock)

自旋鎖簡介 Nginx框架使用了三種訊息傳遞方式:共享記憶體、套接字、訊號。 Nginx主要使用了三種同步方式:原子操作、訊號量、檔案鎖。 基於原子操作,nginx實現了一個自旋鎖。自旋鎖是一種非睡眠鎖。如果某程序檢視獲得自旋鎖,當發現鎖已經被其他程序獲得時,那麼不會使得當前程序進入睡眠狀態,而是始終保持程序在可執行狀態,每當核心排程到這個程序執行時就持續檢查是否可以獲取到所鎖。 自旋鎖的應用場景 自旋鎖主要是為多處理器作業系統而設定的,他要解決的共享資源保護場景就是程序使用鎖的時間非常短(如果鎖的使用時間很久,自旋鎖不合適,因為會佔用大量的CPU資源)。 大部分情況下Nginx的worker程序最好都不要進入睡眠狀態,因為它非常繁忙,在這個程序的epoll上可能有十萬甚至百萬的TCP連線等待著處理,程序一旦睡眠後必須等待其他事件的喚醒,這中間及其頻繁的程序間切換帶來的負載消耗可能無法讓使用者接受。 自旋鎖原始碼分析
下面通過原始碼看自旋鎖的具體實現(Nginx_spinlock.c,Nginx1.4.1版本):
/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) Nginx, Inc.
 */


#include <ngx_config.h>
#include <ngx_core.h>

//函式:基於原子操作的自旋鎖方法ngx_spinlock的實現
//引數解釋:
//lock:原子變量表達的鎖
//value:標誌位,鎖是否被某一程序佔用
//spin:在多處理器系統內,當ngx_spinlock方法沒有拿到鎖時,當前程序在核心的一次排程中該方法等待其他處理器釋放鎖的時間
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為0表示沒有其他程序持有鎖,這時將lock值設定為value引數表示當前程序持有了鎖
        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();//告訴CPU現在處於自旋鎖等待狀態
                }

                //檢查鎖是否被釋放
                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

}

上面的程式碼需要注意的是:在多處理器下,當發現鎖被其他程序佔用時,當前程序並不是立刻讓出正在使用的CPU處理器,而是等待一段時間,看看其他處理器上的程序是否會釋放鎖,這會減少程序間切換的次數。 函式ngx_cpu_pause():是虛度哦架構體系中專門為了自旋鎖而提供的指令,它會告訴CPU現在處於自旋鎖等待狀態,通常一個CPU會將自己置於節能狀態,降低功耗。但是當前程序並沒有讓出正在使用的處理器。 函式 ngx_sched_yield():當前程序仍仍然處於可執行狀態,但暫時讓出處理器,使得處理器優先排程其他可執行狀態的程序,這樣,在程序被核心再次排程時,在for迴圈程式碼中可以期望其他程序釋放鎖。