Linux网络协议栈(四)——链路层(1)
1、接收帧
当网络适配器接收到数据帧时,就会触发一个中断,中断处理程序执行一些需要及时处理的任务,然后在下半部进行其它可以延迟的处理。中断处理程序主要进行以下一些操作:
(1) 分配sk_buff数据结构,并将接收到的数据帧从网络适配器I/O端口拷贝到sk_buff缓冲区中;
(2) 从数据帧中提取出一些信息,并设置sk_buff相应的参数,这些参数将被上层的网络协议使用,例如skb->protocol;
(3) 通过软中断NET_RX_SOFTIRQ通知内核接收到新的数据帧。
内核2.5中引入一组新的API来处理接收的数据帧,即NAPI。所以,当网络适配器接收到数据帧时,驱动有两种方式通知内核:(1)通过以前的函数netif_rx;(2)通过NAPI机制,但是只有很少的驱动使用它。
1.1、softnet_data数据结构
//include/linux/netdevice.h
struct softnet_data
{
int throttle;
int cng_level;
int avg_blog;
struct sk_buff_head input_pkt_queue;
struct list_head poll_list;
struct net_device *output_queue;
struct sk_buff *completion_queue; struct net_device backlog_dev; /* Sorry. 8) */
};
这个数据结构同时用于接收与发送数据包,它为per_CPU结构,这样每个CPU有自己独立的信息,这样在SMP之间就避免了加锁操作,从而大大提高了数据处理的并发性。
throttle、avg_blog和cng_level
这三个参数主要用于阻塞管理(congestion management)。throttle非0时,表示CPU负荷过载,它的值取决于input_pkt_queue,当throttle设置时,CPU接收到的所有数据帧都被丢弃。avg_blog表示输入队列input_pkt_queue的平均长度,它的范围从0到netdev_max_backlog(最大值),它用来计算cng_level。cng_level表示阻塞的程度. avg_blog 和 cng_level与CPU处理的input_pkt_queue相关联,仅用于non_NAPI设备。
struct net_device *output_queue;
struct sk_buff *completion_queue;
这两个域用于发送数据。
struct sk_buff_head input_pkt_queue;
struct list_head poll_list;
struct net_device backlog_dev;
这三个域用于接收数据,其中input_pkt_queue与backlog_dev仅用于non-NAPI的 NIC,input_pkt_queue是接收到的数据队列头,它用于netif_rx()中,并最终由虚拟的poll函数 process_backlog()处理这个SKB队列。
poll_list则是有数据包等待处理的NIC设备队列。对于non-NAPI驱动来说,它始终是backlog_dev。
Softnet_data的初始化:
每个CPU的softnet_data是在net_dev_init中初始化的,代码如下:
for (i = 0; i < NR_CPUS; i++) {
struct softnet_data *queue;
queue = &per_cpu(softnet_data,i);
skb_queue_head_init(&queue->input_pkt_queue);
queue->throttle = 0;
queue->cng_level = 0;
queue->avg_blog = 10; /* arbitrary non-zero */
queue->completion_queue = NULL;
INIT_LIST_HEAD(&queue->poll_list);
set_bit(_ _LINK_STATE_START, &queue->backlog_dev.state);
queue->backlog_dev.weight = weight_p;
queue->backlog_dev.poll = process_backlog;
atomic_set(&queue->backlog_dev.refcnt, 1);
}
1.2、NON-NAPI方式:
来看看vortex_rx是怎么调用netif_rx的,大部分的网络设备驱动使用方式与其相似:
static int vortex_rx(struct net_device *dev)
{
//…
struct sk_buff *skb;
skb = dev_alloc_skb(pkt_len + 5);//分配缓冲区
if (skb != NULL) {
skb->dev = dev;//设置接收包的网络设备
//将data和tail指针下移2个字节,使得IP头在缓冲区存储时可以16字节的边界上对齐
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
//将数据帧从I/O端口拷贝到sk_buff缓冲区
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
dev->last_rx = jiffies;//接收到数据的时间
vp->stats.rx_packets++;
//…
}
}
netif_rx( )函数
netif_rx通常在驱动的中断处理程序(严格意义来说,应该是中断服务例程,ISR)中被调用,但是也有例外,那就是回环设备。
Code
这段代码关键是,将这个SKB加入到相应的input_pkt_queue队列中,并调用netif_rx_schedule(),而对于NAPI方式,它没有使用input_pkt_queue队列,而是使用私有的队列,所以它没有这一个步骤。
1.3、NAPI方式
net_device中与NAPI相关的字段:
poll
从设备的输入队列取缓冲区的虚拟函数。对于NAPI,输入队列是私有的;对于NON_API设备,输入队列为softnet_data->input_pkt_queue。
poll_list
输入队列有新的数据帧需要处理的设备的链表,这些设备此时处于polling状态,表头为softnet_data->poll_list。位于链表中的设备关闭中断,正被内核查询。
quota
weight
quota表示poll每次可以从输入队列中取的最大缓冲区数。weight 成员描述接口的相对重要性:当资源紧张时,有多少流量可以从接口收到。如何设置 weight 参数没有严格的规则;依照惯例, 10 MBps 以太网接口设置 weight 为 16, 而快一些的接口使用 64. 你不能设置 weight 为一个超过你的接口能够存储的报文数目的值。quota值以weight为基础进行更新。
使用NAPI:
Code
可以看到,两种方式的不同之处在于,NAPI方式直接调用 netif_rx_schedule,而非NAPI方式则要通过辅助函数netif_rx()设置好接收队列再调用 netif_rx_schedule(),再者,在非NAPI方式中,提交的是 netif_rx_schedule(&queue->backlog_dev),而NAPI中,提交的是 netif_rx_schedule (netdev),即是设备驱动的net_device结构,而不是queue中的backlog_dev。
netif_rx_schedule函数
Code
整个过程如下:
1.4、net_rx_action( )函数
net_rx_action为处理接收数据帧的下半部函数,输入的数据帧在两个地方等待net_rx_action来处理:
(1) CPU的输入队列。这是针对NON-NAPI方式的,它调用netif_rx,将数据帧加入到CPU的输入队列softnet_data->input_pkt_queue。
(2) 设备缓存。对于NAPI方式,poll函数从设备缓存读取数据帧。
Code
1.5、process_backlog函数
对于non-API方式,poll函数为process_backlog:
Code
该函数主要从CPU输入队列中取出套接字缓冲区,然后通过调用netif_receive_skb,将sb_buff传递给上层协议处理。budget 参数提供了一个我们允许传给内核的最大报文数目。在设备结构里, quota 成员给出了另一个最大值; poll 方法必须遵守这两个限制中的较小者。它也应当以实际收到的报文数目递减 dev->quota 和 *budget. budget 值是当前 CPU 能够从所有接口收到的最多报文数目, 而 quota 是一个每接口值, 常常在初始化时安排给接口以 weight 为起始。
1.6 NAPI的poll函数
这种方式下,NIC驱动程序会提供自己的poll函数和私有接收队列。
如intel 8255x系列网卡程序e100,它有在初始化的时候首先分配一个接收队列,而不像以上那种方式在接收到数据帧的时候再为其分配数据空间。这样,NAPI的poll函数在处理接收的时候,它遍历的是自己的私有队列:
Code
主要工作在e100_rx_indicate()中完成,这主要重设SKB的一些参数,然后跟process_backlog(),一样,最终调用netif_receive_skb(skb)。
1.7 netif_receive_skb函数
netif_receive_skb是链路层接收数据报的最后一站。它根据注册在全局数组ptype_all和ptype_base里的网络层数据报类型,把数据报递交给不同的网络层协议的接收函数(INET域中主要是ip_rcv和arp_rcv)。
Code
该函数主要就是调用第三层协议的接收函数处理该skb包,进入第三层网络层处理。
Linux网络协议栈(四)——链路层(1)的更多相关文章
- linux网络协议栈(四)链路层 vlan处理
转:http://blog.csdn.net/u010246947/article/details/18224517 4.6.VLAN处理: 4.6.1.vlan原理 对于带vlan的以太网报文,其以 ...
- Linux网络协议栈(四)——链路层(2)
2.协议相关2.1.第3层协议的管理在Linux内核中,有两种不同目的的3层协议:(1) ptype_all管理的协议主要用于分析目的,它接收所有到达第3层协议的数据包.(2) ptype ...
- 理解 Linux 网络栈(1):Linux 网络协议栈简单总结
本系列文章总结 Linux 网络栈,包括: (1)Linux 网络协议栈总结 (2)非虚拟化Linux环境中的网络分段卸载技术 GSO/TSO/UFO/LRO/GRO (3)QEMU/KVM + Vx ...
- linux网络协议栈--路由流程分析
转:http://blog.csdn.net/hsly_support/article/details/8797976 来吧,路由 路由是网络的核心,是linux网络协议栈的核心,我们找个入口进去看看 ...
- Linux 网络协议栈开发基础篇—— 网桥br0
一.桥接的概念 简单来说,桥接就是把一台机器上的若干个网络接口"连接"起来.其结果是,其中一个网口收到的报文会被复制给其他网口并发送出去.以使得网口之间的报文能够互相转发. 交换机 ...
- (转)Linux网络协议栈(三)——网络设备(1)
网络设备(network device)是内核对网络适配器(硬件)的抽象与封装,并为各个协议实例提供统一的接口,它是硬件与内核的接口,它有两个特征:(1) 作为基于硬件的网络适配器与基于软件的协 ...
- Linux网络协议栈(三)——网络设备(1)
网络设备(network device)是内核对网络适配器(硬件)的抽象与封装,并为各个协议实例提供统一的接口,它是硬件与内核的接口,它有两个特征:(1) 作为基于硬件的网络适配器与基于软件的协 ...
- 由PPPOE看Linux网络协议栈的实现
http://www.cnblogs.com/zmkeil/archive/2013/05/01/3053545.html 这个标题起得比较纠结,之前熟知的PPPOE是作为PPP协议的底层载体,而实际 ...
- Linux网络栈下两层实现
http://www.cnblogs.com/zmkeil/archive/2013/04/18/3029339.html 1.1简介 VLAN是网络栈的一个附加功能,且位于下两层.首先来学习Linu ...
随机推荐
- iOS第三方地图-百度地图常用功能使用(POI搜索,地理正反编码,定位,添加标注)
百度地图官网: http://developer.baidu.com/map/index.php?title=iossdk 百度地图集成 1.引入相关包
- 洛谷P2058 海港
题目描述 小K是一个海港的海关工作人员,每天都有许多船只到达海港,船上通常有很多来自不同国家的乘客. 小K对这些到达海港的船只非常感兴趣,他按照时间记录下了到达海港的每一艘船只情况:对于第i艘到达的船 ...
- 1027 stl
#include<stdio.h> #include<queue> using namespace std; int main() { int i,n,m,j,k,a[100 ...
- 数字游戏(codevs 1085)
题目描述 Description 丁丁最近沉迷于一个数字游戏之中.这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易.游戏是这样的,在你面前有一圈整数(一共 ...
- Codeforces914E. Palindromes in a Tree
n<=100000的树,每个点上有个字母a-t之一,问有多少这样的链经过每个点:它的某一个排列的字母串起来是回文的. 就是有最多一个字母是奇数个啦..这样点分算一波即可..细节较多详见代码 #i ...
- “亚信科技杯”南邮第七届大学生程序设计竞赛之网络预赛 A noj 2073 FFF [ 二分图最大权匹配 || 最大费用最大流 ]
传送门 FFF 时间限制(普通/Java) : 1000 MS/ 3000 MS 运行内存限制 : 65536 KByte总提交 : 145 测试通过 : 13 ...
- 编译.net .net Core程序 代码,仅做备份
//创建一个ProcessStartInfo对象 使用系统shell 指定命令和参数 设置标准输出 //编译.net core项目 var psi = new ProcessStartInfo(&qu ...
- hdu 4819 Mosaic
无论是线段树还是树状数组维护最大值最小值的时候一定要注意,如果有修改操作的话,这个最小值和最大值的更新一定不能由原来的和修改的值得到,一定要重新查询一次,否则可能出现当前最小值是原来的未修改值,但事实 ...
- [Poj2096]Collecting Bugs(入门期望dp)
Collecting Bugs Time Limit: 10000MS Memory Limit: 64000K Total Submissions: 6237 Accepted: 3065 ...
- 寒武纪camp网络测试赛
寒武纪camp网络测试赛 地址:oj点我进入 A(树形dp+树链剖分) 题意: 分析: 考虑树形dp,f0(x)和f1(x)分别表示以x为根的子树,不取x点或取x点的最大合法子集的元素个数 那么对于一 ...