PHP核心是如何处理客户端连接的


How PHP core is handling client connections?

据我所知,对于连接到服务器的每个客户机,PHP都会为它生成一个新线程。但我想知道这是真的还是假的,如果是真的,这个线程能存活多久?这个线程是否正确地维护了所有的静态变量?(如DB连接)

当这个线程被销毁时,它是否调用所有析构函数?

这完全取决于PHP如何与服务器一起设置。我更熟悉Apache/PHP组合(而不是说Nginx和FastCGI与PHP设置),所以专注于这个领域:

  1. PHP通常作为DSO (dynamic-shared-object)模块集成到Apache。

  2. 现在Apache通常在Linux/Unix下通常被设置为"预分叉"模型-即当它启动时,一堆子进程被分叉(确切的数量可以通过Apache指令配置),并且它们在一个"进程池"中准备处理请求。

  3. 当请求到达时,内核调度程序从池中选择一个Apache进程(如果可用),请求由子进程处理。

  4. 基于Apache的设置,如果它检测到PHP脚本需要执行,它会将其作为(1)中的设置移交给PHP DSO。

  5. 因此在每个请求的基础上不涉及分叉或线程,这是高效的。所有的请求上下文都传递给PHP层,PHP层开始编译和执行PHP脚本。

    注意:如果操作码缓存是启用的,编译步骤可以被绕过-即PHP脚本上的第一个请求被编译,其相关的操作码被缓存以供后续请求重用(它是由所有子进程共享的全局缓存)。由于编译步骤是昂贵的(解析脚本等),对于生产系统,最好启用操作码缓存。

  6. 当PHP脚本完成时,它进入一个清理例程(内置到PHP DSO中),它将清理每个请求的内存,关闭所有文件描述,包括db句柄(基于它是如何打开的)。一些PHP方法具有"持久"句柄(例如打开db连接,文件句柄),可以跨请求持有,因此使用哪个函数打开特定资源很重要(其各自的文档强调)。默认情况下,大多数资源只在PHP请求的生命周期中保存,一旦PHP请求完成,它们就会被销毁。

    关于PHP对象函数,这完全取决于对象创建的范围。因此,对于全局对象,它的函数将只在请求周期结束时被调用,而其中一些函数将在函数返回时超出范围时被调用。

    因此您可以免费获得内存/资源管理。您可以通过unset()调用立即触发free来控制它。同样从PHP 5.3开始,垃圾收集也可以在请求处理阶段启用-查看更多详细信息:http://www.php.net/manual/en/features.gc.php

  7. 现在这个Apache子进程(运行PHP脚本)回到了进程池中,准备像步骤3那样处理下一个请求。

我上面描述的是典型的Apache/PHP在Linux/Unix环境中,但我认为类似的东西也适用于微软设置。

同样在nginx和FastCGI+PHP中,我认为同样的循环是正确的-即在PHP+FastCGI模块处理的请求周期结束时清理东西。在这里,当nginx启动时,它将启动一个由FastCGI+PHP模块处理的单独进程池,并且nginx和FastCGI+PHP之间的通信发生在unix套接字中。

处理请求的不是PHP,而是web服务器。PHP不是自己运行的。如果网页包含PHP代码,这样的请求由适当的PHP SAPI模块委托给PHP。有多线程,多进程的web服务器,甚至可以想象单进程的web服务器,所以如果为一个请求创建单独的线程或进程,它完全依赖于web服务器。

在PHP文档的许多地方,你可以遇到像umask():

这样的行。

避免在多线程web服务器中使用此函数。最好是创建文件后使用chmod()修改文件权限。使用umask()可能导致并发运行的意外行为脚本和web服务器本身,因为它们都使用相同的umask

…这个线程能存活多久?这个线程是否正确地维护了所有的静态变量?(如DB连接)

你听起来像是有c++背景的人。您可能指的是PHP中的全局变量。PHP内置了对数据库连接的支持,您不应该担心这些事情。它们是实现细节。大多数api都提供持久的数据库连接,但这只是为了满足您的好奇心。每个处理请求的进程创建一个持久连接。因此,在这种情况下,它们是由进程而不是线程来维护的。

当这个线程被销毁时,它是否调用所有析构函数?

在PHP中,析构函数在脚本完成执行时被调用。从PHP开发人员的角度来看,脚本在哪里是无关紧要的。