nginx源码学习4——重写共享内存锁类

自旋锁与原子操作
本文介绍了一种基于原子操作的自旋锁实现,并通过示例代码展示了如何在多线程环境中使用这些自旋锁来确保资源访问的安全。此外,还提供了一个简单的测试程序来验证自旋锁的有效性。

先贴代码,细节以后有必要再加。

mytool_lock_base.h:

#ifndef MYTOOL_LOCK_BASE_H
#define MYTOOL_LOCKS_BASE_H

#include <sched.h>
#include <unistd.h>
#include <sys/types.h>
namespace mytool{
/* GCC 4.1 builtin atomic operations */
typedef volatile long atomic_int_t;
typedef volatile unsigned long atomic_uint_t;
//nginx只有增和赋值0、1的需求,所以nginx的atomic_t是unsigned的
typedef atomic_int_t atomic_t;

//__sync_bool_compare_and_swap,gcc built in函数,如果plock指向值等于old,则设置*plock为set,返回1;否则返回0;
//本机x86_64构架,g++ -S显示关键汇编指令为lock cmpxchg
inline bool atomic_cmp_set(atomic_t *plock,atomic_t old,atomic_t set){
        return __sync_bool_compare_and_swap(plock,old,set);
}
//__sync_fetch_and_add,buitin函数,value+add并赋值回value,返回value旧值
//关键汇编指令为lock addq
//nginx仅仅只需要增加值
inline atomic_t atomic_fetch_add(atomic_t *pvalue,atomic_t add){
        return __sync_fetch_and_add(pvalue,add);
}
//nginx更新时间字符串时会用到
//能否inline?
#define mytool_memory_barrier() __sync_synchronize()

//当前单线程的nginx主流程实际只用到的两个加锁函数,加锁失败,立即返回,不强求
//单线程只需给信号处理函数和正常流程冲突部分加锁,如果用下面的自旋锁(同时也实现了互斥锁)函数加锁,会引起死锁
inline bool trylock(atomic_t *plock){
        return *(plock) == 0 && atomic_cmp_set(plock,0,1);
}
inline void unlock(atomic_t *plock){
        *(plock) = 0;
}

//扒自ngx_spinlock,实际功效和名字不同,根据参数和环境的不同,可以体现为自旋锁、互斥锁或二者结合形式
//nginx本函数没有实际使用场景,但是类似代码在ngx_shmtx.c内有
#define mytool_cpu_pause() __asm__ ("pause")
inline void spinlock(atomic_t *lock,atomic_t value,unsigned spin){
        static unsigned ncpu = 0;
    unsigned i,n;
    for (;;){
        if(*lock == 0 && atomic_cmp_set(lock,0,value))
            return;
                if(ncpu == 0)
                        ncpu = sysconf(_SC_NPROCESSORS_ONLN);
        if(ncpu > 1){
            for(n = 1;n < spin;n <<= 1){
                for(i = 0;i < n;i++)
                    mytool_cpu_pause();
                if(*lock == 0 && atomic_cmp_set(lock,0,value))
                    return;
            }
        }
        sched_yield();
    }
}

}

#endif

mytool_shmlock.h

#ifndef MYTOOL_SHMLOCK_H
#define MYTOOL_SHMLOCK_H
#include "mytool_lock_base.h"

//nginx代码内实现了多种形式的互斥,原子操作trylock、自旋锁、信号量、互斥锁,但是当前nginx的单线程多路复用模式使得工作进程挂起于某种事件不能接收。所以实际上使用的也就是trylock,得不到就放手。
//进程间的互斥用原子操作trylock+共享内存实现,也是接下来要试图扒取的
//认为会作为全局变量使用,没有收回内存
namespace mytool{
class Shmlock{
        public:
                //拷贝、赋值等禁止操作delete,todo
                //可以增加当前锁持有时间+试图解锁计数,每次tryLock计数自减,为0时说明长时间没有释放或者获得锁需求很大,此时应该打印日志,todo
                inline bool tryLock() noexcept(true){
                        return mytool::trylock(&lock);
                }
                //可以加当前lock的持有者pid,unlock时验证,todo
                inline void unlock() noexcept(true){
                        mytool::unlock(&lock);
                }
                static Shmlock *getNewShmlock() noexcept(false);
        private:
                static key_t _smid;
                static Shmlock *_shm;
                static unsigned _idcount;
                const static unsigned _maxnid = 20;
                Shmlock():lock(0) {}
                atomic_t lock;
};
}

#endif

mytool_shmlock.cpp

#include "mytool_shmlock.h"
#include <sys/shm.h>
#include <stdexcept>
#include <stdio.h>
#include <string.h>

namespace mytool{

key_t Shmlock::_smid = -1;
Shmlock *Shmlock::_shm = NULL;
unsigned Shmlock::_idcount = 0;
Shmlock *Shmlock::getNewShmlock() noexcept(false) {
        //初始化
        if(_smid < 0){
                if((_smid = shmget(0,sizeof(Shmlock) * _maxnid,0666|IPC_CREAT)) < 0)
                        throw std::runtime_error(strerror(errno));
                if(((long)(_shm = (Shmlock *)shmat(_smid,NULL,0))) == -1)
                        throw std::runtime_error(strerror(errno));
        }
        if(_idcount >= _maxnid)
                throw std::overflow_error("too many shmlock built");
        //Shmlock::Shmlock(_shm + _idcount);
        new (_shm + _idcount) Shmlock;
        return _shm + _idcount++;
}

}

最后的测试cpp:

#include "mytool_shmlock.h"
#include <stdio.h>
#include <unistd.h>

using namespace mytool;
int main(){
        Shmlock *psm0 = Shmlock::getNewShmlock();
        //此时psm测试第二个lock
        Shmlock *psm = Shmlock::getNewShmlock();
        if(fork() == 0){
                if(psm->tryLock()){
                        printf("son got lock\n");
                        sleep(1);
                        psm->unlock();
                        printf("son releaed lock\n");
                }
                else{
                        printf("son failed to get lock\n");
                }
                return 0;
        }
        sleep(1);
        if(psm->tryLock())
                printf("parent got lock\n");
        else{
                printf("parent failed to get lock\n");
                sleep(3);
                if(psm->tryLock())
                        printf("parent finally got the lock\n");
                psm->unlock();
        }
        return 0;
}

输出:

son got lock
parent failed to get lock
son releaed lock
parent finally got the lock


符合预期。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值