• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

nginx 进程间通信-socketpair

php 搞代码 3年前 (2022-01-23) 23次浏览 已收录 0个评论

在nginx中,master进程与worker进程之间使用了一种全双工通信方式–socketpair。socketpair 函数成功执行后会创建一对已经建立连接的socket对,两个相互通信的进程分别使用其中一个socket进行读写操作,就能够实现两进程间的通信。

查看nginx源码,可以看到,下面的函数创建了socketpair

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 != NGX_PROCESS_DETACHED) {        /* Solaris 9 still has no AF_LOCAL */        //创建socketpair        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;        }        //非阻塞        if (ngx_nonblocking(ngx_processes[s].channel[0]) == -1) {            ngx_close_channel(ngx_processes[s].channel, cycle->log);            return NGX_INVALID_PID;        }        //非阻塞        if (ngx_nonblocking(ngx_processes[s].channel[1]) == -1) {            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_close_channel(ngx_processes[s].channel, cycle->log);            return NGX_INVALID_PID;        }        //设置将要在文件描述词fd上接收SIGIO 或 SIGURG事件信号的进程或进程组标识 。        if (fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid) == -1) {            ngx_close_channel(ngx_processes[s].channel, cycle->log);            return NGX_INVALID_PID;        }        //设置close_on_exec,当通过exec函数族创建了新进程后,原进程的该socket会被关闭        if (fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1) {            ngx_close_channel(ngx_processes[s].channel, cycle->log);            return NGX_INVALID_PID;        }        //设置close_on_exec,当通过exec函数族创建了新进程后,原进程的该socket会被关闭        if (fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1) {            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_close_channel(ngx_processes[s].channel, cycle->log);        return NGX_INVALID_PID;    case 0:        //fork成功,子进程创建,同时相关socket描述符也会被复制一份        ngx_pid = ngx_getpid();        proc(cycle, data);        break;    default:        break;    }    /. ......省略...... ./    return pid;}

fork成功后,原进程的descriptor也会被复制一份,如果在fork的进程中该描述符不再使用,需要我们及时关闭。

如果我们使用的是 fork->exec函数族 的形式创建新进程的话,我们可以采用更好的办法来确保原有的descriptor被正常关闭,避免资源的泄漏。也就是上边代码中对socket调用fcntl(FD_CLOEXEC)函数,设置该socket的属性:当exec函数族被调用后,该socket会被自动关闭。使用这种在socket创建后立即设置FD_CLOEXEC属性的办法,避免了我们在exec创建进程前手动6本文来源gao@dai!ma.com搞$代^码!网7

搞gaodaima代码

关闭相关socket的操作,尤其是当有大量的descriptor被创建、管理的时候非常实用。

很多时候我们是使用下边的方法进行操作的:

#include #include #include #include int main(){    pid_t  pid;    int    fds[2];    int    valRead, valWrite;    if (0 > socketpair(AF_UNIX, SOCK_STREAM, 0, fds))    {        return 0;    }    pid = fork();    if (0 == pid)    {        pid = getpid();        printf("[%d]-child process start", pid);        close(fds[0]);                //read write on fds[1]        write(fds[1], &valWrite, sizeof(valWrite));         read(fds[1], &valRead, sizeof(valRead));    }    else if (0 < pid)    {        pid = getpid();        printf("[%d]-parent process continue", pid);        close(fds[1]);        //read write on fds[0]        read(fds[0], &valRead, sizeof(valRead));        write(fds[0], &valWrite, sizeof(valWrite));     }    else    {        printf("%s", "fork failed");    }    return 0;}

可以看到,fork前,当前进程创建了一对socket,也就是socketpair。对于这对socket,可以看作一个是服务器端fds[0],另一个是客户端fds[1],通过fds[0]与fds[1]之间建立的链接,我们可以完成全双工通信。

fork执行后,创建了子进程。在子进程中,之前父进程创建的socketpair自然也会被复制一份为fds’,存在于子进程中。父进程继续执行。这个时候,在父进程和子进程中会存在相同socketpair。

试想,我们在主进程中向fds[0]中写入数据,在子进程中的fds'[1]上就会读取到该数据,这样就实现了父进程与子进程间的通信。当然,在主进程fds[1]上写数据,在子进程fds'[0]上也会读到写入的数据。我们实际使用中,只需要保留一对socket用来通信就可以了,另外两个socket就可以分别在父进程和子进程中关闭不用了。

当然了,如果我们能够把fds中的一个socket通过某种方式传递给另一个进程,那么也可以实现socketpair进程间通信了。

以上就介绍了nginx 进程间通信-socketpair,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:nginx 进程间通信-socketpair

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址