有关Apache的性能分析
时间:2007-05-28 来源:jannock_baby
分析对象
适用于UNIX系统的Apache1.3
分析内容
Apache服务器的运行流程()以及有可能进行的优化
分析的目标
记录Apache运行过程中各个部分消耗的时间记录,提出优化的方案
开始部分:
由于没有什么可以具体借鉴的文档,我们从分析Apache的运行流程入手,一下内容是我从网上查到的有关Apache运行记录(被请求的对象是一个6k左右的静态网页):
accept(15, {sin_family=AF_INET, sin_port=htons(22283), sin_addr=inet_addr("127.0.0.1"}, [16]) = 3
flock(18, LOCK_UN) = 0
sigaction(SIGUSR1, {SIG_IGN}, {0x8059954, [], SA_INTERRUPT}) = 0
getsockname(3, {sin_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("127.0.0.1"}, [16]) = 0
setsockopt(3, IPPROTO_TCP1, [1], 4) = 0
read(3, "GET /6k HTTP/1.0\r\nUser-Agent: "..., 4096) = 60
sigaction(SIGUSR1, {SIG_IGN}, {SIG_IGN}) = 0
time(NULL) = 873959960
gettimeofday({873959960, 404935}, NULL) = 0
stat("/home/dgaudet/ap/apachen/htdocs/6k", {st_mode=S_IFREG|0644, st_size=6144, ...}) = 0
open("/home/dgaudet/ap/apachen/htdocs/6k", O_RDONLY) = 4
mmap(0, 6144, PROT_READ, MAP_PRIVATE, 4, 0) = 0x400ee000
writev(3, [{"HTTP/1.1 200 OK\r\nDate: Thu, 11"..., 245}, {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 6144}], 2) = 6389
close(4) = 0
time(NULL) = 873959960
write(17, "127.0.0.1 - - [10/Sep/1997:23:39"..., 71) = 71
gettimeofday({873959960, 417742}, NULL) = 0
times({tms_utime=5, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 446747
shutdown(3, 1 /* send */) = 0
oldselect(4, [3], NULL, [3], {2, 0}) = 1 (in [3], left {2, 0})
read(3, "", 204 = 0
close(3) = 0
sigaction(SIGUSR1, {0x8059954, [], SA_INTERRUPT}, {SIG_IGN}) = 0
munmap(0x400ee000, 6144) = 0
flock(18, LOCK_EX) = 0
分类:
我们可以大致的对以上内容进行分类
1:socket请求接收部分,很清楚,应该是
accept(15, {sin_family=AF_INET, sin_port=htons(22283), sin_addr=inet_addr("127.0.0.1"}, [16]) = 3
getsockname(3, {sin_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("127.0.0.1"}, [16]) = 0
setsockopt(3, IPPROTO_TCP1, [1], 4) = 0
read(3, "GET /6k HTTP/1.0\r\nUser-Agent: "..., 4096) = 60
2:中断信号的处理部分:
sigaction(SIGUSR1, {SIG_IGN}, {0x8059954, [], SA_INTERRUPT}) = 0
sigaction(SIGUSR1, {SIG_IGN}, {SIG_IGN}) = 0
sigaction(SIGUSR1, {0x8059954, [], SA_INTERRUPT}, {SIG_IGN}) = 0
3:读取请求的静态网页并返回:
stat("/home/dgaudet/ap/apachen/htdocs/6k", {st_mode=S_IFREG|0644, st_size=6144, ...}) = 0
open("/home/dgaudet/ap/apachen/htdocs/6k", O_RDONLY) = 4
mmap(0, 6144, PROT_READ, MAP_PRIVATE, 4, 0) = 0x400ee000
writev(3, [{"HTTP/1.1 200 OK\r\nDate: Thu, 11"..., 245}, {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 6144}], 2) = 6389
close(4) = 0
4:日志记录:
time(NULL) = 873959960
write(17, "127.0.0.1 - - [10/Sep/1997:23:39"..., 71) = 71
gettimeofday({873959960, 417742}, NULL) = 0
times({tms_utime=5, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 446747
5:关闭连接:
shutdown(3, 1 /* send */) = 0
oldselect(4, [3], NULL, [3], {2, 0}) = 1 (in [3], left {2, 0})
read(3, "", 204 = 0
close(3) = 0
6:删除mmap文件链接
munmap(0x400ee000, 6144) = 0
分析:
子进程调度
Unix上的Apache是应用了预分支模型的服务器。父进程的责任仅在于繁衍子进程,它从不响应来自socket的任何请求。真正处理连接的是子进程,每个子进程在终止之前会(逐一地)为多个连接服务。父进程根据服务器负载的变化(通过监视记分板,记分板由子进程负责保持同步)生成新的或者杀掉旧的子进程。
在以上的分类过程中,有两句话
flock(18, LOCK_UN) = 0
flock(18, LOCK_EX) = 0
没有明确的含义,我们从这里开始。我在Apache代码http_main.c中找到了这个调用,它很明显是一段互斥作用的实现,它根据操作系统的不同,最终这组互斥调用都被封装到一组叫做
accept_mutex_off
accept_mutex_on
的函数中。
到这里就很清楚了,这部分是Apache针对不同的操作系统对于由哪个子进程接收请求的调度模块,在这里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;
}
Apache提供了如下的宏定义以改变互斥实现的方式ap_config.h
HAVE_USLOCK_SERIALIZED_ACCEPT
HAVE_PTHREAD_SERIALIZED_ACCEPT
HAVE_SYSVSEM_SERIALIZED_ACCEPT
HAVE_FCNTL_SERIALIZED_ACCEPT
HAVE_FLOCK_SERIALIZED_ACCEPT
HAVE_OS2SEM_SERIALIZED_ACCEPT
HAVE_TPF_CORE_SERIALIZED_ACCEPT
HAVE_BEOS_SERIALIZED_ACCEPT
HAVE_NONE_SERIALIZED_ACCEPT
我们可以在编译之前增加编译选现来实现不同的互斥调用以获得更高的性能,由于时间限制,我只找到了少部分这方面编译的资料
USE_SYSVSEM_SERIALIZED_ACCEPT (1.3版及以后)
此方法借助SysV的信号量(semaphores)实现互斥。但不巧的是SysV信号量有一些负面作用。一是Apache可能在清除信号量之前非正常终止;二是在使用信号量API时需要考虑到任何与服务器UID相同的CGI程序可以进行拒绝服务攻击(就是说所有的CGI程序都可以这样做,除非使用suexec或cgiwrapper之类的方法)。所以,这种方法并不被IRIX之外的系统广泛采纳(由于大多数IRIX系统上,使用前两种方法的代价太大)。
USE_USLOCK_SERIALIZED_ACCEPT
(1.3版及以后)此方法仅在IRIX上可用。它调用usconfig(2)创建互斥量。虽然这种方法避免了对SysV信号量的种种争议,但它不是IRIX的缺省方案。这是由于在单处理器的IRIX系统 (5.3或6.2)上
相关阅读 更多 +