ngx_shmtx_lock 分析

作用

在nginx中,此锁用于共享内存分配时上锁,用于进程互斥以及进程内线程互斥。

初始化

ngx_shmtx_create 函数实现


ngx_int_t
ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr, u_char *name)
{
    mtx->lock = &addr->lock;

    if (mtx->spin == (ngx_uint_t) -1) {
        return NGX_OK;
    }

    mtx->spin = 2048;

#if (NGX_HAVE_POSIX_SEM)

    mtx->wait = &addr->wait;

    if (sem_init(&mtx->sem, 1, 0) == -1) {
        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
                      "sem_init() failed");
    } else {
        mtx->semaphore = 1;
    }

#endif

    return NGX_OK;
}

mtx->lock 原子操作数,在加锁时,作为判断条件值。
mtx->spin 用于判断mtx->lock的次数,nginx的锁不是盲目的轮询判断或者判断只判断一次,它是选取了一个spin的循环判断次数,超过次数,让出cpu或者等待信号处理。
mtx->wait 我理解的是记录等待个数,但是这个值得作用,不太明白,先记着
mtx->semaphore 标记使用信号量,主要在加锁时sem和ngx_sched_yield机制的选取时,做判断。

加锁操作

ngx_shmtx_lock 函数实现


void
ngx_shmtx_lock(ngx_shmtx_t *mtx)
{
    ngx_uint_t         i, n;

    for ( ;; ) {

        if (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) {
            return;
        }

        if (ngx_ncpu > 1) {

            for (n = 1; n < mtx->spin; n <<= 1) {

                for (i = 0; i < n; i++) {
                    ngx_cpu_pause();
                }

                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;
            }
            
            while (sem_wait(&mtx->sem) == -1) {
                ngx_err_t  err;

                err = ngx_errno;

                if (err != NGX_EINTR) {
                    break;
                }
            }
            continue;
        }

#endif

        ngx_sched_yield();
    }
}

```c
if (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) {
            return;
}

使用ngx_pid 对lock赋值,ngx_pid用于区分进程,不可能重复,非常巧妙,不用程序维护。

接下来是一个for循环判断,判断多次无果,才进行下一步休眠操作或等待操作,和nginx的自旋锁一个机制
里面有关于自旋锁的介绍

接下来是一个选择,在自旋锁直接使用ngx_sched_yield函数,让出cpu,等待下次判断,在共享内存锁多了一个
信号量的选择。

 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;
            }

将wait原子加一,然后判断lock的值

 while (sem_wait(&mtx->sem) == -1) {
                ngx_err_t  err;

                err = ngx_errno;

                if (err != NGX_EINTR) {
                    break;
                }
            }
            continue;

等待sem_post唤醒,此时阻塞。

sem_wait和ngx_sched_yield的对比,sem_wait是一个等待通知的机制,sched_yield是一个循环遍历的机制,在自旋锁使用sched_yield是因为自旋时间断,快速循环遍历,在共享内存锁中,可能需要等待时间长,使用sem机制,避免cpu浪费,是这样理解吗?

解锁操作

ngx_shmtx_unlock 函数

if (ngx_atomic_cmp_set(mtx->lock, ngx_pid, 0)) {
        ngx_shmtx_wakeup(mtx);
    }

static void
ngx_shmtx_wakeup(ngx_shmtx_t *mtx)
{
#if (NGX_HAVE_POSIX_SEM)
    ngx_atomic_uint_t  wait;

    if (!mtx->semaphore) {
        return;
    }

    for ( ;; ) {

        wait = *mtx->wait;

        if ((ngx_atomic_int_t) wait <= 0) {
            return;
        }

        if (ngx_atomic_cmp_set(mtx->wait, wait, wait - 1)) {
            break;
        }
    }
    if (sem_post(&mtx->sem) == -1) {
    }

#endif
}

ngx_atomic_cmp_set 将lock设置0,执行ngx_shmtx_wakeup。如果使用ngx_sched_yield休眠,ngx_shmtx_wakeup函数无意义,ngx_shmtx_wakeup主要唤醒sem。

强制解锁

这个操作厉害了,解决程序不稳定的福音,多进程操作共享内存锁,如果在锁操作过程崩了,所有进程都凉凉,此时,在进程启动时,这个强制解锁,就是一个解决办法。

ngx_uint_t
ngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid)
{
    if (ngx_atomic_cmp_set(mtx->lock, pid, 0)) {
        ngx_shmtx_wakeup(mtx);
        return 1;
    }

    return 0;
}

参数是加锁时的进程号。

好嘞,结束!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值