Apache 2.x是一种通用的Web服务器,旨在在灵活性,可移植性和性能之间取得平衡。尽管不是专门为设置基准记录而设计的,但Apache 2.x能够在许多实际情况下实现高性能。
与Apache 1.3相比,版本2.x包含许多其他优化功能,以提高吞吐量和可伸缩性。默认情况下,其中大多数改进都是启用的。但是,有一些编译时和运行时配置选择会严重影响性能。本文档描述了服务器管理员可以配置的选项,以调整Apache 2.x安装的性能。这些配置选项中的一些使httpd可以更好地利用硬件和OS的功能,而其他一些则允许管理员以速度为代价来交换功能。
影响Web服务器性能的最大硬件问题是RAM。Web服务器永远都不必交换,因为交换会增加每个请求的延迟,使之超过用户认为“足够快”的程度。这会导致用户点击停止并重新加载,从而进一步增加了负载。您可以并且应该控制该MaxRequestWorkers
设置,以使服务器不会产生太多子代以至于无法开始交换。这样做的过程很简单:通过使用诸如之类的工具查看进程列表,确定平均Apache进程的大小
top
,然后将其划分为总可用内存,为其他进程留出一些空间。
除此之外,其他一切都很普通:获得足够快的CPU,足够快的网卡和足够快的磁盘,其中“足够快”是需要通过实验确定的东西。
操作系统的选择很大程度上取决于本地情况。但是一些已被证明通常有用的准则是:
运行所选操作系统的最新稳定版本和补丁程序级别。近年来,许多操作系统供应商都对其TCP堆栈和线程库进行了重大的性能改进。
如果您的操作系统支持sendfile(2)
系统调用,请确保安装了启用它所需的版本和/或补丁。(例如,对于Linux,这意味着使用Linux 2.4或更高版本。对于Solaris 8的早期发行版,您可能需要应用补丁。)在可用的系统上,sendfile
使Apache 2能够以更低的CPU更快地交付静态内容。利用率。
相关模块 | 相关指令 |
---|---|
在Apache 1.3之前,HostnameLookups
默认为On
。这会增加每个请求的等待时间,因为它需要在请求完成之前完成DNS查找。在Apache 1.3中,此设置默认为Off
。如果您需要将日志文件中的地址解析为主机名,请使用logresolve
Apache随附的
程序或可用的众多日志报告软件包之一。
建议您在生产Web服务器计算机以外的其他计算机上对日志文件进行这种后处理,以免此活动对服务器性能产生不利影响。
如果您使用任何
或Allow
from domain
指令(即,使用主机名或域名,而不是IP地址),则需要支付两次DNS查找费用(反向查询,然后进行正向查询,以确保不进行反向查询)欺骗)。因此,为了获得最佳性能,请尽可能在使用这些指令时使用IP地址而不是名称。Deny
from domain
请注意,可以对指令进行范围调整,例如在一个<Location "/server-status">
节内。在这种情况下,仅对符合条件的请求执行DNS查找。这是一个禁用查找的示例,除了.html
和.cgi
文件:
HostnameLookups off <Files ~ "\.(html|cgi)$"> HostnameLookups on </Files>
但即使如此,如果您仅在某些CGI中需要DNS名称,则可以考虑在gethostbyname
需要它的特定CGI中进行调用。
无论您在URL空间中的哪个位置没有Options
FollowSymLinks
或Options
SymLinksIfOwnerMatch
,Apache都需要发出额外的系统调用来检查符号链接。(每个文件名组件一个额外的调用。)例如,如果您有:
DocumentRoot "/www/htdocs" <Directory "/"> Options SymLinksIfOwnerMatch </Directory>
和请求针对URI制成/index.html
,然后Apache将执行lstat(2)
上
/www
,/www/htdocs
和
/www/htdocs/index.html
。这些结果
lstats
永远不会被缓存,因此它们将在每个单个请求中出现。如果您确实需要符号链接安全检查,则可以执行以下操作:
DocumentRoot "/www/htdocs" <Directory "/"> Options FollowSymLinks </Directory> <Directory "/www/htdocs"> Options -FollowSymLinks +SymLinksIfOwnerMatch </Directory>
这至少避免了对DocumentRoot
路径的额外检查
。请注意,您需要添加类似的部分,如果您有任何Alias
或
RewriteRule
路径,您的文档根目录之外。为了获得最高性能,并且没有符号链接保护,请在FollowSymLinks
各处设置,并且永远不要设置SymLinksIfOwnerMatch
。
无论您在URL空间中的何处允许覆盖(通常是
.htaccess
文件),Apache都会尝试.htaccess
为每个文件名组件打开
。例如,
DocumentRoot "/www/htdocs" <Directory "/"> AllowOverride all </Directory>
并请求URI /index.html
。然后Apache会试图打开/.htaccess
,
/www/.htaccess
和
/www/htdocs/.htaccess
。解决方案与的先前情况类似Options FollowSymLinks
。为了获得最佳性能AllowOverride None
,请在文件系统中的任何地方使用。
如果您确实对每一盎司的性能都非常感兴趣,请尽可能避免进行内容协商。在实践中,谈判的好处胜于性能损失。在一种情况下,您可以加快服务器速度。而不是使用通配符,例如:
DirectoryIndex index
使用完整的选项列表:
DirectoryIndex index.cgi index.pl index.shtml index.html
首先列出最常见的选择。
还要注意,显式创建type-map
文件比使用来提供更好的性能
MultiViews
,因为可以通过读取单个文件来确定必要的信息,而不必扫描目录中的文件。
如果您的站点需要进行内容协商,请考虑使用
type-map
文件而非Options
MultiViews
指令来完成协商。有关协商
方法的完整讨论和创建type-map
文件的说明,请参阅
内容协商文档。
在Apache 2.x需要查看所传递文件的内容的情况下(例如,在进行服务器端包含处理时),如果操作系统支持某种形式的,通常它会对该文件进行内存映射mmap(2)
。
在某些平台上,此内存映射可提高性能。但是,在某些情况下,内存映射可能会损害httpd的性能甚至稳定性:
在某些操作系统上,当CPU数量增加时,mmap
扩展性不佳read(2)
。例如,在多处理器Solaris服务器上,mmap
禁用Apache 2.x有时会更快地交付服务器解析的文件。
如果对位于NFS挂载的文件系统上的文件进行内存映射,而另一台NFS客户端计算机上的进程删除或截断该文件,则下次尝试访问映射的文件内容时,进程可能会遇到总线错误。
对于其中两个因素都适用的安装,您应使用EnableMMAP off
以禁用已交付文件的内存映射。(注意:可以根据每个目录覆盖此指令。)
在Apache 2.x可以忽略要传递的文件内容的情况下(例如,在提供静态文件内容时),如果操作系统支持该sendfile(2)
操作,则通常使用内核sendfile支持文件。
在大多数平台上,使用sendfile通过消除单独的读取和发送机制来提高性能。但是,在某些情况下,使用sendfile可能会损害httpd的稳定性:
某些平台可能具有构建系统未检测到的损坏的sendfile支持,尤其是如果二进制文件是在另一个盒子上构建的,并且已将其转移到具有损坏的sendfile支持的机器上时。
使用安装了NFS的文件系统,内核可能无法通过其自己的缓存可靠地为网络文件提供服务。
对于其中两个因素均适用的安装,应使用EnableSendfile off
禁用文件内容的sendfile传递。(注意:可以根据每个目录覆盖此指令。)
对于Apache 1.3 MinSpareServers
,MaxSpareServers
和StartServers
设置都对测试结果巨大的影响。特别是,Apache需要一个“加速”期才能达到足以满足所施加负载的子级数。最初生成
StartServers
子代后,每秒只能创建一个子代来满足
MinSpareServers
设置。因此,由100个并发客户端访问的服务器(使用默认值StartServers
)5
将花费95秒左右的时间来产生足够的子代来处理负载。实际上,这在现实生活中的服务器上效果很好,因为它们不经常重启。但是,在只能运行十分钟的基准测试上,它的性能确实很差。
实施每秒一秒的规则是为了避免随着新子代的启动而淹没机器。如果机器忙于产生子代,则无法处理请求。但是它对Apache的感知性能产生了巨大的影响,因此必须将其替换。从Apache 1.3开始,代码将放宽每秒规则。它会先生成一个,然后等待一秒钟,然后再生成两个,然后等待一秒钟,再生成四个,然后它会以指数方式继续运行,直到每秒每秒生成32个孩子。只要满足MinSpareServers
设置,它就会停止
。
这似乎足够灵敏,几乎不需要旋转MinSpareServers
,MaxSpareServers
和StartServers
旋钮。每秒产生4个以上的子代时,将向发出一条消息
ErrorLog
。如果您看到很多此类错误,请考虑调整这些设置。使用mod_status
输出作为指导。
与流程创建相关的是由MaxConnectionsPerChild
设置引起的流程死亡
。默认情况下为0
,这意味着每个孩子处理的连接数没有限制。如果您的配置当前将此值设置为一个很小的数字,例如30
,您可能希望将其设置得较大。如果运行的是SunOS或Solaris的旧版本,则10000
由于内存泄漏而将其限制为大约。
使用保持活动状态时,孩子将一直忙于不做任何事情,等待已打开的连接上的更多请求。默认KeepAliveTimeout
的5
秒数尝试最小化此影响。这里的权衡是在网络带宽和服务器资源之间。在任何情况下,您都不应将其提高到60
几秒钟以上,因为
大多数收益都会丢失。
Apache 2.x支持可插拔并发模型,称为
多处理模块(MPM)。构建Apache时,必须选择要使用的MPM。对于某些平台mpm_netware
,
有针对特定平台的MPM :
mpmt_os2
、和mpm_winnt
。对于一般的Unix类型的系统,有几种MPM可供选择。MPM的选择会影响httpd的速度和可伸缩性:
worker
MPM使用多个子进程,每个许多线程。每个线程一次处理一个连接。通常,对于高流量服务器,Worker是一个不错的选择,因为它的内存占用空间比前叉MPM小。event
MPM螺纹状的工人MPM,而是被设计成允许通过传递了一些处理工作的支撑线,以便释放主线程在新的工作要求同时服务更多的请求。prefork
MPM使用多个子进程,每个一个线程。每个进程一次处理一个连接。在许多系统上,prefork的速度可与worker媲美,但它使用更多的内存。在某些情况下,Prefork的无线程设计具有优于worker的优点:它可以与非线程安全的第三方模块一起使用,并且在具有较差的线程调试支持的平台上更容易调试。有关这些和其他MPM的更多信息,请参阅MPM 文档。
由于内存使用是性能中的重要考虑因素,因此您应该尝试消除实际上不使用的模块。如果已将模块构建为DSO,则消除模块是注释掉LoadModule
该模块的关联指令的简单问题。这使您可以尝试删除模块并查看您的站点在缺少模块的情况下是否仍能正常运行。
另一方面,如果您有静态链接到Apache二进制文件中的模块,则需要重新编译Apache才能删除不需要的模块。
当然,这里出现的一个相关问题是,您需要哪些模块,不需要哪些模块。当然,这里的答案会因一个网站而异。然而,
最小的模块,你可以用得到的名单往往包括mod_mime
,mod_dir
,和mod_log_config
。mod_log_config
当然是可选的,因为您可以运行没有日志文件的网站。但是,不建议这样做。
一些模块(例如mod_cache
工作程序MPM的最新开发版本)使用APR的原子API。该API提供了可用于轻量级线程同步的原子操作。
默认情况下,APR使用每个目标OS / CPU平台上可用的最有效机制来实现这些操作。例如,许多现代CPU都具有在硬件中执行原子比较和交换(CAS)操作的指令。但是,在某些平台上,APR默认使用原子API的基于互斥体的较慢实现,以确保与缺少此类指令的较旧CPU模型兼容。如果要为以下平台之一构建Apache,并且计划仅在较新的CPU上运行,则可以通过使用以下--enable-nonportable-atomics
选项配置Apache,从而在构建时选择更快的原子实现:
./buildconf
./configure --with-mpm=worker --enable-nonportable-atomics=yes
该--enable-nonportable-atomics
选项与以下平台有关:
--enable-nonportable-atomics
但是,如果使用进行配置,则APR会生成将SPARC v8plus操作码用于快速硬件比较和交换的代码。如果使用此选项配置Apache,则原子操作将更高效(允许更低的CPU利用率和更高的并发性),但是生成的可执行文件将仅在UltraSPARC芯片上运行。
--enable-nonportable-atomics
但是,如果使用配置,APR会生成使用486操作码进行快速硬件比较和交换的代码。这将导致更有效的原子操作,但是生成的可执行文件将仅在486和更高版本的芯片上运行(而不是386)。
如果您包括在内,mod_status
并且还在ExtendedStatus On
构建和运行Apache时进行了设置
,则Apache将在每个请求上执行两次调用
gettimeofday(2)
(或times(2)
取决于您的操作系统),以及对1.3进行多次额外的调用time(2)
。这样做是为了使状态报告包含时序指示。为了获得最佳性能,请设置ExtendedStatus off
(这是默认设置)。
考虑到在Apache HTTP Server 2.x版本中所做的更改,本部分尚未完全更新。一些信息可能仍然有用,但请谨慎使用。
这讨论了Unix套接字API中的一个缺点。假设您的Web服务器使用多个Listen
语句来侦听多个端口或多个地址。为了测试每个套接字以查看连接是否就绪,Apache使用了
select(2)
。select(2)
表示套接字上有零个或至少一个连接正在等待。Apache的模型包含多个子级,所有空闲的子级同时测试新连接。一个简单的实现看起来像这样(这些示例与代码不匹配,它们被设计用于教学目的):
for (;;) { for (;;) { fd_set accept_fds; FD_ZERO (&accept_fds); for (i = first_socket; i <= last_socket; ++i) { FD_SET (i, &accept_fds); } rc = select (last_socket+1, &accept_fds, NULL, NULL, NULL); if (rc < 1) continue; new_connection = -1; for (i = first_socket; i <= last_socket; ++i) { if (FD_ISSET (i, &accept_fds)) { new_connection = accept (i, NULL, NULL); if (new_connection != -1) break; } } if (new_connection != -1) break; } process_the(new_connection); }
但是,这种幼稚的实现存在严重的饥饿问题。回想一下,多个子代会同时执行此循环,因此,多个子代会在两次select
请求之间处于阻塞
状态。select
当任何请求出现在任何套接字上时,所有被阻止的子级都将唤醒并返回
。(唤醒的子代数取决于操作系统和时序问题。)然后,它们全都陷入循环并尝试accept
建立连接。但是只有一个能够成功(假设仍然只有一个连接可用)。其余部分将被阻止
在accept
。这有效地将那些子级锁定为能够从该一个套接字(而不是其他套接字)提供请求,并且它们将被卡在那里,直到该套接字上出现足够的新请求以将其唤醒为止。此饥饿问题最早在PR#467中记录。至少有两种解决方案。
一种解决方案是使套接字无阻塞。在这种情况下,accept
不会阻止孩子,他们将被允许立即继续。但这浪费了CPU时间。假设您在中有十个闲置孩子
select
,并且一个连接到达。然后,其中的9个孩子将醒来,尝试accept
建立连接,失败,然后循环回去select
,什么也做不了。同时,这些子级中的任何一个都不会为其他套接字上发生的请求提供服务,直到他们重新回到另一个套接字为止select
。总体而言,除非您拥有(在多处理器设备中)闲置的CPU数量与孩子的闲置数量(不太可能的情况)一样,否则该解决方案似乎效果不佳。
Apache使用的另一种解决方案是将条目序列化到内部循环中。循环如下所示(差异突出显示):
for (;;) { accept_mutex_on (); for (;;) { fd_set accept_fds; FD_ZERO (&accept_fds); for (i = first_socket; i <= last_socket; ++i) { FD_SET (i, &accept_fds); } rc = select (last_socket+1, &accept_fds, NULL, NULL, NULL); if (rc < 1) continue; new_connection = -1; for (i = first_socket; i <= last_socket; ++i) { if (FD_ISSET (i, &accept_fds)) { new_connection = accept (i, NULL, NULL); if (new_connection != -1) break; } } if (new_connection != -1) break; } accept_mutex_off (); process the new_connection; }
这些功能
accept_mutex_on
并accept_mutex_off
实现了互斥信号灯。任何时候只有一个孩子可以使用该互斥锁。实现这些互斥有多种选择。该选择在
src/conf.h
(1.3之前)或
src/include/ap_config.h
(1.3或更高版本)中定义。一些体系结构没有做出任何锁定选择,在这些体系结构上使用多个Listen
指令是不安全的
。
该Mutex
指令可用于mpm-accept
在运行时更改互斥量的互斥量实现
。该指令记录了不同互斥量实现的特殊注意事项。
已经考虑但从未实现的另一种解决方案是部分序列化循环-即,让一定数量的进程进入。这仅在多处理器盒上才有意义,因为多处理器盒可能同时运行多个子代,而序列化实际上并没有利用全部带宽。这是未来研究的可能领域,但是由于高度并行的Web服务器不是标准,因此优先级仍然很低。
理想情况下,Listen
如果要获得最高性能,则应在运行服务器时不使用多条
语句。但是请继续阅读。
上面对于多个套接字服务器来说是不错的选择,但是对于单个套接字服务器呢?从理论上讲,他们不应该遇到任何这些相同的问题,因为所有孩子都可以阻塞,accept(2)
直到建立连接为止,并且没有饥饿。在实践中,这几乎隐藏了非阻塞解决方案中上面讨论的“旋转”行为。在大多数TCP堆栈的实现方式中,内核实际上唤醒了所有被阻塞的进程accept
当单个连接到达时。这些进程之一获取连接并返回到用户空间。其余的在内核中旋转并在他们发现没有连接时回到睡眠状态。这种旋转从用户区代码中隐藏了,但是仍然存在。这可能会导致与多套接字案例的非阻塞解决方案相同的负载高峰浪费行为。
由于这个原因,我们发现,即使序列化单个套接字的情况,许多体系结构的行为也“更漂亮”。因此,几乎在所有情况下,这实际上都是默认设置。在Linux上进行的粗略实验(双Pentium pro 166 w / 128Mb RAM上的2.0.30)表明,与未序列化的单插槽相比,单插槽的序列化导致每秒请求减少不到3%。但是未序列化的单路套接字在每个请求上显示了额外的100毫秒延迟。这种延迟可能是长途运输线路上的麻烦,而只是LAN上的一个问题。如果要覆盖单套接字序列化,则可以定义
SINGLE_LISTEN_UNSERIALIZED_ACCEPT
,然后单套接字服务器将根本不会序列化。
如在 draft-ietf-http-connection-00.txt部分8中所讨论的,为了使HTTP服务器可靠地实现该协议,它需要独立关闭每个通信方向。(回想一下,TCP连接是双向的。每一半都彼此独立。)
当将此功能添加到Apache时,由于目光短浅,它在各种版本的Unix上引起了一系列问题。TCP规范没有声明该FIN_WAIT_2
状态有超时,但是没有禁止它。在没有超时的系统上,Apache 1.2会导致许多套接字永久停留在该FIN_WAIT_2
状态。在许多情况下,可以通过简单地升级到供应商提供的最新TCP / IP修补程序来避免这种情况。在供应商从来没有发布补丁的情况下(即,SunOS4 -虽然有源码许可人可以修补它自己),我们决定禁用此功能。
有两种方法可以完成此操作。一种是套接字选项SO_LINGER
。但是,正如命运所愿,这在大多数TCP / IP堆栈中从未得到正确实现。即使在具有适当实现的堆栈(即 Linux 2.0.31)上,该方法(cputime)也比下一个解决方案更昂贵。
在大多数情况下,Apache通过一个名为lingering_close
(in
http_main.c
)的函数来实现这一点。该函数大致如下所示:
void lingering_close (int s) { char junk_buffer[2048]; /* shutdown the sending side */ shutdown (s, 1); signal (SIGALRM, lingering_death); alarm (30); for (;;) { select (s for reading, 2 second timeout); if (error) break; if (s is ready for reading) { if (read (s, junk_buffer, sizeof (junk_buffer)) <= 0) { break; } /* just toss away whatever is here */ } } close (s); }
这自然会在连接结束时增加一些开销,但是这是可靠实现所必需的。随着HTTP / 1.1变得越来越普遍,并且所有连接都是持久的,这笔费用将在更多请求上摊销。如果您想玩火并禁用此功能,则可以定义
NO_LINGCLOSE
,但是完全不建议这样做。特别是,随着HTTP / 1.1流水线持久连接的使用,这lingering_close
是绝对必要的(
流水线连接速度更快,因此您要支持它们)。
Apache的父母和孩子通过称为记分板的方式相互交流。理想情况下,这应该在共享内存中实现。对于那些我们可以访问或已为其指定详细端口的操作系统,通常使用共享内存来实现。其余默认使用磁盘文件。磁盘上的文件不仅速度慢,而且不可靠(功能较少)。src/main/conf.h
为您的体系结构仔细阅读
文件,然后查找USE_MMAP_SCOREBOARD
或
USE_SHMGET_SCOREBOARD
。限定这两个中的一个(以及他们的同伴HAVE_MMAP
和
HAVE_SHMGET
分别)使提供的共享存储器中的代码。如果您的系统还有其他类型的共享内存,请编辑文件src/main/http_main.c
并添加在Apache中使用它所需的钩子。(也请向我们发送补丁。)
如果您不打算使用动态加载的模块(如果您正在阅读本文并针对每一个性能指标调整服务器,则可能不打算这样做),那么应该-DDYNAMIC_MODULE_LIMIT=0
在构建服务器时添加
。这将节省仅用于支持动态加载模块的RAM。
这是Solaris 8上具有工作程序MPM的Apache 2.0.38的系统调用跟踪。此跟踪是使用以下收集的:
truss -l -p httpd_child_pid.
该-l
选项告诉truss记录调用每个系统调用的LWP(轻量级进程-Solaris形式的内核级线程)的ID。
其他系统可能有不同的系统调用跟踪实用程序,如strace
,ktrace
或par
。它们都产生相似的输出。
在此跟踪中,客户端从httpd请求了10KB静态文件。非静态请求或具有内容协商的请求的痕迹看起来截然不同(在某些情况下非常难看)。
/67: accept(3, 0x00200BEC, 0x00200C0C, 1) (sleeping...) /67: accept(3, 0x00200BEC, 0x00200C0C, 1) = 9
在此跟踪中,侦听器线程在LWP#67中运行。
accept(2)
序列化。在此特定平台上,工作程序MPM默认情况下使用未序列化的接受,除非它正在多个端口上侦听。/65: lwp_park(0x00000000, 0) = 0 /67: lwp_unpark(65, 1) = 0
接受连接后,侦听器线程将唤醒工作线程以进行请求处理。在此跟踪中,将处理请求的工作线程映射到LWP#65。
/65: getsockname(9, 0x00200BA4, 0x00200BC4, 1) = 0
为了实现虚拟主机,Apache需要知道用于接受连接的本地套接字地址。在许多情况下(例如当没有虚拟主机或Listen
使用没有通配符地址的指令时),可以消除此调用
。但是,尚未做出任何优化。
/65: brk(0x002170E8) = 0 /65: brk(0x002190E8) = 0
该brk(2)
呼叫从堆中分配内存。很少会在系统调用跟踪中看到这些内容,因为httpd 在大多数请求处理中都使用了自定义内存分配器(apr_pool
和
apr_bucket_alloc
)。在此跟踪中,httpd刚刚启动,因此必须调用malloc(3)
以获取用于创建自定义内存分配器的原始内存块。
/65: fcntl(9, F_GETFL, 0x00000000) = 2 /65: fstat64(9, 0xFAF7B818) = 0 /65: getsockopt(9, 65535, 8192, 0xFAF7B918, 0xFAF7B910, 2190656) = 0 /65: fstat64(9, 0xFAF7B818) = 0 /65: getsockopt(9, 65535, 8192, 0xFAF7B918, 0xFAF7B914, 2190656) = 0 /65: setsockopt(9, 65535, 8192, 0xFAF7B918, 4, 2190656) = 0 /65: fcntl(9, F_SETFL, 0x00000082) = 0
接下来,工作线程以非阻塞模式将与客户端的连接(文件描述符9)。在setsockopt(2)
和getsockopt(2)
调用是Solaris的libc中如何把手的副作用fcntl(2)
的插座。
/65: read(9, " G E T / 1 0 k . h t m".., 8000) = 97
工作线程从客户端读取请求。
/65: stat("/var/httpd/apache/httpd-8999/htdocs/10k.html", 0xFAF7B978) = 0 /65: open("/var/httpd/apache/httpd-8999/htdocs/10k.html", O_RDONLY) = 10
此httpd已使用Options FollowSymLinks
和配置AllowOverride None
。因此,它不需要到达
lstat(2)
所需文件的路径中的每个目录,也不需要检查.htaccess
文件。它只是调用stat(2)
以验证文件:1)存在,并且2)是常规文件,而不是目录。
/65: sendfilev(0, 9, 0x00200F90, 2, 0xFAF7B53C) = 10269
在此示例中,httpd能够通过单个sendfilev(2)
系统调用发送HTTP响应标头和请求的文件。Sendfile语义在操作系统之间有所不同。在其他一些系统上,有必要在调用之前执行write(2)
或
writev(2)
调用以发送标头
sendfile(2)
。
/65: write(4, " 1 2 7 . 0 . 0 . 1 - ".., 78) = 78
该write(2)
调用将请求记录在访问日志中。请注意,此跟踪中缺少的一件事是
time(2)
调用。与Apache 1.3不同,Apache 2.x用于
gettimeofday(3)
查找时间。在某些操作系统(如Linux或Solaris)上,gettimeofday
它具有优化的实现,不需要像典型的系统调用那样大的开销。
/65: shutdown(9, 1, 1) = 0 /65: poll(0xFAF7B980, 1, 2000) = 1 /65: read(9, 0xFAF7BC20, 512) = 0 /65: close(9) = 0
辅助线程会持续关闭连接。
/65: close(10) = 0 /65: lwp_park(0x00000000, 0) (sleeping...)
最后,工作线程关闭它刚刚传递的文件并阻塞,直到侦听器为它分配另一个连接。
/67: accept(3, 0x001FEB74, 0x001FEB94, 1) (sleeping...)
同时,侦听器线程将连接分配给工作线程后,便能够接受另一个连接(受工作线程MPM中某些流控制逻辑的约束,如果所有可用工作线程都处于繁忙状态,则该流控制逻辑会限制侦听器)。尽管从此跟踪中看不出来,但下一个accept(2)
罐(通常在高负载条件下也可以)与工作线程对刚刚接受的连接的处理并行发生。