nginx是跨平台的库,但以下分享均为基于linux的
nginx 进程间通信的方法:
linux 提供了进程间通信的的共享内存的方式,通过mmap和shmget来获取一块连续的内存,然后通过munmapheshmdt来释放这块内存,多个进程均可访问这块共享内存
-
- 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);
没有使用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子进程来使用。
学习过go语言的应该知道,协程间通讯使用channel,而nginx封装了一个进程间通讯的机制,ngx_channel_t,来实现父子进程间的通讯。
- 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来监控管理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回调并读取。从而实现进程间通信。
- 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;
- }