浅谈/proc/net/dev的由来

来源:互联网 发布:剑三笔记本优化版 编辑:程序博客网 时间:2024/05/17 04:16

 我们都知道可以从/proc/net/dev下去读取网络设备收发包时相关的数据,但之前从来没有关注这些文件的来源,直到前几天遇到一个wifi的tx和rx等数据都为0的问题不得不去探索原因,起初以为/proc/net/dev是按照文件操作的方式写进去的,结果在应用层和驱动中找了一遍没有发现相关的代码,于是到linux内核代码里一探究竟,果不其然。(我使用的linux版本为linux4.1.25)。


进入linux-lsk-v4.1.25\net\core\net_procfs.c,看到这个名字是不是恍然大悟呢?对,这就是产生/proc/net/dev文件的入口,下面将逐一进行分析:

int __init dev_proc_init(void)
{
int ret = register_pernet_subsys(&dev_proc_ops);
if (!ret)
return register_pernet_subsys(&dev_mc_net_ops);
return ret;
}

这里将dev_proc_ops进行了注册,驱动加载后,或将网络设备注册到linux内核,这时候linux内核就会调用这个接口,执行dev_proc_ops定义的函数,该函数如下:

static struct pernet_operations __net_initdata dev_proc_ops = {
.init = dev_proc_net_init,
.exit = dev_proc_net_exit,

};

不用我说吧,这里分别是开始和结束时执行的函数,我们只关注开始的dev_proc_net_init函数,结束时的函数有兴趣的可自己去

看,具体如下:

static int __net_init dev_proc_net_init(struct net *net)
{
int rc = -ENOMEM;


if (!proc_create("dev", S_IRUGO, net->proc_net, &dev_seq_fops))
goto out;
if (!proc_create("softnet_stat", S_IRUGO, net->proc_net,
&softnet_seq_fops))
goto out_dev;
if (!proc_create("ptype", S_IRUGO, net->proc_net, &ptype_seq_fops))
goto out_softnet;


if (wext_proc_init(net))
goto out_ptype;
rc = 0;
out:
return rc;
out_ptype:
remove_proc_entry("ptype", net->proc_net);
out_softnet:
remove_proc_entry("softnet_stat", net->proc_net);
out_dev:
remove_proc_entry("dev", net->proc_net);
goto out;
}

想必大家都看到了,函数中红色的部分就是在创建/proc/net/dev文件,然后指定了文件的操作接口(绿色部分)的函数指针。具体

如下:

static const struct file_operations dev_seq_fops = {
.owner = THIS_MODULE,
.open    = dev_seq_open,
.read    = seq_read,
.llseek  = seq_lseek,
.release = seq_release_net,
};

  同样的道理,我们只关注open函数,如下:

static int dev_seq_open(struct inode *inode, struct file *file)
{
return seq_open_net(inode, file, &dev_seq_ops,
    sizeof(struct seq_net_private));
}

这里又来了一个dev_seq_ops,经常看linux内核代码的都知道,实际上我们不需要注seq_open_net函数,直接进入dev_seq_ops即可,具体如下:

static const struct seq_operations dev_seq_ops = {
.start = dev_seq_start,
.next  = dev_seq_next,
.stop  = dev_seq_stop,
.show  = dev_seq_show,
};

翻了这么多大山,这里终于看了了山顶,那就是这个dev_seq_show函数了,我想大家看到的时候应该都会第一时间去看这个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;
}

上面红色的部分就是用来打印/proc/net/dev头部的,你可以cat /proc/net/dev仔细对照看一下是不是酱紫。绿色的部分是用来打印/proc/net/dev的身体的,为什么是对立的分支呢?因为linux起来后刚开始没有检测到网络设备,此时的v就是SEQ_START_TOKEN,管他有没有呢,先把头搞出来再说。下面看一下身体吧,这才是应该重点关注的。

static void dev_seq_printf_stats(struct seq_file *seq, structnet_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);
}

这里总算看明白了吧?这个net_device就是驱动中注册网络设备时的结构体,至于里面的这些收发包的数据,当然是在驱动中产生的了,具体就不详细叙述了。