您的位置:首页 > 运维架构 > Nginx

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

2017-07-09 23:31 375 查看
先贴代码,细节以后有必要再加。

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

符合预期。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: