网络设备在接收到来自网络中其它主机的数据报,或本地环回接口的数据报之后,交给协议栈的netif_rx函数,该函数首先要为收到的这个skb打上当前的时间戳(skb->tstamp成员),这个时间戳表示该数据到达的时间,它不是必选的,可以通过套接字选项SO_TIMESTAMP将其打开,该选项打开时间戳时,会将链路层的全局变量netstamp_needed加1,netif_rx在检查到这个变量不为零时,为skb打上时间戳。
softnet_data是类型为struct softnet_data结构体的全局变量,每个CPU定义一个,它是链路层的数据接收队列,该结构体的定义如下:
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 net_device backlog_dev;
};
input_pkt_queue是skb的队列,接收到的skb全部进入该队列等待后续处理,netif_rx首先检查该队列当前的长度input_pkt_queue.qlen,即当前排在队列中的skb的数量,当数量超过netdev_max_backlog的值时,直接丢弃新收到的包,netdev_max_backlog在协议栈中定义的缺省值为1000,可以通过文件/proc/sys/net/core/netdev_max_backlog进行修改。如果当前队列长度未达到上限,把新收到的skb加到这个队列中,在加到队列之前,要确保对这个队列的接收处理已启动,如果当前队列为空,则要先调用netif_rx_schedule启动队列的处理,再把skb加到队列中。需要注意的是softnet_data是CPU绑定的,但不是网络设备绑定的,多个网络设备收到的数据报可能存放在同一个队列中待处理。
netif_rx_schedule函数的主要作用是触发一个软中断NET_RX_SOFTIRQ,使中断处理函数net_rx_action处理接收队列中的数据报。net_rx_action开始时会记录下系统的当前时间,然后进行处理,当处理时间持续超过1个时钟嘀嗒时,它会再触发一个中断NET_RX_SOFTIRQ,并退出,在下一个中断中继续处理。一次中断处理除了时间上有限制,处理的数据报的数量上也有限制。
softnet_data的成员poll_list中存放的是成员backlog_dev的地址,由netif_rx_schedule存入,backlog_dev的成员poll在系统初始化时被指向函数process_backlog,net_rx_action调用该函数进行实际的数据报处理,process_backlog把数据报从input_pkt_queue队列中取出,传给netif_receive_skb,由netif_receive_skb传给相应的网络层接收函数。process_backlog的处理时间也有1个时钟嘀嗒的限制,同时一次处理的数据报的数量不得超过backlog_dev->quota和netdev_budget两个值中较小的那个值,backlog_dev->quota由netif_rx_schedule初始化为全局变量weight_p的值,缺省为64,netdev_budget缺省为300。从代码可以看出,process_backlog一次处理最大数据报数量为64,而net_rx_action为300。weight_p和netdev_budget这两个值分别可以在文件/proc/sys/net/core/dev_weight和/proc/sys/net/core/netdev_budget中查看和修改。
netif_receive_skb是链路层接收数据报的最后一站。它根据注册在全局数组ptype_all和ptype_base里的网络层数据报类型,把数据报递交给不同的网络层协议的接收函数(INET域中主要是ip_rcv和arp_rcv)。
阅读(781) | 评论(0) | 转发(0) |