Linux网络设备驱动

  1. linux网络设备驱动体系结构
  2. 网络协议接口层
  3. 网络设备接口层
    1. 设备操作函数
    2. 辅助成员
  4. 设备驱动功能层

linux网络设备驱动体系结构

Linux网络设备驱动程序的体系结构可分为4层,依次为网络协议接口层、网络设备接口层、提供实际功能的设备驱动功能层和网络设备与媒介层。如下所示。

网络设备驱动体系结构

网络协议接口层

网络协议接口层的功能主要是给上层提供透明的数据包发送和接收接口。其接口如下:

1
2
int dev_queue_xmit(struct sk_buff *skb);
int netif_rx(struct sk_buff *skb);

与收发接口紧密联系的是sk_buff这个结构,这是用于描述网络数据的缓存结构,它在Linux网络子系统中的各层之间传递数据,是各层之间数据传输的标准形式。sk_buff中包含4个指针成员:head、data、tail、end共同用来描述网络数据所占的内存区域。如下图所示。

数据包数据的内存表示

对于skb_buff数据结构的操作函数有如下这些:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* sk_buff的申请与释放 */
struct sk_buff *alloc_skb(unsigned int size, gfp_t priority);
struct sk_buff *netdev_alloc_skb(struct net_device *dev, unsigned int length);
void kfree_skb(struct sk_buff *skb);
dev_kfree_skb(struct sk_buff *skb);

/* 在缓冲区尾部增加数据,会导致skb->tail后移len(skb->tail += len) */
void *skb_put(struct sk_buff *skb, unsigned int len);

/* 在缓冲区头部增加数据,会导致skb->data前移len(skb->data -= len) */
void *skb_push(struct sk_buff *skb, unsigned int len);

/* 在缓冲区头部减少数据,会导致skb->data后移len(skb->data += len) */
void *skb_pull(struct sk_buff *skb, unsigned int len);

/* 会导致skb->data与skb->tail同时后移len(skb->data += len,skb->tail += len) */
void skb_reserve(struct sk_buff *skb, int len);

网络设备接口层

网络设备接口层的主要功能是为各种各样的网络设备定义统一、抽象的数据结构net_device结构体,实现各种硬件在软件层次上的统一。

net_device在内核中指代一个网络设备,有了这个定义,网络设备驱动之需要填充net_device的具体成员并注册即可实现硬件操作函数与内核的挂接。

设备操作函数

1
const struct net_device_ops *netdev_ops;

该结构体是网络设备的一系列硬件操作行为的结合,它定义了init,open,close等一系列硬件操作接口。

除了netdev_ops外,在net_device中还存在类似于ethtool_ops、header_ops这样的操作集:

1
2
const struct ethtool_ops *ethtool_ops;
const struct header_ops *header_ops;

ethtool_ops成员函数于用户空间ethtool工具的各个命令选项对应,ethtool提供了网卡及网卡驱动管理能力,能够为linux网络开发人员和管理人员提供对网卡硬件、驱动程序和网络协议栈的设置、查看和调试等功能。

header_ops对应于硬件头部操作,主要是完成创建硬件头部和给定的sk_buff分析出硬件头部等操作。

辅助成员

1
2
unsigned long trans_start;
unsigned long state;

trans_start记录最后的数据包开始发送时的时间戳,state记录最后一次接收到数据包时的时间戳,这两个时间戳记录的都是jiffies,驱动程序应维护这两个成员。

通常情况下,网络设备驱动以中断方式接收数据包,而poll_controller()则采用纯轮询方式,另外一种数据接收方式是NAPI(New API),其数据接收流程为“接收中断来临->关闭接收中断->以轮询方式接收所有数据包直到为空->开启接收中断->接收中断来临……”

内核中提供了如下NAPI的API。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* 初始化与移除napi */
void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
int (*poll)(struct napi_struct *, int), int weight);
void netif_napi_del(struct napi_struct *napi);

/* 检查NAPI是否可以调度 */
bool napi_schedule_prep(struct napi_struct *n);

/* 调度NAPI实例运行 */
void napi_schedule(struct napi_struct *n);

/* 标记NAPI任务处理完成 */
bool napi_complete_done(struct napi_struct *n, int work_done);

/* 使能和禁止NAPI调度 */
void napi_disable(struct napi_struct *n);
void napi_enable(struct napi_struct *n)

设备驱动功能层

net_device结构体的成员需要被设备驱动功能层赋予具体的数值和函数。对于具体的设备xxx,应该标写相应的设备驱动功能层的函数,这些函数形如xxx_open()、xxx_stop()、xxx_tx()等。

由于网络数据包的接收可由中断引发,设备驱动功能层的另一个主体部分将是中断处理函数,它负责读取硬件上接收到的数据包并传送给上层协议。

具体的网络设备驱动设计通常要实现如下部分:

  1. 网络设备驱动的注册与注销
  2. 网络设备的初始化
  3. 网络设备的打开与释放
  4. 数据发送
  5. 数据接收
  6. 网络连接状态
  7. 参数设置和统计数据

对于一个完整的网络设备驱动其实现可参考drivers/net/ethernet/3com/3c509.c


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 yxhlfx@163.com

文章标题:Linux网络设备驱动

本文作者:红尘追风

发布时间:2016-09-01, 15:12:43

原始链接:http://www.micernel.com/2016/09/01/Linux%E7%BD%91%E7%BB%9C%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录