PHP的CGI实现

Jun 23, 2016 pm 02:30 PM

FastCGI简介

CGI全称是“通用网关接口”(Common Gateway Interface), 它可以让一个客户端,从网页浏览器向执行在Web服务器上的程序请求数据。 CGI描述了客户端和这个程序之间传输数据的一种标准。 CGI的一个目的是要独立于任何语言的,所以CGI可以用任何一种语言编写,只要这种语言具有标准输入、输出和环境变量。如php,perl,tcl等

FastCGI像是一个常驻(long-live)型的CGI, 它可以一直执行着,只要激活后,不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork-and-execute 模式)。 它还支持分布式的运算, 即 FastCGI 程序可以在网站服务器以外的主机上执行并且接受来自其它网站服务器来的请求。

FastCGI是语言无关的、可伸缩架构的CGI开放扩展,其主要行为是将CGI解释器进程保持在内存中并因此获得较高的性能。 众所周知,CGI解释器的反复加载是CGI性能低下的主要原因,如果CGI解释器保持在内存中并接受FastCGI进程管理器调度, 则可以提供良好的性能、伸缩性、Fail- Over特性等等。

一般情况下,FastCGI的整个工作流程是这样的。

Web Server启动时载入FastCGI进程管理器(IIS ISAPI或Apache Module) FastCGI进程管理器自身初始化,启动多个CGI解释器进程(可见多个php-cgi)并等待来自Web Server的连接。 当客户端请求到达Web Server时,FastCGI进程管理器选择并连接到一个CGI解释器。Web server将CGI环境变量和标准输入发送到FastCGI子进程php-cgi。 FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时,请求便告处理完成。FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在Web Server中)的下一个连接。 在CGI模式中,php-cgi在此便退出了。 PHP中的CGI实现

PHP的CGI实现本质是是以socket编程实现一个TCP或UDP协议的服务器,当启动时,创建TCP/UDP协议的服务器的socket监听, 并接收相关请求进行处理。这只是请求的处理,在此基础上添加模块初始化,sapi初始化,模块关闭,sapi关闭等就构成了整个CGI的生命周期。

以TCP为例,在TCP的服务端,一般会执行这样几个操作步骤:

调用socket函数创建一个TCP用的流式套接字; 调用bind函数将服务器的本地地址与前面创建的套接字绑定; 调用listen函数将新创建的套接字作为监听,等待客户端发起的连接,当客户端有多个连接连接到这个套接字时,可能需要排队处理; 服务器进程调用accept函数进入阻塞状态,直到有客户进程调用connect函数而建立起一个连接; 当与客户端创建连接后,服务器调用read_stream函数读取客户的请求; 处理完数据后,服务器调用write函数向客户端发送应答。

TCP上客户-服务器事务的时序如图2.6所示:

PHP的CGI实现从cgi_main.c文件的main函数开始,在main函数中调用了定义在fastcgi.c文件中的初始化,监听等函数。 对比TCP的流程,我们查看PHP对TCP协议的实现,虽然PHP本身也实现了这些流程,但是在main函数中一些过程被封装成一个函数实现。 对应TCP的操作流程,PHP首先会执行创建socket,绑定套接字,创建监听:

if (bindpath) {    fcgi_fd = fcgi_listen(bindpath, 128);   //  实现socket监听,调用fcgi_init初始化    ...}
로그인 후 복사

在fastcgi.c文件中,fcgi_listen函数主要用于创建、绑定socket并开始监听,它走完了前面所列TCP流程的前三个阶段,

    if ((listen_socket = socket(sa.sa.sa_family, SOCK_STREAM, 0)) < 0 ||        ...        bind(listen_socket, (struct sockaddr *) &sa, sock_len) < 0 ||        listen(listen_socket, backlog) < 0) {        ...    }
로그인 후 복사

当服务端初始化完成后,进程调用accept函数进入阻塞状态,在main函数中我们看到如下代码:

    while (parent) {        do {            pid = fork();   //  生成新的子进程            switch (pid) {            case 0: //  子进程                parent = 0;                /* don't catch our signals */                sigaction(SIGTERM, &old_term, 0);   //  终止信号                sigaction(SIGQUIT, &old_quit, 0);   //  终端退出符                sigaction(SIGINT,  &old_int,  0);   //  终端中断符                break;                ...                default:                /* Fine */                running++;                break;        } while (parent && (running < children));    ...        while (!fastcgi || fcgi_accept_request(&request) >= 0) {        SG(server_context) = (void *) &request;        init_request_info(TSRMLS_C);        CG(interactive) = 0;                    ...            }
로그인 후 복사

如上的代码是一个生成子进程,并等待用户请求。在fcgi_accept_request函数中,程序会调用accept函数阻塞新创建的进程。 当用户的请求到达时,fcgi_accept_request函数会判断是否处理用户的请求,其中会过滤某些连接请求,忽略受限制客户的请求, 如果程序受理用户的请求,它将分析请求的信息,将相关的变量写到对应的变量中。 其中在读取请求内容时调用了safe_read方法。如下所示: [main() -> fcgi_accept_request() -> fcgi_read_request() -> safe_read()]

static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count){    size_t n = 0;    do {    ... //  省略  对win32的处理        ret = read(req->fd, ((char*)buf)+n, count-n);   //  非win版本的读操作    ... //  省略    } while (n != count);}
로그인 후 복사

如上对应服务器端读取用户的请求数据。

在请求初始化完成,读取请求完毕后,就该处理请求的PHP文件了。 假设此次请求为PHP_MODE_STANDARD则会调用php_execute_script执行PHP文件。 在此函数中它先初始化此文件相关的一些内容,然后再调用zend_execute_scripts函数,对PHP文件进行词法分析和语法分析,生成中间代码, 并执行zend_execute函数,从而执行这些中间代码。关于整个脚本的执行请参见第三节 脚本的执行。

在处理完用户的请求后,服务器端将返回信息给客户端,此时在main函数中调用的是fcgi_finish_request(&request, 1); fcgi_finish_request函数定义在fastcgi.c文件中,其代码如下:

int fcgi_finish_request(fcgi_request *req, int force_close){int ret = 1;if (req->fd >= 0) {    if (!req->closed) {        ret = fcgi_flush(req, 1);        req->closed = 1;    }    fcgi_close(req, force_close, 1);}return ret;}
로그인 후 복사

如上,当socket处于打开状态,并且请求未关闭,则会将执行后的结果刷到客户端,并将请求的关闭设置为真。 将数据刷到客户端的程序调用的是fcgi_flush函数。在此函数中,关键是在于答应头的构造和写操作。 程序的写操作是调用的safe_write函数,而safe_write函数中对于最终的写操作针对win和linux环境做了区分, 在Win32下,如果是TCP连接则用send函数,如果是非TCP则和非win环境一样使用write函数。如下代码:

#ifdef _WIN32if (!req->tcp) {    ret = write(req->fd, ((char*)buf)+n, count-n);} else {    ret = send(req->fd, ((char*)buf)+n, count-n, 0);    if (ret <= 0) {            errno = WSAGetLastError();    }}#elseret = write(req->fd, ((char*)buf)+n, count-n);#endif
로그인 후 복사

在发送了请求的应答后,服务器端将会执行关闭操作,仅限于CGI本身的关闭,程序执行的是fcgi_close函数。 fcgi_close函数在前面提的fcgi_finish_request函数中,在请求应答完后执行。同样,对于win平台和非win平台有不同的处理。 其中对于非win平台调用的是write函数。

以上是一个TCP服务器端实现的简单说明。这只是我们PHP的CGI模式的基础,在这个基础上PHP增加了更多的功能。 在前面的章节中我们提到了每个SAPI都有一个专属于它们自己的sapi_module_struct结构:cgi_sapi_module,其代码定义如下:

/* {{{ sapi_module_struct cgi_sapi_module */static sapi_module_struct cgi_sapi_module = {"cgi-fcgi",                     /* name */"CGI/FastCGI",                  /* pretty name */php_cgi_startup,                /* startup */php_module_shutdown_wrapper,    /* shutdown */sapi_cgi_activate,              /* activate */sapi_cgi_deactivate,            /* deactivate */sapi_cgibin_ub_write,           /* unbuffered write */sapi_cgibin_flush,              /* flush */NULL,                           /* get uid */sapi_cgibin_getenv,             /* getenv */php_error,                      /* error handler */NULL,                           /* header handler */sapi_cgi_send_headers,          /* send headers handler */NULL,                           /* send header handler */sapi_cgi_read_post,             /* read POST data */sapi_cgi_read_cookies,          /* read Cookies */sapi_cgi_register_variables,    /* register server variables */sapi_cgi_log_message,           /* Log message */NULL,                           /* Get request time */NULL,                           /* Child terminate */STANDARD_SAPI_MODULE_PROPERTIES};/* }}} */
로그인 후 복사

同样,以读取cookie为例,当我们在CGI环境下,在PHP中调用读取Cookie时, 最终获取的数据的位置是在激活SAPI时。它所调用的方法是read_cookies。

SG(request_info).cookie_data = sapi_module.read_cookies(TSRMLS_C);
로그인 후 복사

对于每一个服务器在加载时,我们都指定了sapi_module,在第一小节的Apache模块方式中, sapi_module是apache2_sapi_module,其对应read_cookies方法的是php_apache_sapi_read_cookies函数, 而在我们这里,读取cookie的函数是sapi_cgi_read_cookies。 再次说明定义SAPI结构的理由:统一接口,面向接口的编程,具有更好的扩展性和适应性。

参考资料 http://www.fastcgi.com/drupal/node/2 http://baike.baidu.com/view/641394.htm

这是TIPI项目第二章第三小节修改后的版本内容,虽然还有一些问题,但是较之前的版本还是有所进步, 至少我们在努力…

本文地址: PHP的CGI实现    文章出处: PHP源码阅读,PHP设计模式,PHP学习笔记,项目管理-胖胖的空间

转载请以链接形式注明原始出处和作者,谢绝不尊重版权者抄袭!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25 : Myrise에서 모든 것을 잠금 해제하는 방법
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

PHP 로깅 : PHP 로그 분석을위한 모범 사례 PHP 로깅 : PHP 로그 분석을위한 모범 사례 Mar 10, 2025 pm 02:32 PM

PHP 로깅은 웹 애플리케이션을 모니터링하고 디버깅하고 중요한 이벤트, 오류 및 런타임 동작을 캡처하는 데 필수적입니다. 시스템 성능에 대한 귀중한 통찰력을 제공하고 문제를 식별하며 더 빠른 문제 해결을 지원합니다.

Laravel의 플래시 세션 데이터로 작업합니다 Laravel의 플래시 세션 데이터로 작업합니다 Mar 12, 2025 pm 05:08 PM

Laravel은 직관적 인 플래시 방법을 사용하여 임시 세션 데이터 처리를 단순화합니다. 응용 프로그램에 간단한 메시지, 경고 또는 알림을 표시하는 데 적합합니다. 데이터는 기본적으로 후속 요청에만 지속됩니다. $ 요청-

PHP의 컬 : REST API에서 PHP Curl Extension 사용 방법 PHP의 컬 : REST API에서 PHP Curl Extension 사용 방법 Mar 14, 2025 am 11:42 AM

PHP 클라이언트 URL (CURL) 확장자는 개발자를위한 강력한 도구이며 원격 서버 및 REST API와의 원활한 상호 작용을 가능하게합니다. PHP CURL은 존경받는 다중 프로모토콜 파일 전송 라이브러리 인 Libcurl을 활용하여 효율적인 execu를 용이하게합니다.

Laravel 테스트에서 단순화 된 HTTP 응답 조롱 Laravel 테스트에서 단순화 된 HTTP 응답 조롱 Mar 12, 2025 pm 05:09 PM

Laravel은 간결한 HTTP 응답 시뮬레이션 구문을 제공하여 HTTP 상호 작용 테스트를 단순화합니다. 이 접근법은 테스트 시뮬레이션을보다 직관적으로 만들면서 코드 중복성을 크게 줄입니다. 기본 구현은 다양한 응답 유형 단축키를 제공합니다. Illuminate \ support \ Facades \ http를 사용하십시오. http :: 가짜 ([ 'google.com'=> ​​'Hello World', 'github.com'=> ​​[ 'foo'=> 'bar'], 'forge.laravel.com'=>

Codecanyon에서 12 개의 최고의 PHP 채팅 스크립트 Codecanyon에서 12 개의 최고의 PHP 채팅 스크립트 Mar 13, 2025 pm 12:08 PM

고객의 가장 긴급한 문제에 실시간 인스턴트 솔루션을 제공하고 싶습니까? 라이브 채팅을 통해 고객과 실시간 대화를 나누고 문제를 즉시 해결할 수 있습니다. 그것은 당신이 당신의 관습에 더 빠른 서비스를 제공 할 수 있도록합니다.

PHP에서 늦은 정적 결합의 개념을 설명하십시오. PHP에서 늦은 정적 결합의 개념을 설명하십시오. Mar 21, 2025 pm 01:33 PM

기사는 PHP 5.3에 도입 된 PHP의 LSB (Late STATIC BING)에 대해 논의하여 정적 방법의 런타임 해상도가보다 유연한 상속을 요구할 수있게한다. LSB의 실제 응용 프로그램 및 잠재적 성능

프레임 워크 사용자 정의/확장 : 사용자 정의 기능을 추가하는 방법. 프레임 워크 사용자 정의/확장 : 사용자 정의 기능을 추가하는 방법. Mar 28, 2025 pm 05:12 PM

이 기사에서는 프레임 워크에 사용자 정의 기능 추가, 아키텍처 이해, 확장 지점 식별 및 통합 및 디버깅을위한 모범 사례에 중점을 둡니다.

See all articles