linux2.6.24内核源代码分析(2)——扒一扒网络数据包在链路层的流向路径之一
2013-10-05 23:30
597 查看
在2.6.24内核中链路层接收网络数据包出现了两种方法,第一种是传统方法,利用中断来接收网络数据包,适用于低速设备;第二种是New Api(简称NAPI)方法,利用了中断+轮询的方法来接收网络数据包,是linux为了接收高速的网络数据包而加入的,适用于告诉设备,现在大多数NIC都兼容了这个方法。
今天我的任务是扒一扒网络数据包在传统方法也就是低速路径中如何传入链路层以及如何将其发送给上层网络层的。下面先来看看这条低速路径的简略示意图:
接下来就到了net_rx_action了
如果大家不明白上面的n->poll为什么指向了process_backlog,那我们来看一下n的定义:struct napi_struct *n。关键是搞懂napi_struct结构体
结构体中的poll函数指针指向轮询函数,在传统方法(就是我今天介绍的方法)下内核将poll填写为默认的process_backlog函数而在NAPI方法下内核则会填写相应的轮询函数
今天我的任务是扒一扒网络数据包在传统方法也就是低速路径中如何传入链路层以及如何将其发送给上层网络层的。下面先来看看这条低速路径的简略示意图:
//当产生硬件中断时,此中断处理例程被调用.例程确定该中断是否是由接收到的分组引发的,如果是则调用net_rx static irqreturn_t [b]net_interrupt[/b](int irq, void *dev_id) { struct net_device *dev = dev_id; struct net_local *np; int ioaddr, status; int handled = 0; ioaddr = dev->base_addr; np = netdev_priv(dev); status = inw(ioaddr + 0); if (status == 0) goto out; handled = 1; if (status & RX_INTR) { /* 调用此函数来获取数据包!!!!!! */ [b] net_rx(dev)[/b]; } #if TX_RING if (status & TX_INTR) { /* 此出代码为发送数据包的过程,我以后会扒它 */ net_tx(dev); np->stats.tx_packets++; netif_wake_queue(dev); } #endif if (status & COUNTERS_INTR) { np->stats.tx_window_errors++; } out: return IRQ_RETVAL(handled); }
static void [b]net_rx[/b](struct net_device *dev) { struct net_local *lp = netdev_priv(dev); int ioaddr = dev->base_addr; int boguscount = 10; do { int status = inw(ioaddr); int pkt_len = inw(ioaddr); if (pkt_len == 0) break; if (status & 0x40) { lp->stats.rx_errors++; if (status & 0x20) lp->stats.rx_frame_errors++; if (status & 0x10) lp->stats.rx_over_errors++; if (status & 0x08) lp->stats.rx_crc_errors++; if (status & 0x04) lp->stats.rx_fifo_errors++; } else { struct sk_buff *skb; lp->stats.rx_bytes+=pkt_len; //调用dev_alloc_skb来分配一个sk_buff实例 //还记得我上一篇文章扒的这个结构体吗? skb = dev_alloc_skb(pkt_len); if (skb == NULL) { printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); lp->stats.rx_dropped++; break; } //设置skb关联的网络设备 skb->dev = dev; /*将得到的数据拷贝到sk_buff的data处 */ memcpy(skb_put(skb,pkt_len), (void*)dev->rmem_start, pkt_len); insw(ioaddr, skb->data, (pkt_len + 1) >> 1); //接着调用netif_rx!!!!! netif_rx(skb); dev->last_rx = jiffies; lp->stats.rx_packets++; lp->stats.rx_bytes += pkt_len; } } while (--boguscount); return; }
int [b]netif_rx[/b](struct sk_buff *skb) { struct softnet_data *queue; unsigned long flags; if (netpoll_rx(skb)) return NET_RX_DROP; if (!skb->tstamp.tv64) net_timestamp(skb);//设置分组到达时间 /* * The code is rearranged so that the path is the most * short when CPU is congested, but is still operating. */ local_irq_save(flags); //得到cpu的等待队列!!!! [b]queue = &__get_cpu_var(softnet_data); [/b] __get_cpu_var(netdev_rx_stat).total++; if (queue->input_pkt_queue.qlen <= netdev_max_backlog) { if (queue->input_pkt_queue.qlen) { enqueue: dev_hold(skb->dev); //将skb加到等待队列的输入队列队尾!!!!! [b] __skb_queue_tail(&queue->input_pkt_queue, skb); [/b] local_irq_restore(flags); return NET_RX_SUCCESS; } //NAPI调度函数,我以后将扒! napi_schedule(&queue->backlog); goto enqueue; } __get_cpu_var(netdev_rx_stat).dropped++; local_irq_restore(flags); kfree_skb(skb); return NET_RX_DROP; }
接下来就到了net_rx_action了
static void [b]net_rx_action[/b](struct softirq_action *h) { struct list_head *list = &__get_cpu_var(softnet_data).poll_list; unsigned long start_time = jiffies; int budget = netdev_budget; void *have; local_irq_disable(); while (!list_empty(list)) { struct napi_struct *n; int work, weight; if (unlikely(budget <= 0 || jiffies != start_time)) goto softnet_break; local_irq_enable(); n = list_entry(list->next, struct napi_struct, poll_list); have = netpoll_poll_lock(n); weight = n->weight; work = 0; if (test_bit(NAPI_STATE_SCHED, &n->state)) //此时poll函数指针指向默认的process_backlog!!!!重要!!!!!!!!!!!!!!! [b]work = n->poll(n, weight); [/b] WARN_ON_ONCE(work > weight); budget -= work; local_irq_disable(); if (unlikely(work == weight)) { if (unlikely(napi_disable_pending(n))) __napi_complete(n); else list_move_tail(&n->poll_list, list); } netpoll_poll_unlock(have); } out: local_irq_enable(); #ifdef CONFIG_NET_DMA /* * There may not be any more sk_buffs coming right now, so push * any pending DMA copies to hardware */ if (!cpus_empty(net_dma.channel_mask)) { int chan_idx; for_each_cpu_mask(chan_idx, net_dma.channel_mask) { struct dma_chan *chan = net_dma.channels[chan_idx]; if (chan) dma_async_memcpy_issue_pending(chan); } } #endif return; softnet_break: __get_cpu_var(netdev_rx_stat).time_squeeze++; __raise_softirq_irqoff(NET_RX_SOFTIRQ); goto out; }
如果大家不明白上面的n->poll为什么指向了process_backlog,那我们来看一下n的定义:struct napi_struct *n。关键是搞懂napi_struct结构体
结构体中的poll函数指针指向轮询函数,在传统方法(就是我今天介绍的方法)下内核将poll填写为默认的process_backlog函数而在NAPI方法下内核则会填写相应的轮询函数
static int [b]process_backlog[/b](struct napi_struct *napi, int quota) { int work = 0; //得到cpu的等待队列 struct softnet_data *queue = &__get_cpu_var(softnet_data); unsigned long start_time = jiffies; napi->weight = weight_p; do { struct sk_buff *skb; struct net_device *dev; local_irq_disable(); //在等待队列的输入队列中移除一个sk_buff!!!!! skb = [b]__skb_dequeue[/b](&queue->input_pkt_queue); if (!skb) { __napi_complete(napi); local_irq_enable(); break; } local_irq_enable(); dev = skb->dev; //调用此函数处理分组!!!! netif_receive_skb(skb); dev_put(dev); } while (++work < quota && jiffies == start_time); return work; }
int [b]netif_receive_skb[/b](struct sk_buff *skb) { struct packet_type *ptype, *pt_prev; struct net_device *orig_dev; int ret = NET_RX_DROP; __be16 type; …… …… type = skb->protocol; list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type)&15], list) { if (ptype->type == type && (!ptype->dev || ptype->dev == skb->dev)) { if (pt_prev) ret = deliver_skb(skb, pt_prev, orig_dev); pt_prev = ptype; } } if (pt_prev) { /*调用函数指针func将skb传送至网络层!!! *pt_prev为packet_type指针, *在分组传递过程中内核会根据protocol填写func, *func被填写为向网络层传递skb的函数 */
[b]ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev); [/b] } else { kfree_skb(skb); /* Jamal, now you will not able to escape explaining * me how you were going to use this. :-) */ ret = NET_RX_DROP; } out: rcu_read_unlock(); return ret; }
相关文章推荐
- linux2.6.24内核源代码分析(1)——扒一扒sk_buff
- Linux网络协议源代码分析 之 主要数据结构体
- Linux内核网络子系统数据发送模块分析(MAC802.11-OpenWRT)
- linux 内核网络,数据接收流程图
- Linux 网络协议栈开发代码分析篇之数据收发(二) —— dev_queue_xmit()函数
- Linux 内核源代码情景分析 chap 2 存储管理 (三)
- 二、linux网络内核调优:数据重传
- Linux 内核源代码情景分析 chap 2 存储管理(一)
- linux 3.4.10 内核内存管理源代码分析1:源代码阅读工具,编译及调试
- linux 3.4.10 内核内存管理源代码分析4:伙伴系统内存释放
- Linux内核分析 - 网络[四]:路由表
- linux 内核源代码分析 - 获取数组的大小
- ArcGIS 网络分析[1.2] 利用1.1的线shp创建网络数据集/并简单试验最佳路径
- Linux内核--网络协议栈深入分析(三)--BSD socket和传输层sock
- linux 内核源代码情景分析——i386 的页式内存管理机制
- linux路由内核实现分析(二)---FIB相关数据结构
- Linux网络防火墙【4】 Linux内核网络 iptables 之filter分析
- ArcGIS10.2 网络分析之路径分析(二)---构建网络数据集
- mini2440使用linux 3.0内核,默认总线时序过快造成网络数据错误率高的问题
- [转]Linux网络协议栈分析——从设备驱动到链路层