php源码fpm分析

源码为php-7.1.2

在/sapi/fpm/fpm/fpm_main.c中的main方法。

其中最重要的有2点

第一:fcgi_fd = fpm_run(&max_requests);实现了fpm的进程控制。

php-fpm支持三种运行模式,分别为staticondemanddynamic,默认为dynamic
static : 静态模式,启动时分配固定的worker进程。
ondemand: 按需分配,当收到用户请求时fork worker进程。
dynamic: 动态模式,启动时分配固定的进程。伴随着请求数增加,在设定的浮动范围调整worker进程。

 

这三种模式各有千秋,大家可以根据不同的环境调整相应的配置。

 

 

php-fpm采用master/worker架构设计,前面简单地描述master和worker进程模块的功能,其中

master进程通过fpm_event_loop函数处理,其内部是一个死循环,负责事件的收集工作,

work进程处理用户进程

第二:php_execute_script执行php脚本,最终调用zend引擎,执行代码

 

 

 

 

 

int main(int argc, char *argv[])
{
    int exit_status = FPM_EXIT_OK;
    int cgi = 0, c, use_extended_info = 0;
    zend_file_handle file_handle;

    /* temporary locals */
    int orig_optind = php_optind;
    char *orig_optarg = php_optarg;
    int ini_entries_len = 0;
    /* end of temporary locals */

#ifdef ZTS
    void ***tsrm_ls;
#endif

    int max_requests = 500;
    int requests = 0;
    int fcgi_fd = 0;
    fcgi_request *request;
    char *fpm_config = NULL;
    char *fpm_prefix = NULL;
    char *fpm_pid = NULL;
    int test_conf = 0;
    int force_daemon = -1;
    int force_stderr = 0;
    int php_information = 0;
    int php_allow_to_run_as_root = 0;

#ifdef HAVE_SIGNAL_H
#if defined(SIGPIPE) && defined(SIG_IGN)
    signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so
                                that sockets created via fsockopen()
                                don't kill PHP if the remote site
                                closes it.  in apache|apxs mode apache
                                does that for us!  thies@thieso.net
                                20000419 */
#endif
#endif

#ifdef ZTS
    tsrm_startup(1, 1, 0, NULL);
    tsrm_ls = ts_resource(0);
#endif

    zend_signal_startup();

    sapi_startup(&cgi_sapi_module);
    cgi_sapi_module.php_ini_path_override = NULL;
    cgi_sapi_module.php_ini_ignore_cwd = 1;

#ifndef HAVE_ATTRIBUTE_WEAK
    fcgi_set_logger(fpm_fcgi_log);
#endif

    fcgi_init();

#ifdef PHP_WIN32
    _fmode = _O_BINARY; /* sets default for file streams to binary */
    setmode(_fileno(stdin),  O_BINARY);    /* make the stdio mode be binary */
    setmode(_fileno(stdout), O_BINARY);    /* make the stdio mode be binary */
    setmode(_fileno(stderr), O_BINARY);    /* make the stdio mode be binary */
#endif

    while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
        switch (c) {
            case 'c':
                if (cgi_sapi_module.php_ini_path_override) {
                    free(cgi_sapi_module.php_ini_path_override);
                }
                cgi_sapi_module.php_ini_path_override = strdup(php_optarg);
                break;

            case 'n':
                cgi_sapi_module.php_ini_ignore = 1;
                break;

            case 'd': {
                /* define ini entries on command line */
                int len = strlen(php_optarg);
                char *val;

                if ((val = strchr(php_optarg, '='))) {
                    val++;
                    if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') {
                        cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\"\"\n\0"));
                        memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, (val - php_optarg));
                        ini_entries_len += (val - php_optarg);
                        memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"", 1);
                        ini_entries_len++;
                        memcpy(cgi_sapi_module.ini_entries + ini_entries_len, val, len - (val - php_optarg));
                        ini_entries_len += len - (val - php_optarg);
                        memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0"));
                        ini_entries_len += sizeof("\n\0\"") - 2;
                    } else {
                        cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\n\0"));
                        memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
                        memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0"));
                        ini_entries_len += len + sizeof("\n\0") - 2;
                    }
                } else {
                    cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("=1\n\0"));
                    memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
                    memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0"));
                    ini_entries_len += len + sizeof("=1\n\0") - 2;
                }
                break;
            }

            case 'y':
                fpm_config = php_optarg;
                break;

            case 'p':
                fpm_prefix = php_optarg;
                break;

            case 'g':
                fpm_pid = php_optarg;
                break;

            case 'e': /* enable extended info output */
                use_extended_info = 1;
                break;

            case 't':
                test_conf++;
                break;

            case 'm': /* list compiled in modules */
                cgi_sapi_module.startup(&cgi_sapi_module);
                php_output_activate();
                SG(headers_sent) = 1;
                php_printf("[PHP Modules]\n");
                print_modules();
                php_printf("\n[Zend Modules]\n");
                print_extensions();
                php_printf("\n");
                php_output_end_all();
                php_output_deactivate();
                fcgi_shutdown();
                exit_status = FPM_EXIT_OK;
                goto out;

            case 'i': /* php info & quit */
                php_information = 1;
                break;

            case 'R': /* allow to run as root */
                php_allow_to_run_as_root = 1;
                break;

            case 'D': /* daemonize */
                force_daemon = 1;
                break;

            case 'F': /* nodaemonize */
                force_daemon = 0;
                break;

            case 'O': /* force stderr even on non tty */
                force_stderr = 1;
                break;

            default:
            case 'h':
            case '?':
                cgi_sapi_module.startup(&cgi_sapi_module);
                php_output_activate();
                SG(headers_sent) = 1;
                php_cgi_usage(argv[0]);
                php_output_end_all();
                php_output_deactivate();
                fcgi_shutdown();
                exit_status = (c == 'h') ? FPM_EXIT_OK : FPM_EXIT_USAGE;
                goto out;

            case 'v': /* show php version & quit */
                cgi_sapi_module.startup(&cgi_sapi_module);
                if (php_request_startup() == FAILURE) {
                    SG(server_context) = NULL;
                    php_module_shutdown();
                    return FPM_EXIT_SOFTWARE;
                }
                SG(headers_sent) = 1;
                SG(request_info).no_headers = 1;

#if ZEND_DEBUG
                php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2017 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__,        __TIME__, get_zend_version());
#else
                php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2017 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__,      get_zend_version());
#endif
                php_request_shutdown((void *) 0);
                fcgi_shutdown();
                exit_status = FPM_EXIT_OK;
                goto out;
        }
    }

    if (php_information) {
        cgi_sapi_module.phpinfo_as_text = 1;
        cgi_sapi_module.startup(&cgi_sapi_module);
        if (php_request_startup() == FAILURE) {
            SG(server_context) = NULL;
            php_module_shutdown();
            return FPM_EXIT_SOFTWARE;
        }
        SG(headers_sent) = 1;
        SG(request_info).no_headers = 1;
        php_print_info(0xFFFFFFFF);
        php_request_shutdown((void *) 0);
        fcgi_shutdown();
        exit_status = FPM_EXIT_OK;
        goto out;
    }

    /* No other args are permitted here as there is no interactive mode */
    if (argc != php_optind) {
        cgi_sapi_module.startup(&cgi_sapi_module);
        php_output_activate();
        SG(headers_sent) = 1;
        php_cgi_usage(argv[0]);
        php_output_end_all();
        php_output_deactivate();
        fcgi_shutdown();
        exit_status = FPM_EXIT_USAGE;
        goto out;
    }

    php_optind = orig_optind;
    php_optarg = orig_optarg;

#ifdef ZTS
    SG(request_info).path_translated = NULL;
#endif

    cgi_sapi_module.additional_functions = NULL;
    cgi_sapi_module.executable_location = argv[0];

    /* startup after we get the above ini override se we get things right */
    if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) {
#ifdef ZTS
        tsrm_shutdown();
#endif
        return FPM_EXIT_SOFTWARE;
    }

    if (use_extended_info) {
        CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
    }

    /* check force_cgi after startup, so we have proper output */
    if (cgi && CGIG(force_redirect)) {
        /* Apache will generate REDIRECT_STATUS,
         * Netscape and redirect.so will generate HTTP_REDIRECT_STATUS.
         * redirect.so and installation instructions available from
         * http://www.koehntopp.de/php.
         *   -- kk@netuse.de
         */
        if (!getenv("REDIRECT_STATUS") &&
            !getenv ("HTTP_REDIRECT_STATUS") &&
            /* this is to allow a different env var to be configured
             * in case some server does something different than above */
            (!CGIG(redirect_status_env) || !getenv(CGIG(redirect_status_env)))
        ) {
            zend_try {
                SG(sapi_headers).http_response_code = 400;
                PUTS("<b>Security Alert!</b> The PHP CGI cannot be accessed directly.\n\n\
<p>This PHP CGI binary was compiled with force-cgi-redirect enabled.  This\n\
means that a page will only be served up if the REDIRECT_STATUS CGI variable is\n\
set, e.g. via an Apache Action directive.</p>\n\
<p>For more information as to <i>why</i> this behaviour exists, see the <a href=\"http://php.net/security.cgi-bin\">\
manual page for CGI security</a>.</p>\n\
<p>For more information about changing this behaviour or re-enabling this webserver,\n\
consult the installation file that came with this distribution, or visit \n\
<a href=\"http://php.net/install.windows\">the manual page</a>.</p>\n");
            } zend_catch {
            } zend_end_try();
#if defined(ZTS) && !defined(PHP_DEBUG)
            /* XXX we're crashing here in msvc6 debug builds at
             * php_message_handler_for_zend:839 because
             * SG(request_info).path_translated is an invalid pointer.
             * It still happens even though I set it to null, so something
             * weird is going on.
             */
            tsrm_shutdown();
#endif
            return FPM_EXIT_SOFTWARE;
        }
    }

    if (0 > fpm_init(argc, argv, fpm_config ? fpm_config : CGIG(fpm_config), fpm_prefix, fpm_pid, test_conf, php_allow_to_run_as_root, force_daemon, force_stderr)) {

        if (fpm_globals.send_config_pipe[1]) {
            int writeval = 0;
            zlog(ZLOG_DEBUG, "Sending \"0\" (error) to parent via fd=%d", fpm_globals.send_config_pipe[1]);
            zend_quiet_write(fpm_globals.send_config_pipe[1], &writeval, sizeof(writeval));
            close(fpm_globals.send_config_pipe[1]);
        }
        return FPM_EXIT_CONFIG;
    }

    if (fpm_globals.send_config_pipe[1]) {
        int writeval = 1;
        zlog(ZLOG_DEBUG, "Sending \"1\" (OK) to parent via fd=%d", fpm_globals.send_config_pipe[1]);
        zend_quiet_write(fpm_globals.send_config_pipe[1], &writeval, sizeof(writeval));
        close(fpm_globals.send_config_pipe[1]);
    }
    fpm_is_running = 1;

    fcgi_fd = fpm_run(&max_requests);
    parent = 0;

    /* onced forked tell zlog to also send messages through sapi_cgi_log_fastcgi() */
    zlog_set_external_logger(sapi_cgi_log_fastcgi);

    /* make php call us to get _ENV vars */
    php_php_import_environment_variables = php_import_environment_variables;
    php_import_environment_variables = cgi_php_import_environment_variables;

    /* library is already initialized, now init our request */
    request = fpm_init_request(fcgi_fd);

    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_finish_request(request, 1);
                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

#if defined(PHP_WIN32) && ZEND_DEBUG && 0
    _CrtDumpMemoryLeaks();
#endif

    return exit_status;
}
/* }}} */
posted @ 2017-03-05 13:23  z497688734  阅读(496)  评论(0编辑  收藏  举报