Nginx共享内存
1. 介绍
1.1. 介绍
nginx共享内存是利用mmap将内容存储在内存中以及自旋锁。当master启动的时候,根据相应的指令去初始化共享内存。利用共享内存实现一个轻量级的k/v系统。
2. 结构
2.1. 全局变量
ngx_cycle_s结构:
struct ngx_cycle_s {
void ****conf_ctx; //配置上下文数组(含所有模块)
ngx_pool_t *pool; //内存池
ngx_log_t *log; //日志
ngx_log_t new_log;
ngx_connection_t **files; //连接文件
ngx_connection_t *free_connections; //空闲连接
ngx_uint_t free_connection_n; //空闲连接个数
ngx_queue_t reusable_connections_queue; //再利用连接队列
ngx_array_t listening; //监听数组
ngx_array_t pathes; //路径数组
ngx_list_t open_files; //打开文件链表
ngx_list_t shared_memory; //共享内存链表
ngx_uint_t connection_n; //连接个数
ngx_uint_t files_n; //打开文件个数
ngx_connection_t *connections; //连接
ngx_event_t *read_events; //读事件
ngx_event_t *write_events; //写事件
ngx_cycle_t *old_cycle; //old cycle指针
ngx_str_t conf_file; //配置文件
ngx_str_t conf_param; //配置参数
ngx_str_t conf_prefix; //配置前缀
ngx_str_t prefix; //前缀
ngx_str_t lock_file; //锁文件
ngx_str_t hostname; //主机名
};
2.2. 共享内存结构
ngx_zone_s结构:
struct ngx_shm_zone_s {
void *data; //指向自定义数据结构,一般用来初始化时使用,可能指向本地地址
ngx_shm_t shm; //真正的共享内存
ngx_shm_zone_init_pt init; //初始化函数
void *tag; //标记
};
ngx_shm_t结构:
typedef struct {
u_char *addr; // 共享内存首地址
size_t size; // 共享内存大小
ngx_str_t name; // 共享内存名称
ngx_log_t * // 日志
ngx_uint_t exists;
} ngx_shm_t
回收共享内存:
void
ngx_shm_free(ngx_shm_t *shm)
{
if(munmap((void *) shm->addr, shm->size) == -1) {
ngx_log_error(NGX_LOG_ALERT,shm->log, ngx_errno,
"munmap(%p, %uz) failed", shm->addr, shm->size);
}
}
分配共享内存:
ngx_int_t
ngx_shm_alloc(ngx_shm_t *shm)
{
shm->addr =(u_char *) mmap(NULL, shm->size,
PROT_READ|PROT_WRITE,
MAP_ANON|MAP_SHARED, -1, 0);
if(shm->addr == MAP_FAILED) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"mmap(MAP_ANON|MAP_SHARED, %uz) failed", shm->size);
returnNGX_ERROR;
}
return NGX_OK;
}
ngx_shm_zone_t * ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void*tag)
{
ngx_uint_t i;
ngx_shm_zone_t *shm_zone;
ngx_list_part_t *part;
part =&cf->cycle->shared_memory.part;
shm_zone = part->elts;
//先查找所有已经存在的共享内存,看看要创建的共享内存是否存在于这里面,如果存在的话就直接返回,否则
//创建一个新的共享内存结构体再返回
for (i = 0; /* void */ ; i++) {
if(i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
shm_zone = part->elts;
i = 0;
}
if(name->len != shm_zone[i].shm.name.len) {
continue;
}
if(ngx_strncmp(name->data, shm_zone[i].shm.name.data, name->len)
!= 0)
{
continue;
}
if(size && size != shm_zone[i].shm.size) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the size %uz of shared memory zone\"%V\" "
"conflictswith already declared size %uz",
size,&shm_zone[i].shm.name, shm_zone[i].shm.size);
return NULL;
}
if(tag != shm_zone[i].tag) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the sharedmemory zone \"%V\" is "
"alreadydeclared for a different use",
&shm_zone[i].shm.name);
return NULL;
}
//此共享内存已经存在,直接返回
return &shm_zone[i];
}
//插入一个新的共享内存结构体结点
shm_zone = ngx_list_push(&cf->cycle->shared_memory);
if(shm_zone == NULL) {
return NULL;
}
shm_zone->data = NULL;
shm_zone->shm.log = cf->cycle->log;
shm_zone->shm.size = size;
shm_zone->shm.name = *name;
shm_zone->shm.exists = 0;
shm_zone->init = NULL;
shm_zone->tag = tag;
returnshm_zone;
}
2.3. 自旋锁数据结构及实现
ngx_shmtx_t数据结构:
typedef struct {
#if (NGX_HAVE_ATOMIC_OPS)
ngx_atomic_t *lock; //如果支持原子锁的话,那么使用它
#if (NGX_HAVE_POSIX_SEM)
ngx_atomic_t *wait;
ngx_uint_t semaphore;
sem_t sem;
#endif
#else
ngx_fd_t fd; //不支持原子操作的话就使用文件锁来实现
u_char *name;
#endif
ngx_uint_t spin; //自旋锁
} ngx_shmtx_t;
创建锁:
ngx_int_t
ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t*addr, u_char *name)
{
mtx->lock = &addr->lock; //其实就是直接将内存地址赋个mtx的lock域就完事了
if(mtx->spin == (ngx_uint_t) -1) { //如果等于-1是自旋锁
return NGX_OK;
}
mtx->spin = 2048;
returnNGX_OK;
}
尝试加锁(加锁失败则直接返回,不等待):
ngx_shmtx_trylock(ngx_shmtx_t *mtx){
ngx_err_t err;
err =ngx_trylock_fd(mtx->fd);
if (err== 0) {
return 1;
}
}
ngx_err_t
ngx_trylock_fd(ngx_fd_t fd)
{
structflock fl;
ngx_memzero(&fl, sizeof(struct flock));
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
if(fcntl(fd, F_SETLK, &fl) == -1) {
return ngx_errno;
}
return0;
}
解锁:
void
ngx_shmtx_unlock(ngx_shmtx_t *mtx)
{
ngx_err_t err;
err =ngx_unlock_fd(mtx->fd);
if (err== 0) {
return;
}
ngx_log_abort(err, ngx_unlock_fd_n " %s failed",mtx->name);
}
ngx_err_t
ngx_unlock_fd(ngx_fd_t fd)
{
structflock fl;
ngx_memzero(&fl, sizeof(struct flock));
fl.l_type = F_UNLCK;
fl.l_whence = SEEK_SET;
if(fcntl(fd, F_SETLK, &fl) == -1) {
return ngx_errno;
}
return0;
}
spinlock的实现原理是?
a. 用户态尝试竞争一个共享资源. 如果竞争不到, 则不断尝试竞争. 但是不借助内核提供的mutex等变量机制. 因为涉及到内核,就意味这效率低下.
b. 要想在用户态实现竞争一个共享资源, 必须借助cpu提供的原子操作指令. 如果是SMP多cpu,还需要lock指令锁总线.
c. 为了避免在长时间竞争却一直得不到资源导致的不断尝试浪费cpu, 在每两次尝试之间间隔一段时间. 并且随着尝试次数