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