/*
* Copyright (C) Igor Sysoev
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_channel.h>
typedef struct
{
int signo;
char *signame;
char *name;
void (*handler)(int signo);
} ngx_signal_t;
static void ngx_execute_proc(ngx_cycle_t *cycle, void *data);
static void ngx_signal_handler(int signo);
static void ngx_process_get_status(void);
int ngx_argc;
char **ngx_argv;
char **ngx_os_argv;
ngx_int_t ngx_process_slot;
ngx_socket_t ngx_channel;
ngx_int_t ngx_last_process;
ngx_process_t ngx_processes[NGX_MAX_PROCESSES];
// 设定了信号和对应的信号处理函数
//#define ngx_value_helper(n) #n
//#define ngx_value(n) ngx_value_helper(n)
ngx_signal_t signals[] =
{
{ ngx_signal_value(NGX_RECONFIGURE_SIGNAL),
"SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),
"reload",
ngx_signal_handler },
{ ngx_signal_value(NGX_REOPEN_SIGNAL),
"SIG" ngx_value(NGX_REOPEN_SIGNAL),
"reopen",
ngx_signal_handler },
{ ngx_signal_value(NGX_NOACCEPT_SIGNAL),
"SIG" ngx_value(NGX_NOACCEPT_SIGNAL),
"",
ngx_signal_handler },
{ ngx_signal_value(NGX_TERMINATE_SIGNAL),
"SIG" ngx_value(NGX_TERMINATE_SIGNAL),
"stop",
ngx_signal_handler },
{ ngx_signal_value(NGX_SHUTDOWN_SIGNAL),
"SIG" ngx_value(NGX_SHUTDOWN_SIGNAL),
"quit",
ngx_signal_handler },
{ ngx_signal_value(NGX_CHANGEBIN_SIGNAL),
"SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),
"",
ngx_signal_handler },
{ SIGALRM, "SIGALRM", "", ngx_signal_handler },
{ SIGINT, "SIGINT", "", ngx_signal_handler },
{ SIGIO, "SIGIO", "", ngx_signal_handler },
{ SIGCHLD, "SIGCHLD", "", ngx_signal_handler },
{ SIGSYS, "SIGSYS, SIG_IGN", "", SIG_IGN },
{ SIGPIPE, "SIGPIPE, SIG_IGN", "", SIG_IGN },
{ 0, NULL, "", NULL }
};
ngx_pid_t
ngx_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 )
{
s = respawn;
}
else
{
for ( s = 0; s < ngx_last_process; s++ )
{
if ( ngx_processes[s].pid == -1 )
{
break;
}
}
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;
}
}
// detached 的子进程,不需要建立管道
if ( respawn != NGX_PROCESS_DETACHED )
{
/* Solaris 9 still has no AF_LOCAL */
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;
/*
FIOASYNC Enables a simple form of asynchronous I/O notification.
This command causes the kernel to send SIGIO signal to a process or
a process group when I/O is possible. Only sockets, ttys, and pseudo-ttys implement this functionality.
FIONBIO Enables nonblocking I/O. The effect is similar to setting the O_NONBLOCK
flag with the fcntl subroutine. The third parameter to the ioctl subroutine for this
command is a pointer to an integer that indicates whether nonblocking I/O is
being enabled or disabled. A value of 0 disables non-blocking I/O.
(src)
ioctl和FIOASYNC等价于fcntl和O_ASYNC。
ioctl和FIONBIO等价于fcntl和O_NONBLOCK。
这两个是等价的:
fcntl(socket, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
nb = 1;
ioctl(s, FIONBIO, &nb);
FIOASYNC设置O_ASYNC标记,该标记决定fd可以IO时进程是否会收到SIGIO和SIGPOLL信号。
FIONBIO设置O_NONBLOCK标记,该标记会改变read,write和同类函数的行为,使得在fd还不能IO时立即返回而不是hang住。
后者经常跟select,poll等函数一起使用,使得主程序不会因为个别socket而影响其他。
关于何时需要用FIOASYNC
Although the combination of blocking and nonblocking operations and the select method are sufficient for querying the device most of the time, some situations aren't efficiently managed by the techniques we've seen so far.
Let's imagine a process that executes a long computational loop at low priority but needs to process incoming data as soon as possible. If this process is responding to new observations available from some sort of data acquisition peripheral, it would like to know immediately when new data is available. This application could be written to call poll regularly to check for data, but, for many situations, there is a better way. By enabling asynchronous notification, this application can receive a signal whenever data becomes available and need not concern itself with polling.
User programs have to execute two steps to enable asynchronous notification from an input file. First, they specify a process as the "owner" of the file. When a process invokes the F_SETOWN command using the fcntl system call, the process ID of the owner process is saved in filp->f_owner for later use. This step is necessary for the kernel to know just whom to notify. In order to actually enable asynchronous notification, the user programs must set the FASYNC flag in the device by means of the F_SETFL fcntl command.
After these two calls have been executed, the input file can request delivery of a SIGIO signal whenever new data arrives. The signal is sent to the process (or process group, if the value is negative) stored in filp->f_owner.
For example, the following lines of code in a user program enable asynchronous notification to the current process for the stdin input file:
signal(SIGIO, &input_handler); /* dummy sample; sigaction( ) is better
fcntl(STDIN_FILENO, F_SETOWN, getpid( ));
oflags = fcntl(STDIN_FILENO, F_GETFL);
fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC);
*/
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;
}
/*
An fcntl(2) F_SETOWN operation can be used to specify a process or process group to receive
a SIGURG signal when the out-of-band data arrives or SIGPIPE signal when a SOCK_STREAM
connection breaks unexpectedly. This operation may also be used to set the process or
process group that receives the I/O and asynchronous notification of I/O events via SIGIO.
Using F_SETOWN is equivalent to an ioctl(2) call with the FIOSETOWN or SIOCSPGRP argument.
//fcntl(2)的F_SETOWN 操作可以用来指定进程或者进程组来接受 SIGURG 信号,
当接收到过界数据或者接收到当SOCK_STREAM连接异常中断是的SIGPIPE的时候.
*/
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;
}
/*
FD_CLOEXEC用来设置文件的close-on-exec状态标准。在exec()调用后,
close-on-exec标志为0的情况,此文件不被关闭。非零则在exec()后被关闭。
默认close-on-exec状态为0,需要通过FD_CLOEXEC设置。
*/
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(); // 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:
ngx_pid = ngx_getpid(); //子进程,调用execve函数 替换成其他进程
proc( cycle, data );
break;
default:
break;
}
//父进程,设置该选项的pid
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;
}
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;
}