nginx读写锁的实现逻辑

我们一般认为nginx是一个多进程单线程的应用服务,虽然nginx在一个worker进程内是没有数据竞争问题的(因为是单线程),但是不免nginx在多个进程间还有一些需要共享的数据,譬如ngx_http_upstream_zone_module模块将peers数据放在了共享内存中供多个worker进程来使用,又譬如ngx_http_limit_conn_module模块将并发连接数限制也放在了共享内存中,诸如此类的,自然会涉及到共享内存访问的互斥锁的问题,本文对nginx实现的互斥锁进行分析,通过分析学习nginx的实现代码,以便将来可以应用到自己的日常应用程序中去。

nginx的读写锁实现逻辑是通过自旋锁来实现的。

nginx一共实现了以下几个api函数:

c 复制代码
void ngx_rwlock_wlock(ngx_atomic_t *lock);
void ngx_rwlock_rlock(ngx_atomic_t *lock);
void ngx_rwlock_unlock(ngx_atomic_t *lock);
void ngx_rwlock_downgrade(ngx_atomic_t *lock);

ngx_rwlock_wlock用来加写锁,ngx_rwlock_rlock用来加读锁,ngx_rwlock_unlock用来对加的锁进行释放,ngx_rwlock_downgrade对写锁进行降级为读锁。

锁变量是ngx_atomic_t类型,对应的就是一个unsigned long的类型。

以下是ngx_rwlock_wlock的实现代码:

c 复制代码
void
ngx_rwlock_wlock(ngx_atomic_t *lock)
{
    ngx_uint_t  i, n;

    for ( ;; ) {
		/* 如果*lock的值是0表示现在没有加任何读写锁
		   ngx_atomic_cmp_set比较如果是lock是0,则将其设置为NGX_RWLOCK_WLOCK
		   表示加锁成功,可以返回了
		*/
        if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, NGX_RWLOCK_WLOCK)) {
            return;
        }

        
        if (ngx_ncpu > 1) {
        
			/* 对于多cpu的情况需要进行自旋加锁检测 */
            for (n = 1; n < NGX_RWLOCK_SPIN; n <<= 1) {

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

                if (*lock == 0
                    && ngx_atomic_cmp_set(lock, 0, NGX_RWLOCK_WLOCK))
                {
                    return;
                }
            }
        }

		/* 通知os将自己切出,调度到其他进程 */
        ngx_sched_yield();
    }
}

以下是ngx_rwlock_rlock的实现代码:

c 复制代码
void
ngx_rwlock_rlock(ngx_atomic_t *lock)
{
    ngx_uint_t         i, n;
    ngx_atomic_uint_t  readers;

    for ( ;; ) {
        readers = *lock;
		
		/* 如果*lock的值不是NGX_RWLOCK_WLOCK表示现在没有加写锁,则可以尝试获取读锁,
		   ngx_atomic_cmp_set比较如果是lock和之前保存的readers一致,
		   则将其设置为readers+1,表示加锁成功,可以返回了
		*/
		
        if (readers != NGX_RWLOCK_WLOCK
            && ngx_atomic_cmp_set(lock, readers, readers + 1))
        {
            return;
        }

        if (ngx_ncpu > 1) {
            /* 对于多cpu的情况需要进行自旋加锁检测 */
            for (n = 1; n < NGX_RWLOCK_SPIN; n <<= 1) {

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

                readers = *lock;

                if (readers != NGX_RWLOCK_WLOCK
                    && ngx_atomic_cmp_set(lock, readers, readers + 1))
                {
                    return;
                }
            }
        }
         /* 通知os将自己切出,调度到其他进程 */
        ngx_sched_yield();
    }
}

以下是ngx_rwlock_unlock的实现代码:

c 复制代码
void
ngx_rwlock_unlock(ngx_atomic_t *lock)
{
    if (*lock == NGX_RWLOCK_WLOCK) {
        /* 如果是写锁定了,那么将*lock置为0,表示没有加任何锁了*/
        (void) ngx_atomic_cmp_set(lock, NGX_RWLOCK_WLOCK, 0);
    } else {
        /*如果当前是读锁定了,那么只是将*lock-1,表示少了一个读者 */
        (void) ngx_atomic_fetch_add(lock, -1);
    }
}

以下是ngx_rwlock_downgrade的实现代码:

c 复制代码
void
ngx_rwlock_downgrade(ngx_atomic_t *lock)
{
	/* 如果当前是加上了写锁的,因为肯定没有读者,将自己变为读者,所以只有1个读者,
	   因此将*lock设置为1
	*/
    if (*lock == NGX_RWLOCK_WLOCK) {
        *lock = 1;
    }
}
相关推荐
dessler14 分钟前
Docker-如何启动docker
运维·docker·云原生·容器·eureka
zhy2956314 分钟前
【DOCKER】基于DOCKER的服务之DUFS
运维·docker·容器·dufs
无为之士19 分钟前
Linux自动备份Mysql数据库
linux·数据库·mysql
秋名山小桃子28 分钟前
Kunlun 2280服务器(ARM)Raid卡磁盘盘符漂移问题解决
运维·服务器
与君共勉1213829 分钟前
Nginx 负载均衡的实现
运维·服务器·nginx·负载均衡
岑梓铭36 分钟前
(CentOs系统虚拟机)Standalone模式下安装部署“基于Python编写”的Spark框架
linux·python·spark·centos
努力学习的小廉36 分钟前
深入了解Linux —— make和makefile自动化构建工具
linux·服务器·自动化
MZWeiei40 分钟前
Zookeeper基本命令解析
大数据·linux·运维·服务器·zookeeper
7yewh1 小时前
嵌入式Linux QT+OpenCV基于人脸识别的考勤系统 项目
linux·开发语言·arm开发·驱动开发·qt·opencv·嵌入式linux
Arenaschi1 小时前
在Tomcat中部署应用时,如何通过域名访问而不加端口号
运维·服务器