apache性能调优研究笔记

阅读次数: 2,538

  • A+

问题:一大群真实的肉鸡用户来访问我的网站时,CPU飙升,服务器卡死。

想到的解决办法:

1.换服务器(由原来的apache换成nginx)结果:不行程序会出错,而且网站本身一直在运行中,如果修改代码那也将是很耗时。

2.找了一款集成软件 UPUPW ANK  介绍说是可以 apache  nginx 同时服务器您的网站,也有更强大的K模式。结果:安装使用后也会出问题,没花太多时间去研究,等待新的项目时再用这个工具。

3.购买高防服务器,硬防加强。结果:价格高昂-自己也学习不到真正的技术。


至此:开始找资料,百度 Google ,感觉好像有了思路但是能力有限,打算一点一点突破。

思路:1 .查看apache 运行模式 (三种模式 event模式,prefork模式,worker模式)

[root@qyi-5b5b1cfc3a5cd sysconfig]# httpd -l
Compiled in modules:
  core.c
  mod_so.c
  http_core.c
  event.c

worker模式:

<IfModule worker.c>
StartServers 2
MaxClients 150
MinSpareThreads 25
MaxSpareThreads 75
ThreadsPerChild 25
MaxRequestsPerChild 0
</IfModule>

apache启动时,

第一步根据 startservers 的配置建立子进程数2(默认是3)个,

第二步根据threadsperchild 25 来建立线程,

第三部根据 minsparethreads 25 来保持空闲的线程数一直大于或等于25个,

第四步,apache可以开始服务了(服务于客户),

第五步,来一个请求(客户)空闲的姑娘(线程)就要开始接客,第六步,每来一个客户空闲姑娘就少一个,需要生成一个新的空闲的姑娘(保持在25个),这样如此下去不断来新的客人,姑娘会不够的,

第七步,什么时候不够呢,如果按照上边的配置 每个子进程可以开25个线程,那50个客人来后岂不是姑娘不够了?

#define DEFAULT_SERVER_LIMIT 256
#define MAX_SERVER_LIMIT 2000

第八步,threadlimit 手动配置每个子进程可以开启的线程数上限,最大值为20000. (源码位置  server/mpm/worker/worker.c)

#define DEFAULT_THREAD_LIMIT 64
#define MAX_THREAD_LIMIT 20000

第九步,可以算出worker模式下最大的同时处理请求数,是由子进程数 startservers 乘以 threadsperchild 的积。默认最大的子进程数是16,也可以强行调大 通过 MAX_SERVER_LIMIT 20000 。由此可见(最大子进程数 20000 每个子进程可以开启线程数 20000,这两个相乘那么最大连接数 应该是 200000000个)

第十步,算出每一个线程所消耗的CPU资源和内存资源,这样就可以算出服务器的配置(相反知道服务器的配置也可以配置相应的apache配置)

event模式:

<IfModule mpm_event_module>
    ServerLimit         1000
    StartServers         20
    MinSpareThreads        25
    MaxSpareThreads      1200
    ThreadsPerChild      50
    MaxRequestWorkers    2000
    MaxConnectionsPerChild  1000
</IfModule>

 

跟worker模式基本一样

注意:这里有一个我不懂

MaxRequestWorkers 2000

其他文档里是这样介绍的:最大数量的工作线程,等于 ServerLimit*ThreadPerChild (子进程数*线程数)ServerLimit=16(默认的),2000>16*50(这个我不懂)文档说这样日志中会报错“MaxRequestWorkers of 1000 would require 20 servers and exceed ServerLimit of 16,decreasing to 800”

1000大最大的工作请求,需要20个进程并且超过了服务器设置的16这个值,正在减少到800个中。

prefork模式:

<IfModule prefork.c>
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxClients 150
MaxRequestsPerChild 0
</IfModule>

copy大佬文章 start


prefork的具体工作原理是,控制进程在最初建立“StartServers”个子进程后,为了满足MinSpareServers设置的需要创建一个进程,等待一秒钟,继续创建两个,再等待一秒钟,继续创建四个……如此按指数级增加创建的进程数,最多达到每秒32个,直到满足MinSpareServers设置的值为止。这就是预派生(prefork)的由来。这种模式可以不必在请求到来时再产生新的进程,从而减小了系统开销以增加性能。

当并发量请求数到达MaxClients(如256)时,而空闲进程只有10个。apache为继续增加创建进程。直到进程数到达256个。

当并发量高峰期过去了,并发请求数可能只有一个时,apache逐渐删除进程,直到进程数到达MaxSpareServers为止

StartServers:指定服务器启动时建立的子进程数量,prefork默认为5。

MinSpareServers :指定空闲子进程的最小数量,默认为5。假如当前空闲子进程数少于MinSpareServers ,那么Apache将以最大每秒一个的速度产生新的子进程。此参数不要设的太大。

MaxSpareServers:设置了最大的空闲进程数,默认为10。如果空闲进程数大于这个值,Apache父进程会自动kill掉一些多余子进程。这个值不要设得过大,但如果设的值比MinSpareServers小,Apache会自动把其调整为MinSpareServers+1。如果站点负载较大,可考虑同时加大MinSpareServers和MaxSpareServers。

MaxRequestsPerChild:设置的是每个子进程可处理的请求数。每个子进程在处理了“MaxRequestsPerChild”个请求后将自动销毁。0意味着无限,即子进程永不销毁。虽然缺省设为0可以使每个子进程处理更多的请求,但如果设成非零值也有两点重要的好处:

◆ 可防止意外的内存泄漏;

◆ 在服务器负载下降的时侯会自动减少子进程数。

因此,可根据服务器的负载来调整这个值。个人认为10000左右比较合适。

MaxClients:是这些指令中最为重要的一个,设定的是Apache可以同时处理的请求,是对Apache性能影响最大的参数。

其缺省值150是远远不够的,如果请求总数已达到这个值(可通过ps -ef|grep http|wc -l来确认),那么后面的请求就要排队,直到某个已处理请求完毕。这就是系统资源还剩下很多而HTTP访问却很慢的主要原因。系统管理员可以根据硬件配置和负载情况来动态调整这个值。

虽然理论上这个值越大,可以处理的请求就越多,但在Apache1.3默认的最大只能设置为256(这是个硬限制)。如果把这个值设为大于256,那么Apache将无法起动。事实上,256对于负载稍重的站点也是不够的。如果要加大这个值,必须在“configure”前手工修改的源代码树下的src/include/httpd.h中查找256,就会发现“#define HARD_SERVER_LIMIT 256”这行。把256改为要增大的值(如4000),然后重新编译Apache即可。

但在Apache 2.0中,新加入了ServerLimit指令,可以突破最大请求数为256的限制。 使得无须重编译Apache就可以加大MaxClients。下面是prefork配置段:

<IfModule prefork.c>
ServerLimit 2000
StartServers 10
MinSpareServers 10
MaxSpareServers 15
MaxClients 1000
MaxRequestsPerChild 10000
</IfModule>

ServerLimit:上述配置中,ServerLimit的最大值是2000,对于大多数站点已经足够。如果一定要再加大这个数值,对位于源代码树下server/mpm/prefork/prefork.c中以下两行做相应修改即可:

#define DEFAULT_SERVER_LIMIT 256
#define MAX_SERVER_LIMIT 2000

此时必须 MaxClients ≤ ServerLimit ≤ 2000. 即prefork的默认并发量最大是2000。

ServerLimit 生效前提:必须放在其他指令的前面,同时要想改变这个硬限制必须完全停止服务器然后再启动服务器(直接重启是不行的)。


copy大佬文章 end

开始计算 httpd 占用内存的平均数

ps aux|grep -v grep|awk '/httpd/{sum+=$6;n++};END{print sum/n}'


[root@qyi-5b5b1cfc3a5cd ~]# ps aux|grep -v grep|awk '/httpd/{sum+=$6;n++};END{print sum/n}'
560510

我的大概是 560kb 服务器剩余 3.5G可以使用

理论上可以 有6600个进程支持多少人同时访问我不会算。(这里我发现写的不对,在找资料文档中。。。)

ps aux 注释

USER: 行程拥有者
PID: pid
%CPU: 占用的 CPU 使用率
%MEM: 占用的记忆体使用率
VSZ: 占用的虚拟记忆体大小
RSS: 占用的记忆体大小
TTY: 终端的次要装置号码 (minor device number of tty)
STAT: 该行程的状态:
D: 不可中断的静止 (通悸□□缜b进行 I/O 动作)
R: 正在执行中
S: 静止状态
T: 暂停执行
Z: 不存在但暂时无法消除
W: 没有足够的记忆体分页可分配
<: 高优先序的行程
N: 低优先序的行程
L: 有记忆体分页分配并锁在记忆体内 (实时系统或捱A I/O)
START: 行程开始时间
TIME: 执行的时间
COMMAND:所执行的指令

查看当前的 http进程

[root@cloud mariadb]# ps -e -o 'pid,comm,args,pcpu,rsz,vsz,stime,user,uid' | grep httpd
 1829 httpd           /usr/local/apache/bin/httpd  0.0 40840 691580 11:45 root         0
 1831 httpd           /usr/local/apache/bin/httpd  0.0 21456 2060500 11:45 apache    500
 1846 httpd           /usr/local/apache/bin/httpd  0.0 21468 2060500 11:45 apache    500
 1890 httpd           /usr/local/apache/bin/httpd  0.0 21460 2060500 11:45 apache    500
 1966 httpd           /usr/local/apache/bin/httpd  0.0 21436 2060500 11:45 apache    500
 2033 httpd           /usr/local/apache/bin/httpd  0.0 21464 2060500 11:45 apache    500
 2105 httpd           /usr/local/apache/bin/httpd  0.0 21464 2060500 11:45 apache    500
 2186 httpd           /usr/local/apache/bin/httpd  0.0 21432 2060500 11:45 apache    500
 2263 httpd           /usr/local/apache/bin/httpd  0.0 32444 2324948 11:45 apache    500
 2319 httpd           /usr/local/apache/bin/httpd  0.0 25560 2126612 11:45 apache    500
 2329 httpd           /usr/local/apache/bin/httpd  0.0 21468 2060500 11:45 apache    500
 2366 httpd           /usr/local/apache/bin/httpd  0.0 21460 2060500 11:45 apache    500
 2544 httpd           /usr/local/apache/bin/httpd  0.0 32520 2324948 11:45 apache    500
 2578 httpd           /usr/local/apache/bin/httpd  0.0 21464 2060500 11:45 apache    500
 2919 httpd           /usr/local/apache/bin/httpd  0.0 21464 2060500 11:45 apache    500
 3213 httpd           /usr/local/apache/bin/httpd  0.0 41616 2654932 11:45 apache    500
 3245 httpd           /usr/local/apache/bin/httpd  0.0 64280 3184404 11:45 apache    500
 3262 httpd           /usr/local/apache/bin/httpd  0.0 34756 2391060 11:45 apache    500
 3392 httpd           /usr/local/apache/bin/httpd  0.0 21468 2060500 11:45 apache    500
 3468 httpd           /usr/local/apache/bin/httpd  0.0 50796 2853844 11:45 apache    500
 3582 httpd           /usr/local/apache/bin/httpd  0.0 64536 3316052 11:45 apache    500
 6806 grep            grep httpd                   0.0   888 103312 15:10 root         0

查看某个进程下的所有线程

方法1

ps -T -p <pid>

//这里线程太多就不贴代码视图了

方法2

top -H -p <pid>

apache性能调优研究笔记

使用系统命令top即可看到如下类似信息:
Cpu(s):  0.0%us,  0.5%sy,  0.0%ni, 99.5%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
但不知什么含义?google之

I try to explain  these:
us: is meaning of "user CPU time"
sy: is meaning of "system CPU time"
ni: is meaning of" nice CPU time"
id: is meaning of "idle"
wa: is meaning of "iowait" 
hi:is meaning of "hardware irq"
si : is meaning of "software irq"
st : is meaning of "steal time"

中文翻译为:

us 用户空间占用CPU百分比
sy 内核空间占用CPU百分比
ni 用户进程空间内改变过优先级的进程占用CPU百分比
id 空闲CPU百分比
wa 等待输入输出的CPU时间百分比
hi 硬件中断
si 软件中断 
st: 实时

查看某个TCP链接所用的进程及线程是哪个

netstat -natp  #比较简单的一种 (这里只看到了进程并没有线程)

进一步研究请用 strace netstat -ntpa 命令 追踪 netstat 命令流程 参考文档 https://blog.csdn.net/morphad/article/details/16867851

清除 不是 ESTABLISHED状态的链接==>对应的线程 (计算好不让服务器有压力)

监控某个线程对应的PHP中的SESSION (大量攻击来临时有必要清除没有SESSION的用户连接)(计算好清除没有登录的用户)(利用肉鸡攻击基本上不可能做登录后攻击的,大都是直接派兵出击)


内存信息查询 start

top -M

free -m

atop

cat /proc/meminfo

slabtop

ll /proc/12306/task/*/fd/ |grep socket |wc -l   //计算socket
ll /proc/12306/task/*/fd/  |wc -l   //计算数量
内存信息查询 end

TCP 链接中大量的 time_wait 出现的分析

Linux中是无法修改tcp的TIME_WAIT值的,除非重新编译,起码我是没有找到怎么改。值得注意的是,net.ipv4.tcp_fin_timeout这个参数是FIN_WAIT_2的值,而不是TIME_WAIT的值。我不知道为何很多人都会把它当成是TIME_WAIT的值,想了一下,我觉得是两点:
1.TIME_WAIT过于耀眼,以至于所有出现timeout,加上里面有个tcp的配置,都会想当然往TIME_WAIT上联系;
2.FIN_WAIT_2过于默默无闻,以至于很少有人知道它也是一种状态。

所以,我想大家在学习的时候,不能想当然。

TIME_WAIT的作用

TCP初见于互联网早期,当时的网络很不稳定,大量的丢包,为了冗余,大量包复制多径传输,网速慢--正因为如此,TCP才会更有意义。为了保证TCP的严格语义,就要避免上述冗余机制以及网速慢导致的问题,这些问题集中体现在连接关闭时的四次挥手上。
由于TCP是全双工的,因此关闭连接必须在两个方向上分别进行。首先发起关闭的一方为主动关闭方,另一方为被动关闭方。很多人都会在这里晕掉,实际上四次挥手比三次握手还简单。四次挥手简单地分为三个过程:
过程一.主动关闭方发送FIN,被动关闭方收到后发送该FIN的ACK;
过程二.被动关闭方发送FIN,主动关闭方收到后发送该FIN的ACK;
过程三.被动关闭方收到ACK。

以上三步下来,圆圈就闭合了!也就是说,在过程三后,被动关闭方就可以100%确认连接已经关闭,因此它便可以直接进入CLOSE状态了,然而主动关闭的一方,它无法确定最后的那个发给被动关闭方的ACK是否已经被收到,据TCP协议规范,不对ACK进行ACK,因此它不可能再收到被动关闭方的任何数据了,因此在这里就陷入了僵局,TCP连接的主动关闭方如何来保证圆圈的闭合?这里,协议外的东西起作用了,和STP(Spanning tree)依靠各类超时值来收敛一样,IP也有一个超时值,即MSL,这类超时值超级重要,因为它们给出了一个物理意义上的不可逾越的界限,它们是自洽协议的唯一外部输入。MSL表明这是IP报文在地球上存活的最长时间,如果在火星上,Linux的代码必须要重新定义MSL的值并且要重新编译。
于是问题就解决了,主动关闭一方等待MSL时间再释放连接,这个状态就是TIME_WAIT。对于被动关闭的一方,发出FIN之后就处在了LAST_ACK状态了,既然已经发出FIN了,缺的无非也就是个ACK,连接本身其实已经关闭了,因此被动关闭的一方就没有TIME_WAIT状态。
实际上,两倍MSL才能说明一个报文的彻底丢失,因为还要记入其ACK返回时的MSL。

TIME_WAIT的问题

这个就不多说了,由于TIME_WAIT的存在,短连接时关闭的socket会长时间占据大量的tuple空间。

TIME_WAIT的快速回收

Linux实现了一个TIME_WAIT状态快速回收的机制,即无需等待两倍的MSL这么久的时间,而是等待一个Retrans时间即释放,也就是等待一个重传时间(一般超级短,以至于你都来不及能在netstat -ant中看到TIME_WAIT状态)随即释放。释放了之后,一个连接的tuple元素信息就都没有了,而此时,新建立的TCP却面临着危险,什么危险呢?即:
1.可能被之前迟到的FIN包给终止的危险;
2.被之前连接劫持的危险;
...

于是需要有一定的手段避免这些危险。什么手段呢?虽然曾经连接的tuple信息没有了,但是在IP层还可以保存一个peer信息,注意这个信息不单单是用于TCP这个四层协议的,路由逻辑也会使用它,其字段包括但不限于:
对端IP地址
peer最后一次被TCP触摸到的时间戳

...
在快速释放掉TIME_WAIT连接之后,peer依然保留着。丢失的仅仅是端口信息。不过有了peer的IP地址信息以及TCP最后一次触摸它的时间戳就足够了,TCP规范给出一个优化,即一个新的连接除了同时触犯了以下几点,其它的均可以快速接入,即使它本应该处在TIME_WAIT状态(但是被即快速回收了):
1.来自同一台机器的TCP连接携带时间戳;
2.之前同一台peer机器(仅仅识别IP地址,因为连接被快速释放了,没了端口信息)的某个TCP数据在MSL秒之内到过本机;
3.新连接的时间戳小于peer机器上次TCP到来时的时间戳,且差值大于重放窗口戳。

看样子只有以上的3点的同时满足才能拒绝掉一个新连接,要比TIME_WAIT机制设置的障碍导致的连接拒绝几率小很多,但是要看到,上述的快速释放机制没有端口信息!这就把几率扩大了65535倍。然而,如果对于单独的机器而言,这不算什么,因此单台机器的时间戳不可能倒流的,出现上述的3点均满足时,一定是老的重复数据包又回来了。
但是,一旦涉及到NAT设备,就悲催了,因为NAT设备将数据包的源IP地址都改成了一个地址(或者少量的IP地址),但是却基本上不修改TCP包的时间戳,这就带来了问题。假设PC1和PC2均启用了TCP时间戳,它们经过NAT设备N1往服务器S1的22端口连接:
PC1:192.168.100.1
PC2:192.168.100.2
N1外网口(即NAT后的地址):172.16.100.1
S1:172.16.100.2
所有涉事机器的配置:
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1
TCP的时间戳是根据本机的jiffers,uptime计算的,现在我能保证PC2的时间戳肯定远小于PC1。现在在PC1上先做一个telnet:
telnet 172.16.100.2 22
连接成功,S1上抓包,得到时间戳timestamps:TS val 698583769
为了让S1主动关闭进而快速回收TIME_WAIT,在S1上执行:
kill $(ps -ef|grep 展开sh|grep acce|awk -F ' ' '{print $2}');
目的是把仅仅光完成三次握手的连接终止掉而不触动已经连接的ssh。此时马上在PC2上telnet:
telnet 172.16.100.2 22
不通!在S1上抓包,得到时间戳timestamps:TS val 27727766。明显小于PC1的!由于有NAT设备,S1看来是同一台机器发出的,且出现了时间戳倒流,连接拒绝!此时在S1上查看计数值:
cat /proc/net/netstat
发现了PAWSPassive对应的值增加了1,每次PC2重发SYN,该计数值均会增加1,直到一个MSL时间过后,才能连接成功。如果反过来就没有问题,即先在PC2上telnet,然后S1主动关闭,然后紧接着PC1上telnet依然可以成功,这是因为时间戳是递增的,不满足上述的第三点。

仅仅两台机器就出现了这个问题,试问如果大量的源端机器在服务器的入口处遇到了NAT设备会怎样?即一台三层NAT设备部署在高负载网站的入口处...没有谁能保证时间戳小的机器一定先发起连接,各个机器频繁连接断开后依然按照时间戳从小到大的顺序连接!!
TIME_WAIT快速回收在Linux上通过net.ipv4.tcp_tw_recycle启用,由于其根据时间戳来判定,所以必须开启TCP时间戳才有效。建议:如果前端部署了三/四层NAT设备,尽量关闭快速回收,以免发生NAT背后真实机器由于时间戳混乱导致的SYN拒绝问题。

TIME_WAIT重用

如果说TIME_WAIT(输入法切换太烦人了,后面简称TW)回收只是一种特定系统的优化实现的话,那么TW重用则有相关的规范,即:如果能保证以下任意一点,一个TW状态的四元组(即一个socket连接)可以重新被新到来的SYN连接使用:
1.初始序列号比TW老连接的末序列号大
2.如果使能了时间戳,那么新到来的连接的时间戳比老连接的时间戳大
Linux上完美实现了上述的特性,可以通过下面的实验来证实:
S1上的服务程序:侦听端口1234,accept新连接,发送一段数据后调用close主动关闭连接。
S1上的额外配置:通过iptables禁止RESET包进入第四层,因为它会将TW状态终结。
PC1上客户端程序:绑定192.168.100.1,2000端口,连接S1,获取数据后调用close关闭连接。
PC2上客户端程序:使用IP_TRANSPARENT选项同样绑定192.168.100.1地址和2000端口,其它和PC1的程序相同。
启动服务端S1:172.16.100.2,不断侦听端口1234;
启动PC1上的C1:192.168.100.1,端口2000,连接S1的1234端口;
此时在S1上抓包,获取正常的三次握手/数据传输/四次挥手数据包。此时在S1上netstat -ant可以看到一个TW状态的连接。
启动PC2上的C2:192.168.100.1,端口2000,连接S1的1234端口;
此时在S1上抓包,SYN序列号seq 3934898078大于PC1发起连接时的最后一个序列号[F.], seq 2513913083, ack 3712390788,S1正常回复SYNACK:Flags [S.], seq 3712456325, ack 3934898079, ...对于这种TW重用的情况,S1的SYNACK的初始序列号是通过TW状态老连接的最后一个ack,即3712390788,加上常量65535+2算出来的!
      以上的实验是在关闭时间戳的情况下完成的,实际上开启时间戳的话,重用的可能性更高一些,毕竟是否能重用一个TW连接是通过以上的条件之一来判断的!

从外部干掉TIME_WAIT

TIME_WAIT状态时则一个阑尾!Linux系统上,除了使能recycle tw,在Linux系统上你无法更简单地缩短TW状态的时间,但是80%的问题却都是由TW状态引发,在Windows系统上,你需要在注册表添加一个隐式的项,稍微拼写错误都会引发沉默的失败!TW确实让人生气,因此我一直都希望干掉TW状态的连接,特别是干掉服务端TW状态的连接!我们可以通过TCP的RESET来干死TW连接。这个怎么说呢?
根据TCP规范,收到任何的发送到未侦听端口或者序列号乱掉(窗口外)的数据,都要回执以RESET,这就是可以利用的!一个连接等待在TW,它自身无能为力,但是可以从外部间接杀掉它!具体来讲就是利用了IP_TRANSPARENT这个socket选项,它可以bind不属于本地的地址,因此可以从任意机器绑定TW连接的peer地址以及端口,然后发起一个连接,TW连接收到后由于序列号乱序会直接发送一个ACK,该ACK会回到TW连接的peer处,由于99%的可能该peer已经释放了连接(对端由于不能收到FIN-ACK的ACK,进而不放心ACK是否已经到达对端,等待MSL以便所有的老数据均已经丢失),因此peer由于没有该连接会回复RESET,TW连接收到RESET后会释放连接,进而为后续的连接腾出地方!

Linux实现Tips

Linux照顾到了一种特殊情况,即杀死进程的情况,在系统kill进程的时候,会直接调用连接的close函数单方面关闭一个方向的连接,然后并不会等待对端关闭另一个方向的连接进程即退出。现在的问题是,TCP规范和UNIX进程的文件描述符规范直接冲突!进程关闭了,套接字就要关闭,但是TCP是全双工的,你不能保证对端也在同一个时刻同意并且实施关闭动作,既然连接不能关闭,作为文件描述符,进程就不会关闭得彻底!所以,Linux使用了一种“子状态”的机制,即在进程退出的时候,单方面发送FIN,然后不等后续的关闭序列即将连接拷贝到一个占用资源更少的TW套接字,状态直接转入TIMW_WAIT,此时记录一个子状态FIN_WAIT_2,接下来的套接字就和原来的属于进程描述符的连接没有关系了。等到新的连接到来的时候,直接匹配到这个主状态为TW,子状态为FIN_WAIT_2的TW连接上,它负责处理FIN,FIN ACK等数据。

TIME_WAIT快速回收与重用

通过以上描述,我们看到TW状态的连接既可以被快速回收又可以被重用,但是二者的副作用是不同的。对于快速回收,由于丢失了TW连接的端口信息,全部映射到了IP地址信息,所以整个IP地址,也就是整机均被列入了考察对象,这本身并没有什么问题,因为快速回收只考虑时间戳信息,只要其保持单调递增即可,一般的机器时间是不会倒流的,但是遇到NAT合并就不行了,NAT设备为所有的内部设备代理一个IP地址即主机标识,然而却不触动其时间戳,而各个机器的时间戳并不满足任何规律...
TW重用解决了整机范围拒绝接入的问题,但是却面临资源消耗的问题。它这个做法的依据之一仍然为,一般一个单独的主机是不可能在MSL内用同一个端口连接同一个服务的,除非它做了bind。因此等待一些遗留的数据丢失或者到达是有盼头的。有一点我有异议,我个人感觉,如果处在默默地TW等待中,有默默地非递增SYN或者递增时间戳SYN到来,千万别发ACK,只要默默丢弃即可,因为你发了ACK,对方在已经终止了连接的情况下,就会发RESET,进而终止掉本段连接。

TIME_WAIT的80/20悲剧

80%的问题都由20%的TW引发,甚至在各种的TCP实现中,大量的代码在处理TW!我个人觉得这有点过了!引入TW状态是为了确认老数据到来或者消失,且等待时延那么久,这已经是很多年以前的事了,那时我可能刚出生,家里可能还没有装电话...那时的网络条件,引入这些机制是确实需要的,但是随着网络技术的发展,TW已经慢慢成了鸡肋。即便新的TCP连接被老的FIN终止又怎样,即使新的连接被老的劫持又能怎样,即便不考虑这些,MSL未免也太长了些吧,话说当年DDN年代,这个值就已经很久了...不要试图保持TCP的安全了,即使面对中间人又能怎样?我们不是可以用SSL吗?TCP作为一种底层的传输协议,一定要简单,可是现在呢?虽然其内核保持着原汁原味,但是其细节使多少求知若渴的人踱步门外啊,不得不说,TCP的细节太复杂了,即使是再好的作家,也无法写出一本让人彻底明白的关于TCP细节的书。
看看规范,各种公式,各种不可插拔的算法,各种魔术字,即使作者本人估计都很难说清楚内中细节。不得不说,TCP有点过度设计了,作为当年的设计精品,在当今越发往上层移动的年代,不合适了。如今越来越多的协议或者开元软件使用简单的UDP做扩展,在实现按序到达,确认,否认,时间戳,可靠连接等机制中实现自己需要的而不是所有,从TLS到OpenVPN,无一没有把UDP当成下一代的天骄。我很讨厌TCP,很讨厌这种乱七八糟的东西。你可能会反驳我,但我觉得你被洗脑了,你要知道,如果让你设计一个可靠的有连接协议,你可能做的真的比TCP更好。

 


1. socket的状态

1.1 状态说明


CLOSED 没有使用这个套接字[netstat 无法显示closed状态]
LISTEN 套接字正在监听连接[调用listen后]
SYN_SENT 套接字正在试图主动建立连接[发送SYN后还没有收到ACK]
SYN_RECEIVED 正在处于连接的初始同步状态[收到对方的SYN,但还没收到自己发过去的SYN的ACK]
ESTABLISHED 连接已建立
CLOSE_WAIT 远程套接字已经关闭:正在等待关闭这个套接字[被动关闭的一方收到FIN]
FIN_WAIT_1 套接字已关闭,正在关闭连接[发送FIN,没有收到ACK也没有收到FIN]
CLOSING 套接字已关闭,远程套接字正在关闭,暂时挂起关闭确认[在FIN_WAIT_1状态下收到被动方的FIN]
LAST_ACK 远程套接字已关闭,正在等待本地套接字的关闭确认[被动方在CLOSE_WAIT状态下发送FIN]
FIN_WAIT_2 套接字已关闭,正在等待远程套接字关闭[在FIN_WAIT_1状态下收到发过去FIN对应的ACK]
TIME_WAIT 这个套接字已经关闭,正在等待远程套接字的关闭传送[FIN、ACK、FIN、ACK都完毕,这是主动方的最后一个状态,在过了2MSL时间后变为CLOSED状态]

 

1.2 状态变迁图

apache性能调优研究笔记

2.1 总结图

 

apache性能调优研究笔记

2.2 说明

2.2.1   connect返回-1

             errno=110(ETIMEDOUT),当服务器端网线拔了的时候,客户端发送SYN过去就会收不到ACK,因此就会出现这个错误,1分钟内就会返  回这个错误。

             errno=111(ECONNREFUSED),当服务器未listen时,就会报这个错

2.2.2 ESTABLISHED不一定真的establish

              会出现这种情况:client为ESTABLISHED状态而server为SYN_REVD状态。

             这是因为LINUX不像其他操作系统在收到SYN为该连接立马分配一块内存空间用于存储相关的数据和结构,而是延迟到接收到client的ACK,即三次握手    真正完成后才分配空间,这是为了防范SYN flooding攻击。 如果是这种情况,那么就会出现client端未ESTABLISHED状态,server为SYN_RECV状态。

             并且server的SYN_RECV状态在一定时间后会消失,client的established状态也会消失。这是因为server在SYN_RECV状态时,会像client发送多次的SYN+ACK(因为他以为自己的这个包对方没收到),发送的次数定义在/proc/sys/net/ipv4/tcp_synack_retries中,默认为5.在发送5次之后还没有收到ACK,就将其回收了,所以用netstat查看就看不到这个SYN_RECV状态了。并且会像client发送RST信号。这就会导致client的这种半连接最后也会消失。这个可以通过tcpdump抓包得到(最好知道src这样看到的包比较集中)。


监听例子

#!/bin/bash
. /etc/init.d/functions         #引入函数库
check_count=0
# 定义检测的URL数组,包含多个URL地址
url_list=(
http://www.baidu.com
http://127.0.0.1
)
function wait()
{
    echo -n '3秒后,执行检查URL操作.';
    for ((i=0;i<3;i++))
    do
        echo -n "."; sleep 1
    done
    echo
}
function check_url()    #<==定义检测URL的函数
{
    wait #<==执行倒计时函数
    for((i=0;i<`echo ${#url_list[*]}`;i++)) #<==循环数组元素
    do
        wget -o /dev/null -T 3 --tries=1 --spider ${url_list[$i]} >/dev/null 2>&1
                                        #<==检测是否可以访问数组元素的地址
        if [ $? -eq 0 ]                 #<==如果返回值为0,则表示访问成功
            then
               action "${url_list[$i]}" /bin/true #<==优雅地显示成功结果
        else
               action "${url_list[$i]}" /bin/false #<==优雅地显示失败结果
        fi
    done
    ((check_count++)) #<==检测次数+1
}
main(){                 #<==定义主函数
    while true          #<==开启一个持续循环
    do
        check_url       #<==加载检测url的函数
        echo "--------------check count:${check_count}-------------------"
        sleep 10                #<==间歇10秒
    done
}
main            #<==调用主函数运行程序

 

附:apache  main.c

#include "apr.h"  
#include "apr_strings.h"  
#include "apr_getopt.h"  
#include "apr_general.h"  
#include "apr_lib.h"  
#include "apr_md5.h"  
#include "apr_time.h"  
#include "apr_version.h"  
#include "apu_version.h"  
  
#define APR_WANT_STDIO  
#define APR_WANT_STRFUNC  
#include "apr_want.h"  
  
#define CORE_PRIVATE  
#include "ap_config.h"  
#include "httpd.h"  
#include "http_main.h"  
#include "http_log.h"  
#include "http_config.h"  
#include "http_core.h"  
#include "http_vhost.h"  
#include "apr_uri.h"  
#include "util_ebcdic.h"  
#include "ap_mpm.h"  
#include "mpm_common.h"  
  
/* WARNING: Win32 binds http_main.c dynamically to the server. Please place 
 *          extern functions and global data in another appropriate module. 
 * 
 * Most significant main() global data can be found in http_config.c 
 */  
  
static void show_mpm_settings(void)  
{  
    int mpm_query_info;  
    apr_status_t retval;  
  
    printf("Server MPM:     %s\n", ap_show_mpm());  
  
    retval = ap_mpm_query(AP_MPMQ_IS_THREADED, &mpm_query_info);  
  
    if (retval == APR_SUCCESS) {  
        printf("  threaded:     ");  
  
        if (mpm_query_info == AP_MPMQ_DYNAMIC) {  
            printf("yes (variable thread count)\n");  
        }  
        else if (mpm_query_info == AP_MPMQ_STATIC) {  
            printf("yes (fixed thread count)\n");  
        }  
        else {  
            printf("no\n");  
        }  
    }  
  
    retval = ap_mpm_query(AP_MPMQ_IS_FORKED, &mpm_query_info);  
  
    if (retval == APR_SUCCESS) {  
        printf("    forked:     ");  
  
        if (mpm_query_info == AP_MPMQ_DYNAMIC) {  
            printf("yes (variable process count)\n");  
        }  
        else if (mpm_query_info == AP_MPMQ_STATIC) {  
            printf("yes (fixed process count)\n");  
        }  
        else {  
            printf("no\n");  
        }  
    }  
}  
  
static void show_compile_settings(void)  
{  
    printf("Server version: %s\n", ap_get_server_description());  
    printf("Server built:   %s\n", ap_get_server_built());  
    printf("Server's Module Magic Number: %u:%u\n",  
           MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR);  
    printf("Server loaded:  APR %s, APR-Util %s\n",  
           apr_version_string(), apu_version_string());  
    printf("Compiled using: APR %s, APR-Util %s\n",  
           APR_VERSION_STRING, APU_VERSION_STRING);  
    /* sizeof(foo) is long on some platforms so we might as well 
     * make it long everywhere to keep the printf format 
     * consistent 
     */  
    printf("Architecture:   %ld-bit\n", 8 * (long)sizeof(void *));  
  
    show_mpm_settings();  
  
    printf("Server compiled with....\n");  
#ifdef BIG_SECURITY_HOLE  
    printf(" -D BIG_SECURITY_HOLE\n");  
#endif  
  
#ifdef SECURITY_HOLE_PASS_AUTHORIZATION  
    printf(" -D SECURITY_HOLE_PASS_AUTHORIZATION\n");  
#endif  
  
#ifdef OS  
    printf(" -D OS=\"" OS "\"\n");  
#endif  
  
#ifdef APACHE_MPM_DIR  
    printf(" -D APACHE_MPM_DIR=\"" APACHE_MPM_DIR "\"\n");  
#endif  
  
#ifdef HAVE_SHMGET  
    printf(" -D HAVE_SHMGET\n");  
#endif  
  
#if APR_FILE_BASED_SHM  
    printf(" -D APR_FILE_BASED_SHM\n");  
#endif  
  
#if APR_HAS_SENDFILE  
    printf(" -D APR_HAS_SENDFILE\n");  
#endif  
  
#if APR_HAS_MMAP  
    printf(" -D APR_HAS_MMAP\n");  
#endif  
  
#ifdef NO_WRITEV  
    printf(" -D NO_WRITEV\n");  
#endif  
  
#ifdef NO_LINGCLOSE  
    printf(" -D NO_LINGCLOSE\n");  
#endif  
  
#if APR_HAVE_IPV6  
    printf(" -D APR_HAVE_IPV6 (IPv4-mapped addresses ");  
#ifdef AP_ENABLE_V4_MAPPED  
    printf("enabled)\n");  
#else  
    printf("disabled)\n");  
#endif  
#endif  
  
#if APR_USE_FLOCK_SERIALIZE  
    printf(" -D APR_USE_FLOCK_SERIALIZE\n");  
#endif  
  
#if APR_USE_SYSVSEM_SERIALIZE  
    printf(" -D APR_USE_SYSVSEM_SERIALIZE\n");  
#endif  
  
#if APR_USE_POSIXSEM_SERIALIZE  
    printf(" -D APR_USE_POSIXSEM_SERIALIZE\n");  
#endif  
  
#if APR_USE_FCNTL_SERIALIZE  
    printf(" -D APR_USE_FCNTL_SERIALIZE\n");  
#endif  
  
#if APR_USE_PROC_PTHREAD_SERIALIZE  
    printf(" -D APR_USE_PROC_PTHREAD_SERIALIZE\n");  
#endif  
  
#if APR_USE_PTHREAD_SERIALIZE  
    printf(" -D APR_USE_PTHREAD_SERIALIZE\n");  
#endif  
  
#if APR_PROCESS_LOCK_IS_GLOBAL  
    printf(" -D APR_PROCESS_LOCK_IS_GLOBAL\n");  
#endif  
  
#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT  
    printf(" -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT\n");  
#endif  
  
#if APR_HAS_OTHER_CHILD  
    printf(" -D APR_HAS_OTHER_CHILD\n");  
#endif  
  
#ifdef AP_HAVE_RELIABLE_PIPED_LOGS  
    printf(" -D AP_HAVE_RELIABLE_PIPED_LOGS\n");  
#endif  
  
#ifdef BUFFERED_LOGS  
    printf(" -D BUFFERED_LOGS\n");  
#ifdef PIPE_BUF  
    printf(" -D PIPE_BUF=%ld\n",(long)PIPE_BUF);  
#endif  
#endif  
  
    printf(" -D DYNAMIC_MODULE_LIMIT=%ld\n",(long)DYNAMIC_MODULE_LIMIT);  
  
#if APR_CHARSET_EBCDIC  
    printf(" -D APR_CHARSET_EBCDIC\n");  
#endif  
  
#ifdef NEED_HASHBANG_EMUL  
    printf(" -D NEED_HASHBANG_EMUL\n");  
#endif  
  
#ifdef SHARED_CORE  
    printf(" -D SHARED_CORE\n");  
#endif  
  
/* This list displays the compiled in default paths: */  
#ifdef HTTPD_ROOT  
    printf(" -D HTTPD_ROOT=\"" HTTPD_ROOT "\"\n");  
#endif  
  
#ifdef SUEXEC_BIN  
    printf(" -D SUEXEC_BIN=\"" SUEXEC_BIN "\"\n");  
#endif  
  
#if defined(SHARED_CORE) && defined(SHARED_CORE_DIR)  
    printf(" -D SHARED_CORE_DIR=\"" SHARED_CORE_DIR "\"\n");  
#endif  
  
#ifdef DEFAULT_PIDLOG  
    printf(" -D DEFAULT_PIDLOG=\"" DEFAULT_PIDLOG "\"\n");  
#endif  
  
#ifdef DEFAULT_SCOREBOARD  
    printf(" -D DEFAULT_SCOREBOARD=\"" DEFAULT_SCOREBOARD "\"\n");  
#endif  
  
#ifdef DEFAULT_LOCKFILE  
    printf(" -D DEFAULT_LOCKFILE=\"" DEFAULT_LOCKFILE "\"\n");  
#endif  
  
#ifdef DEFAULT_ERRORLOG  
    printf(" -D DEFAULT_ERRORLOG=\"" DEFAULT_ERRORLOG "\"\n");  
#endif  
  
#ifdef AP_TYPES_CONFIG_FILE  
    printf(" -D AP_TYPES_CONFIG_FILE=\"" AP_TYPES_CONFIG_FILE "\"\n");  
#endif  
  
#ifdef SERVER_CONFIG_FILE  
    printf(" -D SERVER_CONFIG_FILE=\"" SERVER_CONFIG_FILE "\"\n");  
#endif  
}  
  
#define TASK_SWITCH_SLEEP 10000  
  
static void destroy_and_exit_process(process_rec *process,  
                                     int process_exit_value)  
{  
    /* 
     * Sleep for TASK_SWITCH_SLEEP micro seconds to cause a task switch on 
     * OS layer and thus give possibly started piped loggers a chance to 
     * process their input. Otherwise it is possible that they get killed 
     * by us before they can do so. In this case maybe valueable log messages 
     * might get lost. 
     */  
    apr_sleep(TASK_SWITCH_SLEEP);  
    apr_pool_destroy(process->pool); /* and destroy all descendent pools */  
    apr_terminate();  
    exit(process_exit_value);  
}  
  
static process_rec *init_process(int *argc, const char * const * *argv)  
{  
    process_rec *process;  
    apr_pool_t *cntx;  
    apr_status_t stat;  
    const char *failed = "apr_app_initialize()";  
  
    stat = apr_app_initialize(argc, argv, NULL);  
    if (stat == APR_SUCCESS) {  
        failed = "apr_pool_create()";  
        stat = apr_pool_create(&cntx, NULL);  
    }  
  
    if (stat != APR_SUCCESS) {  
        /* For all intents and purposes, this is impossibly unlikely, 
         * but APR doesn't exist yet, we can't use it for reporting 
         * these earliest two failures; 
         */  
        char ctimebuff[APR_CTIME_LEN];  
        apr_ctime(ctimebuff, apr_time_now());  
        fprintf(stderr, "[%s] [crit] (%d) %s: %s failed "  
                        "to initial context, exiting\n",   
                        ctimebuff, stat, (*argv)[0], failed);  
        apr_terminate();  
        exit(1);  
    }  
  
    apr_pool_tag(cntx, "process");  
    ap_open_stderr_log(cntx);  
  
    /* Now we have initialized apr and our logger, no more 
     * exceptional error reporting required for the lifetime 
     * of this server process. 
     */  
  
    process = apr_palloc(cntx, sizeof(process_rec)); /* 分配内存,cntx为分配内存的地址 */  
    process->pool = cntx;  
  
    apr_pool_create(&process->pconf, process->pool);  
    apr_pool_tag(process->pconf, "pconf");  
    process->argc = *argc;  
    process->argv = *argv;  
    process->short_name = apr_filepath_name_get((*argv)[0]);  
    return process;  
}  
  
static void usage(process_rec *process)  
{  
    const char *bin = process->argv[0];  
    int pad_len = strlen(bin);  
  
#ifdef SHARED_CORE  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL ,  
                 "Usage: %s [-R directory] [-D name] [-d directory] [-f file]",  
                 bin);  
#else  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "Usage: %s [-D name] [-d directory] [-f file]", bin);  
#endif  
  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "       %*s [-C \"directive\"] [-c \"directive\"]", pad_len, " ");  
  
#ifdef WIN32  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "       %*s [-w] [-k start|restart|stop|shutdown]", pad_len, " ");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "       %*s [-k install|config|uninstall] [-n service_name]",  
                 pad_len, " ");  
#endif  
#ifdef AP_MPM_WANT_SIGNAL_SERVER  
#ifdef AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "       %*s [-k start|restart|graceful|graceful-stop|stop]",  
                 pad_len, " ");  
#else  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "       %*s [-k start|restart|graceful|stop]", pad_len, " ");  
#endif /* AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN */  
#endif  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "       %*s [-v] [-V] [-h] [-l] [-L] [-t] [-T] [-S]",  
                 pad_len, " ");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "Options:");  
  
#ifdef SHARED_CORE  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -R directory       : specify an alternate location for "  
                 "shared object files");  
#endif  
  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -D name            : define a name for use in "  
                 "<IfDefine name> directives");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -d directory       : specify an alternate initial "  
                 "ServerRoot");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -f file            : specify an alternate ServerConfigFile");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -C \"directive\"     : process directive before reading "  
                 "config files");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -c \"directive\"     : process directive after reading "  
                 "config files");  
  
#ifdef NETWARE  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -n name            : set screen name");  
#endif  
#ifdef WIN32  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -n name            : set service name and use its "  
                 "ServerConfigFile");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -k start           : tell Apache to start");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -k restart         : tell running Apache to do a graceful "  
                 "restart");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -k stop|shutdown   : tell running Apache to shutdown");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -k install         : install an Apache service");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -k config          : change startup Options of an Apache "  
                 "service");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -k uninstall       : uninstall an Apache service");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -w                 : hold open the console window on error");  
#endif  
  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -e level           : show startup errors of level "  
                 "(see LogLevel)");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -E file            : log startup errors to file");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -v                 : show version number");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -V                 : show compile settings");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -h                 : list available command line options "  
                 "(this page)");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -l                 : list compiled in modules");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -L                 : list available configuration "  
                 "directives");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -t -D DUMP_VHOSTS  : show parsed settings (currently only "  
                 "vhost settings)");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -S                 : a synonym for -t -D DUMP_VHOSTS");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -t -D DUMP_MODULES : show all loaded modules ");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -M                 : a synonym for -t -D DUMP_MODULES");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                 "  -t                 : run syntax check for config files");  
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
                "  -T                 : start without DocumentRoot(s) check");  
  
    destroy_and_exit_process(process, 1);  
}  
  
int main(int argc, const char * const argv[])  
{  
    char c;  
    int configtestonly = 0;  
    const char *confname = SERVER_CONFIG_FILE;  
    const char *def_server_root = HTTPD_ROOT;  
    const char *temp_error_log = NULL;  
    const char *error;  
    process_rec *process;  
    server_rec *server_conf;  
    apr_pool_t *pglobal; /* 基本池类型 */  
    apr_pool_t *pconf;  
    apr_pool_t *plog; /* Pool of log streams, reset _after_ each read of conf */  
    apr_pool_t *ptemp; /* Pool for temporary config stuff, reset often */  
    apr_pool_t *pcommands; /* Pool for -D, -C and -c switches */  
    apr_getopt_t *opt;  
    apr_status_t rv;  
    module **mod;  
    const char *optarg;  
    APR_OPTIONAL_FN_TYPE(ap_signal_server) *signal_server;  
  
    AP_MONCONTROL(0); /* turn off profiling of startup 代码分析开关 */  
  
    process = init_process(&argc, &argv); /* process_rec表示进程的结构体 */  
    pglobal = process->pool;  
    pconf = process->pconf;  
    ap_server_argv0 = process->short_name;  
  
#if APR_CHARSET_EBCDIC  
    if (ap_init_ebcdic(pglobal) != APR_SUCCESS) {  
        destroy_and_exit_process(process, 1);  
    }  
#endif  
  
    apr_pool_create(&pcommands, pglobal); /* 创建命令行参数池 */  
    apr_pool_tag(pcommands, "pcommands"); /* 给参数池加一个标签 */  
    ap_server_pre_read_config  = apr_array_make(pcommands, 1, sizeof(char *));  
    ap_server_post_read_config = apr_array_make(pcommands, 1, sizeof(char *));  
    ap_server_config_defines   = apr_array_make(pcommands, 1, sizeof(char *));  
  
    error = ap_setup_prelinked_modules(process);  
    if (error) {  
        ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_EMERG, 0, NULL, "%s: %s",  
                     ap_server_argv0, error);  
        destroy_and_exit_process(process, 1);  
    }  
  
    ap_run_rewrite_args(process);  
  
    /* Maintain AP_SERVER_BASEARGS list in http_main.h to allow the MPM 
     * to safely pass on our args from its rewrite_args() handler. 
     */  
    apr_getopt_init(&opt, pcommands, process->argc, process->argv);  
  
    while ((rv = apr_getopt(opt, AP_SERVER_BASEARGS, &c, &optarg))  
            == APR_SUCCESS) {  
        char **new;  
  
        switch (c) {  
        case 'c':  
            new = (char **)apr_array_push(ap_server_post_read_config);  
            *new = apr_pstrdup(pcommands, optarg);  
            break;  
  
        case 'C':  
            new = (char **)apr_array_push(ap_server_pre_read_config);  
            *new = apr_pstrdup(pcommands, optarg);  
            break;  
  
        case 'd':  
            def_server_root = optarg;  
            break;  
  
        case 'D':  
            new = (char **)apr_array_push(ap_server_config_defines);  
            *new = apr_pstrdup(pcommands, optarg);  
            /* Setting -D DUMP_VHOSTS is equivalent to setting -S */  
            if (strcmp(optarg, "DUMP_VHOSTS") == 0)  
                configtestonly = 1;  
            /* Setting -D DUMP_MODULES is equivalent to setting -M */  
            if (strcmp(optarg, "DUMP_MODULES") == 0)  
                configtestonly = 1;  
            break;  
  
        case 'e':  
            if (strcasecmp(optarg, "emerg") == 0) {  
                ap_default_loglevel = APLOG_EMERG;  
            }  
            else if (strcasecmp(optarg, "alert") == 0) {  
                ap_default_loglevel = APLOG_ALERT;  
            }  
            else if (strcasecmp(optarg, "crit") == 0) {  
                ap_default_loglevel = APLOG_CRIT;  
            }  
            else if (strncasecmp(optarg, "err", 3) == 0) {  
                ap_default_loglevel = APLOG_ERR;  
            }  
            else if (strncasecmp(optarg, "warn", 4) == 0) {  
                ap_default_loglevel = APLOG_WARNING;  
            }  
            else if (strcasecmp(optarg, "notice") == 0) {  
                ap_default_loglevel = APLOG_NOTICE;  
            }  
            else if (strcasecmp(optarg, "info") == 0) {  
                ap_default_loglevel = APLOG_INFO;  
            }  
            else if (strcasecmp(optarg, "debug") == 0) {  
                ap_default_loglevel = APLOG_DEBUG;  
            }  
            else {  
                usage(process);  
            }  
            break;  
  
        case 'E':  
            temp_error_log = apr_pstrdup(process->pool, optarg);  
            break;  
  
        case 'X':  
            new = (char **)apr_array_push(ap_server_config_defines);  
            *new = "DEBUG";  
            break;  
  
        case 'f':  
            confname = optarg;  
            break;  
  
        case 'v':  
            printf("Server version: %s\n", ap_get_server_description());  
            printf("Server built:   %s\n", ap_get_server_built());  
            destroy_and_exit_process(process, 0);  
  
        case 'V':  
            show_compile_settings();  
            destroy_and_exit_process(process, 0);  
  
        case 'l':  
            ap_show_modules();  
            destroy_and_exit_process(process, 0);  
  
        case 'L':  
            ap_show_directives();  
            destroy_and_exit_process(process, 0);  
  
        case 't':  
            configtestonly = 1;  
            break;  
  
       case 'T':  
           ap_document_root_check = 0;  
           break;  
  
        case 'S':  
            configtestonly = 1;  
            new = (char **)apr_array_push(ap_server_config_defines);  
            *new = "DUMP_VHOSTS";  
            break;  
  
        case 'M':  
            configtestonly = 1;  
            new = (char **)apr_array_push(ap_server_config_defines);  
            *new = "DUMP_MODULES";  
            break;  
  
        case 'h':  
        case '?':  
            usage(process);  
        }  
    }  
  
    /* bad cmdline option?  then we die */  
    if (rv != APR_EOF || opt->ind < opt->argc) {  
        usage(process);  
    }  
  
    apr_pool_create(&plog, pglobal);  
    apr_pool_tag(plog, "plog");  
    apr_pool_create(&ptemp, pconf);  
    apr_pool_tag(ptemp, "ptemp");  
  
    /* Note that we preflight the config file once 
     * before reading it _again_ in the main loop. 
     * This allows things, log files configuration 
     * for example, to settle down. 
     */  
  
    ap_server_root = def_server_root;  
    if (temp_error_log) {  
        ap_replace_stderr_log(process->pool, temp_error_log);  
    }  
    server_conf = ap_read_config(process, ptemp, confname, &ap_conftree);  
    if (!server_conf) {  
        destroy_and_exit_process(process, 1);  
    }  
  
    if (ap_run_pre_config(pconf, plog, ptemp) != OK) {  
        ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR, 0,  
                     NULL, "Pre-configuration failed");  
        destroy_and_exit_process(process, 1);  
    }  
  
    rv = ap_process_config_tree(server_conf, ap_conftree,  
                                process->pconf, ptemp);  
    if (rv == OK) {  
        ap_fixup_virtual_hosts(pconf, server_conf);  
        ap_fini_vhost_config(pconf, server_conf);  
        apr_hook_sort_all();  
  
        if (configtestonly) {  
            ap_run_test_config(pconf, server_conf);  
            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, "Syntax OK");  
            destroy_and_exit_process(process, 0);  
        }  
    }  
  
    signal_server = APR_RETRIEVE_OPTIONAL_FN(ap_signal_server);  
    if (signal_server) {  
        int exit_status;  
  
        if (signal_server(&exit_status, pconf) != 0) {  
            destroy_and_exit_process(process, exit_status);  
        }  
    }  
  
    /* If our config failed, deal with that here. */  
    if (rv != OK) {  
        destroy_and_exit_process(process, 1);  
    }  
  
    apr_pool_clear(plog);  
  
    if ( ap_run_open_logs(pconf, plog, ptemp, server_conf) != OK) {  
        ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,  
                     0, NULL, "Unable to open logs");  
        destroy_and_exit_process(process, 1);  
    }  
  
    if ( ap_run_post_config(pconf, plog, ptemp, server_conf) != OK) {  
        ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR, 0,  
                     NULL, "Configuration Failed");  
        destroy_and_exit_process(process, 1);  
    }  
  
    apr_pool_destroy(ptemp);  
  
    for (;;) {  
        apr_hook_deregister_all();  
        apr_pool_clear(pconf);  
  
        for (mod = ap_prelinked_modules; *mod != NULL; mod++) {  
            ap_register_hooks(*mod, pconf);  
        }  
  
        /* This is a hack until we finish the code so that it only reads 
         * the config file once and just operates on the tree already in 
         * memory.  rbb 
         */  
        ap_conftree = NULL;  
        apr_pool_create(&ptemp, pconf);  
        apr_pool_tag(ptemp, "ptemp");  
        ap_server_root = def_server_root;  
        server_conf = ap_read_config(process, ptemp, confname, &ap_conftree);  
        if (!server_conf) {  
            destroy_and_exit_process(process, 1);  
        }  
  
        if (ap_run_pre_config(pconf, plog, ptemp) != OK) {  
            ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,  
                         0, NULL, "Pre-configuration failed");  
            destroy_and_exit_process(process, 1);  
        }  
  
        if (ap_process_config_tree(server_conf, ap_conftree, process->pconf,  
                                   ptemp) != OK) {  
            destroy_and_exit_process(process, 1);  
        }  
        ap_fixup_virtual_hosts(pconf, server_conf);  
        ap_fini_vhost_config(pconf, server_conf);  
        apr_hook_sort_all();  
        apr_pool_clear(plog);  
        if (ap_run_open_logs(pconf, plog, ptemp, server_conf) != OK) {  
            ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,  
                         0, NULL, "Unable to open logs");  
            destroy_and_exit_process(process, 1);  
        }  
  
        if (ap_run_post_config(pconf, plog, ptemp, server_conf) != OK) {  
            ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,  
                         0, NULL, "Configuration Failed");  
            destroy_and_exit_process(process, 1);  
        }  
  
        apr_pool_destroy(ptemp);  
        apr_pool_lock(pconf, 1);  
  
        ap_run_optional_fn_retrieve();  
  
        if (ap_mpm_run(pconf, plog, server_conf))  
            break;  
  
        apr_pool_lock(pconf, 0);  
    }  
  
    apr_pool_lock(pconf, 0);  
    destroy_and_exit_process(process, 0);  
  
    return 0; /* Termination 'ok' */  
}  
  
#ifdef AP_USING_AUTOCONF  
/* This ugly little hack pulls any function referenced in exports.c into 
 * the web server.  exports.c is generated during the build, and it 
 * has all of the APR functions specified by the apr/apr.exports and 
 * apr-util/aprutil.exports files. 
 */  
const void *suck_in_APR(void);  
const void *suck_in_APR(void)  
{  
    extern const void *ap_ugly_hack;  
  
    return ap_ugly_hack;  
}  
#endif 

来自时刻需

  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: