CPU cache:
http://cenalulu.github.io/linux/all-about-cpu-cache/
other:
http://www.cnblogs.com/zhengsyao/p/mcs_lock_scalable_spinlock.html
http://simohayha.iteye.com/blog/658012
http://www.searchtb.com/2011/06/spinlock%E5%89%96%E6%9E%90%E4%B8%8E%E6%94%B9%E8%BF%9B.html
Nginx workers 要获取新的请求,需要互斥的得到监听端口的socket句柄。nginx通过自己实现的锁来实现进程间互斥.
1, 原子操作
原子操作就是可以在执行这几个操作的时候不会被其他指令打断,可以实现排他的内存访问,实际是在内存总线层次的临界区操作。
Nginx 使用gcc的原子操作:
ngx_atomic.h:
#elif (NGX_HAVE_GCC_ATOMIC)
/* GCC 4.1 builtin atomic operations */
#define NGX_HAVE_ATOMIC_OPS 1 //表示支持原子操作
typedef long ngx_atomic_int_t;
typedef unsigned long ngx_atomic_uint_t;
#if (NGX_PTR_SIZE == 8)
#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1)
#else
#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") -1)
#endif
typedef volatile ngx_atomic_uint_t ngx_atomic_t; //volatile类型,表示该变量随时会被修改,每次都需要从其存储地址读取
#define ngx_atomic_cmp_set(lock, old, set) \ // 原子的比较和交换,如果*lock==old,就将set写入*lock
__sync_bool_compare_and_swap(lock, old, set) // 相等并写入的情况下返回true/操作之前的值
#define ngx_atomic_fetch_add(value, add) \ // *value+=add并返回更新前的值
__sync_fetch_and_add(value,add)
#define ngx_memory_barrier() __sync_synchronize() // 发出一个full barrier
memory barrier有几种类型:
acquire barrier : 不允许将barrier之后的内存读取指令移到barrier之前(linuxkernel中的wmb())。
release barrier : 不允许将barrier之前的内存读取指令移到barrier之后(linux kernel中的rmb())。
full barrier : 以上两种barrier的合集(linux kernel中的mb())。
Nginx自己实现原子操作:
read-modify-write(RMW)原子操作
例如Intel平台支持在指令前面加上lock前缀锁定总线,实现原子的比较-交换操作(CAS,通过cmpxchg指令,原子地执行判断条件并交换值的操作),原子递增和原子递减(inc和dec指令)操作。还支持一个交换操作,xchg指令,不需要lock前缀,可以原子地交换两个寄存器或一个寄存器和一个内存位置的值。在Intel平台上通过这些基本的原子操作支持几乎所有的原子操作。
ngx_gcc_atomic_amd64.h:
static ngx_inline ngx_atomic_uint_t
ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_told, ngx_atomic_uint_t set)
{
u_char res;
__asm__ volatile (
lock
" cmpxchgq %3, %1; "
" sete %0; "
: "=a"(res) : "m" (*lock), "a" (old), "r" (set) :"cc", "memory"); //set是input;
return res;
}
//"=a" : =是output operand字段特有的约束,表示该操作数是只写的(write-only);
a表示先将命令执行结果输出至%eax,然后再由寄存器%eax更新位于内存中的out_var;
//不想通过寄存器中转,而是直接操作内存时,可以用"m"来约束
// "r"约束指明gcc可以先将%eax值存入任一可用的寄存器,然后由该寄存器负责更新内存变量。
//The "cc" means that flags were changed.
2,锁的实现
在用户空间进程间锁实现的原理,就是有一个让所有进程共享的东西,比如mmap的内存或者文件,然后通过这个东西来控制进程的互斥。
就是共享一个变量,然后通过设置这个变量来控制进程的行为。
初始化:ngx_event_module_init
....
/* cl should be equal to or greater thancache line size */
//mmap分配共享内存,cache line可以简单的理解为CPU Cache中的最小缓存单位。
cl = 128;
size = cl /* ngx_accept_mutex */
+ cl /* ngx_connection_counter */
+ cl; /* ngx_temp_number */
shm.size = size;
shm.name.len =sizeof("nginx_shared_zone");
shm.name.data = (u_char *)"nginx_shared_zone";
shm.log = cycle-log;
if (ngx_shm_alloc(shm) != NGX_OK) {
return NGX_ERROR;
}
shared = shm.addr;
ngx_accept_mutex_ptr = (ngx_atomic_t *)shared;
ngx_accept_mutex.spin = (ngx_uint_t) -1;
//把分配的共享内存地址赋值给ngx_accept_mutex-lock;
if (ngx_shmtx_create(ngx_accept_mutex,(ngx_shmtx_sh_t *) shared,
cycle-lock_file.data)
!= NGX_OK)
{
return NGX_ERROR;
}
......
typedef struct {
u_char *addr; //mmap分配的共享内存地址
size_t size;
ngx_str_t name;
ngx_log_t *log;
ngx_uint_t exists; /* unsigned exists:1; */
} ngx_shm_t;
ngx_shm_t shm;
ngx_shmtx.h
typedef struct {
#if (NGX_HAVE_ATOMIC_OPS)
ngx_atomic_t *lock;
#if (NGX_HAVE_POSIX_SEM)
ngx_atomic_t *wait;
ngx_uint_t semaphore;
sem_t sem;
#endif
#else
ngx_fd_t fd;
u_char *name;
#endif
ngx_uint_t spin;
} ngx_shmtx_t;
ngx_shmtx_t ngx_accept_mutex;
非阻塞获取锁:ngx_shmtx_trylock(ngx_accept_mutex)
//尝试获取锁,如果锁已经被其他进程占用,返回false,否则给lock赋值表示该已进程获取锁。
ngx_uint_t
ngx_shmtx_trylock(ngx_shmtx_t*mtx)
{
return (*mtx-lock == 0 ngx_atomic_cmp_set(mtx-lock, 0, ngx_pid));
}
阻塞获取锁:
void
ngx_shmtx_lock(ngx_shmtx_t *mtx)
{
ngx_uint_t i, n;
ngx_log_debug0(NGX_LOG_DEBUG_CORE,ngx_cycle-log, 0, "shmtx lock");
for ( ;; ) {
//直接获取锁
if (*mtx-lock == 0 ngx_atomic_cmp_set(mtx-lock, 0, ngx_pid)) {
return;
}
//多核情况下,进入spin-wait loop
if (ngx_ncpu 1) {
for (n = 1; n mtx-spin; n= 1) {
//busy waiting
for (i = 0; i n; i++) {
ngx_cpu_pause();
}
//try again
if (*mtx-lock == 0
ngx_atomic_cmp_set(mtx-lock, 0, ngx_pid))
{
return;
}
}
}
#if (NGX_HAVE_POSIX_SEM)
if (mtx-semaphore) {
(void)ngx_atomic_fetch_add(mtx-wait, 1);
if (*mtx-lock == 0 ngx_atomic_cmp_set(mtx-lock, 0, ngx_pid)) {
(void)ngx_atomic_fetch_add(mtx-wait, -1);
return;
}
ngx_log_debug1(NGX_LOG_DEBUG_CORE,ngx_cycle-log, 0,
"shmtx wait %uA",*mtx-wait);
while (sem_wait(mtx-sem)== -1) {
ngx_err_t err;
err = ngx_errno;
if (err != NGX_EINTR) {
ngx_log_error(NGX_LOG_ALERT, ngx_cycle-log, err,
"sem_wait() failed while waiting on shmtx");
break;
}
}
ngx_log_debug0(NGX_LOG_DEBUG_CORE,ngx_cycle-log, 0,
"shmtx awoke");
continue;
}
#endif
//强迫当前运行的进程放弃占有处理器
ngx_sched_yield();
}
}