网络子系统大杂烩

来源:互联网 发布:手机淘宝怎么登不上 编辑:程序博客网 时间:2024/06/06 07:24

阅读《深入理解linux网络技术内幕》和Linux2.6.37.1源码记录

三个数据结构

1.net_device

2.softnet_data

3.sk_buff

softnet_data是一个cpu对应一个。

网卡接收数据的流程如下

网卡收到数据收首先会出发中断,导致执行中断处理函数dm9000_rx。


以太网驱动接收函数中的skb_reserve(skb, 2)

这是为了保证紧接Ethernet报头的ip报头可以从四节对齐的地址开始

因为Ethernet报头为14字节(目标mac+源mac+类型 = 6+6+2),所以需要额外填充

2字节。


值得一提的是,此中断处理函数在dm9000_open函数中注册,并非在driver_probe

函数中注册。中断处理函数的具体注册过程如下:
(net/core/dev.c)dev_open->__dev_open-> dm9000_open(ops->ndo_open)

最后在dm9000_open中通过request_irq注册网卡的中断处理函数dm9000_interrupt


在中断处理函数dm9000_interrupt中首先通过spin_lock_irqsave获取锁并将中断关闭,

然后判断中断的类型,是接收中断还是发送中断或者是其它,若是接收中断则调用

dm9000_rx函数进行帧接收。


linux2.6.37.1的dm9000驱动中 帧接收方式 采用的是中断期间处理多帧的策略(具体可见<网络技术内幕>)

在接收时,若网卡产生一次中断则会持续进行帧接收。多帧接收的具体实现是在dm9000_rx函数中实现的。

dm9000_rx函数的主体是一个do...while循环,若网卡一直有数据则会持续进行帧接收(中断期间处理多帧),

而此时的中断是关闭的(被spin_lock_irqsave关闭)。每次接受下来的帧则通过netif_rx->enqueue_to_backlog

函数将接收到的skb添加到softnet_data的input_pkt_queue队列中。


dm9000的数据接收方式为非napi方式,采用napi方式和非napi方式的重要区别在帧接收处理。

napi的接受过程如下:

网卡产生中断,调度napi_schedule执行

在napi_schedule中触发软中断

软中断处理函数轮训poll_list

调用napi_struct中的poll方法

在poll方法中执行帧接收等操作

而非napi的操作是方式是如dm9000所示:

网卡产生中断,通过dm9000rx、netif_rx在中断期间接受多帧

将接收到的帧存放到入口队列中,然后触发软中断

在软中断处理函数中轮询poll_list

调用的是非napi的系统默认poll方法process_backlog

在prcess_backlog中处理入口队列的帧


这两种方法的区别在于napi接收过程在中断下半部的poll中完成

非napi在中断处理函数中完成



内核中关于napi的问题

napi和非napi最大的区别在于 napi需要在网卡初始化的时候(网卡初始化分在probe和open两个函数中完成)

通过函数netif_napi_add初始化一个napi_struct数据中的poll方法,而非napi直接使用的是在net_dev_inti函数中

初始化的默认poll函数procee_backlog。最关键的区别在于这两种poll函数的工作方式。


封包类型设置

skb->protocol = eth_type_trans(skb, dev)

eth_type_trans函数其实需要完成两件事情,1.是设置包类型2.设置协议

设置包协议的是通过

skb->pkt_type = PACKET_BROADCAST;

skb->pkt_type = PACKET_MULTICAST;

skb->pkt_type = PACKET_OTHERHOST;

来实现的,具体需要根据mac_addr中ethernet的报头6+6+2)第一个6中的第一个byte的最高两位来普安段是

单播还是多播。如果未显式的设置skb->pkt_type则其实为0,表示PACKET_HOST,即MAC地址匹配。

封包类型设置完毕则进行协议设置。协议的类型可设置为:

1.ethernet帧

2.raw 802.3

3.802.3/LLC

4.802.3/SNAP

各协议之间的区别可参考 网络技术内幕 P289

主要区别是6+6+2中的2字节内数值表示的含义不同

2域中存放的的数值

>1536 表示这是一个ethernet帧

<1500的表示需要使用LLC报头的帧


802.3/LLC:   DSAP SSAP CTL

802.3/SNAP:DSAP SSAP CTL SNAP(其中DSAP SSAP CTL固定)


若不是多播帧则将帧中的mac地址与本机mac做比较,判断是否是传给本机的。若是传给本机的

则解包提取出报文头中的上层使用的协议类型。



出处:http://www.blogjava.net/shiliqiang/articles/304505.html
2.6.24.4内核网络接收数据包分析
瀚海书香
在2.6.24.4中所有的网卡,不管是否支持napi,都是通过struct napi_struct结构进行。所有我们先说一下这个结构。
struct napi_struct{
   struct list_head poll_list;
   unsigned long state;
   int weight;
   int (*poll)(struct napi_struct *,int);
}
对应支持napi的网卡,自己填充这个结构体;而非napi网卡,则使用per cpu的softnet_data>backlog,这个结构的初始化在net_dev_init()中完成。
我们先说一下非napi机制的网卡:
   网卡接收到数据包后dma到内核空间,然后调用netif_rx()将数据包挂接到softnet_data>input_pkt_queue中,如果backlog这个napi_struct没有被调度,则napi_schedule(&backlog).napi_schedule()会将backlog的poll_list挂接到softnet_data->poll_list上,同时出发软中断NET_RX_SOFTIRQ。NET_RX_SOFTIRQ软中断,调用相应的函数net_rx_action()。
对应napi机制的网卡:
    网卡初始化是会自己初始化一个自己的数据包接收队列,当有数据包到达时,将数据包dma到自己的数据包队列中,如果自己的napi没有调度,则napi_schedule(mynapi),这里的mynapi是网卡自己的napi_struct.napi_schedule()会将网卡自己的poll_list挂接到softnet_data->poll_list上,同时出发软中断NET_RX_SOFTIRQ。NET_RX_SOFTIRQ软中断,调用相应的函数net_rx_action()。
 
net_rx_action():
   首先获取softnet_data->poll_list,通过遍历poll_list,获取每个poll_list对应的napi_struct结构(container_of实现),然后根据napi_struct的weight调用poll函数,如果是非napi网卡,这里的napi_struct是backlog,所以poll函数就是process_backlog;如果是napi的网卡,则会使自己的poll函数。
napi网卡的poll函数就是从自己数据包队列中dequeue出一个skb,然后调用netif_receive_skb().
非napi的process_backlog会获取softnet_data->input_pkt_queue,然后对队列input_pkt_queue进行dequeue操作,获得一个skb,之后调用netif_receive_skb(skb)。
netif_receive_skb():
   对skb做一些准备工作,例如设置mac_len等,调用deliver_skb()给所有的注册ptype_all类型的协议处理handle,然后是网桥和VLAN的处理,之后会给注册的相应协议的ptype_base的handle。这里假设是ip协议,则会调用相应的ip协议handle的处理函数ip_rcv。
ip_rcv():
   对skb做一些检查工作,如果skb->users!=1,则clone一个skb,之后会转入netfilter的NF_IP_PRE_ROUTING的hook点,调用所有在该点注册的hook函数。比如说如果开启了conntrack,则会在这里进行数据包重组。之后调用ip_rcv_finish().
ip_rcv_finish():
    首先调用ip_route_input()决定数据包的路由,初始化skb->dst,调用dst_input(skb).
dst_input():
   实际上是调用skb->dst->input(skb),对应input的初始化在route.c中。如果是发往本地的数据包dst->input=ip_local_deliver;如果是转发的数据包dst->input=ip_forward;
本地流程:
ip_local_deliver():
    首先是对分片的数据包重组,会转入netfilter的NF_IP_LOCAL_IN的hook点,调用所有在该点注册的hook函数。之后会调用ip_local_deliver_finish(),之后就到第四层了。
转发流程:
ip_forward():
    做一些源路由等方面的检查后,会转入netfilter的NF_IP_FORWARD的hook点,调用所有在该点注册的hook函数。之后会调用ip_forward_finish().
ip_forward_finish():
    调用dst_output().
dst_output():
    skb->dst->output(skb).一般output=ip_output.
ip_output():
    设置skb的dev为发包的dev,同时设置skb->protocol,会转入netfilter的NF_IP_POST_ROUTING的hook点,调用所有在该点注册的hook函数。之后会调用ip_finish_output().
ip_finish_output():
    检查一下数据包是否需要分片,如果需要分片,则进行ip_fragement(),之后调用ip_finish_output2().
ip_finish_output2():
    根据neighbour,调用dst->neighbour->output.
到这为止,数据包会经过dev_queue_xmit放入dev的qdisc中。之后就是流控出队列。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 早上上班忘记穿内衣了怎么办 高中知识都忘了怎么办 留鼻涕跟水一样怎么办 鼻涕像水一样流怎么办 肩膀很疼怎么办睡不着觉 夏天穿内衣出汗后很臭怎么办 脸过敏后严重缺水怎么办 过敏后脸上反复出现湿疹怎么办 孕妇脸上长湿疹过敏红肿怎么办 一岁宝宝一直流鼻涕怎么办 三岁宝宝一直流鼻涕怎么办 3岁宝宝一直流鼻涕不好怎么办 7岁半边鼻子不通气怎么办 感冒流鼻涕鼻子不通气怎么办 鼻子不通气干的怎么办 7个月婴儿流鼻涕怎么办 鼻子火辣辣的光想流鼻涕怎么办? 三岁宝宝鼻塞流鼻涕怎么办 一岁宝宝感冒流鼻涕鼻塞怎么办 1岁宝宝鼻塞流鼻涕怎么办 一岁多宝宝感冒鼻塞流鼻涕怎么办 宝宝鼻塞流鼻涕怎么办速效办法 八个月宝宝感冒流鼻涕鼻塞怎么办 7岁儿童流清鼻涕怎么办 1岁的宝宝流鼻涕怎么办 3岁宝宝鼻塞严重怎么办 宝宝流鼻涕2个月怎么办 一个月宝宝流鼻子怎么办 六个月宝宝流清水鼻涕怎么办 婴儿流鼻涕怎么办最简单方法 婴儿咳嗽流鼻涕怎么办最简单方法 6个月小孩流鼻涕怎么办 宝宝风寒感冒咳嗽流鼻涕怎么办 三个月宝宝流清鼻涕怎么办 三个月宝宝留清鼻涕怎么办 三个月婴儿流清鼻涕怎么办 宝宝7个月流鼻涕怎么办 6个月孩子流鼻涕怎么办 十一个月孩子感冒流鼻涕怎么办 7个月孩子流鼻涕怎么办 18个月的宝宝流鼻涕怎么办