Linux网络设备驱动指南(1)


51CTO.com独家特稿】前言介绍,本文所涉及概念:

1)snull:是一种虚拟的网络模型,通过它可以基本了解真实物理网络接口设备驱动程序的运作。

2)本文代码都是标准C语言格式。

3)*skb:重要的指针量,指向一块内存区域用以缓冲待处理的进出网络数据包。

Linux网络接口设备概述

Linux中网络接口是3大标准类设备之一,其他2种类型分别是字符设备和块设备。下面介绍网络接口设备的驱动程序如何和内核模块进行交互的知识。网络设备驱动程序异步地接收来自外部世界的网络数据包,通过push操作将进来的数据包压向内核,而块设备驱动程序则将一片数据缓冲发送给内核,驱动程序都需要将相应设备的特征信息登记到内核中特定的数据结构中,块设备驱动程序可以使用文件形式来描述,而网络驱动程序则不可以使用一般的文件读写操作调用,网络设备驱动程序有自己的内存名字空间,使用push等操作来完成数据包的转换与递送。

Unix世界里的"一切皆是文件"的论述,对网络设备接口来说是不适用的。块设备在系统文件树的/dev目录下可以找到特定的文件入口标志,而网络设备则没有这种文件操作入口,一个物理网络接口上的数百个用于网络数据交换的插口可以被应用程序重复使用。

图1

如上图,内核中有专门为网络设备驱动程序设计的数据包操作接口。通过网络接口sn0到达网络snullnet0就是路由的意思.使用route add -net snullnet0 dev sn0指令来完成,2.2以上的Linux内核无需这样做了,内核自动将添加。如果网络非C类地址,请用掩码指定255.255.255.0指定的是C类网络,因为在snull实验网络模型中地址机制被修改,所以发向192.168.0.33的数据包将通过sn0接口送达snullnet1中得192.168.1.33机器,送到其它网络地址的数据包将被sn1接口丢弃.说到物理数据传输,snull网络模型机制隶属于以太网Ethernet范畴.

由于以太网已经被广泛使用的原因,甚至连打印协议plip接口也声称自己隶属于以太网设备范畴.你还可以用tcpdump工具来察看snull实验网模型中的网络数据走向。使用tcpdump工具在2.0内核中加载snull设备驱动时需要显示指定Eth=1项。注意snull网络模型只能用来试验ip网络协议的数据包。利用snull网络模型传送非IP网络数据包必须对snull模块的源代码进行修改,否则会破坏其它非ip网络数据包。

Snull模型核心操作

snull核心操作大概有11多项,它们是:

 ether_setup,
open,
stop,
set_config,
hard_start_xmit,
do_ioctl,get_stats,
rebuild_header,
tx_timeout,
watchdog_timeo,
flags,
hard_header_cache,
SET_MODULE_OWNER等.

注意:snull网络不处理arp数据包,因为核心操作中的IFF_NOARP标志位.ARP是一个底层的以太网协议标准。任务是将IP地址对应到以太网物理媒质访问控制地址,也就是MAC地址。因为snull网络模拟出来的网络机器无需处理MAC地址,故不处理了。同样道理hard_header_cache也无需处理了,snull模型中将它设成了NULL。

如何操作网络设备

以下要介绍如何对网络设备展开具体操作。网络接口可以操作数据包之前必须由内核打开接口并赋予内存地址,使用ifconfig命令就可以做到,首先通过ioctl分配地址,再将IFF_UP标志位打开,这时候驱动程序的功能还没有用到,只是内核在执行而已。插槽输入输出接口标志位打开就调用了open方法打开了设备接口。关闭接口方法同理。如果执行成功将返回0,否则返回一个负数。
驱动程序的实际代码完成很多类似于char或block类型的操作,open调用并分配系统资源,stop停止并释放系统资源。接口和外部世界通讯之前需要将物理设备上的地址复制到dev->dev_addr变量中,snull模型驱动程序中使用ASCII字符串ETH_ALEN来杜撰一个物理网卡地址。

open方法也开启了一个传输队列,内核提供了一个函数来实现一个队列:

void netif_start_queue (struct net_device *dev);
另外snull模型的代码类似以下:

int snull_open(struct net_device *dev) 
{
MOD_INC_USE_COUNT; 
memcpy(dev->dev_addr, "\0SNUL0", ETH_ALEN); 
dev->dev_addr[ETH_ALEN-1] += (dev - snull_devs); 
netif_start_queue(dev); return 0; 
}

因为snull模型中并没有真实的硬件网络设备所以open方法中没有太多代码,stop方法同理:

int snull_release(struct net_device *dev) 
{
/*释放端口和irq,类似于fops->close */ 
netif_stop_queue(dev);
/*无法再传输数据了*/ 
MOD_DEC_USE_COUNT; 
return 0;
}
开启和关闭接口的方法是一对矛盾。

网络接口的最重要任务:发送和接受网络数据包

接着讲网络接口要完成的最重要的任务:发送和接受网络数据包。发送包容易理解所以先讲,在高级网络层中每个包都属于一个特定编号的网络插槽(socket)进出的包使用sk_buff结构变量来列表表示,插槽缓冲(sk_buff)结构变量贯穿于整个linux网络之系统中。具体定义可以在linux/skbuff.h文件中找到。
指向sk_buff变量的指针通常被称作skb,插槽缓冲是一个复杂的结构,内核负责提供一定数量的动作函数来操作该结构,开始通过调用hard_start_transmit方法函数把需要发送的包弄进出站队列,hard_start_xmit中含有包在物理媒介上的地址,skb->data指向正在传输的包,skb->len是用八进制表示的包长度。snull模型中包的传输代码和实际物理网卡的驱动代码是隔离的,snull的包传输代码如下:

int snull_tx(struct sk_buff *skb, struct net_device *dev) 
{
int len; 
char *data; 
struct snull_priv *priv = (struct snull_priv *) dev->priv; 
len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; data = skb->data; 
dev->trans_start = jiffies; 
/*保存时戳*/ 
priv->skb = skb;
/*记住缓冲指针以便随时中断释放,
实际传送数据与设备相关,这里省略了*/ snull_hw_tx(data, len, dev); 
return 0;
}

那么如何并发地控制传输呢?对于缓冲暂时满载时就要通过netif_stop_queue来暂停,并使用void netif_wake_queue(struct net_device *dev)来继续重新启动,设备被分的内存中可同时有多个包缓冲。

一种机制控制设定的动作没有在规定时间内完成则被视为超过时间,有问题发生,相关的监测函数是watchdog_timeo,该函数在net_device结构中,jiffies是时间控制表。然后调用tx_timeout方法来处理。超时一旦发生,驱动程序代码必须做出错误标记,snull模型中错误一旦发生将调用snull_interrupt,netif_wake_queue等函数。


相关内容