分类: LINUX
2012-01-09 21:02:08
上次有个运维的同学让帮忙分析下tcpconn代码,他想用tcpconn插件来统计当前服务器80端口TCP链接数。但collectd加载了tcpconn插件后,cpu的load突然飙升,而没有使用tcpconn插件的时候load很低。想先从tcpconn代码上看看能否找到使得cpu load飙升的原因。
首先来简要介绍下collectd。collectd - The system statistics collection daemon。其官网上是这么解释的,是一个后台进程,定期统计系统性能数据,能够多种方式地存储这些性能数据,供报表输出,这些统计数据可以用来寻找当前系统的性能瓶颈,并预测未来系统的load。下图为collectd统计得到数据的一张图表输出。
是collectd的一个插件,可以统计当前服务器某个端口的TCP链接数,其既可以统计连进当前系统某个端口的TCP数,也可以统计当前系统连出的TCP数,前面的端口称为localport,而后面的端口称为remoteport。tcpconn不仅支持linux系统,而且支持bsd系列(freebsd,openbsd,netbsd,mac os x)的系统,本文为了简单起见,只分析linux系统下tcpconns。linux下tcpconn实现是基于/proc文件系统的,因此有必要对/proc做一个最简单的介绍。
/proc文件系统(/procfs)是个虚拟的文件系统,我们可以读取/procfs内的某个文件得到当前内核存储的系统信息,也可以通过向/procfs内的某个文件写入一些数据控制内核的行为。如/proc/cpuinfo可以输出cpu信息,/proc/net/tcp提供了当前系统的TCP链接信息,实际上tcpconns就是通过解析/proc/net/tcp和/proc/net/tcp6来统计当前系统某个端口的TCP链接数的。 每次读取/procfs内文件的时候,内核会动态地生成文件内容。
/proc/net/tcp文件格式:
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 00000000:00C7 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 10591797 1 ecb4c940 300 0 0 2 -1
1: 00000000:2FA8 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1801 0 10819883 1 e51a04c0 300 0 0 2 -1
2: 00000000:044A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1801 0 10818963 1 c5a34dc0 300 0 0 2 –1
上述字段中,几个较重要的字段为:
local_address 00000000:00C7 tcp链接的本地地址,冒号前面8位(十六进制)的ip地址,冒号后面的4位(十六进制)的端口,
rem_address 00000000:0000 tcp链接的远程地址,冒号前面8位(十六进制)的pi地址,冒号后面的4位(十六进制)为端口,
st 0A tcp连接的状态,总共有十一种状态,TCP链接的状态转换图可以参考。
tcpconn插件读取/proc/net/tcp文件,就是分析上述格式的文件,得到某个端口的TCP链接数。tcpconns的基本数据结构为端口节点链表,端口节点定义如下:
typedef struct port_entry_s
{
uint16_t port; // 端口
uint16_t flags; //
uint32_t count_local[TCP_STATE_MAX + 1]; // local TCP连接数,不同的状态存放在不同的数组元素中
uint32_t count_remote[TCP_STATE_MAX + 1]; // remote TCP连接数
struct port_entry_s *next;
} port_entry_t;
static port_entry_t *port_list_head=NULL;
tcpconns读取/proc/net/tcp文件,一行一行地分析,得到一个port_entry_s链表,然后把统计结果提交给collectd。如要统计localport=80的端口号的tcp连接数,那么tcpconns会读取每行,判断localport是否为0x50,如果是就给0x80的端口的count_local[st]加1,其中st为tcp链接的状态。
load飙高原因假设为什么用了tcpconns插件后,cpu的load就飙高,最终我们做了以下假设并进行了分析:
虽然还没有得到最终的原因,但我在这里可以做个猜想:引起cpu load飙高的原因是前面提到第2点和第3点的综合,即tcpconns被调用的频率高,而且每次要分析10000多行的文件。tcpconn调用频率高本身并不会存在什么问题,如调用/proc/cpuinfo的频率再高也不会存在性能问题,因为/proc/cpuinfo文件较小,而是在内核中是固定不变的;而系统的tcp连接数是实时变化的,意味着linux内核须维护一个tcp连接的数据结构,每次调用/proc/net/tcp时,内核都会生成tcp文件,同时因为文件较大,使得tcpconn分析也较耗时,最终使得load飙升。