数据链路层3

 

5 处理输入数据帧

netif_receive_skb函数是napi_struct实例的poll函数使用的,帮助函数处理输入数据帧。

netif_receive_skb主要流程:

Ø  给每个协议标签发送一个数据帧的拷贝

Ø  给skb->protocol中指定的网络层协议处理程序发送一个数据帧拷贝

Ø  执行需要在该层处理的功能特点。

如果上层没有skb->protocol指定的协议处理程序,也没有别的功能特点要在netif_receive_skb中执行,内核就会不知道怎么处理数据帧,因此会丢弃掉数据帧。

关键数据机构

struct softnet_data

struct softnet_data

{

         struct net_device   *output_queue;

         struct sk_buff_head       input_pkt_queue;

         struct list_head       poll_list;

         struct sk_buff          *completion_queue;

 

         struct napi_struct  backlog;

};

此处的queue->input_pkt_queue,当有数据到来时,分配一个skb来接收数据并将接收到的数据挂接在queue->input_pkt_queue队列中.在中断轮询的时候,软中断总函数do_softirq()直接到达网卡的接收软中断函数net_rx_action(),在此函数中调用queue->backlog_dev.poll=process_backlog;即process_backlog()函数,它将queue->input_pkt_queue队列中的数据向上层协议传输,比如网络层的ip协议等。

这边讲述个算法优化的问题:

关于ptype_all和pypte_base中的pt_prev的说明

一种结束:为了优化最common的一个case,就是只有一个handler match的话,省去了skb_clone的开销。

因为当有多个匹配的话,我们就要在deliver_skb函数总先增加引用计数,这要就要skb_clone了,增加了开销,但是如果只有一次match,我们直接就调用pt_prev->func功能,减少了一次clone。而且也减少了一次kfree_skb,就是少了一次原子读操作,所以这都是从效率方面进行了优化。

6 数据链路层和网络层的接口

在上面的分析中,我们看到数据在继续往上层协议传送过程中,会调用pt_prev->func这个函数指针,这里主要用到的数据结构是struct packet_type。

该结构描述了网络层协议的标识符,接收处理函数与接收网络设备等地相互关系,每个在网络层的协议实例都有一个struct packrt_type类型的变量,来定义协议处理接收数据帧的实体。

6.1 数据结构定义

struct packet_type {

         __be16                       type;        /* This is really htons(ether_type). */

         struct net_device   *dev;        /* NULL is wildcarded here         */

         int                       (*func) (struct sk_buff *,

                                                struct net_device *,

                                                struct packet_type *,

                                                struct net_device *);

         struct sk_buff          *(*gso_segment)(struct sk_buff *skb,

                                                        int features);

         int                       (*gso_send_check)(struct sk_buff *skb);

         struct sk_buff          **(*gro_receive)(struct sk_buff **head,

                                                      struct sk_buff *skb);

         int                       (*gro_complete)(struct sk_buff *skb);

         void                   *af_packet_priv;

         struct list_head       list;

};

数据结构中各个数据域的具体含义:

²  Type:协议标识符。协议标识符必须由网络设备驱动程序接收实例从数据帧中解析出来。

²  Dev:指向接收网络数据帧的网络设备。指针不为空,表示只接收从指定网络设备上传过来的数据。如果空,则选择协议处理时,网络设备不起作用。

²  Func:指向协议实例实现的接收处理程序的函数指针。

 

6.2 struct packrt_type数据结构实例化

要实现L2和L3之间的接口实现,每个L3协议都得实例化一个struct packet_type 结构。

例如:IP协议在af_inet.c文件中进行了初始化。

/*

 *     IP protocol layer initialiser

 */

 

static struct packet_type ip_packet_type __read_mostly = {

         .type = cpu_to_be16(ETH_P_IP),

         .func = ip_rcv,

         .gso_send_check = inet_gso_send_check,

         .gso_segment = inet_gso_segment,

         .gro_receive = inet_gro_receive,

         .gro_complete = inet_gro_complete,

};

6.3 接口的组织

Linux内核中定义了两个哈希表,哈希表中每个元素都是一个struct list_head变量,指向网络层协议的struct packet_type数据结构实例。

有两种网络层协议类型需要区分

²  接口向量表

以上两种接口分别为ptype_all和ptype_base,两者都定义在Dev.c (net\core)中。

ptype_all中注册的接口主要用于网络工具和网络探测器接收数据,其目的是接收处理程序接收所有数据帧。

ptype_base[PTYPE_HASH_SIZE],其实例只接收与协议标识符相匹配的网络数据帧。

netif_receive_skb函数向网络层传递数据,查询ptype_base[PTYPE_HASH_SIZE],找到type数据域和skb->protocol相匹配的packet_type成员,将数据帧发送给对应的func函数。

²  向接口中增加/删除协议

内核实现了两个API:Dev.c (net\core):void dev_add_pack(struct packet_type *pt;和

void dev_remove_pack(struct packet_type *pt)。

分别将packet_type实例变量加入/移出ptype_all和ptype_base哈希表。

²  何时注册packet_type实例

协议需要定义自己的packet_type变量,并且为数据域赋值,随后在协议的初始化函数中调用dev_remove_pack函数向内核注册。

例如在inet_init函数中调用dev_add_pack(&ip_packet_type)将IP协议向内核注册。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值