现在的位置: 首页 > 综合 > 正文

nginx spinlock 自旋锁的实现

2013年10月03日 ⁄ 综合 ⁄ 共 1988字 ⁄ 字号 评论关闭
两个问题, 
1) spinlock的实现原理是?
   a. 在用户态尝试竞争一个共享资源. 如果竞争不到, 则不断尝试竞争. 但是不借助内核提供的mutex等变量机制. 因为涉及到内核,就意味这效率低下.
   b. 要想在用户态实现竞争一个共享资源, 必须借助cpu提供的原子操作指令. 如果是SMP多cpu,还需要lock指令锁总线.
   c. 为了避免在长时间竞争却一直得不到资源导致的不断尝试浪费cpu, 在每两次尝试之间间隔一段时间. 并且随着尝试次数的增加,间隔时间也增加.
     间隔期间可以让cpu稍加休息(注意,绝不是让出cpu),这依赖于cpu提供pausse指令. (当然如果cpu没有提供pause也没关系,只是会很消耗电力资源)
      PAUSE指令提升了自旋等待循环(spin-wait loop)的性能。
      http://blog.csdn.net/misterliwei/article/details/3951103
   d. 在等待相当长时间还是得不到锁之后,只好让出cpu. 但必须让出很小一会. 否则就不叫自旋锁了.
      如何让出cpu,却有可以很快的回来? 内核提供了 sched_yield()函数
      sched_yield()主要功能: 
          简单的讲,可以使用另一个级别等于或高于当前线程的线程先运行。如果没有符合条件的线程,那么这个函数将会立刻返回然后继续执行当前线程的程序
      参考:
      sched_yield()函数 高级进程管理
      http://blog.csdn.net/magod/article/details/7265555
      当然,如果系统不支持sched_yield, nginx被迫使用了usleep()休息1u秒.
2) spinlock的使用场景是?
   nginx借助spinlock的技术,实现了用户态的进程间的mutex. 由于spinlock是阻塞的
   #define ngx_shmtx_lock(mtx)   ngx_spinlock((mtx)->lock, ngx_pid, 1024)
 
   PS: 我在nginx1.0.2版本代码里面查到 ngx_shmtx_lock的这个函数的实现没有直接调用ngx_spinlock,但实现和ngx_spinlock类似.
3) 具体分析一个spinlock的开源实现?
// 输入参数 
//    lock:一个整形变量的指针
//    value:将lock设置新的值
//    spin: 自旋的次数. 该值越大会尝试更多次获得锁. 然后才会转入让内核调度线程暂时让出cpu.
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 ( ;; ) {
        if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {
            return;
        }
        // 为何只在多个cpu的时候才多尝试spin几次?
        // 呵呵,很简单,如果是单核的话,既然自己没有拿到锁,那说明别的线程/进程正在使用锁,就这么一个cpu,咱就不占着自旋了,否则别人没机会得到cpu,更不会释放锁了.

        if (ngx_ncpu > 1) {

            for (n = 1; n < spin; n <<= 1) {
                // 空转的时间随着n的变大而变大

                for (i = 0; i < n; i++) {
                    ngx_cpu_pause(); // 在空转的同时, 降低cpu功耗,提高效率
                }
                if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {
                    return;
                }
            }

        }
        // 已经尝试这么久了还没有得到锁, 让cpu忙别人的事情吧. 让出cpu等待一下.

        ngx_sched_yield();
    }
#else
#if (NGX_THREADS)
#error ngx_spinlock() or ngx_atomic_cmp_set() are not defined !
#endif
#endif
}

#if (NGX_HAVE_SCHED_YIELD)
#define ngx_sched_yield()  sched_yield()
#else
#define ngx_sched_yield()  usleep(1)
#endif

一个分析spinlock的文章:
(转)自旋锁(spinlock) 解释得经典,透彻

抱歉!评论已关闭.