You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Ubuntu 22.1中修改dev结构体stats的rx_bytes和tx_bytes字段

解决Linux内核网络设备统计值(rx_bytes/tx_bytes)重启后恢复的问题

核心思路

要实现重启后恢复统计值,需分两步完成:关机前将当前统计值保存到持久化存储,开机后读取备份值并修改内核中对应字段。

步骤1:扩展现有模块,添加统计值保存功能

修改内核模块,在卸载或系统关机时,把各网卡的rx_bytes、tx_bytes写入文件系统(示例路径为/var/lib/net_stats_backup)。

修改后的保存模块代码

#include <linux/kernel.h>
#include <linux/module.h> 
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include <linux/reboot.h>

#define BACKUP_PATH "/var/lib/net_stats_backup"

static void save_net_stats(void) {
    struct net_device *dev = first_net_device(&init_net);
    struct rtnl_link_stats64 stats64;
    struct file *file;
    loff_t pos = 0;
    char buf[256];
    int ret;

    file = filp_open(BACKUP_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (IS_ERR(file)) {
        printk(KERN_ERR "Failed to open backup file: %ld\n", PTR_ERR(file));
        return;
    }

    while (dev) {
        if (netif_running(dev)) {
            dev_get_stats(dev, &stats64);
            snprintf(buf, sizeof(buf), "%s %llu %llu\n", 
                     dev->name, stats64.rx_bytes, stats64.tx_bytes);
            ret = kernel_write(file, buf, strlen(buf), &pos);
            if (ret < 0) {
                printk(KERN_ERR "Failed to write stats for %s: %d\n", dev->name, ret);
            }
        }
        dev = next_net_device(dev);
    }

    filp_close(file, NULL);
    printk(KERN_INFO "Net stats saved to %s\n", BACKUP_PATH);
}

static int shutdown_notify(struct notifier_block *nb, unsigned long code, void *unused) {
    if (code == SYS_DOWN || code == SYS_HALT || code == SYS_POWER_OFF) {
        save_net_stats();
    }
    return NOTIFY_OK;
}

static struct notifier_block shutdown_nb = {
    .notifier_call = shutdown_notify,
};

static int mymodule_init(void) {
    struct net_device *dev = first_net_device(&init_net);
    struct rtnl_link_stats64 stats64;

    printk(KERN_INFO "Loading net stats backup module\n");
    register_reboot_notifier(&shutdown_nb);

    while (dev) {
        if (netif_running(dev)) {
            dev_get_stats(dev, &stats64);
            printk(KERN_INFO "%s: rx_bytes=%llu, tx_bytes=%llu\n", 
                   dev->name, stats64.rx_bytes, stats64.tx_bytes);
        }
        dev = next_net_device(dev);
    }
    return 0;
}

static void mymodule_exit(void) {
    unregister_reboot_notifier(&shutdown_nb);
    save_net_stats();
    printk(KERN_INFO "Backup module unloaded, stats saved\n");
}

module_init(mymodule_init);
module_exit(mymodule_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kiavash Rocky");
MODULE_DESCRIPTION("Read and backup network device stats");

步骤2:编写开机恢复模块

实现一个模块,开机加载时读取备份文件,修改对应网卡的统计值。注意:不同驱动的统计存储结构可能有差异,以下是通用修改逻辑,需根据实际驱动调整。

恢复模块代码

#include <linux/kernel.h>
#include <linux/module.h> 
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include <linux/string.h>
#include <linux/spinlock.h>

#define BACKUP_PATH "/var/lib/net_stats_backup"

static int restore_net_stats(void) {
    struct net_device *dev;
    struct file *file;
    loff_t pos = 0;
    char buf[256];
    char dev_name[IFNAMSIZ];
    unsigned long long rx_bytes, tx_bytes;
    int ret;

    file = filp_open(BACKUP_PATH, O_RDONLY, 0);
    if (IS_ERR(file)) {
        printk(KERN_ERR "Failed to open backup file: %ld\n", PTR_ERR(file));
        return PTR_ERR(file);
    }

    while ((ret = kernel_read(file, buf, sizeof(buf)-1, &pos)) > 0) {
        buf[ret] = '\0';
        char *line = buf;
        while (*line) {
            while (*line && isspace(*line)) line++;
            if (!*line) break;

            char *name_end = line;
            while (*name_end && !isspace(*name_end)) name_end++;
            if (name_end == line) break;
            strncpy(dev_name, line, name_end - line);
            dev_name[name_end - line] = '\0';
            line = name_end;

            while (*line && isspace(*line)) line++;
            if (!*line) break;
            rx_bytes = simple_strtoull(line, &line, 10);

            while (*line && isspace(*line)) line++;
            if (!*line) break;
            tx_bytes = simple_strtoull(line, &line, 10);

            dev = dev_get_by_name(&init_net, dev_name);
            if (!dev) {
                printk(KERN_WARNING "Device %s not found, skip restore\n", dev_name);
                continue;
            }

            if (netif_running(dev)) {
                spin_lock_bh(&dev->stats_lock);
                dev->stats.rx_bytes = rx_bytes;
                dev->stats.tx_bytes = tx_bytes;
                // 若驱动使用64位统计结构,需打开以下注释(根据实际驱动调整)
                // #ifdef CONFIG_NETSTATS_INC_USE_STATS64
                // dev->stats64.rx_bytes = rx_bytes;
                // dev->stats64.tx_bytes = tx_bytes;
                // #endif
                spin_unlock_bh(&dev->stats_lock);
                printk(KERN_INFO "Restored %s: rx_bytes=%llu, tx_bytes=%llu\n", 
                       dev_name, rx_bytes, tx_bytes);
            }
            dev_put(dev);
        }
    }

    filp_close(file, NULL);
    return 0;
}

static int mymodule_init(void) {
    printk(KERN_INFO "Loading net stats restore module\n");
    return restore_net_stats();
}

static void mymodule_exit(void) {
    printk(KERN_INFO "Restore module unloaded\n");
}

module_init(mymodule_init);
module_exit(mymodule_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kiavash Rocky");
MODULE_DESCRIPTION("Restore network device stats from backup");

关键注意事项

  • 权限配置:提前创建/var/lib/net_stats_backup文件并设置权限为644,确保内核模块能读写该文件。
  • 驱动兼容性:部分网络驱动使用私有统计结构体而非dev->stats,需查看对应驱动源码(如e1000、virtio_net)确定正确的修改字段。
  • 加载时机:恢复模块需在网络设备初始化完成后加载,可通过/etc/modules-load.d/net_stats_restore.conf配置开机自启。

编译与测试

  1. 编写Makefile:
obj-m += net_stats_backup.o
obj-m += net_stats_restore.o
all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
  1. 编译模块:执行make
  2. 加载备份模块:insmod net_stats_backup.ko
  3. 关机或卸载备份模块:rmmod net_stats_backup,此时统计值会保存到备份文件
  4. 重启后加载恢复模块:insmod net_stats_restore.ko,通过dmesg | grep Restored查看恢复结果

内容的提问来源于stack exchange,提问作者Kiavash Rocky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 07:27:50