php-src/sapi/fpm/fpm/fpm.c关于fpm初始化关键代码
0 > fpm_php_init_main() ||
0 > fpm_stdio_init_main() ||
0 > fpm_conf_init_main(test_conf, force_daemon) ||
0 > fpm_unix_init_main() ||
0 > fpm_scoreboard_init_main() ||
0 > fpm_pctl_init_main() ||
0 > fpm_env_init_main() ||
0 > fpm_signals_init_main() ||
0 > fpm_children_init_main() ||
0 > fpm_sockets_init_main() ||
0 > fpm_worker_pool_init_main() ||
0 > fpm_event_init_main()
fpm_unix_init_main代码重点:
fpm_scoreboard_init_main代码重点:
fpm_worker_pool_init_main重点:
/* children: return listening socket
parent: never return */
int fpm_run(int *max_requests) /* {{{ */
{
struct fpm_worker_pool_s *wp;
/* create initial children in all pools */
for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
int is_parent;
is_parent = fpm_children_create_initial(wp);//TO-DO doing
if (!is_parent) {
goto run_child;
}
/* handle error */
if (is_parent == 2) {
fpm_pctl(FPM_PCTL_STATE_TERMINATING, FPM_PCTL_ACTION_SET);
fpm_event_loop(1);
}
}
/* run event loop forever */
fpm_event_loop(0);
run_child: /* only workers reach this point */
fpm_cleanups_run(FPM_CLEANUP_CHILD);
*max_requests = fpm_globals.max_requests;
return fpm_globals.listening_socket;
}
/* }}} */
php-src/sapi/fpm/fpm/fpm.c:fpm_run->
php-src/sapi/fpm/fpm/fpm_children.c:fpm_children_create_initial
int fpm_children_create_initial(struct fpm_worker_pool_s *wp) /* {{{ */
{
if (wp->config->pm == PM_STYLE_ONDEMAND) {//根据具体需求fork出worker进程,即:有请求来了,到wp->scoreboard查看有没有空闲的子进程,若满负荷运转,则fork 出新进程;否则让空闲子进程处理请求
wp->ondemand_event = (struct fpm_event_s *)malloc(sizeof(struct fpm_event_s));
if (!wp->ondemand_event) {
zlog(ZLOG_ERROR, "[pool %s] unable to malloc the ondemand socket event", wp->config->name);
// FIXME handle crash
return 1;
}
memset(wp->ondemand_event, 0, sizeof(struct fpm_event_s));
fpm_event_set(wp->ondemand_event, wp->listening_socket, FPM_EV_READ | FPM_EV_EDGE, fpm_pctl_on_socket_accept, wp);
wp->socket_event_set = 1;
fpm_event_add(wp->ondemand_event, 0);
return 1;
}
return fpm_children_make(wp, 0 /* not in event loop yet */, 0, 1);
}
wp->config->pm==PM_STYLE_ONDEMAND:
fpm_event_set重点:
给wp->ondemand_event结构赋值
wp->ondemand_event->fd = wp->listening_socket;
wp->ondemand_event->callback = fpm_pctl_on_socket_accept;
wp->ondemand_event->arg = FPM_EV_READ | FPM_EV_EDGE;
wp->ondemand_event->flags = wp;
fpm_event_add重点:
/*
* Add a FD to the fd set
*/
static int fpm_event_epoll_add(struct fpm_event_s *ev) /* {{{ */
{
struct epoll_event e;
/* fill epoll struct */
#if SIZEOF_SIZE_T == 4
/* Completely initialize event data to prevent valgrind reports */
e.data.u64 = 0;
#endif
e.events = EPOLLIN;
e.data.fd = ev->fd;
e.data.ptr = (void *)ev;
if (ev->flags & FPM_EV_EDGE) {
e.events = e.events | EPOLLET;
}
/* add the event to epoll internal queue */
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ev->fd, &e) == -1) {
zlog(ZLOG_ERROR, "epoll: unable to add fd %d", ev->fd);
return -1;
}
/* mark the event as registered */
ev->index = ev->fd;
return
php-src/sapi/fpm/fpm/fpm.c:fpm_run->
php-src/sapi/fpm/fpm/fpm_children.c:fpm_children_create_initial->
php-src/sapi/fpm/fpm/fpm_process_ctl.c:fpm_pctl_on_socket_accept:
wp->listening_socket监听可读事件的响应函数:
void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
{
struct fpm_worker_pool_s *wp = (struct fpm_worker_pool_s *)arg;
struct fpm_child_s *child;
if (fpm_globals.parent_pid != getpid()) {
/* prevent a event race condition when child process
* have not set up its own event loop */
return;
}
wp->socket_event_set = 0;
/* zlog(ZLOG_DEBUG, "[pool %s] heartbeat running_children=%d", wp->config->name, wp->running_children);*/
if (wp->running_children >= wp->config->pm_max_children) {
if (!wp->warn_max_children) {
fpm_scoreboard_update(0, 0, 0, 0, 0, 1, 0, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
zlog(ZLOG_WARNING, "[pool %s] server reached max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
wp->warn_max_children = 1;
}
return;
}
for (child = wp->children; child; child = child->next) {
/* if there is at least on idle child, it will handle the connection, stop here */
if (fpm_request_is_idle(child)) {
return;
}
}
wp->warn_max_children = 0;
fpm_children_make(wp, 1, 1, 1);
if (fpm_globals.is_child) {
return;
}
zlog(ZLOG_DEBUG, "[pool %s] got accept without idle child available .... I forked", wp->config->name);
}
wp->ondemand_event->fd = wp->listening_socket;
wp->ondemand_event->callback = fpm_pctl_on_socket_accept;
wp->ondemand_event->arg = FPM_EV_READ | FPM_EV_EDGE;
wp->ondemand_event->flags = wp;
所以参数列表值分别为:fpm_event_s *ev = wp->ondemand_event, short which=FPM_EV_READ(php-src/sapi/fpm/fpm/fpm_events.c->fpm_event_add()), void *arg = FPM_EV_READ | FPM_EV_EDGE
php-src/sapi/fpm/fpm/fpm.c:fpm_run->
php-src/sapi/fpm/fpm/fpm_children.c:fpm_children_create_initial->
php-src/sapi/fpm/fpm/fpm_children.c:fpm_children_make
由新的daemon master 进程 fork出worker进程
/*
* fork children while:
* - fpm_pctl_can_spawn_children : FPM is running in a NORMAL state (aka not restart, stop or reload)
* - wp->running_children < max : there is less than the max process for the current pool
* - (fpm_global_config.process_max < 1 || fpm_globals.running_children < fpm_global_config.process_max):
* if fpm_global_config.process_max is set, FPM has not fork this number of processes (globaly)
*/
while (fpm_pctl_can_spawn_children() && wp->running_children < max && (fpm_global_config.process_max < 1 || fpm_globals.running_children < fpm_global_config.process_max)) {
warned = 0;
child = fpm_resources_prepare(wp);
if (!child) {
return 2;
}
pid = fork();
switch (pid) {
case 0 :
fpm_child_resources_use(child);
fpm_globals.is_child = 1;
fpm_child_init(wp);
return 0;
case -1 :
zlog(ZLOG_SYSERROR, "fork() failed");
fpm_resources_discard(child);
return 2;
default :
child->pid = pid;
fpm_clock_get(&child->started);
fpm_parent_resources_use(child);
zlog(is_debug ? ZLOG_DEBUG : ZLOG_NOTICE, "[pool %s] child %d started", wp->config->name, (int) pid);
}
}
**child = fpm_resources_prepare(wp)**重点:
fpm_child_resources_use重点(已fork):
fpm_child_init重点:
if (0 > fpm_stdio_init_child(wp) ||
0 > fpm_log_init_child(wp) ||
0 > fpm_status_init_child(wp) ||
0 > fpm_unix_init_child(wp) ||
0 > fpm_signals_init_child() ||
0 > fpm_env_init_child(wp) ||
0 > fpm_php_init_child(wp)) {
zlog(ZLOG_ERROR, "[pool %s] child failed to initialize", wp->config->name);
exit(FPM_EXIT_SOFTWARE);
}
php-src/sapi/fpm/fpm/fpm.c:fpm_run->
php-src/sapi/fpm/fpm/fpm_events.c:fpm_event_loop:
fpm_event_set(&signal_fd_event, fpm_signals_get_fd(), FPM_EV_READ, &fpm_got_signal, NULL);
fpm_event_add(&signal_fd_event, 0);
/* add timers */
if (fpm_globals.heartbeat > 0) {
fpm_pctl_heartbeat(NULL, 0, NULL);
}
if (!err) {
fpm_pctl_perform_idle_server_maintenance_heartbeat(NULL, 0, NULL);
zlog(ZLOG_DEBUG, "%zu bytes have been reserved in SHM", fpm_shm_get_size_allocated());
zlog(ZLOG_NOTICE, "ready to handle connections");
#ifdef HAVE_SYSTEMD
fpm_systemd_heartbeat(NULL, 0, NULL);
#endif
}
while (1) {
struct fpm_event_queue_s *q, *q2;
struct timeval ms;
struct timeval tmp;
struct timeval now;
unsigned long int timeout;
int ret;
/* sanity check */
if (fpm_globals.parent_pid != getpid()) {
return;
}
fpm_clock_get(&now);
timerclear(&ms);
/* search in the timeout queue for the next timer to trigger */
q = fpm_event_queue_timer;
while (q) {
if (!timerisset(&ms)) {
ms = q->ev->timeout;
} else {
if (timercmp(&q->ev->timeout, &ms, <)) {
ms = q->ev->timeout;
}
}
q = q->next;
}
/* 1s timeout if none has been set */
if (!timerisset(&ms) || timercmp(&ms, &now, <) || timercmp(&ms, &now, ==)) {
timeout = 1000;
} else {
timersub(&ms, &now, &tmp);
timeout = (tmp.tv_sec * 1000) + (tmp.tv_usec / 1000) + 1;
}
ret = module->wait(fpm_event_queue_fd, timeout);
/* is a child, nothing to do here */
if (ret == -2) {
return;
}
if (ret > 0) {
zlog(ZLOG_DEBUG, "event module triggered %d events", ret);
}
/* trigger timers */
q = fpm_event_queue_timer;
while (q) {
fpm_clock_get(&now);
if (q->ev) {
if (timercmp(&now, &q->ev->timeout, >) || timercmp(&now, &q->ev->timeout, ==)) {
fpm_event_fire(q->ev);
/* sanity check */
if (fpm_globals.parent_pid != getpid()) {
return;
}
if (q->ev->flags & FPM_EV_PERSIST) {
fpm_event_set_timeout(q->ev, now);
} else { /* delete the event */
q2 = q;
if (q->prev) {
q->prev->next = q->next;
}
if (q->next) {
q->next->prev = q->prev;
}
if (q == fpm_event_queue_timer) {
fpm_event_queue_timer = q->next;
if (fpm_event_queue_timer) {
fpm_event_queue_timer->prev = NULL;
}
}
q = q->next;
free(q2);
continue;
}
}
}
q = q->next;
}
}
request = fpm_init_request(fcgi_fd);
php-src/sapi/fpm/fpm/fpm_main.c:main
worker进程监听请求,解析请求并处理,处理结果输出并结束此次请求,循环处理
zend_first_try {
while (EXPECTED(fcgi_accept_request(request) >= 0)) {
char *primary_script = NULL;
request_body_fd = -1;
SG(server_context) = (void *) request;
init_request_info();
fpm_request_info();
/* request startup only after we've done all we can to
* get path_translated */
if (UNEXPECTED(php_request_startup() == FAILURE)) {
fcgi_finish_request(request, 1);
SG(server_context) = NULL;
php_module_shutdown();
return FPM_EXIT_SOFTWARE;
}
/* check if request_method has been sent.
* if not, it's certainly not an HTTP over fcgi request */
if (UNEXPECTED(!SG(request_info).request_method)) {
goto fastcgi_request_done;
}
if (UNEXPECTED(fpm_status_handle_request())) {
goto fastcgi_request_done;
}
/* If path_translated is NULL, terminate here with a 404 */
if (UNEXPECTED(!SG(request_info).path_translated)) {
zend_try {
zlog(ZLOG_DEBUG, "Primary script unknown");
SG(sapi_headers).http_response_code = 404;
PUTS("File not found.\n");
} zend_catch {
} zend_end_try();
goto fastcgi_request_done;
}
if (UNEXPECTED(fpm_php_limit_extensions(SG(request_info).path_translated))) {
SG(sapi_headers).http_response_code = 403;
PUTS("Access denied.\n");
goto fastcgi_request_done;
}
/*
* have to duplicate SG(request_info).path_translated to be able to log errrors
* php_fopen_primary_script seems to delete SG(request_info).path_translated on failure
*/
primary_script = estrdup(SG(request_info).path_translated);
/* path_translated exists, we can continue ! */
if (UNEXPECTED(php_fopen_primary_script(&file_handle) == FAILURE)) {
zend_try {
zlog(ZLOG_ERROR, "Unable to open primary script: %s (%s)", primary_script, strerror(errno));
if (errno == EACCES) {
SG(sapi_headers).http_response_code = 403;
PUTS("Access denied.\n");
} else {
SG(sapi_headers).http_response_code = 404;
PUTS("No input file specified.\n");
}
} zend_catch {
} zend_end_try();
/* we want to serve more requests if this is fastcgi
* so cleanup and continue, request shutdown is
* handled later */
goto fastcgi_request_done;
}
fpm_request_executing();
php_execute_script(&file_handle);
fastcgi_request_done:
if (EXPECTED(primary_script)) {
efree(primary_script);
}
if (UNEXPECTED(request_body_fd != -1)) {
close(request_body_fd);
}
request_body_fd = -2;
if (UNEXPECTED(EG(exit_status) == 255)) {
if (CGIG(error_header) && *CGIG(error_header)) {
sapi_header_line ctr = {0};
ctr.line = CGIG(error_header);
ctr.line_len = strlen(CGIG(error_header));
sapi_header_op(SAPI_HEADER_REPLACE, &ctr);
}
}
fpm_request_end();
fpm_log_write(NULL);
efree(SG(request_info).path_translated);
SG(request_info).path_translated = NULL;
php_request_shutdown((void *) 0);
requests++;
if (UNEXPECTED(max_requests && (requests == max_requests))) {
fcgi_request_set_keep(request, 0);
fcgi_finish_request(request, 0);
break;
}
/* end of fastcgi loop */
}
fcgi_destroy_request(request);
fcgi_shutdown();
if (cgi_sapi_module.php_ini_path_override) {
free(cgi_sapi_module.php_ini_path_override);
}
if (cgi_sapi_module.ini_entries) {
free(cgi_sapi_module.ini_entries);
}
} zend_catch {
exit_status = FPM_EXIT_SOFTWARE;
} zend_end_try();
out:
SG(server_context) = NULL;
php_module_shutdown();
if (parent) {
sapi_shutdown();
}
#ifdef ZTS
tsrm_shutdown();
#endif
return exit_status;
}
php-src/sapi/fpm/fpm/fpm_main.c:main->
php-src/main/fastcgi.c:fcgi_accept_request
子进程从监听端口获取数据,使用了锁,并执行了请求体的hook响应函数:on_accept,on_read,on_close;;
php-src/sapi/fpm/fpm/fpm_main.c:main->
php-src/main/fastcgi.c:fcgi_accept_request->
php-src/main/fastcgi.c:fcgi_read_request
读取请求数据,根据fastcgi协议来解析数据
init_request_info()
fpm_request_info()
php_request_startup
fpm_request_executing();
php_execute_script(&file_handle)
fpm_request_end()
php_request_shutdown
fcgi_request_set_keep
fcgi_finish_request