Linux 的网络系统主要是基于 BSD UNIX
的套接字机制。在系统与驱动程序之间定义了数据结构 sk_buff
进行数据传输。系统支持对发送数据和接收数据缓存,提供流控机制并提供对多协议的支持。
1、 linux 网络驱动程序的体系结构
linux 网络驱动程序的体系结构从上到下分为4层,各层作用如下:
(1) 网络协议接口层向网络层协议提供统一的数据包收发接口,不论上层是IP还是ARP,都通过
dev_queue_xmit 函数发送数据,并通过 net_rx
函数接收数据。这一层的存在使得上层协议独立于具体的设备。
(2) 网络设备接口层向协议接口层提供统一的用于描述据具体网络设备属性和操作的结构体 net_dev,net_dev是设备驱动功能层中各函数的容器。网络协议接口层从宏观上规划了具体操作硬件的设备驱动功能层的结构。
(3) 设备驱动功能层各函数是网络设备接口层 net_dev
数据结构的具体成员,是驱使网络设备硬件完成相应动作的程序。通过 hard_start_xmit
函数启动发送操作,并通过网络设备上的中断触发接收操作。
由于网络数据包的接收由中断引发,设备驱动功能层中另一个主体部分是中断处理函数,中断处理函数负责读取硬件上接收的数据包并传递给上层协议,可能包含了xxx_interrupt
和 xxx_rx
函数。xxx_interrupt
函数完成中断类型判断等基本工作, xxx_rx
函数完成数据包的生成和递交上层等复杂工作。
(4) 网络设备与媒介层是完成数据包发送和接收的物理实体,包括了网路适配器饿坏具体的传输媒介,网络设备被设备驱动功能层中的函数物理上驱动。在linux中,网络设备和媒介都可以是虚拟的。
在linux 中所有网络系统都抽象为一个接口,由结构体 struct net_device
来表示网络设备在内核中的运行情况,即网络设备接口。网络设备接口既包括了纯软件网络设备接口,也包括了硬件网络设备接口。所有的网络设备都是通过以 dev_base
为头指针的设备链表来管理的。该设备链表中的每一个元素代表一个网络接口。结构体 net_device
中包含了很多供系统访问和协议层调用的设备方法,包括了 init
函数(设备初始化和系统注册)、open
函数(打开网络设备)、stop
函数(关闭网络设备)、hard_start_xmit
函数(处理数据包发送)和中断处理函数等。
2、 网络设备的初始化
网络设备的初始化主要是 struct net_device
中的 init
函数指针所指向的初始化函数来完成。当内核启动或者加载网络驱动模块的时候,就会调用 init
函数。init
函数通过检测物理设备的硬件特征判断网络物理设备是不是存在,然后才决定是不是启用这个驱动程序。接着对设备进行资源配置,配置好之后就可以向系统申请资源,如中断和 i/o
空间。最后对 net_device
相应的成员变量初始化,这样一个网络设备才能被系统使用。
对于一个新添加的设备,则需要构造设备的 net_device
数据结构,然后向linux
内核注册设备并申请内存空间。
3、 数据包的发送和接收
当网络设备被激活的时候,调用 net_device
中的 open
函数指针,打开网络设备,并使用 net_device
中的 hard_header
函数指针来建立硬件帧头信息。然后通过 dev_queue_xmit
协议接口层函数来调用 net_device
中的 hard_start_xmit
函数指针完成数据包的发送。hard_start_xmit
函数负责把存放在套接字缓冲区(sk_buff)中的数据发送到物理设备。
一般情况下,设备受到数据就会产生一个中断,在中断处理时驱动程序申请一块 sk_buff
,从硬件读出数据并放到申请好的缓冲区中,然后填充sk_buff
的一些信息。最后调用 netif_rx
函数把接收到的数据包传到网络协议的上层进行处理。
当网络设备被激活的时候,调用 net_device
中的 open
函数指针,打开网络设备,并使用 net_device
中的 hard_header
函数指针来建立硬件帧头信息。然后通过 dev_queue_xmit
协议接口层函数来调用 net_device
中的 hard_start_xmit
函数指针完成数据包的发送。hard_start_xmit
函数负责把存放在套接字缓冲区(sk_buff)中的数据发送到物理设备。
一般情况下,设备受到数据就会产生一个中断,在中断处理时驱动程序申请一块 sk_buff
,从硬件读出数据并放到申请好的缓冲区中,然后填充sk_buff
的一些信息。最后调用 netif_rx
函数把接收到的数据包传到网络协议的上层进行处理。
sk_buff 结构体(即套接字缓冲区)用于在linux 网络子系统中的各层之间传递数据。当发送数据包的时候,linux内核的网络处理模块必须建立一个包含要传输的数据包的 sk_buff
,然后将 sk_buff
递交给下层,各层在 sk_buff
中添加不同的协议头直至交给网络设备发送。当网络设备从网络媒介上接收到数据包的时候,必须将接收到的数据装换为 sk_buff
数据结构并传递给上层,各层去掉相应的协议头直至给交用户。
4、 网络设备的模块加载
网络设备驱动程序加载方式有内核加载和模块加载两种方式。系统内核有专门加载网络设备的过程,也可以通过 module_init
宏为系统添加新网络设备。此处主要讨论网络设备的模块加载,其基本流程为:
(1) 通过 module_init
宏修饰的函数会在模块加载(或系统启动)时被调用;
(2) 网络设备被检测到后,通过调用
register_netdev 函数在 linux
系统中把设备添加到系统的网络设备链表 dev_base
的末尾;
(3) 注册成功,将调用net_device
中的 init
函数,初始化设备。
通过 register_netdev 函数注册网络设备是一个动态加载的过程,在 linux 2.6
内核中,所有网络设备都是用该函数加载的。模块加载不存在为网络设备预留空间,从而节省了系统内存资源。
5、 net_device
结构细节
netdevice.h文件保存在 include/linux
目录中
(1) 全局信息
char name[IFNAMSIZ];
设备名称。分配的编号从零开始。
unsigned long state;
设备状态。包含若干标识。
struct net_device *next;
指向全局链表下一个设备的指针。
(2) 硬件信息
unsignedlong mem_end; unsigned long mem_start;
设备内存信息。保存了设备使用的共享内存值起始和终止地址
unsigned long base_addr;
网络接口的的I/O基地址。
unsigned int irq;
被赋予的中断号。
unsigned char if_port;
指定在多端口设备上使用哪个端口。
unsigned char dma;
为设备分配的DMA通道。用于显示信息(ifconfig命令)。
(3) 设备方法
以下仅列举使用接口必需的方法。
int (*open)(struct net_device*dev);
打开接口。在ifconfig激活接口时,接口将被打开。open函数应该注册所有的系统资源,打开硬件并对设备执行其他所需的设置。
int (*stop)(struct net_device*dev);
停止接口。该函数执行的操作与open函数相反。
int (*hard_header) (struct sk_buff *skb, structnet_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len);
该函数根据先前检索到的源和目标硬件地址建立硬件头。该函数任务是将作为参数传递进入的信息,组织成设备特有的适当硬件头。eth_header
是以太网类型接口的默认函数,ether_setup
将该成员赋值成eth_header。
int (*rebuild_header)(structsk_buff *skb);
该函数用来在传输数据包之前,完成ARP解析之后,重新建立硬件头。
void (*tx_timeout) (structnet_device *dev);
如果数据包的传输在合理的时间段内失败,则假设丢失了中断或接口被锁住,这时将调用该方法。tx_timeout
负责解决问题并重新开始数据包的传输。
int (*set_config)(structnet_device *dev, struct ifmap *map);
改变接口配置。该函数是配置驱动程序的入口点。利用set_config
可以在运行中改变设备的I/O地址和中断号。
struct net_device_stats* (*get_stats)(struct net_device*dev);
当应用程序需要获得接口上的统计信息的时候,将会调用该函数。
版权声明:本文为博主原创文章,未经博主允许不得转载。