美文网首页
nginx 锁实现思路

nginx 锁实现思路

作者: x1wan | 来源:发表于2019-03-30 14:15 被阅读0次

    锁从功能上划分主要有互斥锁和读写锁。

    不主动释放CPU的锁是spinlock,可以用于竞争较少并且临界区较小的场景,也可以用于中断上下文中。

    锁一般用信号量、原子操作(cas)或文件锁来实现。

    nginx中实现了互斥锁和读写锁以及spinlock。

    互斥锁

    加锁:

    lock是一个原子变量,0表示没有上锁,ngx_pid表示被对应的进程加锁,如果当前被其它进程加锁,说明其它进程正在执行临界区内的代码,不会马上释放锁,所以会根据spin一会儿再尝试加锁,如果不spin而一直不断尝试访问共享变量将会增加核间通信的负担,ngx_cpu_pause()防止循环代码被优化。

    支持posix信号量的情况下,如果spin几次都没有加锁成功则使用信号量进行排队,wait表示排队的个数。

    循环调用sem_wait(&mtx->sem) 将进程阻塞等待其它进程释放锁,其它进程释放锁时会检查是否有进程进行wait,如果有则通过信号量wakeup正在wait的一个进程,用信号量避免一直占用CPU。

    解锁:

    解锁就是将lock赋值为0。然后会调用ngx_shmtx_wakeup(mtx)函数。

    wakeup函数检查当前有没有进程在排队,如果有排队的进程,则通过sem_post(&mtx->sem)激活一个排队的进程。

    这个for循环是一个小技巧,先将wait共享变量保存在本地局部变量中,然后对wait进行检查,最后再通过cas修改wait共享变量,如果在这中间wait变量被其它进程修改,那么本次cas就是失败,这样就可以保证将wait检查和wait修改两个步骤原子执行,这也是乐观锁的实现。

    读写锁

    nginx实现的读写锁对写锁不太友好,可能会有写锁饥饿的问题。

    #define NGX_RWLOCK_WLOCK ((ngx_atomic_uint_t) -1) 定义写锁宏

    加写锁:

    没有锁的时候加写锁。

    加读锁:

    加读锁的时候依然用到了上面的技巧,如果没有加写锁,则加读共享锁。

    解锁:

    先解写锁,再解读锁。

    spinlock

    加锁:

    spinlock加锁居然也会调用yield,说明这个spinlock目前只能用于进程上下文。

    解锁:

    #define ngx_unlock(lock) *(lock) = 0

    这个地方没有指明内存序会不会有问题呢?我理解其它进程会在本进程解锁的一段时间之后才会发现这次解锁,会不会造成饥饿?待研究。

    读写锁(优先写锁)

    加写锁:

    如果当前无锁,则加写锁,如果当前被上锁,则通过信号量等待。

    加读锁:

    如果当前不是写锁并且队列中没有等待的写锁,则加读锁,否则自旋加读锁。

    解锁:

    解写锁,然后weakup等待的写锁。

    解读锁,如果读锁全部完成,然后weakup等待的写锁。

    weakup函数不变,如果wait不为0,则激活一个写锁。不知道是不是正确啊,哈哈

    也看到用两个互斥锁实现读写锁的例子,仅使用互斥锁实现读写锁,有才啊 

    相关文章

      网友评论

          本文标题:nginx 锁实现思路

          本文链接:https://www.haomeiwen.com/subject/qjvjbqtx.html