2.6内核中SNMP统计信息基本操作

本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: [email protected]
来源:http://yfydz.cublog.cn
1. 前言
 
Linux2.6内核中网络SNMP统计数据的处理方式和2.4相比有了比较大的变化,本文介绍2.6下的SNMP信息的操作过程。
各种协议的SNMP统计方法相同,只是参数有所不同,为篇幅简单,使用UDP的统计信息来说明,因为UDP协议的SNMP统计信息较少,以下内核代码版本为2.6.17.11。
 
2. 数据结构
 
MIB库单位项基本结构:
SNMP MIB单位项结构简单,就是一个名称字符串和相应的数组索引值:
/* include/linux/snmp.h */
struct snmp_mib {
 char *name;
 int entry;
};
下面是为方便代码编辑而定义的两个宏:
#define SNMP_MIB_ITEM(_name,_entry) { \
 .name = _name,    \
 .entry = _entry,   \
}
#define SNMP_MIB_SENTINEL { \
 .name = NULL,  \
 .entry = 0,  \
}

实例,UDP协议SNMP统计表:
static const struct snmp_mib snmp4_udp_list[] = {
 SNMP_MIB_ITEM("InDatagrams", UDP_MIB_INDATAGRAMS),
 SNMP_MIB_ITEM("NoPorts", UDP_MIB_NOPORTS),
 SNMP_MIB_ITEM("InErrors", UDP_MIB_INERRORS),
 SNMP_MIB_ITEM("OutDatagrams", UDP_MIB_OUTDATAGRAMS),
 SNMP_MIB_SENTINEL
};

其中各项UDP索引宏定义为:
enum
{
 UDP_MIB_NUM = 0,
 UDP_MIB_INDATAGRAMS,   /* InDatagrams */
 UDP_MIB_NOPORTS,   /* NoPorts */
 UDP_MIB_INERRORS,   /* InErrors */
 UDP_MIB_OUTDATAGRAMS,   /* OutDatagrams */
 __UDP_MIB_MAX
};

UDP的SNMP MIB库定义,就是一个保存各统计项的数组,struct snmp_mib结构中的索引项就是索引这个数组的值:
/* include/net/snmp.h */
#define UDP_MIB_MAX __UDP_MIB_MAX
struct udp_mib {
 unsigned long mibs[UDP_MIB_MAX];
} __SNMP_MIB_ALIGN__;

对于不同的协议,需要统计的参数和数量各自不同,但定义格式都是一样的,其实写程序时麻烦就在定义这块,定义完后操作都很简单了。
 
3. SNMP统计操作
 
3.1 以下宏用来定义各协议的SNMP统计信息
/* include/net/snmp.h */
#define DEFINE_SNMP_STAT(type, name) \
 __typeof__(type) *name[2]
#define DECLARE_SNMP_STAT(type, name) \
 extern __typeof__(type) *name[2]
#define SNMP_STAT_BHPTR(name) (name[0])
#define SNMP_STAT_USRPTR(name) (name[1])

3.2 SNMP统计操作
 
以下各宏定义了安全地进行SNMP信息增减操作,保证的多CPU情况下也正确:
/* include/net/snmp.h */
#define SNMP_INC_STATS_BH(mib, field)  \
 (per_cpu_ptr(mib[0], raw_smp_processor_id())->mibs[field]++)
#define SNMP_INC_STATS_OFFSET_BH(mib, field, offset) \
 (per_cpu_ptr(mib[0], raw_smp_processor_id())->mibs[field + (offset)]++)
#define SNMP_INC_STATS_USER(mib, field) \
 (per_cpu_ptr(mib[1], raw_smp_processor_id())->mibs[field]++)
#define SNMP_INC_STATS(mib, field)  \
 (per_cpu_ptr(mib[!in_softirq()], raw_smp_processor_id())->mibs[field]++)
#define SNMP_DEC_STATS(mib, field)  \
 (per_cpu_ptr(mib[!in_softirq()], raw_smp_processor_id())->mibs[field]--)
#define SNMP_ADD_STATS_BH(mib, field, addend)  \
 (per_cpu_ptr(mib[0], raw_smp_processor_id())->mibs[field] += addend)
#define SNMP_ADD_STATS_USER(mib, field, addend)  \
 (per_cpu_ptr(mib[1], raw_smp_processor_id())->mibs[field] += addend)
3.3 UDP协议SNMP定义
UDP统计定义:
/* net/ipv4/udp.h */
DECLARE_SNMP_STAT(struct udp_mib, udp_statistics);
UDP各参数增加定义,通常SNMP统计信息是只增不减的,所以只定义了INC操作而没有DEC操作:
/* net/ipv4/udp.h */
#define UDP_INC_STATS(field)  SNMP_INC_STATS(udp_statistics, field)
#define UDP_INC_STATS_BH(field)  SNMP_INC_STATS_BH(udp_statistics, field)
#define UDP_INC_STATS_USER(field)  SNMP_INC_STATS_USER(udp_statistics, field)
在程序中就直接使用上述宏来增加SNMP统计量即可。
 
 
内存分配:
DEFINE_SNMP_STAT等宏定义的是指针,因此要提前进行内存分配:
/* net/ipv4/af_inet.c */
static int __init init_ipv4_mibs(void)
{
......
 udp_statistics[0] = alloc_percpu(struct udp_mib);
 udp_statistics[1] = alloc_percpu(struct udp_mib);
......
 
4. SNMP参数显示
 
一般协议的SNMP参数可通过/proc/net/snmp文件获取,自己也可以定义新的文件获取自己协议的SNMP信息,显示过程就是普通的/proc只读文件显示过程,现在在2.6中通常使用seq流来显示:
/* net/ipv4/proc.c */
......
// 打印参数名称
 seq_puts(seq, "\nUdp:");
 for (i = 0; snmp4_udp_list[i].name != NULL; i++)
  seq_printf(seq, " %s", snmp4_udp_list[i].name);

// 打印参数具体数值
 seq_puts(seq, "\nUdp:");
 for (i = 0; snmp4_udp_list[i].name != NULL; i++)
  seq_printf(seq, " %lu",
      fold_field((void **) udp_statistics,
          snmp4_udp_list[i].entry));
......

值得注意的是具体参数值不是直接从数组取,而是通过fold_field()函数来读取的,实际上是将各CPU的bottom half和user部分所有统计值之和:
/* net/ipv4/proc.c */
static unsigned long
fold_field(void *mib[], int offt)
{
 unsigned long res = 0;
 int i;
 for_each_possible_cpu(i) {
  res += *(((unsigned long *) per_cpu_ptr(mib[0], i)) + offt);
  res += *(((unsigned long *) per_cpu_ptr(mib[1], i)) + offt);
 }
 return res;
}

5. 2.4内核处理方式简介
 
在2.4内核中,和2.6区别重点在于协议的MIB库定义,定义过程是非常直接的,而2.6中通过宏隐藏的多CPU处理的细节:
/* 2.4.26, include/net/udp.h */
extern struct udp_mib udp_statistics[NR_CPUS*2];
struct udp_mib
{
  unsigned long UdpInDatagrams;
  unsigned long UdpNoPorts;
  unsigned long UdpInErrors;
  unsigned long UdpOutDatagrams;
 unsigned long   __pad[0];
} ____cacheline_aligned;
其他宏操作定义形式上就都是一样的了。
 
6. 结论
 
2.6的SNMP操作和2.4最大区别就是掩盖了多CPU处理的细节,在定义时不用带多CPU的相关参数。

猜你喜欢

转载自cxw06023273.iteye.com/blog/867085