ifstat源码使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kongshuai19900505/article/details/82222607

#1.工具原理
是一个统计网络接口活动状态的工具,统计的数据是通过读取/proc/net/dev里面的内容根据两次读取的时间差来计算的,文件内容如下:

[root@host231 ~]# cat /proc/net/dev
Inter-|   Receive                                                |  Transmit
 face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
enp4s0f0: 4777818771 3600003    0    0    0     0          0         0 277629329  710459    0    0    0     0       0          0
enp4s0f1: 4763742770 3583476    0    0    0     0          0         0 278193524  710621    0    0    0     0       0          0
 bond0: 19077907736 14353590    0    0    0     0          0         0 1111956676 2842142    0    0    0     0       0          0
    lo: 1029406377 2485062    0    0    0     0          0         0 1029406377 2485062    0    0    0     0       0          0
ens2f0: 4750042260 3575975    0    0    0     0          0         0 278239584  710491    0    0    0     0       0          0
ens3f0:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0
ens2f1: 4786315657 3594219    0    0    0     0          0         0 277894239  710571    0    0    0     0       0          0
ens3f1:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0

bytes: 接口发送或接收的数据的总字节数
packets: 接口发送或接收的数据包总数
errs: 由设备驱动程序检测到的发送或接收错误的总数
drop: 设备驱动程序丢弃的数据包总数
fifo: FIFO缓冲区错误的数量)
frame: 分组帧错误的数量
colls: 接口上检测到的冲突数
compressed: 设备驱动程序发送或接收的压缩数据包数
carrier: 由设备驱动程序检测到的载波损耗的数量
multicast: 设备驱动程序发送或接收的多播帧数

#2.代码说明
本代码摘自ifstat工具源码,将其中最直接的获取网卡流量的部分拿出来做测试了
1)涉及到的数据结构如下:

#define PROC_FILE_PATH "/proc/net/dev"
/*网卡列表*/
struct ifstat_data 
{
    char *name;//网卡名称
    int namelen;//长度
    unsigned long obout, obin, bout, bin;//流量数据
    struct ifstat_data *next;
};

这是一个单链表,根据扫描上来的网卡加入链表,之后再读取/proc/net/dev文件里面的数据进行计算
2)接口

/*说明:根据输入的网卡名称获取网卡节点
* 输入:struct ifstat_data *ifs 网卡列表
* 输入:char *ifname 要查询的网卡名称
* 返回:从链表中获取到的网卡节点
*/
struct ifstat_data *ifstat_get_interface(struct ifstat_data *ifs, char *ifname)

/*说明:将读取到的流量信息写入到链表中对应的网卡节点上
* 输入:struct ifstat_data *data 网卡节点
* 输入:bytesin:网络入数据
* 输入:bytesout:网络出数据
* 输出:struct ifstat_data *data 填充完毕数据的网卡节点
* 返回:无
*/
void ifstat_set_interface_stats(struct ifstat_data *data,unsigned long bytesin,unsigned long bytesout)

/*说明:将扫描出来的网卡检查是否是活动的,活动的网卡则加入到链表
* 输入:int sd :socket句柄
* 输入:struct ifreq *ifr:传入的网卡信息
* 输入:struct ifstat_data **ifs:网卡链表
* 输出:struct ifstat_data **ifs 网卡节点链表
* 返回:1没有实际意义
*/
static int ioctl_map_scan(int sd, struct ifreq *ifr, struct ifstat_data **ifs) 

/*说明:网卡扫描,将设备里面所有的网卡都扫描出来
* 输入:struct ifstat_data **ifs:网卡链表
* 输出:struct ifstat_data **ifs 网卡节点链表
* 返回:1没有实际意义
*/
static int ioctl_scan_interfaces(struct ifstat_data **ifs) 


/*说明:读取文件,获取网卡流量数据
* 输入:int *checked :读取标志
* 输入:struct ifstat_data **ifs:网卡链表
* 输出:struct ifstat_data **ifs 网卡节点链表
* 返回:1没有实际意义
*/
static int proc_get_stats(int *checked,struct ifstat_data *ifs) 

/*说明:打印链表中的数据
* 输入:struct ifstat_data *ifs:网卡链表
* 输入:struct timeval *start:开始时间
* 输入:struct timeval *end:结束时间
* 返回:1没有实际意义
*/
static void print_stats(struct ifstat_data *ifs,struct timeval *start,struct timeval *end)

#3.源码

#include <unistd.h>
#include <string.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <errno.h>

#define PROC_FILE_PATH "/proc/net/dev"

/*网卡列表*/
struct ifstat_data 
{
    char *name;
    int namelen;
    unsigned long obout, obin, bout, bin;
    struct ifstat_data *next;
};

struct ifstat_data *ifstat_get_interface(struct ifstat_data *ifs, char *ifname)
{
    struct ifstat_data *ptr;
    int len = strlen(ifname);

    for (ptr = ifs; ptr != NULL; ptr = ptr->next)
    {
        if (len == ptr->namelen && ptr->name[0] == ifname[0] &&
            !strncmp(ptr->name + 1, ifname + 1, len - 1))
        {
            return ptr;
        }
    }
    return NULL;
}

void ifstat_set_interface_stats(struct ifstat_data *data,
												unsigned long bytesin,
												unsigned long bytesout)
{
    if (data->bout > bytesout || data->bin > bytesin) 
    {
        printf("[%s %d]warning: rollover for interface %s, reinitialising\n",__FUNCTION__,__LINE__, data->name);
        data->obout = bytesout;
        data->obin = bytesin;
    } 
    else
    {
        data->obout = data->bout;
        data->obin = data->bin;
    }
    data->bout = bytesout;
    data->bin = bytesin;
}

static int ioctl_map_scan(int sd, struct ifreq *ifr, struct ifstat_data **ifs) 
{
    if (ioctl(sd, SIOCGIFFLAGS, (char *)ifr) != 0)
        return 1;

	if ((ifr->ifr_flags & IFF_LOOPBACK))
        return 1;
    if (!(ifr->ifr_flags & IFF_UP))
        return 1;

	struct ifstat_data *cur, *last = NULL;
    /*检查网卡名称*/
    if (*ifr->ifr_name == '\0')
        return 1;
	
    int len = strlen(ifr->ifr_name);
    for (cur = *ifs; cur != NULL; cur = cur->next)
    {
        if (len == cur->namelen && cur->name[0] == ifr->ifr_name[0] &&
            !strncmp(cur->name + 1, ifr->ifr_name + 1, len - 1))
            return 1;
        last = cur;
    }

    if ((cur = calloc(1, sizeof(struct ifstat_data))) == NULL) 
    {
        printf("[%s %d]malloc error\n",__FUNCTION__,__LINE__);
        exit(EXIT_FAILURE);
    }
	
    cur->name = strdup(ifr->ifr_name);
    cur->namelen = len;
    if (last != NULL)
        last->next = cur;
    if (*ifs == NULL)
        *ifs = cur;
    return 1;
}

static int ioctl_scan_interfaces(struct ifstat_data **ifs) 
{
    int sd;
	struct ifstat_data *ifs_temp = NULL;
    if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        printf("[%s %d]socket error\n",__FUNCTION__,__LINE__);
        return 0;
    }
    //ioctl_map_ifs(sd, &ioctl_map_scan, (void *) ifs);

	/*******************************************函数调用说明*************************************************
	if_nametoindex():指定网络接口名称字符串作为参数;
					  若该接口存在,则返回相应的索引,
					  否则返回0
	if_indextoname():指定网络接口索引以及一块长度至少为IF_NAMESIZE(16)字节的内存区域作为参数;
					  若索引对应的网络接口存在,则在内存区域中返回该接口的名称字符串,
					  否则返回NULL,并将errno设置为相应的值
	if_nameindex():返回动态分配的struct if_nameindex结构数组,数组中的每一个元素分别对应一个本地网络接口;
	struct if_nameindex结构的if_index字段为接口索引,if_name字段为接口名称字符串;
						索引为0且名称字符串为NULL表示结构数组的末尾;
						调用出错时,返回NULL,并将errno设置为相应的值
	if_freenameindex():通过if_nameindex()获取完毕接口名称与索引后,调用该函数以释放动态分配的内存区域。
	以上4个函数在系统的man文档中都可以查看相应的描述,且都是POSIX标准支持的,Linux内核可能未实现这些函数,
	或已实现但不同于POSIX标准。这些函数的原型声明与定义并未出现在CentOS 6.7的定制内核2.6.32-573.26.1.el6.x86_64
	以及原版内核2.6.32.5中,而是由系统的glibc-2.12实现:在glibc-2.12.2源码树中,
	函数的原型声明位于sysdeps/gnu/net/if.h与sysdeps/generic/net/if.h,
	函数的定义位于sysdeps/unix/sysv/linux/if_index.c中,
	本质上是对ioctl(2)的SIOCGIFNAME,SIOCGIFCONF,SIOCGIFINDEX等操作以及netlink套接字进行了封装
	************************************************************************************************************/
    struct if_nameindex *iflist, *cur;
    struct ifreq ifr;
    if ((iflist = if_nameindex()) == NULL) 
    {
        printf("[%s %d]if_nameindex error\n",__FUNCTION__,__LINE__);
        return 0;
    }

    for(cur = iflist; cur->if_index != 0 && cur->if_name != NULL; cur++) 
    {
        memcpy(ifr.ifr_name, cur->if_name, sizeof(ifr.ifr_name));
        ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
		
        if (!ioctl_map_scan(sd, &ifr, &ifs_temp))
            return 0;
		*ifs = ifs_temp;
    }
    if_freenameindex(iflist);
    close(sd);
    return 1;
} 

static int proc_get_stats(int *checked,struct ifstat_data *ifs) 
{
    char buf[1024];
    FILE *f;
    char *iface, *stats;
    unsigned long bytesin, bytesout;
    struct ifstat_data *cur;
    if ((f = fopen(PROC_FILE_PATH, "r")) == NULL)
    {
        printf("[%s %d]can't open %s: %s\n",__FUNCTION__,__LINE__, PROC_FILE_PATH, strerror(errno));
        return 0;
    }

    /*检查首行*/
    if (fgets(buf, sizeof(buf), f) == NULL)
        goto badproc;
    if (!*checked && strncmp(buf, "Inter-|", 7))
        goto badproc;
    if (fgets(buf, sizeof(buf), f) == NULL)
        goto badproc;
    if (!*checked)
    {
        if (strncmp(buf, " face |by", 9))
            goto badproc;
        *checked = 1;
    }

    while (fgets(buf, sizeof(buf), f) != NULL) 
    {
        if ((stats = strchr(buf, ':')) == NULL)
            continue;
        *stats++ = '\0';
        iface = buf;
        while (*iface == ' ')
            iface++;
        if (*iface == '\0')
            continue;

        if (sscanf(stats, "%lu %*u %*u %*u %*u %*u %*u %*u %lu %*u", &bytesin, &bytesout) != 2)
            continue;

        if ((cur = ifstat_get_interface(ifs, iface)) != NULL)
            ifstat_set_interface_stats(cur, bytesin, bytesout);
    }
    fclose(f);
    return 1;

badproc:
    fclose(f);
    printf("[%s %d]%s: unsupported format\n",__FUNCTION__,__LINE__, PROC_FILE_PATH);
    return 0;
}

static void print_stats(struct ifstat_data *ifs,struct timeval *start,struct timeval *end)
{
    struct ifstat_data *ptr;
    double delay, kbin, kbout;

    delay = (end->tv_sec - start->tv_sec + ((double) (end->tv_usec - start->tv_usec))/ (double) 1000000)*1024;

    for (ptr = ifs; ptr != NULL; ptr = ptr->next) 
    {
        kbin = (double) (ptr->bin - ptr->obin) / (double) delay;
        kbout = (double) (ptr->bout - ptr->obout) / (double) delay;
        if (kbin < 0)
            kbin = 0;

        if (kbout < 0)
            kbout = 0;

        printf("name:%s\tkbin:%0.2lfKB\tkout:%0.2lfKB\n",ptr->name,kbin,kbout);
    }
}


int main(int argc, char **argv)
{
    struct ifstat_data *ifs = NULL;
	int checked = 0;
    struct timeval start, tv_delay, tv;

    ioctl_scan_interfaces(&ifs);

    /*第一次查询网卡数据*/
    if (ifs != NULL) 
    {
        if (!proc_get_stats(&checked,ifs))
            return -1;
        gettimeofday(&start, NULL);//获取当前时间
    }else
        return -1;
      
    tv_delay.tv_sec = 1;
    tv_delay.tv_usec = (int) ((1 - tv_delay.tv_sec) * 1000000);
    while(1)  
    {
    	printf("\n###############################################\n");
        tv = tv_delay;
        select(0, NULL, NULL, NULL, &tv);
        if (!proc_get_stats(&checked,ifs))
            return -1;
        gettimeofday(&tv, NULL);
        print_stats(ifs, &start, &tv);
        start = tv;
        fflush(stdout);
    }	
    return 0;
}

#4.Makefile文件

CXX=gcc
CFLAGS=-O3 -g -Wall -fmessage-length=0 -fPIC -DARCH_x86
OBJS=ifstat.o
LIBS+=
TARGET= ifnet
BIN=./

$(TARGET):$(OBJS)
	$(CXX) -o $(TARGET) $(OBJS) $(CFLAGS) $(LIBS)
	chmod 6755 $(TARGET)

objPath = $(firstword $(subst source,source ,$(shell pwd)))
	
all:$(TARGET)
	-cp -f $(TARGET) $(objPath)/OBJ/
install: all
	cp -f $(TARGET) $(BIN)/
	chmod 6755 $(TARGET)
clean:
	rm -f $(OBJS) $(TARGET)

这里写图片描述

猜你喜欢

转载自blog.csdn.net/kongshuai19900505/article/details/82222607