Linux 网络设备驱动之层次结构

    学习网络设备驱动之前,先来分析一下网络子系统的结构等知识。

1.分为用户空间和内核空间,以及物理设备空间;

2.用户空间是应用层,内核空间与应用层交互的是系统文件调用层(system call interface),系统调用和网络协议层之间有个协议无关接口(protocol agnostic interface),网络协议层和设备驱动层(device dirver )之间有个设备无关接口(device agnostic interface);

3.为什么要有协议无关接口?这样系统调用可以用统一的接口来将不同类型的传输协议包送给协议无关接口,经过协议无关接口后来送给协议层处理,方便了应用层的系统调用。

4.为什么要有设备无关接口?这一层提供一组通用函数供底层驱动程序使用,让它们可以对高层协议栈进行操作。向上,为我们的网络协议层来访问设备驱动提供了统一的接口,不管网卡驱动如何写,我们的协议层访问网卡驱动都是用的统一的接口;向下,它为设备驱动程序处理协议提供了统一的接口,比如说网卡接收到了IP包,我们的驱动程序不需要去关心这个IP包是TCP的还是UDP的,甚至它都不需要关心是不是IP包,它将这个包直接丢给设备无关接口来处理了。

    有了网络框架结构的了解,下面从net_device这个结构体来讲起。

1.每个网络接口都由一个net_device来描述,这个结构可以用alloc_netdev或者alloc_etherdev()来分配;

2.这个结构体主要的成员有 

  • 全局信息 设备名:name[IFNAMESIZ]   设备初始化函数指针:int(*init)(struct net_device *dev)
  • 硬件信息 网络设备的基地址:base_addr  设备使用的中断号:irq
  • 接口信息 用于存放设备的硬件地址:dev_addr[MAX_ADDR_LEN]
  • 设备操作函数 打开网络接口设备:open 关闭网络接口设备:stop 启动数据包的发送:hard_start_xmit(skb, dev) 数据包发送超时调用tx_timeout(dev) ,该函数需要采用重新启动数据包发送过程或重新启动硬件等措施来恢复网络设备到正常状态,获得网络设备的状态信息:get_stats(dev), do_ioctl, set_config, set_mac_address
  • 私有指针void *priv

    网络接口驱动的注册方式与字符驱动不同的地方是它没有主次设备号,用register_netdev(struct net_device*dev)注册

    刚才提到设备无关接口,那么它为网络协议层提供统一的数据包收发接口,不论上层协议为arp还是ip,都通过dev_queue_xmit(struct sk_buff*skb)函数发送数据,并且通过netif_rx(struct sk_buff*skb)函数来接受数据,说到这儿,我们要引入套接字缓冲区struct sk_buff这个非常重要的结构体了。

    该结构主要的成员如下:unsigned char*head;unsigned char*end;unsigned char*data;unsigned char*end;

         head                                  data                     tail                           end

   分配空间的开始             有效数据的开始      有效数据的结尾       分配空间的结束

    skb_buff 的操作函数主要有struct sk_buff *dev_alloc_skb(unsigned int len, int priority) ----------------供驱动代码使用

                                                struct sk_buff *alloc_skb(unsigned int len, int priority)        -------------供协议栈代码使用

                       另外三个操作函数分别是unsigned char*skb_put(struct sk_buff *skb, int len)和unsigned                                                char*skb_push(struct sk_buff *skb, int len),以及static inline void skb_reserve(struct sk_buff *skb, int                        len)。skb_put的作用是将data指针前移len的长度,函数返回的是移动之后的指针;而skb_push的作用                        是将tail指针往后移len的长度,并且返回的是移动之前的指针;skb_reserve的作用是将data和tail的指针                        同时向后移动len的长度。

   skb_buff的释放函数dev_kfree_skb(struct sk_buff *skb);


   下面是网络设备驱动的注册与注销

   先alloc_etherdev(sizeof_pri)函数来对net_device的生成和对其成员的赋值,它是alloc_netdev()针对以太网的快捷函数,分配并赋值后用register_netdev()注册设备进内核。net_device结构体的分配与驱动注册是在模块加载函数中进行。同理,释放函数free_netdev()和注销函数unregister_netdev()是在模块卸载函数中完成。

    网络设备驱动注册,当寻找到device时,会去执行probe函数,此时我们的网络设备的初始化工作可以放在probe函数中去执行。具体的初始化主要包含以下几个方面:

  • 进行硬件上的准备工作,检查网络设备是否存在,如果存在,则检测设备所使用的硬件资源;
  • 进行软件接口上的准备工作,分配net_device结构体并且对其数据和函数指针成员赋值;
  • 获得设备的私有信息指针并且初始化其各个成员,比如板极信息

    网络设备的打开函数需要完成如下的工作:

  •     使能设备使用的硬件资源,申请I/O区域、注册中断、DMA等
  •     调用linux内核提供的netif_start_queue()函数激活启动设备发送队列

      

    网络设备的关闭函数需要完成如下的工作:

  •     调用netif_stop_queue()函数,停止发送队列
  •     释放设备所使用的I/O区域、中断、DMA等

       linux网络子系统发送数据包时,会调用驱动程序提供的hard_start_transmit()函数,在设备初始化的时候这个函数指针需要被初始化。hard_start_transmit()函数中,根据skb->data,skb->len来发送数据,当发送队列满了,或者其他原因来不及发送当前上层传下来的包时,我们调用netif_stop_queue()函数来阻止上层继续向网络设备驱动发送数据包。当忙于发送的数据包发送完成后,传送结束的中断处理中,应该调用netif_wake_queue唤醒被阻塞的上层。


    当数据传输超时时,此时超时处理函数xxx_tx_timeout()将会被调用,这个函数里面也需要调用linux内核提供的netif_wake_queue()函数重新启动设备发送队列。


    网络接收数据包的主要方法是中断引发设备的中断处理函数,中断处理判断中断类型,如果为接收中断则读取接收到的数据,分配sk_buff数据结构,将数据复制到数据缓冲区,并且调用netif_rx()函数传递给上层协议。我们经常在接收函数中看到skb=dev_alloc_skb(length+2);skb_reserve(skb,2),这里的2是因为IP协议头是4字节对齐的,而接收到的IP头首部是6个字节目的地址,6个字节源地址,2个字节数据字段长度,一共加起来是14个字节,所以分配skb数据缓冲区的时候多分配2个字节。

      



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
NAPI 是 Linux 上采用的一种提高网络处理效率的技术,它的核心概念就是不采用中断的方式读取数据,而代之以首先采用中断唤醒数据接收的服务程序,然后 POLL 的方法来轮询数据,(类似于底半(bottom-half)处理模式);从我们在实验中所得到的数据来看,在随着网络的接收速度的增加,NIC 触发的中断能做到不断减少,目前 NAPI 技术已经在网卡驱动层和网络层得到了广泛的应用,驱动层次上已经有 E1000 系列网卡,RTL8139 系列网卡,3c50X 系列等主流的网络适配器都采用了这个技术,而在网络层次上,NAPI 技术已经完全被应用到了著名的 netif_rx 函数中间,并且提供了专门的 POLL 方法--process_backlog 来处理轮询的方法;根据实验数据表明采用NAPI技术可以大大改善短长度数据包接收的效率,减少中断触发的时间;由于 RTL8139CP 是一种应用比较广泛的网络适配器,所以本文以其为例,说明了NAPI技术在网络适配器上的应用和基本原理。 但是 NAPI 存在一些比较严重的缺陷:而对于上层的应用程序而言,系统不能在每个数据包接收到的时候都可以及时地去处理它,而且随着传输速度增加,累计的数据包将会耗费大量的内存,经过实验表明在 Linux 平台上这个问题会比在 FreeBSD 上要严重一些;另外采用 NAPI 所造成的另外一个问题是对于大的数据包处理比较困难,原因是大的数据包传送到网络层上的时候耗费的时间比短数据包长很多(即使是采用 DMA 方式),所以正如前面所说的那样,NAPI 技术适用于对高速率的短长度数据包的处理,在本文的末尾提出了 NAPI 的改善方法,和实验数据。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值