- 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>
使用系统命令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 状态变迁图
2.1 总结图
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
- 我的微信
- 这是我的微信扫一扫
-
- 我的微信公众号
- 我的微信公众号扫一扫
-