kk Blog —— 通用基础

date [-d @int|str] [+%s|"+%F %T"]

CentOS 5.x安装新内核之后时钟混乱问题

1
2
$ ll /etc/rc.sysinit
/etc/rc.sysinit -> rc.d/rc.sysinit

el5在调用mkinitrd命令时,会将/dev/rtc生成好,放到initrd- x.x.x.img文件中。而el6的系统在 /etc/rc.sysinit的/sbin/start_udev 之前是有这两个文件,也没找到el6的系统是在哪里加的这两句。

el5可选的一个做法是:修改/etc/rc.sysinit,在/sbin/start_udev这行之前加入两行:

1
2
mv /dev/rtc /dev/rtc0
ln -sf rtc0 /dev/rtc

在/sbin/start_udev这行之后加入一行

1
[ -x /sbin/hwclock ] && /sbin/hwclock $CLOCKFLAGS

这样el5系统用18、32内核都没问题了。

el5试着将这两句改在/sbin/mkinitrd里修改,但不知道为什么改完后在执行到 /etc/rc.sysinit 时 /dev/rtc 这个软连接不见了。

或者直接将/dev/rtc改成254,0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
diff --git a/mkinitrd b/mkinitrd
index 5ddb909..dcba61d 100755
--- a/mkinitrd
+++ b/mkinitrd
@@ -1708,7 +1708,14 @@ done
 mknod $MNTIMAGE/dev/tty c 5 0
 mknod $MNTIMAGE/dev/console c 5 1
 mknod $MNTIMAGE/dev/ptmx c 5 2
-mknod $MNTIMAGE/dev/rtc c 10 135
+
+kernelval=`echo $kernel | awk -F "[-|.]" '{print $1*65536+$2*256+$3}'`
+#echo "kernel=$kernel kernelval=$kernelval"
+if [ $kernelval -lt 132640 ]; then
+ mknod $MNTIMAGE/dev/rtc c 10 135
+else
+ mknod $MNTIMAGE/dev/rtc c 254 0
+fi
 
 if [ "$(uname -m)" == "ia64" ]; then
	 mknod $MNTIMAGE/dev/efirtc c 10 136
@@ -1911,8 +1918,16 @@ mknod /dev/systty c 4 0
 mknod /dev/tty c 5 0
 mknod /dev/console c 5 1
 mknod /dev/ptmx c 5 2
-mknod /dev/rtc c 10 135
 EOF
+
+kernelval=`echo $kernel | awk -F "[-|.]" '{print $1*65536+$2*256+$3}'`
+#echo "kernel=$kernel kernelval=$kernelval"
+if [ $kernelval -lt 132640 ]; then
+ emit "mknod /dev/rtc c 10 135"
+else
+ emit "mknod /dev/rtc c 254 0"
+fi
+
 if [ "$(uname -m)" == "ia64" ]; then
	 emit "mknod /dev/efirtc c 10 136"
 fi

然后重建img

1
/sbin/new-kernel-pkg --package kernel --mkinitrd --depmod --install 2.6.32-XXX

http://www.csdn123.com/html/mycsdn20140110/59/59dd8c5f069a09bf9dc1785e19eb329f.html

CentOS在安装完新内核之后,每次重启之后时钟总是会发生一些变化,使得系统时钟不准确。在多操作系统的情况下(例如windows和 linux双系统),还可能会出现时区的偏差,而且无论如何设置,在重启之后都会恢复原样。如何解决这个问题还得从操作系统的时钟原理开始。

1. 操作系统中的时钟

操作系统为实现其功能,必须知道当前外部世界的时间(年月日时分秒等)。为实现这一目的,计算机设计者在主板上设置了一个硬件时钟,由主板上的一块纽扣电池(Cell)供电,这个硬件时钟无论计算机电源是否接通都会不停的数秒,来计算当前时间。

操作系统在启动的时候,会调用一段程序来读取主板上的硬件时钟,并记录在操作系统的一个(或一组)变量中。自此之后,操作系统的时钟便脱离主板的硬件时钟,开始单独运行(操作系统时钟的运行是由时钟中断来驱动的,不同于主板上的时钟)。

无论做工多么精细,主板硬件时钟和由时钟中断维护的操作系统内的时钟多多少少会有一些误差。所以,操作系统在每次关闭的时候会调用另一段程序,将操作系统 内的时钟写到主板硬件时钟里(这样设计是不是说明时钟中断比主板硬件时钟更准确一些呢?)。类似的,当用户在操作系统内修改时钟之后,也不会立即写入主板 时钟,而是在关机的时候写入硬件时钟。

2. 旧汤和新药的冲突

主板上的硬件时钟在Linux操作系统中呈现为一个设备,设备名称为rtc(Real Time Clock)。

使用旧的系统(如CentOS的2.6.18内核)编译新内核时,在调用mkinitrd命令时,会将/dev/rtc生成好,放到initrd- x.x.x.img文件中;而新的内核是自己生成/dev/rtc文件的,当kernel生成/dev/rtc时,发现系统内已经有了这个设备,于是就会 创建/dev/rtc0设备。这时hwclock程序仍然会读取rtc设备,就会造成设备读写失败。运行hwclock --debug命令可以看到如下输出:

1
2
3
4
5
[root@localhost ~]# hwclock --debug
hwclock from util-linux-2.13-pre7
hwclock: Open of /dev/rtc failed, errno=19: No such device.
No usable clock interface found.
Cannot access the Hardware Clock via any known method.

但是有的能够直接读写I/O,这样虽然/dev/rtc是错的,但还能正常运行

1
2
3
4
5
[root@localhost ~]# hwclock --debug
hwclock from util-linux-2.13-pre7
hwclock: Open of /dev/rtc failed, errno=19: No such device.
Using direct I/O instructions to ISA clock.
.....

其实,对应这个问题,新版的hwclock已经做出了调整。新的hwclock会主动去寻找/dev/rtc0设备,来操作主板硬件时钟。于是,解决方法就出现了。

3. 新汤配新药

既然内核这剂药已经换成了新的,那我们就把外围应用程序hwclock也换成新的。

从这里可以下载比较新的(不用最新的是因为最新的源码在旧版的CentOS上编译会出现错误)程序源码:http://now-code.com/download/util-linux-ng-2.17.tar.bz2

如果需要更多版本的程序源码,请到这里下载:ftp://ftp.kernel.org/pub/linux/utils/%E3%80%82

下载完成之后,编译该程序:

1
2
3
4
tar xfv util-linux-ng-2.17.tar.bz2
cd util-linux-ng-2.17
./configure
make

编译完成之后,将生成的hwclock文件拷贝到指定位置即可:

1
cp hwclock/hwclock /sbin/

之后,操作系统和主板的硬件时钟就可以同步起来了。

linux系统时间和硬件时钟问题(date和hwclock)

http://rpf413.blog.163.com/blog/static/4556376020122831444674/

总结一下hwclock,这个容易晕:

1)/etc/sysconfig/clock 文件,只对 hwclock 命令有效,且只在系统启动和关闭的时候才有用(修改了其中的 UTC=true 到 UTC=false 的前后,执行 hwclock (--utc, 或 --localtime) 都没有变化,要重启系统后才生效);

2)/etc/rc.d/rc.sysinit 文件,run once at boot time,其中有从硬件时钟同步时间到系统时间的操作;

3)hwclock --localtime 的输出,才是硬件时钟真正的时间。如果输出结果带时区(比如CST),还要看/etc/sysconfig/clock里的UTC参数,如果 UTC=false,那时区有意义;如果 UTC=true,那时区没意义,实际上是UTC时间。

4)在 /etc/sysconfig/clock 中 UTC=false 时,date、hwclock、hwclcok --localtime 输出的时间应该都一致,且此时 hwclock --utc是没有意义的;

5)在 /etc/sysconfig/clock 中 UTC=ture 时,date、hwclock 的输出是一致的,hwclock --localtime 的输出则是UTC时间;

6)如果不想在输出中带时区,则 export LANG=C ,然后再运行 hwclock 就没有什么CST了,免得时区误导你;

7)hwclock --utc 很闹腾,还是别看了,你会晕的。。。

8)系统关闭时会同步系统时间到硬件时钟,系统启动时会从硬件时钟读取时间更新到系统,这2个步骤都要根据 /etc/sysconfig/clock 文件中UTC的参数来设置时区转换。

实际案例分析

修改了 /etc/sysconfig/clock 中UTC参数但系统未正常关闭的情况

修改 /etc/sysconfig/clock 文件后,如果系统内核突然崩溃,然后直接按电源重启,则系统没有进行 系统时间到硬件时钟的 同步;但是 系统启动时,又根据 /etc/sysconfig/clock 中UTC的参数,来同步硬件时钟到系统,这时就会出现时间问题:

0)假设系统的时区为CST(UTC+8);
1)假设原 /etc/sysconfig/clock 中 UTC=true,修改成 UTC=false;
2)如果此时系统未正常关机,系统时间未按参数 UTC=false 同步时间到硬件时钟(没有+8小时);
3)但系统被按电源重启后,系统读取到 UTC=false,认为硬件时钟为CST时间,直接用于系统时间;
4)那么此时,系统时间将少了8小时。


http://hi.baidu.com/lujunqianglw/blog/item/bc2d9144d24fc48fb3b7dc1d.html

一、首先要弄清几个概念:

1. “系统时间”与“硬件时间”

系统时间: 一般说来就是我们执行 date 命令看到的时间,linux系统下所有的时间调用(除了直接访问硬件时间的命令)都是使用的这个时间。

硬件时间: 主板上BIOS中的时间,由主板电池供电来维持运行,系统开机时要读取这个时间,并根据它来设定系统时间(注意:系统启动时根据硬件时间设定系统时间的过程可能存在时区换算,这要视具体的系统及相关设置而定)。

2. “UTC时间”与“本地时间”

UTC时间:Coordinated Universal 8 e2 i( H7 t0 ^/ ^Time 世界协调时间(又称世界标准时间、世界统一时间),在一般精度要求下,它与GMT(Greenwich Mean Time,格林威治标准时间)是一样的,其实也就是说 GMT≈UTC,但 UTC 是以原子钟校准的,更精确。

本地时间:由于处在不同的时区,本地时间一般与UTC是不同的,换算方法就是

本地时间 = UTC + 时区 或 UTC = 本地时间 - 时区

时区东为正,西为负,例如在中国,本地时间都使用北京时间,在linux上显示就是 CST(China Standard Time,中国标准时,注意美国的中部标准时Central Standard Time也缩写为CST,与这里的CST不是一回事!),时区为东八区,也就是 +8 区,所以 CST=UTC+(+8小时) 或 UTC=CST-(+8小时)。

二、时间命令

1. 系统时间 date

直接调用 date,得到的是本地时间。如果想得到UTC时间的话,使用 date -u。

1
2
3
4
[12-01 19:07> ~]$ date
2009年 12月 07日 星期一 14:22:20 CST
[12-01 19:07> ~]$ date -u
2009年 12月 07日 星期一 06:22:22 UTC
2. 硬件时间 /sbin/hwclock

直接调用 /sbin/hwclock 显示的时间就是 BIOS 中的时间吗?未必!这要看 /etc/sysconfig/clock 中是否启用了UTC,如果启用了UTC(UTC=true),显示的其实是经过时区换算的时间而不是BIOS中真正的时间,如果加上 –localtime 选项,则得到的总是 BIOS 中实际的时间.

1
2
3
4
5
6
[12-01 19:07> ~]# hwclock
2009年12月07日 星期一 14时28分43秒 -0.611463 seconds
[12-01 19:07> ~]# hwclock --utc
2009年12月07日 星期一 14时28分46秒 -0.594189 seconds
[12-01 19:07> ~]# hwclock --localtime
2009年12月07日 星期一 06时28分50秒 -0.063875 seconds
3. /etc/localtime

这个文件用来设置系统的时区,将 /usr/share/zoneinfo/ 中相应文件拷贝到/etc下并重命名为 localtime 即可修改时区设置,而且这种修改对 date 命令是及时生效的。不论是 date 还是 hwclock 都会用到这个文件,会根据这个文件的时区设置来进行UTC和本地之间之间的换算。

4. /etc/sysconfig/clock

这个文件只对 hwclock 有效,而且似乎是只在系统启动和关闭的时候才有用,比如修改了其中的 UTC=true 到 UTC=false 的前后,执行 hwclock (--utc, 或 --localtime) 都没有变化,要重启系统后才生效。注:如果设置 UTC=false 并重启系统后,执行一些命令结果如下:

1
2
3
4
5
date 2009年 12月 07日 星期一 19:26:29 CST
date -u 2009年 12月 07日 星期一 11:26:29 UTC
hwclock 2009年12月07日 星期一 19时26分30秒 -0.442668 seconds
hwclock --utc 2009年12月08日 星期二 03时26分31秒 -0.999091 seconds
hwclock --localtime 2009年12月07日 星期一 19时26分32秒 -0.999217 seconds

可见,如果不使用UTC,BIOS时间(红色部分)就是系统本地时间,而且注意这时执行 hwclock --utc 得到的结果没有任何意义,因为这里我们已经禁用了UTC,而且也明显不符合“本地时间=UTC+时区”的关系。

三、linux与windows双系统间的时间同步

系统启动和关闭时,硬件时间与系统时间之间的同步有两种方式(假设在中国,用CST代表本地时间):

方式A: 使用UTC(对linux就是 /etc/sysconfig/clock 中 UTC=true)

开机: BIOS——->UTC(将BIOS中的时间看成是UTC)——(时区变化)—–>CST
关机: CST ——-(时区变化)—–>UTC——-存储到——>BIOS

方式B: 不使用UTC(对linux就是 /etc/sysconfig/clock 中 UTC=false)

开机: BIOS———————>CST(将BIOS中的时间看成是CST)
关机: CST ———存储到——>BIOS


FIX:

方式A: 使用UTC(对linux就是 /etc/sysconfig/clock 中 UTC=true)

关机: CST ——-操作系统根据时区算出UTC时间——-存储到——>BIOS
开机: BIOS——->BIOS中的时间是UTC———–操作系统根据时区计算出localtime———-CST

方式B: 不使用UTC(对linux就是 /etc/sysconfig/clock 中 UTC=false)

关机: CST ——–操作系统中UTC=false,直接将localtime存储到——>BIOS
开机: BIOS——–BIOS中的时间是localtime—–操作系统中UTC=false,BIOS时间当成localtime——–>CST(将BIOS中的时间看成是CST)


通过设定 /etc/sysconfig/clock,linux可以支持这两种方式,然而windows只支持方式B(至少是默认支持B,而我不知道怎么能让它支 持A),那么在双系统情况下,如果linux设成A方式,那么在linux与windows系统切换时一定会造成时间混乱的,解决办法就是将linux中 的UTC禁用,也设成B方式就可以了。

注:可以通过 hwclock --hctosys 来利用硬件时间来设置系统时间(注意不是简单的复制BIOS中的时间为系统时间,要看是否使用UTC,如果使用的话则要做时区换算),通过 hwclock --systohc 来根据系统时间设置硬件时间(也要看是否启用UTC来决定是否做时区换算)。

总之,不论使用 --systohc 还是 --hctosys,同步后直接运行不带参数的 hwclock 得到的时间与直接运行 date 得到的时间应该一致,这个时间是否就是BIOS中的时间(hwclock --localtime)那就不一定了,如果启用了UTC就不是,没启用UTC就是。

而且还要注意:在系统中手动使用 hwclock hwclock --set --date='yyyy-mm-dd' 来设置BIOS时间只在系统运行时有效,因为当系统关闭时,还会按设定好的方式根据系统时间来重设BIOS时间的,于是手动的设置便被覆盖掉了。

Web压力测试工具

curl wget 不验证证书进行https请求

wget ‘https://x.x.x.x/get_ips’ –no-check-certificate

curl ‘https://x.x.x.x/get_ips’ -k


https://blog.csdn.net/hqzxsc2006/article/details/50547684

通过curl得到http各阶段的响应时间

url_effective The URL that was fetched last. This is most meaningful if you’ve told curl to follow location: headers.

filename_effective The ultimate filename that curl writes out to. This is only meaningful if curl is told to write to a file with the –remote-name or –output option. It’s most useful in combination with the –remote-header-name option. (Added in 7.25.1)

http_code http状态码,如200成功,301转向,404未找到,500服务器错误等。(The numerical response code that was found in the last retrieved HTTP(S) or FTP(s) transfer. In 7.18.2 the alias response_code was added to show the same info.)

http_connect The numerical code that was found in the last response (from a proxy) to a curl CONNECT request. (Added in 7.12.4)

time_total 总时间,按秒计。精确到小数点后三位。 (The total time, in seconds, that the full operation lasted. The time will be displayed with millisecond resolution.)

time_namelookup DNS解析时间,从请求开始到DNS解析完毕所用时间。(The time, in seconds, it took from the start until the name resolving was completed.)

time_connect 连接时间,从开始到建立TCP连接完成所用时间,包括前边DNS解析时间,如果需要单纯的得到连接时间,用这个time_connect时间减去前边time_namelookup时间。以下同理,不再赘述。(The time, in seconds, it took from the start until the TCP connect to the remote host (or proxy) was completed.)

time_appconnect 连接建立完成时间,如SSL/SSH等建立连接或者完成三次握手时间。(The time, in seconds, it took from the start until the SSL/SSH/etc connect/handshake to the remote host was completed. (Added in 7.19.0))

time_pretransfer 从开始到准备传输的时间。(The time, in seconds, it took from the start until the file transfer was just about to begin. This includes all pre-transfer commands and negotiations that are specific to the particular protocol(s) involved.)

time_redirect 重定向时间,包括到最后一次传输前的几次重定向的DNS解析,连接,预传输,传输时间。(The time, in seconds, it took for all redirection steps include name lookup, connect, pretransfer and transfer before the final transaction was started. time_redirect shows the complete execution time for multiple redirections. (Added in 7.12.3))

time_starttransfer 开始传输时间。在client发出请求之后,Web 服务器返回数据的第一个字节所用的时间(The time, in seconds, it took from the start until the first byte was just about to be transferred. This includes time_pretransfer and also the time the server needed to calculate the result.)

size_download 下载大小。(The total amount of bytes that were downloaded.)

size_upload 上传大小。(The total amount of bytes that were uploaded.)

size_header 下载的header的大小(The total amount of bytes of the downloaded headers.)

size_request 请求的大小。(The total amount of bytes that were sent in the HTTP request.)

speed_download 下载速度,单位-字节每秒。(The average download speed that curl measured for the complete download. Bytes per second.)

speed_upload 上传速度,单位-字节每秒。(The average upload speed that curl measured for the complete upload. Bytes per second.)

content_type 就是content-Type,不用多说了,这是一个访问我博客首页返回的结果示例(text/html; charset=UTF-8);(The Content-Type of the requested document, if there was any.)

num_connects Number of new connects made in the recent transfer. (Added in 7.12.3)

num_redirects Number of redirects that were followed in the request. (Added in 7.12.3)

redirect_url When a HTTP request was made without -L to follow redirects, this variable will show the actual URL a redirect would take you to. (Added in 7.18.2)

ftp_entry_path The initial path libcurl ended up in when logging on to the remote FTP server. (Added in 7.15.4)

ssl_verify_result ssl认证结果,返回0表示认证成功。( The result of the SSL peer certificate verification that was requested. 0 means the verification was successful. (Added in 7.19.0))

1、可以直接访问使用:

1
curl -o /dev/null -s -w %{http_code}:%{http_connect}:%{content_type}:%{time_namelookup}:%{time_redirect}:%{time_pretransfer}:%{time_connect}:%{time_starttransfer}:%{time_total}:%{speed_download} www.baidu.com

输出变量需要按照%{variable_name}的格式,如果需要输出%,double一下即可,即%%,同时,\n是换行,\r是回车,\t是TAB。

-w 指定格式化文件

-o 请求重定向到,不带此参数则控制台输出返回结果

-s 静默,不显示进度

2、也可以定义时间格式化文件访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#vim  curl-time.txt 
\n
            http: %{http_code}\n
               dns: %{time_namelookup}s\n
          redirect: %{time_redirect}s\n
      time_connect: %{time_connect}s\n
   time_appconnect: %{time_appconnect}s\n
  time_pretransfer: %{time_pretransfer}s\n
time_starttransfer: %{time_starttransfer}s\n
     size_download: %{size_download}bytes\n
    speed_download: %{speed_download}B/s\n
                  ----------\n
        time_total: %{time_total}s\n
\n
1
curl -w "@curl_time.txt"  -s  -H "Content-Type: application/json" --insecure --header 'Host: passport.500.com' --data '{"platform":"android","userimei":"F5D815EA2BD8DBARD","app_channel":"10000","mbimei":"9DB358AF","version":"3.1.4","username":"hqzx","userpass":"976af4"}' --compressed https://119.147.113.177/user/login

http://297020555.blog.51cto.com/1396304/592386

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
*/1 * * * * cd /root/test ; ./curl.sh > /dev/null 2>&1
*/1 * * * * cd /root/test ; ./ping.sh > /dev/null 2>&1

curl.sh:
if ps aux | grep curl | grep "30M" > /dev/null ; then
	echo "..." > /dev/null
else
	./curl_1.sh &
	./curl_2.sh &
fi

curl_1.sh & curl_2.sh:
URL=192.168.1.3:80/30M
T=`date "+%F %T"`
curl $URL -s -o /tmp/df -w "$T %{time_connect} %{time_starttransfer} %{time_total} %{speed_download} %{http_code}\n" >> t_down

ping.sh:
date "+%F %T" >> ping_out
ping -q -f -c 1000 192.168.1.3 >> ping_out

res.sh:

1
2
3
4
5
6
#sed -i -e ':a;N;$!ba;s/\(:..\)\n/\1 /g' t_down
cat ping_out | grep -E "\-|loss" | grep -v statistics > ping_tmp
sed -i -e ':a;N;$!ba;s/\(:..\)\n/\1 /g' ping_tmp

python res.py > res_out
python res2.py

统计结果和ping汇聚 res.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import time

ff = open('t1_down').readlines()
qq = open('t2_down').readlines()
ping = open('ping_tmp').readlines()

j = 0
for i in range(0, len(ff)):
	f = ff[i].strip().split(' ')
	q = qq[i].strip().split(' ')

	if (len(f) != len(q)):
		break

	timeArray = time.strptime(f[0]+" "+f[1], "%Y-%m-%d %H:%M:%S")
	ft = time.mktime(timeArray)

	if (i < len(ff) - 1):
		fn = ff[i+1].strip().split(' ')
		fnt = time.mktime(time.strptime(fn[0] + " " + fn[1], "%Y-%m-%d %H:%M:%S"))
	else:
		fnt = time.mktime(time.strptime("2020-01-01 00:00:00", "%Y-%m-%d %H:%M:%S"))

	po = "0"
	while (j < len(ping)):
		p = ping[j].strip().split(' ')
		pt = time.mktime(time.strptime(p[0]+" "+p[1], "%Y-%m-%d %H:%M:%S"))
		if (pt > fnt):
			break
		j = j + 1
		if (pt >= ft - 10):
			po = p[8]
			break

	print f[0], f[1], int(float(f[6])/1000), int(float(q[6])/1000), po

分时段统计res2.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#coding:utf-8

r = open('res_out').readlines()

hs1 = {}
hs2 = {}
hsw = {}
hl = {}
hr = {}
hm = {}

for line in r:
	arr = line.strip().split(' ')
	h1 = int(arr[1][0:2])
	if h1 >= 2 and h1 <= 9:
		h2 = "H2-9"
	else:
		h2 = "H10-25"
	h3 = 'Hall'
	for h in (h1, h2, h3):
		if h not in hsw:
			hs1[h] = hs2[h] = hsw[h] = hl[h] = hr[h] = hm[h] = 0
		hs1[h] += int(arr[2])
		hs2[h] += int(arr[3])
		hsw[h] += 1
		hl[h] += int(arr[4][0:-1])
		hr[h] += int(arr[5])
		hm[h] += int(arr[6])

print "时段", "下载次数", "ff(KB/s)", "qq(KB/s)", "提升比例", "丢包率", "recovery_skb", "mark_skb"
for h in sorted(hs1.keys()):
	s1 = hs1[h]/hsw[h]
	s2 = hs2[h]/hsw[h]
	s3 = hl[h]/hsw[h]
	s4 = hr[h]/hsw[h]
	s5 = hm[h]/hsw[h]
	#print h, hsw[h], s1, s2, 100*(s1-s2)/s2, s3, s4, s5
	print "%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d" % (h, hsw[h], s1, s2, 100*(s1-s2)/s2, s3, s4, s5)

一、http_load

http_load以并行复用的方式运行,用以测试web服务器的吞吐量与负载。但是它不同于大多数压力测试工具,它可以以一个单一的进程运行,一般不会把客户机搞死。还可以测试HTTPS类的网站请求。

下载地址:http://www.acme.com/software/http_load/

1
./http_load -verbose -proxy 192.168.99.6:80 -parallel 24 -seconds 1000 url.txt

http_load 改进版下载 http_load-09Mar2016-kk.tar.gz
改进点:
2018-01-19:
1. 异常时打印更多信息(“want_bytes=%ld got_bytes=%ld sport=%d connect_at=%ld now=%ld last=%ld”)
2. http1.0 改成 http1.1 支持多次request
3. 增加 [ -requests times ] 参数, 在一条流中会发起times次request, 默认为1
2018-01-26:
4. 增加 [ -fastopen ] 参数,http协议增加fastopen测试,fastopen连接时改为阻塞模式。非阻塞模式syn无法附带数据
2018-04-12:
5. 修复 num_connections 可能出现的统计错误,以及fastopen可能出现的请求超时
2018-06-13:
6. https增加SNI信息,Makefile默认开启https支持
2019-02-19:
7. 修复IPV6的bug
2020-08-29: 8. fastopen 支持非阻塞模式-nonblock,但第一次连接还得用block

二、webbench

webbench是Linux下的一个网站压力测试工具,最多可以模拟3万个并发连接去测试网站的负载能力。

1
2
用法:webbench -c 并发数 -t 运行测试时间 URL
如:webbench -c 5000 -t 120 http://www.163.com

三、ab

ab是apache自带的一款功能强大的测试工具。安装了apache一般就自带了,用法可以查看它的说明

参数众多,一般我们用到的是-n 和-c

例如:

1
./ab -c 1000 -n 100 http://www.vpser.net/index.php

这个表示同时处理1000个请求并运行100次index.php文件.

四、Siege

一款开源的压力测试工具,可以根据配置对一个WEB站点进行多用户的并发访问,记录每个用户所有请求过程的相应时间,并在一定数量的并发访问下重复进行。 官方:http://www.joedog.org/

使用

1
siege -c 200 -r 10 -f example.url

-c是并发量,-r是重复次数。 url文件就是一个文本,每行都是一个url,它会从里面随机访问的。

乘2加1

两个数A B,A<B,两种操作:A=A+1 或 A=A*2,求A到B的最少操作次数。

首先如果A、B的二进制前缀不一样则一直A=A+1

然后A=A<<1,A、B前缀不一样再A=A+1

TCP拥塞控制窗口有效性验证机制

blog.csdn.net/zhangskd/article/details/7609465

概述

问题1:当发送方长时间受到应用程序的限制,不能发送数据时,会使拥塞窗口无效。TCP是根据拥塞窗口来动态地估计网络带宽的。发送方受到应用程序的限制后,没有数据可以发送。那么此时的拥塞窗口就不能准确的反应网络状况,因为这个拥塞窗口是很早之前的。

问题2:当发送方受到应用程序限制,不能利用完拥塞窗口,会使拥塞窗口的增长无效。TCP不断调整cwnd来测试网络带宽。如果不能完全使用掉cwnd,就不知道网络能否承受得了cwnd的数据量,这种情况下的cwnd增长是无效的。

原理

TCP sender受到的两种限制

(1) application-limited :when the sender sends less than is allowed by the congestion or receiver window.

(2) network-limited:when the sender is limited by the TCP window. More precisely, we define a network-limited period as any period when the sender is sending a full window of data.

问题1描述

TCP’s congestion window controls the number of packets a TCP flow may have in the network at any time. However, long periods when the sender is idle or application-limited can lead to the invalidation of the congestion window, in that the congestion window no longer reflects current information about the state of the network.

The congestion window is set using an Additive-Increase, Multiplicative-Decrease(AIMD) mechanism that probes for available bandwidth, dynamically adapting to changing network conditions. This AIMD works well when the sender continually has data to send, as is typically the case for TCP used for bulk-data transfer. In contrast, for TCP used with telnet applications, the data sender often has little or no data to send, and the sending rate is often determined by the rate at which data is generated by the user.

问题2描述

An invalid congestion window also results when the congestion window is increased (i.e., in TCP’s slow-start or congestion avoidance phases) during application-limited periods, when the previous value of the congestion window might never have been fully utilized. As far as we know, all current TCP implementations increase the congestion window when an acknowledgement arrives, if allowed by the receiver’s advertised window and the slow-start or congestion avoidance window increase algorithm, without checking to see if the previous value of the congestion window has in fact been used.

This document proposes that the window increase algorithm not be invoked during application- limited periods. This restriction prevents the congestion window from growing arbitrarily large, in the absence of evidence that the congestion window can be supported by the network.

实现(1)

发送方在发送数据包时,如果发送的数据包有负载,则会检测拥塞窗口是否超时。如果超时,则会使拥塞窗口失效并重新计算拥塞窗口。然后根据最近接收段的时间,确定是否进入pingpong模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* Congestion state accounting after a packet has been sent. */  
static void tcp_event_data_sent (struct tcp_sock *tp, struct sock *sk)  
{  
	struct inet_connection_sock *icsk = inet_csk(sk);  
	const u32 now = tcp_time_stamp;  
  
	if (sysctl_tcp_slow_start_after_idle &&   
		(!tp->packets_out && (s32) (now - tp->lsndtime) > icsk->icsk_rto))  
		tcp_cwnd_restart(sk, __sk_dst_get(sk)); /* 重置cnwd */  
  
	tp->lsndtime = now; /* 更新最近发包的时间*/  
  
	/* If it is a reply for ato after last received packets,  
	 * enter pingpong mode. */  
	if ((u32)(now - icsk->icsk_ack.lrcvtime) < icsk.icsk_ack.ato)  
		icsk->icsk_ack.pingpong = 1;  
}  

tcp_event_data_sent()中,符合三个条件才重置cwnd:

(1)tcp_slow_start_after_idle选项设置,这个内核默认置为1 (2)tp->packets_out == 0,表示网络中没有未确认数据包 (3)now - tp->lsndtime > icsk->icsk_rto,距离上次发送数据包的时间超过了RTO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* RFC2861. Reset CWND after idle period longer RTO to "restart window". 
 * This is the first part of cnwd validation mechanism. 
 */  
static void tcp_cwnd_restart (struct sock *sk, const struct dst_entry *dst)  
{  
	struct tcp_sock *tp = tcp_sk(sk);  
	s32 delta = tcp_time_stamp - tp->lsndtime;  
  
	/* 关于tcp_init_cwnd()可见上一篇blog.*/  
	u32 restart_cwnd = tcp_init_cwnd(tp, dst);  
	u32 cwnd = tp->snd_cwnd;  
	  
	/* 触发拥塞窗口重置事件*/  
	tcp_ca_event(sk, CA_EVENT_CWND_RESTART);  
  
	/* 阈值保存下来,并没有重置。*/  
	tp->snd_ssthresh = tcp_current_ssthresh(sk);  
	restart_cwnd = min(restart_cwnd, cwnd);  
  
	/* 闲置时间每超过一个RTO且cwnd比重置后的大时,cwnd减半。*/  
	while((delta -= inet_csk(sk)->icsk_rto) > 0 && cwnd > restart_cwnd)  
		cwnd >> 1;  
  
	tp->snd_cwnd = max(cwnd, restart_cwnd); /* 取其大者!*/  
	tp->snd_cwnd_stamp = tcp_time_stamp;  
	tp->snd_cwnd_used = 0;  
}  

那么调用tcp_cwnd_restart()后,tp->snd_cwnd是多少呢?这个是不确定的,要看闲置时间delta、闲置前的cwnd、路由器中设置的initcwnd。当然,最大概率的是:拥塞窗口降为闲置前cwnd的一半。

实现(2)

在发送方成功发送一个数据包后,会检查从发送队列发出而未确认的数据包是否用完拥塞窗口。 如果拥塞窗口被用完了,说明发送方收到网络限制; 如果拥塞窗口没被用完,且距离上次检查时间超过了RTO,说明发送方收到应用程序限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* Congestion window validation.(RFC2861) */  
static void tcp_cwnd_validate(struct sock *sk) {  
	struct tcp_sock *tp = tcp_sk(sk);  
  
	if (tp->packets_out >= tp->snd_cwnd) {  
		/* Network is feed fully. */  
		tp->snd_cwnd_used = 0; /*不用这个变量*/  
		tp->snd_cwnd_stamp = tcp_time_stamp; /* 更新检测时间*/  
  
	} else {  
		/* Network starves. */  
		if (tp->packets_out > tp->snd_cwnd_used)  
			tp->snd_cwnd_used = tp->packets_out; /* 更新已使用窗口*/  
  
			/* 如果距离上次检测的时间,即距离上次发包时间已经超过RTO*/  
			if (sysctl_tcp_slow_start_after_idle &&  
				(s32) (tcp_time_stamp - tp->snd_cwnd_stamp) >= inet_csk(sk)->icsk_rto)  
				tcp_cwnd_application_limited(sk);  
	}  
}  

在发送方收到应用程序的限制期间,每隔RTO时间,都会调用tcp_cwnd_application_limited()来重新设置sshresh和cwnd,具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/* RFC2861, slow part. Adjust cwnd, after it was not full during one rto. 
 * As additional protections, we do not touch cwnd in retransmission phases, 
 * and if application hit its sndbuf limit recently. 
 */  
void tcp_cwnd_application_limited(struct sock *sk)  
{  
	struct tcp_sock *tp = tcp_sk(sk);  
  
	/* 只有处于Open态,应用程序没受到sndbuf限制时,才进行 
	 * ssthresh和cwnd的重置。 
	 */  
	if (inet_csk(sk)->icsk_ca_state == TCP_CA_Open &&   
		sk->sk_socket && !test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) {  
  
		/* Limited by application or receiver window. */  
		u32 init_win = tcp_init_cwnd(tp, __sk_dst_get(sk));  
		u32 win_used = max(tp->snd_cwnd_used, init_win);  
  
		/* 没用完拥塞窗口*/  
		if (win_used < tp->snd_cwnd) {  
			/* 并没有减小ssthresh,反而增大,保留了过去的信息,以便之后有数据发送 
			  * 时能快速增大到接近此时的窗口。 
			  */  
			tp->snd_ssthresh = tcp_current_ssthresh(sk);   
			/* 减小了snd_cwnd */  
			tp->snd_cwnd = (tp->snd_cwnd + win_used) >> 1;  
		}  
		tp->snd_cwnd_used = 0;  
	}  
	tp->snd_cwnd_stamp = tcp_time_stamp; /* 更新最近的数据包发送时间*/  
}  

发送方受到应用程序限制,且限制的时间每经过RTO后,就会调用以上函数来处理snd_ssthresh和snd_cwnd:

(1)snd_ssthresh = max(snd_ssthresh, ¾ cwnd)

慢启动阈值并没有减小,相反,如果此时cwnd较大,ssthresh会相应的增大。ssthresh是一个很重要的参数,它保留了旧的信息。这样一来,如果应用程序产生了大量的数据,发送方不再受到限制后,经过慢启动阶段,拥塞窗口就能快速恢复到接近以前的值了。

(2)snd_cwnd = (snd_cwnd + snd_cwnd_used) / 2

因为snd_cwnd_used < snd_cwnd,所以snd_cwnd是减小了的。减小snd_cwnd是为了不让它盲目的增长。因为发送方没有利用完拥塞窗口,并不能检测到网络是否能承受该拥塞窗口,这时的增长是无根据的。

结论

在发送完数据包后,通过对拥塞窗口有效性的检验,能够避免使用不合理的拥塞窗口。

拥塞窗口代表着网络的状况,通过避免使用不合理的拥塞窗口,就能得到正确的网络状况,而不会采取一些不恰当的措施。

在上文的两种情况下,通过TCP的拥塞窗口有效性验证机制(TCP congestion window validationmechanism),能够更合理的利用网络、避免丢包,从而提高传输效率。

Reference

RFC2861