网络层次

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. Kotlin------类和对象(一)

    类声明 和Java一样,Kotlin中使用关键字class来声明一个类.如下即是声明一个最简单的没有任何属性和方法的类 // 没有任何属性.方法的Invoice 类 class Invoice {} ...

  2. MYSQL题讲答案

    2丶查询‘生物’课程比‘物理’课程成绩高的所有学生的学号 思路: 获取所有有生物课成的人(学号,成绩) -- 临时表 获取所有有物理课程的人(学号,成绩)  -- 临时表 根据[学号]连接两个临时表: ...

  3. 从零开始搭建webpack+react开发环境

    环境主要依赖版本 webpack@4.8.1 webpack-cli@2.1.3 webpack-dev-server@3.1.4 react@16.3.2 babel-core@6.26.3 bab ...

  4. ansible入门一(Ansible介绍及安装部署)

    本节内容: 运维工具 Ansible特性 Ansible架构图和核心组件 安装Ansible 演示使用示例 一.运维工具 作为一个Linux运维人员,需要了解大量的运维工具,并熟知这些工具的差异,能够 ...

  5. python 黑客书籍 ——扫描+暴力破解

    https://legacy.gitbook.com/book/germey/net-security/details 网络安全 介绍 构建一个端口扫描器 利用Pexpect模拟SSH连接 利用Pxs ...

  6. Algorithm2: 重复查过半数的元素

    数组中,有一个元素的值在数组中重复的个数是超过一半,获得元素超过一半的元素值 int MoreThanHalfNumber(int * arr, int n){                  in ...

  7. fegin---@FeginClient参数介绍

    一.FeignClient注解 @FeignClient标签的常用属性如下: name:指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现 ur ...

  8. Django-自定义分页组件

    1.封装的分页代码: class PageInfo(object): def __init__(self,current_page,all_count,per_page,base_url,show_p ...

  9. LeetCode OJ:Bulls and Cows (公牛与母牛)

    You are playing the following Bulls and Cows game with your friend: You write down a number and ask ...

  10. qml自定义带文字的button tabbutton

    https://blog.csdn.net/u014416260/article/details/54579480