nginx源码学习(五)进程间的通信
来源:互联网 发布:中国金融战略2020知乎 编辑:程序博客网 时间:2024/04/29 11:17
nginx启动worker进程的函数主体还是比较简单的
static voidngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type){ ngx_int_t i; ngx_channel_t ch; ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes"); ngx_memzero(&ch, sizeof(ngx_channel_t)); ch.command = NGX_CMD_OPEN_CHANNEL; for (i = 0; i < n; i++) { //启动进程 ngx_spawn_process(cycle, ngx_worker_process_cycle, (void *) (intptr_t) i, "worker process", type); ch.pid = ngx_processes[ngx_process_slot].pid; ch.slot = ngx_process_slot; ch.fd = ngx_processes[ngx_process_slot].channel[0]; ngx_pass_open_channel(cycle, &ch); }}
for循环启动配置的worker进程个数,下面主要分析两个函数:
ngx_spawn_process和ngx_pass_open_channel;
ngx_pid_tngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data, char *name, ngx_int_t respawn){ u_long on; ngx_pid_t pid; ngx_int_t s; if (respawn >= 0) { //进程启动时respawn = -3,不走这里 s = respawn; } else { //找到一个空的槽记录子进程信息 for (s = 0; s < ngx_last_process; s++) { if (ngx_processes[s].pid == -1) { break; } } //超过最大进程数会报错 unix:1024 if (s == NGX_MAX_PROCESSES) { ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "no more than %d processes can be spawned", NGX_MAX_PROCESSES); return NGX_INVALID_PID; } } if (respawn != NGX_PROCESS_DETACHED) { //这里主要是创建本地父子进程通信的pair,因为在Solaris 9不支持AF_LOCAL ,所以用了兼容的AF_UNIX,其实也是本地通信,ngx_processes[s]是一个结构体,存储进程信息,channel[2]属性主要存储socket对象,其中channel[0]为父进程的socket,channel[1]为子进程的socket。 /* Solaris 9 still has no AF_LOCAL 在该函数进行fork()之前,先调用了socketpair()创建一对socket描述符,存放在变量ngx_processes[s].chanel内,(其中s标志在ngx_processes数组内第一个可用元素的下标,比如最开始产生第一个工作进程时,可用元素的下标s为0),而在fork() 之后,由于子进程继承了父进程的资源,那么父子进程都拥有一对socket描述符,而nginx将channel[0]给父进程使用, channel[1]给子进程使用,这样分别错开使用不同的描述符,即可实现父子进程之间的双向通信. 除此之外,对于各个子进程也能进行双向通信.父子进程的通信channel设定是自然而然的事情,而子进程之间的通信channel设定就涉及到进程之间文件描述符(socket描述符也属于文件描述符)的传递,因为虽然后生成的子进程通过继承的channel[0]能够往前生成的子进程发送信息,但前生成的子进程无法获知后生成子进程的channel[0]而不能发送信息,所以后生成的子进程必须利用已知的前生成子进程的channel[0]进行主动告知。 在子进程的初始话函数ngx_worker_process_init()里,会把ngx_channel(channel[1])加入到读事件监听集里,对应回调处理函数ngx_channel_handler(); */ 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_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, "channel %d:%d", ngx_processes[s].channel[0], ngx_processes[s].channel[1]); //下面是设置非阻塞和异步模式 if (ngx_nonblocking(ngx_processes[s].channel[0]) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, ngx_nonblocking_n " failed while spawning \"%s\"", name); ngx_close_channel(ngx_processes[s].channel, cycle->log); return NGX_INVALID_PID; } if (ngx_nonblocking(ngx_processes[s].channel[1]) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, ngx_nonblocking_n " failed while spawning \"%s\"", name); ngx_close_channel(ngx_processes[s].channel, cycle->log); return NGX_INVALID_PID; } on = 1; if (ioctl(ngx_processes[s].channel[0], FIOASYNC, &on) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "ioctl(FIOASYNC) failed while spawning \"%s\"", name); ngx_close_channel(ngx_processes[s].channel, cycle->log); return NGX_INVALID_PID; } if (fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "fcntl(F_SETOWN) failed while spawning \"%s\"", name); ngx_close_channel(ngx_processes[s].channel, cycle->log); return NGX_INVALID_PID; } if (fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "fcntl(FD_CLOEXEC) failed while spawning \"%s\"", name); ngx_close_channel(ngx_processes[s].channel, cycle->log); return NGX_INVALID_PID; } if (fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "fcntl(FD_CLOEXEC) failed while spawning \"%s\"", name); ngx_close_channel(ngx_processes[s].channel, cycle->log); return NGX_INVALID_PID; } ngx_channel = ngx_processes[s].channel[1]; } else { ngx_processes[s].channel[0] = -1; ngx_processes[s].channel[1] = -1; } ngx_process_slot = s; //创建子进程 pid = fork(); switch (pid) { case -1: //创建失败 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "fork() failed while spawning \"%s\"", name); ngx_close_channel(ngx_processes[s].channel, cycle->log); return NGX_INVALID_PID; case 0: //子进程进入proc循环工作 ngx_pid = ngx_getpid(); proc(cycle, data); break; default: break; } ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start %s %P", name, pid); ngx_processes[s].pid = pid; ngx_processes[s].exited = 0; if (respawn >= 0) { return pid; } //主进程记录信息后return 子进程pid ngx_processes[s].proc = proc; ngx_processes[s].data = data; ngx_processes[s].name = name; ngx_processes[s].exiting = 0; switch (respawn) { case NGX_PROCESS_NORESPAWN: ngx_processes[s].respawn = 0; ngx_processes[s].just_spawn = 0; ngx_processes[s].detached = 0; break; case NGX_PROCESS_JUST_SPAWN: ngx_processes[s].respawn = 0; ngx_processes[s].just_spawn = 1; ngx_processes[s].detached = 0; break; case NGX_PROCESS_RESPAWN: ngx_processes[s].respawn = 1; ngx_processes[s].just_spawn = 0; ngx_processes[s].detached = 0; break; case NGX_PROCESS_JUST_RESPAWN: ngx_processes[s].respawn = 1; ngx_processes[s].just_spawn = 1; ngx_processes[s].detached = 0; break; case NGX_PROCESS_DETACHED: ngx_processes[s].respawn = 0; ngx_processes[s].just_spawn = 0; ngx_processes[s].detached = 1; break; } if (s == ngx_last_process) { ngx_last_process++; } return pid;}
接下来看看:ngx_pass_open_channel,主进程如何告知之前创建的worker进程与新创建的worker进程通信
ngx_int_tngx_write_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size,ngx_log_t *log){ ssize_t n; ngx_err_t err; struct iovec iov[1]; struct msghdr msg;#if (NGX_HAVE_MSGHDR_MSG_CONTROL) union { struct cmsghdr cm; char space[CMSG_SPACE(sizeof(int))]; } cmsg; if (ch->fd == -1) { msg.msg_control = NULL; msg.msg_controllen = 0; } else { msg.msg_control = (caddr_t) &cmsg; msg.msg_controllen = sizeof(cmsg); ngx_memzero(&cmsg, sizeof(cmsg)); cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int)); cmsg.cm.cmsg_level = SOL_SOCKET; cmsg.cm.cmsg_type = SCM_RIGHTS; /* * We have to use ngx_memcpy() instead of simple * *(int *) CMSG_DATA(&cmsg.cm) = ch->fd; * because some gcc 4.4 with -O2/3/s optimization issues the warning: * dereferencing type-punned pointer will break strict-aliasing rules * * Fortunately, gcc with -O1 compiles this ngx_memcpy() * in the same simple assignment as in the code above */ ngx_memcpy(CMSG_DATA(&cmsg.cm), &ch->fd, sizeof(int)); } msg.msg_flags = 0;#else if (ch->fd == -1) { msg.msg_accrights = NULL; msg.msg_accrightslen = 0; } else { msg.msg_accrights = (caddr_t) &ch->fd; msg.msg_accrightslen = sizeof(int); }#endif iov[0].iov_base = (char *) ch; iov[0].iov_len = size; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = 1; //关于匿名套接字传递fd可查看 //http://blog.csdn.net/shujuliu818/article/details/52922284 n = sendmsg(s, &msg, 0); if (n == -1) { err = ngx_errno; if (err == NGX_EAGAIN) { return NGX_AGAIN; } ngx_log_error(NGX_LOG_ALERT, log, err, "sendmsg() failed"); return NGX_ERROR; } return NGX_OK;}
最后,就目前nginx代码来看,子进程并没有往父进程发送任何消息,子进程之间也没有相互通信的逻辑,也许是因为nginx有其它一些更好的进程通信方式,比如共享内存等,所以这种channel通信目前仅做为父进程往子进程发送消息使用,但由于有这个基础在这,如果未来要使用channel做这样的事情,的确是可以的。
阅读全文
0 0
- nginx源码学习(五)进程间的通信
- nginx源码分析1———进程间的通信机制五(文件锁)
- Nginx源码阅读(进程间通信)
- nginx源码分析--nginx进程间通信
- nginx源码分析--nginx进程间通信
- nginx源码学习——进程间通信机制
- Nginx学习——Nginx进程间的通信
- Nginx学习——Nginx进程间的通信
- Nginx源码分析-master和worker进程间的通信
- Nginx源码分析--master和worker进程间的通信
- Nginx源码分析-master和worker进程间的通信
- nginx源码学习(五)
- nginx源码分析1———进程间的通信机制一(信号量)
- nginx源码分析1———进程间的通信机制二(自旋锁)
- nginx源码分析1———进程间的通信机制三(mmap)
- nginx进程间的通信机制源码分析(一)----共享内存
- nginx进程间的通信机制源码分析(二)-------原子操作、自旋锁、文件锁
- Nginx源码分析 ——Nginx的进程通信方式
- iOS OpenCV 图像灰度处理
- Android集成腾讯互动直播如何切换流视频
- aliyun阿里云Maven仓库地址——加速你的maven构建
- JSP基础 Cookie Session
- Python:从subprocess运行的子进程中实时获取输出
- nginx源码学习(五)进程间的通信
- 理解python里的工厂模式方法(好文)
- kafka参数
- LeetCode@Bit_461_Hamming_Distance
- 《西方艺术史》——1
- Gradle版本统一配置
- 计算机基础技能
- android集成多个5+webview
- 微信 扫码支付模式二 开发