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


符合预期。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值