Nginx高级数据结构之共享内存ngx_shm_t

本文介绍了Linux下的共享内存原理,重点剖析了Nginx如何使用mmap、以/dev/zero映射和shmget进行共享内存的创建、映射与释放。展示了Nginx在进程间通信中的应用和跨平台支持。
摘要由CSDN通过智能技术生成

简介

共享内存是Linux下提供的最基本的进程间通信方法,它通过mmap或者shmget系统调用在内存中创建了一块连续的线性地址空间,而通过munmap或者shmdt系统调用可以释放这块内存。使用共享内存的好处是当多个进程使用同一块共享内存时,在任何一个进程修改了共享内存中的内容后,其他进程通过访问这段共享内存都能够得到修改后的内容。

注意:虽然mmap可以以磁盘文件的方式映射共享内存,但在Nginx封装的共享内存操作方法中是没有使用到映射文件功能的。

核心结构体定义

typedef struct {
    u_char      *addr; //共享内存起始地址  
    size_t       size; //共享内存空间大小
    ngx_str_t    name; //这块共享内存的名称
    ngx_log_t   *log;  //记录日志的ngx_log_t对象
    ngx_uint_t   exists;//表示共享内存是否已经分配过的标志位,为1时表示已经存在
} ngx_shm_t;

实现剖析

Nginx各进程间共享数据的主要方式就是使用共享内存(在使用共享内存时,Nginx一般是由master进程创建,在master进程fork出worker子进程后,所有的进程开始使用这块内存中的数据)。在开发Nginx模块时如果需要使用它,不妨用Nginx已经封装好的ngx_shm—alloc方法和ngx_shm_free方法,它们有3种实现(不映射文件使用mmap分配共享内存、以/dev/zero文件使用mmap映射共享内存、用shmget调用来分配共享内存),对于Nginx的跨平台特性考虑得很周到。

操作ngx_shm_t结构体的方法有以下两个:ngx_shm_alloc用于分配新的共享内存,而ngx_shm_free用于释放已经存在的共享内存。

ngx_int_t ngx_shm_alloc(ngx_shm_t *shm);
void ngx_shm_free(ngx_shm_t *shm);

一,不映射文件使用mmap分配共享内存

这是最简单实现方式,使用mmapMAP_ANON标志直接进行匿名映射,munmap进行释放

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);
        return NGX_ERROR;
    }

    return NGX_OK;
}


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);
    }
}

二,以/dev/zero文件使用mmap映射共享内存

与第一种实现方式的区别在于,使用dev/zero文件进行映射,可以获得一块干净的,没有脏数据的内存.

/dev/zero是一个特殊文件,会提供源源不断的`0`数据

ngx_int_t
ngx_shm_alloc(ngx_shm_t *shm)
{
    ngx_fd_t  fd;
  
    fd = open("/dev/zero", O_RDWR);

    if (fd == -1) {
        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
                      "open(\"/dev/zero\") failed");
        return NGX_ERROR;
    }

    shm->addr = (u_char *) mmap(NULL, shm->size, PROT_READ|PROT_WRITE,
                                MAP_SHARED, fd, 0);

    if (shm->addr == MAP_FAILED) {
        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
                      "mmap(/dev/zero, MAP_SHARED, %uz) failed", shm->size);
    }

    if (close(fd) == -1) {
        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
                      "close(\"/dev/zero\") failed");
    }

    return (shm->addr == MAP_FAILED) ? NGX_ERROR : NGX_OK;
}


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);
    }
}

三,用shmget调用来分配共享内存

mmapposix标准,nginx还提供了system Vshmget的共享内存

Linux进程间的通信万字总结大全:对于system Vshmget共享内存相关api以及操作不了解的可以看看这篇文章

ngx_int_t
ngx_shm_alloc(ngx_shm_t *shm)
{
    int  id;
 
    id = shmget(IPC_PRIVATE, shm->size, (SHM_R|SHM_W|IPC_CREAT));

    if (id == -1) {
        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
                      "shmget(%uz) failed", shm->size);
        return NGX_ERROR;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, shm->log, 0, "shmget id: %d", id);

    shm->addr = shmat(id, NULL, 0);

    if (shm->addr == (void *) -1) {
        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, "shmat() failed");
    }

    if (shmctl(id, IPC_RMID, NULL) == -1) {
        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
                      "shmctl(IPC_RMID) failed");
    }

    return (shm->addr == (void *) -1) ? NGX_ERROR : NGX_OK;
}


void
ngx_shm_free(ngx_shm_t *shm)
{
    if (shmdt(shm->addr) == -1) {
        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
                      "shmdt(%p) failed", shm->addr);
    }
}

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@马云

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值