您的当前位置:首页数据包处理过程

数据包处理过程

来源:小侦探旅游网
 Linux网络协议栈之数据包处理过程详解

这篇文档是基于 x86 体系结构和转发 IP 分组的。

数据包在 Linux 内核链路层路径

图为NAPI 调用关系

2 接收分组

2.1 接收中断

如果网卡收到一个和自己 MAC 地址匹配或链路层广播的以太网帧,它就会产生一个中断。此网卡的驱动程序会处理此中断:

从 DMA/PIO 或其他得到分组数据,写到内存里去;

接着,会分配一个新的套接字缓冲区 skb ,并调用与协议无关的、网络设备均支持的通用网络接收处理函数 netif_rx(skb) 。 netif_rx() 函数让内核准备进一步处理 skb 。

然后, skb 会进入到达队列以便 CPU 处理(对于多核 CPU 而言,每个 CPU 维护一个队列)。如果 FIFO 队列已满,就会丢弃此分组。在 skb 排队后,调用 __cpu_raise_softirq() 标记 NET_RX_SOFTIRQ 软中断,等待 CPU 执行。

至此, netif_rx() 函数调用结束,返回调用者状况信息(成功还是失败等)。此时,中断上下文进程完成任务,数据分组继续被上层协议栈处理。

以下是中断接收函数snull_regular_interrupt()的具体实现过程

通常的中断过程能够告知新报文到达中断和发送完成通知的区别, 通过检查物理设备中的状态寄存器. snull 接口类似地工作, 但是它的状态字在软件中实现, 位于 dev->priv. 网络接口的中断处理看来如此:

static void snull_regular_interrupt(int irq, void *dev_id, struct pt_regs *regs) {

int statusword;

struct snull_priv *priv;

struct snull_packet *pkt = NULL; /*

As usual, check the \"device\" pointer to be sure it is really interrupting.

Then assign \"struct device *dev\" */

struct net_device *dev = (struct net_device *)dev_id; /* ... and check with hw if it's really ours */

/* paranoid */ if (!dev) return;

/* Lock the device */ priv = netdev_priv(dev); spin_lock(&priv->lock);

/* retrieve statusword: real netdevices use I/O instructions */ statusword = priv->status; priv->status = 0;

if (statusword & SNULL_RX_INTR) {

/* send it to snull_rx for handling */ pkt = priv->rx_queue; if (pkt) {

priv->rx_queue = pkt->next;

snull_rx(dev, pkt);//见下文snull_rx() } }

if (statusword & SNULL_TX_INTR) {

/* a transmission is over: free the skb */ priv->stats.tx_packets++;

priv->stats.tx_bytes += priv->tx_packetlen; dev_kfree_skb(priv->skb); }

/* Unlock the device and we are done */

spin_unlock(&priv->lock);

if (pkt) snull_release_buffer(pkt); /* Do this outside the lock! */ return; }

中断处理的第一个任务是取一个指向正确 net_device 结构的指针. 这个指针通常来自作为参数收到的 dev_id 指针. 以下是数据包接收函数snull_rx()具体实现过程

snull_rx ()在硬件收到报文后从 snull 的\"中断\"处理中调用, 并且报文现在已经在计算机的内存中. snull_rx 收到一个数据指针和报文长度; 它唯一的责任是发走这个报文和运行附加信息给上层的网络代码. 这个代码独立于获得数据指针和长度的方式.

void snull_rx(struct net_device *dev, struct snull_packet *pkt) {

struct sk_buff *skb;

struct snull_priv *priv = netdev_priv(dev); /*

The packet has been retrieved from the transmission

medium. Build an skb around it, so upper layers can handle it */

skb = dev_alloc_skb(pkt->datalen + 2); if (!skb) {

if (printk_ratelimit())

printk(KERN_NOTICE \"snull rx: low on mem - packet dropped\\n\");

priv->stats.rx_dropped++; goto out; }

memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);

/* Write metadata, and then pass to the receive level */ skb->dev = dev;

skb->protocol = eth_type_trans(skb, dev);

skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ priv->stats.rx_packets++;

priv->stats.rx_bytes += pkt->datalen; netif_rx(skb); out:

return; }

这个函数足够普通以作为任何网络驱动的一个模板, 但是在你有信心重用这个 代码段前需要一些解释.

因篇幅问题不能全部显示,请点此查看更多更全内容