linux下进程间通信的机制

今天突然想起了nginx解决惊群的方法,就是在多个进程间利用锁来保证同一时刻只能有一个worker进程在自己的epoll中加入监听的句柄,那么进程间是怎么共享变量的呢,下面就介绍一下共享内存

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

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

Nginx 定义了 ngx_shm_t 结构体,用于描述一块共享内存:

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

操作 ngx_shm_t 结构体的方法有以下两个:

  • ngx_shm_alloc:用于分配新的共享内存;
  • ngx_shm_free:用于释放已经存在的共享内存。

mmap 系统调用简述

void *mmap(void *start, size_t length, int prot, int flags, 
                int fd, off_t offset);

mmap 可以将磁盘文件映射到内存中,直接操作内存时 Linux 内核将负责同步内存和磁盘文件中的数据:

  • fd 参数就指向需要同步的磁盘文件
  • offset 则代表从文件的这个偏移量开始共享。
  • 当 flags 参数中加入 MAP_ANON 或者 MAP_ANONYMOUS 参数时表示不使用文件映射方式,这时 fd 和 offset 参数就没有意义了,也不需要传递,此时的 mmap 方法和 ngx_shm_alloc 的功能几乎完全相同。
  • length 参数就是将要在内存中开辟的线性地址空间大小。
  • prot 参数则是操作这段共享内存的方式(如只读或可读可写)。
  • start 参数说明希望的共享内存起始映射地址,通常设为 NULL,即由内存选择映射的起始地址。

MAP_ANON 是 MAP_ANONYMOUS 的同义词,已过时。表示不使用文件映射方式,并且共享内存被初始化为0,因此忽略 mmap 中的 fd 和 offset 参数,但是为了可移植性,当 MAP_ANONYMOUS(或 MAP_ANON)被指定时,fd 应该设置为 -1。

如下为使用 mmap 实现的 ngx_shm_alloc 方法:

ngx_int_t 
ngx_shm_alloc(ngx_shm_t *shm)
{
    /* 开辟一块 shm->size 大小且可读/写的共享内存,内存首地址存放在 shm->addr 中 */
    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;
}

当不在使用共享内存时,需要调用 munmap 或者 shmdt 来释放共享内存:

int munmap(void *start, size_t length);
  • start:指向共享内存的首地址
  • length:表示这段共享内存的长度

Nginx 的 ngx_shm_free 方法封装了该 munmap 方法:

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

Nginx 各进程间共享数据的主要方式就是使用共享内存(在使用共享内存时,Nginx 一般是由 master 进程创建,在 master 进程 fork 出 worker 子进程后,所有的进程开始使用这块内存中的数据)。

Nginx 的共享内存有三种实现:

  • 不映射文件使用 mmap 分配共享内存(即上面的代码)
  • 以 /dev/zero 文件使用 mmap 映射共享内存
  • 用 shmget 调用来分配共享内存
原文地址:https://www.cnblogs.com/wangshaowei/p/10862346.html