ifconfig如何获得网卡的统计信息

本文详细解析了Linux系统中通过`/proc/net/dev`获取网络设备统计信息的过程。内容包括`/proc/net/dev`的生成原理,涉及内核函数如`dev_seq_show()`、`dev_seq_printf_stats()`和`dev_get_stats()`。这些函数通过驱动程序接口或直接读取设备状态,收集并展示如接收/发送字节、错误、丢包等统计信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        1)ifconfig 是通过解析 /proc/net/dev 文件来获得网卡的统计信息的,如下所示:

$ cat /proc/net/dev
Inter-|   Receive                                                |  Transmit
 face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
enp3s0: 9666434  145598    0    0    0     0          0        20 28796033  143998    0    0    0     0       0          0
enp0s3f0: 89753829 1339304    0    0    0     0          0         0 220083622 1303148    0    0    0     0       0          0
    lo: 8551535   24076    0    0    0     0          0         0  8551535   24076    0    0    0     0       0          0
virbr0:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0
virbr0-nic:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0

        2)/proc/net/dev 里面的内容是如何产生的?

        /proc/net/dev 是由内核 dev_seq_show() 函数输出的,它的实现如下所示:

static int dev_seq_show(struct seq_file *seq, void *v) 
{
    if (v == SEQ_START_TOKEN)
        seq_puts(seq, "Inter-|   Receive                            "
                  "                    |  Transmit\n"
                  " face |bytes    packets errs drop fifo frame "
                  "compressed multicast|bytes    packets errs "
                  "drop fifo colls carrier compressed\n");
    else
        dev_seq_printf_stats(seq, v); 
    return 0;
}

        其中,dev_seq_printf_stats() 函数完成了最终的输出,它的实现如下所示:

static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
{  
    struct rtnl_link_stats64 temp;
    const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);
   
    seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu "
           "%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n",
           dev->name, stats->rx_bytes, stats->rx_packets,
           stats->rx_errors,
           stats->rx_dropped + stats->rx_missed_errors,
           stats->rx_fifo_errors,
           stats->rx_length_errors + stats->rx_over_errors +
            stats->rx_crc_errors + stats->rx_frame_errors,
           stats->rx_compressed, stats->multicast,
           stats->tx_bytes, stats->tx_packets,
           stats->tx_errors, stats->tx_dropped,
           stats->tx_fifo_errors, stats->collisions,
           stats->tx_carrier_errors +
            stats->tx_aborted_errors +
            stats->tx_window_errors +
            stats->tx_heartbeat_errors,
           stats->tx_compressed);
} 

        其中,dev_get_stats() 函数是用来获取网卡统计信息的,它的实现如下所示:

struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev,
                    struct rtnl_link_stats64 *storage)
{   
    const struct net_device_ops *ops = dev->netdev_ops;
    
    if (ops->ndo_get_stats64) {
        memset(storage, 0, sizeof(*storage));
        ops->ndo_get_stats64(dev, storage);
    } else if (ops->ndo_get_stats) {
        netdev_stats_to_stats64(storage, ops->ndo_get_stats(dev));
    } else {
        netdev_stats_to_stats64(storage, &dev->stats);
    }   
    storage->rx_dropped += (unsigned long)atomic_long_read(&dev->rx_dropped);
    storage->tx_dropped += (unsigned long)atomic_long_read(&dev->tx_dropped);
    storage->rx_nohandler += (unsigned long)atomic_long_read(&dev->rx_nohandler);
    return storage;
} 

        从上面可以看到,dev_get_stats() 函数首先判断网卡驱动是否实现了 dev->netdev_ops->ndo_get_stats64()、dev->netdev_ops->ndo_get_stats() 这两个接口,如果实现了,则通过这两个接口来获取网卡的统计信息,如果没有实现,则使用 netdev_stats_to_stats64() 函数来获取网卡的统计信息,该函数的实现如下所示:

void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64,
                 const struct net_device_stats *netdev_stats)
{   
#if BITS_PER_LONG == 64
    BUILD_BUG_ON(sizeof(*stats64) < sizeof(*netdev_stats));
    memcpy(stats64, netdev_stats, sizeof(*netdev_stats));
    /* zero out counters that only exist in rtnl_link_stats64 */
    memset((char *)stats64 + sizeof(*netdev_stats), 0,
           sizeof(*stats64) - sizeof(*netdev_stats));
#else
    size_t i, n = sizeof(*netdev_stats) / sizeof(unsigned long);
    const unsigned long *src = (const unsigned long *)netdev_stats;
    u64 *dst = (u64 *)stats64;
    
    BUILD_BUG_ON(n > sizeof(*stats64) / sizeof(u64));
    for (i = 0; i < n; i++)
        dst[i] = src[i];
    /* zero out counters that only exist in rtnl_link_stats64 */
    memset((char *)stats64 + n * sizeof(u64), 0,
           sizeof(*stats64) - n * sizeof(u64));
#endif
}

        可以看到,netdev_stats_to_stats64() 函数只是做了一个数据拷贝,其数据源来自于是网卡对应的 struct net_device 结构体中的 stats 成员,它的类型是 struct net_device_stats。以 Gmac 为例,它是在处理单个包的过程中,顺便更新了该结构体里的各个成员。例如,在驱动的发包函数 stmmac_xmit() 中更新发送字节数:

static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
{
...
    dev->stats.tx_bytes += skb->len;
...
}

在C语言中,获取Linux网卡的收计数通常需要系统调用或者底层的网络编程知识。你可以通过读取网卡统计信息文件,如`/proc/net/dev` 来获得这个数据。这个目录下有每个网络设备的详细统计数据,括接收和发送的字节数、数等。 下面是一个简单的示例,展示如何使用C语言从`/proc/net/dev` 文件中获取特定网卡(比如eth0)的接收到的数据数量: ```c #include <stdio.h> #include <string.h> #define ETH_SOCK_FILE "/proc/net/dev" // 获取网卡名称 char* get_interface_name(char *interface) { FILE *file = fopen(ETH_SOCK_FILE, "r"); char line[256]; while (fgets(line, sizeof(line), file)) { if (strstr(line, interface)) { fclose(file); return line; } } fclose(file); return NULL; // 如果未找到网卡名则返回NULL } int main() { char interface[] = "eth0"; // 替换为你想查询的网卡名称 char device_path[PATH_MAX]; strcpy(device_path, ETH_SOCK_FILE); strcat(device_path, "/"); strcat(device_path, get_interface_name(interface)); FILE *fp = fopen(device_path, "r"); if (!fp) { printf("Failed to open %s\n", device_path); return -1; } char stats[256]; fgets(stats, sizeof(stats), fp); // 跳过前两行标题 int received_packets = 0; sscanf(stats + strlen(stats) - 2, "%d:%d", &received_packets, NULL); // 提取接收计数部分 printf("Received packets on %s: %d\n", interface, received_packets); fclose(fp); return 0; } ``` 请注意这只是一个基本示例,实际操作可能会因为文件格式变化、权限限制等原因导致无法直接读取,可能需要添加错误处理代码。另外,如果你需要频繁获取实时更新的信息,可能需要学习更专业的网络编程库,如libpcap 或者使用socket API。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值