nginx 的进程建通信机制-共享内存/channel/信号

nginx是跨平台的库,但以下分享均为基于linux的

nginx 进程间通信的方法:

1、共享内存

linux 提供了进程间通信的的共享内存的方式,通过mmap和shmget来获取一块连续的内存,然后通过munmapheshmdt来释放这块内存,多个进程均可访问这块共享内存

nginx定义的结构体


typedef struct {
    u_char      *addr;
    size_t       size;
    ngx_str_t    name;
    ngx_log_t   *log;
    ngx_uint_t   exists;   /* unsigned  exists:1;  */
} ngx_shm_t;

// 用于分配内存
ngx_int_t ngx_shm_alloc(ngx_shm_t *shm);
// 用于释放内存
void ngx_shm_free(ngx_shm_t *shm);

nginx 共享内存接口

没有使用linux可以直接映射到文件的方式,所以mmap的最后两个参数没有送。

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

nginx 释放共享内存

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

使用方式:

master进程创建,fork子进程来使用。

2、nginx channel频道

学习过go语言的应该知道,协程间通讯使用channel,而nginx封装了一个进程间通讯的机制,ngx_channel_t,来实现父子进程间的通讯。

nginx channel的数据结构

typedef struct {
    // 传递的TCP消息中的命令
    ngx_uint_t  command;
    // 进程ID
    ngx_pid_t   pid;
    // 表示发送命令方在ngx_processes进程数组间的序号
    ngx_int_t   slot;
    // 通讯的套接字
    ngx_fd_t    fd;
} ngx_channel_t;
// 实现channel的读,写,添加,关闭。
ngx_int_t ngx_write_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size,
    ngx_log_t *log);
ngx_int_t ngx_read_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size,
    ngx_log_t *log);
ngx_int_t ngx_add_channel_event(ngx_cycle_t *cycle, ngx_fd_t fd,
    ngx_int_t event, ngx_event_handler_pt handler);
void ngx_close_channel(ngx_fd_t *fd, ngx_log_t *log);

另外,需要涉及到的另一个接口

typedef struct {
    .....
    ngx_socket_t        channel[2];
    ngx_spawn_proc_pt   proc;
    .....
} ngx_process_t;

nginx channel 实现方法

nginx就是通过channel来监控管理worker的子进程,每次创建子进程时,就通过socketpair创建了套接字对,并存储到channel的数组中。

ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
    char *name, ngx_int_t respawn)
{
    ...
        if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1)
        {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "socketpair() failed while spawning \"%s\"", name);
            return NGX_INVALID_PID;
        }
    ...
}

其中 ngx_processes时所有的进程的数组,包括master进程。

其中master进程使用channel[0],子进程使用channel[1],channel分别对应socketpair的两个套接字。

nginx进程间时如果调度来进行ngx_read_channel的呢?

就是通过nginx 添加事件函数ngx_add_channel_event,然后epoll发现有事件时,实现ngx_channel_handler回调并读取。从而实现进程间通信。

3、信号 

信号的数据结构 ngx_signal_t

typedef struct{

    //需要处理的信号
    int signo;
    //信号对应的字符串名称
    char*signame;
    //这个信号对应着的Nginx命令
    char*name;
    //收到signo信号后就会回调handler方法
    void(*handler) (int signo) ;
}ngx_signal_t;

需要处理的信号定义在signals中

ngx_signal_t  signals[] = {
    { ngx_signal_value(NGX_RECONFIGURE_SIGNAL),
      "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),
      "reload",
      ngx_signal_handler },
    ......
}

信号的初始化:

其实现以下已经注释

// 初始化所有信号
ngx_int_t ngx_init_signals(ngx_log_t *log)
{
    ngx_signal_t      *sig;
    struct sigaction   sa;
    // 遍历所有信号数组
    for (sig = signals; sig->signo != 0; sig++) {
        ngx_memzero(&sa, sizeof(struct sigaction));
        // 添加回调函数
        if (sig->handler) {
            sa.sa_sigaction = sig->handler;
            sa.sa_flags = SA_SIGINFO;

        } else {
            sa.sa_handler = SIG_IGN;
        }
        // 清空信号标志位
        sigemptyset(&sa.sa_mask);
        // 向系统注册信号的回调。
        if (sigaction(sig->signo, &sa, NULL) == -1) {
            return NGX_ERROR;
        }
    }

    return NGX_OK;
}

你可能感兴趣的:(高并发服务,nginx,linux)