网络层次

linux网络设备驱动与字符设备和块设备有很大的不同。 
1. 字符设备和块设备对应/dev下的一个设备文件。而网络设备不存在这样的设备文件。网络设备使用套接字socket访问,虽然也使用read,write系统调用,但这些调用只作用于软件对象。 
2. 块设备只响应来自内核的请求,而网络驱动程序异步接收来自外部世界的数据包,并向内核请求发送到内核。

linux内核中网络子系统的设计基于设备无关及协议无关思想。即无论什么网卡驱动、网络协议,都对应统一的驱动程序。

inux网络协议栈层次有四层: 
网络协议接口层 
网络设备接口层 
设备驱动功能层 
网络设备媒介层

下面只分析下linux是如何实现网络设备的设备无关性及协议无关性。

网络协议接口层: 给上层协议提供透明的数据包发送和接收接口。与协议无关 
当要发送数据包时,ARP协议或者IP协议,都将调用这一层中的dev_queue_xmit()函数发送该数据包。 
上层对数据包的接收时,通过netif_rx()接收。具体协议处理在上级网络协议处理。 
这其中需要一个很重要的结构体struct sk_buff.

网络设备接口层:为千变万化的网络设备定义统一、抽象的数据结构net_device结构体,实现多种硬件在软件层次上的统一。与设备无关。 
net_device结构体在内核中指代一个网络设备,网络设备驱动程序只需通过填充net_device中的具体成员并注册net_device即可实现硬件操作函数与内核的挂接。使用统一的注册、注销及初始化等一系列函数。

设备驱动功能层:填充net_device中的成员。管理物理网络设备。 
包括设备打开、停止、传输等操作。由于网络包得接收可以由中断产生,所以设备驱动功能层中另一个主题部分是中断处理函数,负责读取硬件上接收的数据包并传送给上层协议。

网络设备与媒介层:对应于实际的物理设备 
包括设备寄存器的描述,寄存器读写函数等。

网络协议

这里主要进行数据包的收发,使用函数原型为: 
dev_queue_xmit(struct sk_buff *skb);int netif_rx(struct sk_buff *skb); 
这里使用了一个skb_buff结构体,定义于include/linux/skbuff.h中,它的含义为“套接字缓冲区”,用于在Linux网络子系统各层间传输数据

sk_buff中重要的数据成员

struct device *dev;正在处理该包的设备
__u32 sadd;r//IP元地址
__u32 daddr;//IP目的地址
__u32 raddr;//IP路由器地址
unsigned char *head;//分配空间的开始
unsigned char *data;//有效数据的开始
unsigned char *tail;//有效数据的结束
unsigned char *end;//分配空间的结束
unsigned long len;//有效数据的长度
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

sk_buff操作 
1) 分配:分配一个sk_buff结构,供协议栈代码使用

struct sk_buff *alloc_skb(unsigned int len, int priority);
struct sk_buff *dev_alloc_skb(unsigned int len);
  • 1
  • 2

分配一个缓冲区。alloc_skb函数分配一个缓冲区并初始化skb->data和skb->tail为skb->head。参数len为数据缓冲区的空间大小,通常以L1_CACHE_BYTES字节(对ARM为32)对齐,参数priority为内存分配的优先级。dev_alloc_skb()函数以GFP_ATOMIC优先级进行skb的分配。 
2) 释放:

void kfree_skb(struct sk_buff *skb);
void dev_kfree_skb(struct sk_buff *skb);
  • 1
  • 2

Linux内核内部使用kfree_skb()函数,而网络设备驱动程序中则最好使用dev_kfree_skb()。 
sk_buff中比较重要的成员是指向数据包中数据的指针 
用于寻址数据包中数据的指针,head指向已分配空间开头,data指向有效的octet开头,tail指向有效的octet结尾,而end指向tail可以到达的最大地址。如果不这样做而分配一个大小固定的缓冲区,如果buffer不够用,则要申请一个更大的buffer,拷贝进去再增加,这样降低了性能。 
3)变更 
unsigned char *skb_put(struct sk_buff *skb, int len); 
将taill指针向后移动len长度,并返回tail移动之前的值。用于向skb有效数据区域末尾添加数据。 
unsigned char *skb_push(struct sk_buff *skb, int len); 
将data指针向前移动len长度。并返回移动之后的值。用于向skb有效数据区域前端添加数据(包头)。 
unsigned char *skb_pull(struct sk_buff *skb, int len); 
void skb_reserve(struct sk_buff ×skb, int len);

网络设备

系统检测到了PCI设备,并启用它,但它只是一支硬件设备(网卡设备),而Linux的网络协议栈只认得[网络设备]。[网络设备]是一支逻辑设备,由结构net_device表征。也就是说,网络协议栈向[网络设备]发出命令,而[网络设备]的驱动将这些命令传递到PCI[网卡设备]。表3列出了结构net_device的一些重要数据域,

struct net_device
{
char *name;
unsigned long base_addr;
unsigned char addr_len;
unsigned char dev_addr[MAX_ADDR_LEN];
unsigned char broadcast[MAX_ADDR_LEN];
unsigned short hard_header_len;
unsigned char irq;
int (*open) (struct net_device *dev);
int (*stop) (struct net_device *dev);
int (*hard_start_xmit) (struct sk_buff *skb, struct net_device *dev);
struct net_device_stats* (*get_stats)(struct net_device *dev);
void *priv;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

以上是结构net_device部分重要成员,以下简介这些成员的用途:

  • name – 设备的名称。如果名称的第一个字符是null,那么register_netdev分配给它取名为“ethN”,其中N是合适的数字。例如,如果您的系统已经有eth0和eth1,您的设备将被命名的eth2。
  • base_addr – I/O基地址。
  • addr_len – 硬件地址(MAC地址)的长度。以太网接口地址长度为6字节。
  • dev_addr – 硬件地址(以太网地址或MAC地址)
  • broadcast – 设备的广播地址。以太网接口的广播地址是FF:FF:FF:FF:FF:FF
  • hard_header_len – “硬件头的长度”是数据包硬件头的八位位组(octets)的数量。 以太网接口的hard_header_len的值是14
  • IRQ – 分配的中断号
  • open – 这是打开设备函数的指针。这个函数在用ifconfig命令激活设备时被调用,例如“ifconfig eth0 up”。 open函数负责向系统申请所需的系统资源需求(I/O端口,IRQ,DMA等),启用硬件和递增模块的使用计数。
  • stop – 这是停止设备函数的指针。这个函数在用ifconfig命令停用设备时被调用,例如“ifconfig eth0 down”。 stop函数释放所有open函数获得的资源。
  • hard_start_xmit – 此函数在传输线路上发送一个给定的数据包。该函数的第一个参数是指向结构sk_buff指针。结构sk_buff的是通过Linux网络协议栈的数据包。本文并不需要详细了解有关的sk_buff的结构的细节,你可以在下网址获得更多的结构sk_buff的信息:http://www.tldp.org/LDP/khg/HyperNews/get/net/net-intro.html
  • get_stats – 此函数提供了接口统计信息。命令“ifconfig eth0”的很多输出内容来自get_stats。
  • priv – 驱动程序的私有数据域。驱动程序拥有这一数据域,并可以使用它。

特别注意,net_device没有接收数据包的成员函数,这是因为接收数据包是由设备的[中断处理程序]负责的

驱动的实现

1).初始化(init) 
设备探测工作在init方法中进行,一般调用一个称之为probe方法的函数 
初始化的主要工作时检测设备,配置和初始化硬件,最后向系统申请这些资源。此外填充该设备的dev结构,我们调用内核提供的ether_setup方法来设置一些以太网默认的设置。

2)打开(open) 
open这个方法在网络设备驱动程序里是网络设备被激活时被调用(即设备状态由down变成up) 
实际上很多在初始化的工作可以放到这里来做。比如说资源的申请,硬件的激活。如果dev->open返回非0,则硬件状态还是down, 
注册中断、DMA等;设置寄存器,启动设备;启动发送队列 
一般注册中断都在init中做,但在网卡驱动程序中,注册中断大部分都是放在open中注册,因为要经常关闭和重启网卡

3)关闭(stop) 
stop方法做和open相反的工作 
可以释放某些资源以减少系统负担 
stop是在设备状态由up转为down时被调用

4)发送(hard_start_xmit) 
在系统调用的驱动程序的hard_start_xmit时,发送的数据放在一个sk_buff结构中。一般的驱动程序传给硬件发出去。也有一些特殊的设备比如说loopback把数据组成一个接收数据在传送给系统或者dummy设备直接丢弃数据。 
如果发送成功,hard_start_xmit方法释放sk_buff。如果设备暂时无法处理,比如硬件忙,则返回1。

5) 接收 
驱动程序并存在一个接受方法。当有数据收到时驱动程序调用netif_rx函数将skb交交给设备无关层。 
一般设备收到数据后都会产生一个中断,在中断处理程序中驱动程序申请一块sk_buff(skb)从硬件中读取数据位置到申请号的缓冲区里。 
接下来填充sk_buff中的一些信息。 
中断有可能是收到数据产生也可能是发送完成产生,中断处理程序要对中断类型进行判断,如果是收到数据中断则开始接收数据,如果是发送完成中断,则处理发送完成后的一些操作,比如说重启发送队列。 
接收流程: 
1、分配skb=dev_alloc_skb(pkt->datalen+2) 
2、从硬件中读取数据到skb 
3、调用netif_rx将数据交给协议栈

ELISA试剂盒:http://www.shxrsw.net           http://www.hdbsw.com

linux网络设备理解的更多相关文章

  1. linux网络设备驱动程序

    4.linux网络设备驱动程序体系结构 -------------------------------------- | 数据包发送 | 数据包接收 | ----->网络协议接口层 | dev_ ...

  2. Linux网络设备驱动架構學習(三)

    Linux网络设备驱动架構學習(三) 接下來會從以下幾個方面介紹網絡設備驅動的編寫流程: 1.網絡設備的註冊與註銷 2.網絡設備的初始化 3.網絡設備的打開與釋放 4.網絡數據發送流程 5.網絡數據接 ...

  3. Linux网络设备驱动架构

    Linux网络设备驱动程序体系结构分为四层:网络协议接口层.网络设备接口层.提供实际功能的设备驱动层以及网络设备与媒介层. (1)网络协议接口层向网络层协议提供统一的数据包收发接口,不论上层协议是AR ...

  4. linux网络设备驱动

    Linux网络设备驱动 Linux网络驱动程序的体系结构可划分为4个层次.Linux内核源代码中提供了网络设备接口及以网络子系统的上层的代码,移植特定网络硬件的驱动程序的主要工作就是完成设备驱动功能层 ...

  5. Linux网络设备驱动架構學習(二)

    Linux网络设备驱动架構學習(二) 接下來會從以下幾個方面介紹網絡設備驅動的編寫流程: 1.網絡設備的註冊與註銷 2.網絡設備的初始化 3.網絡設備的打開與釋放 4.網絡數據發送流程 5.網絡數據接 ...

  6. Linux 网络设备驱动开发(一) —— linux内核网络分层结构

    Preface Linux内核对网络驱动程序使用统一的接口,并且对于网络设备采用面向对象的思想设计. Linux内核采用分层结构处理网络数据包.分层结构与网络协议的结构匹配,既能简化数据包处理流程,又 ...

  7. linux 网络设备驱动

    linux 网络驱动 谨以此文纪念过往的岁月 一.前言在linux中网络驱动也是一个大头,如何去理解网络驱动是作为一个linux驱动工程师必备的技能.不过同样的设备,在不同人的手中会有不同的效果,其原 ...

  8. [Linux]系统调用理解(3)

    本文介绍了Linux下的进程的一些概念,并着重讲解了与Linux进程管理相关的重要系统调用wait,waitpid和exec函数族,辅助一些例程说明了它们的特点和使用方法. 1.7 背景 在前面的文章 ...

  9. [Linux]系统调用理解(2)

    本文介绍了Linux下的进程概念,并着重讲解了与Linux进程管理相关的4个重要系统调用getpid,fork,exit和_exit,辅助一些例程说明了它们的特点和使用方法. 关于进程的一些必要知识 ...

随机推荐

  1. 1-17-Linux中计划任务与日志的管理

    本节所讲内容: 1-1 Linux中的计划任务 1-1-1 at计划任务的使用 1-1-2 cron 计划任务的使用 1-1 Linux服务器的日志管理 1-1-1 日志的种类和记录的方式 1-1-2 ...

  2. PANDAS 数据合并与重塑(concat篇)

    转自:http://blog.csdn.net/stevenkwong/article/details/52528616

  3. nyoj998——欧拉+折半查找

    Sum 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 给你一个数N,使得在1~N之间能够找到x使得x满足gcd( x ,  N  ) >= M, 求解gcd( ...

  4. SqlServer 转 Oracle 的几点注意

    (转自:http://www.2cto.com/database/201208/146740.html) 1.字符型的字段相加需要用“||”,如果用“+”的话,会报“无效的数字”的错误.   2.类似 ...

  5. IEnumerable.Select和SelectMany的区别

    例子(一个人可以有多个手机) public class People { public string Name { get; set; } public List<Phone> Phone ...

  6. iOS 可能用到的三方框架

    1.MWPhotoBrowser 第三方图片浏览器 https://github.com/mwaterfall/MWPhotoBrowser 2.SlackTextViewController  强大 ...

  7. Java -- 使用阻塞队列(BlockingQueue)控制线程通信

    BlockingQueeu接口是Queue的子接口,但是它的主要作用并不是作为容器,而是作为线程同步的工具. 特征: 当生产者线程试图向BlockingQueue中放入元素时,如果该队列已满,则该线程 ...

  8. weblogic应用加载不上

    这个的问题是编译的问题,在web-inf文件中的classes中少了config文件夹的配置信息 可在项目的build path 中的source中配置

  9. SpringMVC札集(06)——转发和重定向

    自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onL ...

  10. ROS机器人操作系统在线练习

    废话不说,先看图吧: 1. ROS in 5 Days Entering ROS 2. ROS Navigation in 5 Days Mastering ROS 3. ROS Autonomous ...