- 浏览: 1404176 次
- 性别:
- 来自: 火星
文章分类
最新评论
-
aidd:
内核处理time_wait状态详解 -
ahtest:
赞一下~~
一个简单的ruby Metaprogram的例子 -
itiProCareer:
简直胡说八道,误人子弟啊。。。。谁告诉你 Ruby 1.9 ...
ruby中的类变量与类实例变量 -
dear531:
还得补充一句,惊群了之后,数据打印显示,只有一个子线程继续接受 ...
linux已经不存在惊群现象 -
dear531:
我用select试验了,用的ubuntu12.10,内核3.5 ...
linux已经不存在惊群现象
我这里描述的只是2层的处理。
首先,我们来看softnet_data这个结构,每个cpu都有这样的一个队列,它主要是用来存储incoming frame。由于他是每个cpu都有一个队列,因此在不同的cpu之间我们就不要任何锁来控制并发的处理这个帧队列。我们在操作系统层要取得帧数据,都是通过这个数据来读取。
这张图很好的表示了coy的softnet_data结构和网络设备的关系:
接下来我们来看它的初始化:
当一个新的帧的到达之后,内核处理的函数有两种(其实也就是2层的处理,当处理完后,就将帧扔到3层):
1 老的netif_rx函数
2 新的napi接口。
首先来介绍napi。
简单来说就是,当内核还在处理一个帧的时候,有另外的帧到来,这时napi不需要再执行中断,而是保持轮询设备的输入队列,从而取得新到的帧,当队列为空时,退出轮询。重新打开中断。
napi的数据结构:
下面的这张图可以看出napi和net_rx_action(软中断处理函数)的关系:
接下来我们来看一下内核如何把老的驱动模型和napi统一到一起,关键的数据结构就是我们上面讲的softnet_data的backlog结构。
先来看下napi和非napi驱动的区别:
这里很清楚的看到在飞napi的驱动中,要调用netif_rx函数,然后再调用netif_rx_schedule来把所需处理的帧交给软中断处理链表。
我们来看它的实现:
然后我们来看网络代码最核心的一个函数net_rx_action 也就是软中断(NET_RX_SOFTIRQ)的处理函数:
下来我们来看process_backlog函数,这个函数也就是非napi的驱动的默认poll的实现,napi的驱动的poll的实现,与它大体类似。
首先,我们来看softnet_data这个结构,每个cpu都有这样的一个队列,它主要是用来存储incoming frame。由于他是每个cpu都有一个队列,因此在不同的cpu之间我们就不要任何锁来控制并发的处理这个帧队列。我们在操作系统层要取得帧数据,都是通过这个数据来读取。
/* * Incoming packets are placed on per-cpu queues so that * no locking is needed. */ struct softnet_data { ///qdisc是queueing discipline的简写,也就是排队规则,就是我们经常说的qos.这里也就是输出帧的控制。 struct Qdisc *output_queue; ///当输入帧被驱动取得之前,就保存在这个队列里,这里要注意,这个只是非napi的驱动才会这样,而napi的驱动则是有自己的私有的队列。 struct sk_buff_head input_pkt_queue; ///表示有输入帧待处理的设备链表。 struct list_head poll_list; ///表示已经成功被传递出的帧的链表。 struct sk_buff *completion_queue; ///用来兼容非napi的驱动。 struct napi_struct backlog; #ifdef CONFIG_NET_DMA struct dma_chan *net_dma; #endif };
这张图很好的表示了coy的softnet_data结构和网络设备的关系:
接下来我们来看它的初始化:
static int __init net_dev_init(void) { .............................. for_each_possible_cpu(i) { struct softnet_data *queue; ///取每一个cpu的队列,并初始化。 queue = &per_cpu(softnet_data, i); skb_queue_head_init(&queue->input_pkt_queue); queue->completion_queue = NULL; INIT_LIST_HEAD(&queue->poll_list); ///这里我们就很清楚的看到backlog的功能了,由于napi的驱动都会有一个poll的虚函数,而非napi是没有的,因此这里会给所有的非napi的驱动赋值一个默认的处理方法。 queue->backlog.poll = process_backlog; queue->backlog.weight = weight_p; } ............................................... return rc; }
当一个新的帧的到达之后,内核处理的函数有两种(其实也就是2层的处理,当处理完后,就将帧扔到3层):
1 老的netif_rx函数
2 新的napi接口。
首先来介绍napi。
简单来说就是,当内核还在处理一个帧的时候,有另外的帧到来,这时napi不需要再执行中断,而是保持轮询设备的输入队列,从而取得新到的帧,当队列为空时,退出轮询。重新打开中断。
napi的数据结构:
struct napi_struct { /* The poll_list must only be managed by the entity which * changes the state of the NAPI_STATE_SCHED bit. This means * whoever atomically sets that bit can add this napi_struct * to the per-cpu poll_list, and whoever clears that bit * can remove from the list right before clearing the bit. */ ///有新的帧等到被执行的设备链表,这个链表的头就是softnet_data->poll_list. struct list_head poll_list; ///napi的状态 unsigned long state; ///也就是表示分配给此napi的所能处理的帧的限额。 int weight; ///从设备的输入队列中取得数据的虚函数。 int (*poll)(struct napi_struct *, int); #ifdef CONFIG_NETPOLL spinlock_t poll_lock; int poll_owner; struct net_device *dev; struct list_head dev_list; #endif };
下面的这张图可以看出napi和net_rx_action(软中断处理函数)的关系:
接下来我们来看一下内核如何把老的驱动模型和napi统一到一起,关键的数据结构就是我们上面讲的softnet_data的backlog结构。
先来看下napi和非napi驱动的区别:
这里很清楚的看到在飞napi的驱动中,要调用netif_rx函数,然后再调用netif_rx_schedule来把所需处理的帧交给软中断处理链表。
我们来看它的实现:
int netif_rx(struct sk_buff *skb) { struct softnet_data *queue; unsigned long flags; /* if netpoll wants it, pretend we never saw it */ 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的softnet_data数据 queue = &__get_cpu_var(softnet_data); ///将被当前cpu所接受的总帧的数目加一。 __get_cpu_var(netdev_rx_stat).total++; ///监测设备是否还有空间来存储帧,如果空间已满,表示网络阻塞严重,则返回一个错误,此后cpu将丢掉再来的帧。 if (queue->input_pkt_queue.qlen <= netdev_max_backlog) { if (queue->input_pkt_queue.qlen) { enqueue: ///这个帧被加入到softnet_data的输入队列。并返回成功。 __skb_queue_tail(&queue->input_pkt_queue, skb); local_irq_restore(flags); return NET_RX_SUCCESS; } ///当队列是空的时候,表明这个队列并没有被软中断所schedule,因此我们需要将此队列加入到软中断的处理链表中。可以看到加入的正好是backlog,由于调用netif_rx的是非napi的驱动,因此backlog就是初始化时的process_backlog函数。 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 也就是软中断(NET_RX_SOFTIRQ)的处理函数:
static void net_rx_action(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(); ///取出设备的napi数据结构。 n = list_entry(list->next, struct napi_struct, poll_list); ///加锁( 自旋锁) have = netpoll_poll_lock(n); weight = n->weight; work = 0; ///测试napi的状态,只有为NAPI_STATE_SCHED状态时,我们才能调用napi的poll方法。它会返回所处理的帧的数目。 if (test_bit(NAPI_STATE_SCHED, &n->state)) work = n->poll(n, weight); 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(); ...................................... return; softnet_break: __get_cpu_var(netdev_rx_stat).time_squeeze++; __raise_softirq_irqoff(NET_RX_SOFTIRQ); goto out; }
下来我们来看process_backlog函数,这个函数也就是非napi的驱动的默认poll的实现,napi的驱动的poll的实现,与它大体类似。
static int process_backlog(struct napi_struct *napi, int quota) { ///得到一些初始化值。 int work = 0; struct softnet_data *queue = &__get_cpu_var(softnet_data); unsigned long start_time = jiffies; napi->weight = weight_p; ///进入循环处理。 do { struct sk_buff *skb; local_irq_disable(); ///得到输入队列。 skb = __skb_dequeue(&queue->input_pkt_queue); if (!skb) { ///如果输入队列为空,则设置此napi的标志,并退出。 __napi_complete(napi); local_irq_enable(); break; } local_irq_enable(); ///处理输入帧,也就是进行一些2层的处理从而发给三层。 netif_receive_skb(skb); } while (++work < quota && jiffies == start_time);///设备处理帧的配额已经完成,或者时间太长,则退出。 return work; }
发表评论
-
Receive packet steering patch详解
2010-07-25 16:46 12164Receive packet steering简称rp ... -
内核中拥塞窗口初始值对http性能的影响分析
2010-07-11 00:20 9710这个是google的人提出的 ... -
linux 内核tcp拥塞处理(一)
2010-03-12 16:17 9582这次我们来分析tcp的拥塞控制,我们要知道协议栈都是很保守的, ... -
内核tcp协议栈SACK的处理
2010-01-24 21:13 12187上一篇处理ack的blog中我 ... -
内核tcp的ack的处理
2010-01-17 03:06 11170我们来看tcp输入对于ack,段的处理。 先是ack的处理, ... -
内核处理time_wait状态详解
2010-01-10 17:39 6826这次来详细看内核的time_wait状态的实现,在前面介绍定时 ... -
tcp协议栈处理各种事件的分析
2009-12-30 01:29 13645首先我们来看socket如何将一些状态的变化通知给对应的进程, ... -
linux内核sk_buff的结构分析
2009-12-25 00:42 47926我看的内核版本是2.6.32. 在内核中sk_buff表示一 ... -
tcp的输入段的处理
2009-12-18 00:56 8368tcp是全双工的协议,因此每一端都会有流控。一个tcp段有可能 ... -
内核协议栈tcp层的内存管理
2009-11-28 17:13 12096我们先来看tcp内存管理相关的几个内核参数,这些都能通过pro ... -
linux内核定时器的实现
2009-10-31 01:44 10199由于linux还不是一个实时的操作系统,因此如果需要更高精度, ... -
linux内核中tcp连接的断开处理
2009-10-25 21:47 10345我们这次主要来分析相关的两个断开函数close和shotdow ... -
linux内核tcp的定时器管理(二)
2009-10-05 20:52 5438这次我们来看后面的3个定时器; 首先是keep alive定 ... -
linux内核tcp的定时器管理(一)
2009-10-04 23:29 9843在内核中tcp协议栈有6种 ... -
linux 内核tcp接收数据的实现
2009-09-26 20:24 14535相比于发送数据,接收数据更复杂一些。接收数据这里和3层的接口是 ... -
linux 内核tcp数据发送的实现
2009-09-10 01:41 19802在分析之前先来看下SO_RCVTIMEO和SO_SNDTIME ... -
tcp connection setup的实现(三)
2009-09-03 00:34 5211先来看下accept的实现. 其实accept的作用很简单, ... -
tcp connection setup的实现(二)
2009-09-01 00:46 8440首先来看下内核如何处理3次握手的半连接队列和accept队列( ... -
tcp connection setup的实现(一)
2009-08-23 04:10 5835bind的实现: 先来介绍几个地址结构. struct ... -
linux内核中socket的实现
2009-08-15 04:38 21112首先来看整个与socket相关的操作提供了一个统一的接口sys ...
相关推荐
总结,Linux系统内核的以太帧处理机制是通过packet_type结构体和对应的处理函数来实现的。开发者可以通过自定义packet_type并注册处理函数,来扩展对特定以太帧类型的支持,从而实现对网络数据包的定制化处理。这一...
内核空间则包含了网络协议处理的核心模块,负责数据包的接收、处理和发送。 1. **物理层**:这一层主要处理数据的物理传输,如以太网、令牌环、光纤等。在Linux内核中,驱动程序实现了对硬件设备的访问,如网卡驱动...
在Linux内核中,网络层由ip_rcv函数处理,它负责接收IP包,并根据路由表决定如何转发或交付给目标进程。 3. 传输层:主要涉及TCP(传输控制协议)和UDP(用户数据报协议)。TCP提供面向连接的服务,确保数据的可靠...
- 驱动程序在硬件层面接收和发送帧,而网络核心负责帧的进一步处理。 3. **网络层**: - 主要协议是IP,Linux内核中的`net/core/ip_input.c`处理IP数据包的接收,`ip_output.c`处理发送。 - 路由选择是网络层的...
总结来说,Linux内核中的skb处理流程是一个复杂而精细的过程,涉及了网络协议栈的各个层次,确保了数据包从网络到应用的正确传输。通过理解这一流程,开发者能更好地优化网络应用性能,排查问题,以及实现特定的网络...
该协议栈位于Linux内核的网络子系统中,负责处理遵循IEEE 802.11标准的无线局域网(WLAN)通信。它不仅需要处理MAC层的协议细节,还要与硬件设备进行交互,并与用户空间的配置工具进行沟通。 802.11协议栈的设计...
Linux内核中的网络子系统是操作系统的核心组成部分,它负责处理所有与网络相关的任务,包括数据包的接收、发送、协议栈的实现以及网络接口的管理等。本篇将深入探讨这个复杂的子系统的各个方面。 首先,网络子系统...
《LINUX-2.4.0内核网络栈实现源代码分析》这本书深入探讨了Linux 2.4.0版本内核中的网络栈实现,它为理解Linux内核的网络处理机制提供了宝贵的参考资料。网络栈是操作系统的核心部分,负责网络数据的接收、处理和...
Linux内核协议栈是操作系统核心的一部分,负责处理所有与网络相关的任务,包括数据包的接收、处理和发送。在2.6.18这个版本中,协议栈已经相当成熟,支持多种网络协议,如TCP/IP(传输控制协议/互联网协议),这是...
在Linux内核中,网络数据包的传送是一个复杂的多层次过程,涉及到硬件、驱动程序、中断处理、各个网络层次协议栈的处理等多个方面。下面,我们将详细解析Linux内核中的网络数据包传送通道。 首先,我们来看数据包的...
在Linux系统中,网络功能是操作系统的核心组成部分之一,它负责处理数据的发送和接收,实现不同网络间的通信。通过分析Linux网络内核,我们可以深入了解TCP/IP协议栈的实现,包括网络接口层、网络层、传输层以及应用...
Linux内核协议栈是Linux操作系统中负责处理网络数据包传输的核心组件。它按照TCP/IP协议模型的分层设计,将网络通信划分为链路层、网络层、运输层和应用层四个层次,并为每层提供了相应的协议实现与数据结构。 在...
在Linux操作系统中,二层(Layer 2)收发包机制是网络通信的基础,它涉及到数据链路层的协议处理,如以太网、令牌环等。本文将深入探讨Linux内核下的二层socket(L2socket)及其工作原理。 首先,理解Linux中的...
在Linux内核中,这些层的实现涉及多个关键模块,如socket接口、协议处理函数和网络设备驱动。 1. **应用层**:这一层是用户程序与网络通信的接口,如HTTP、FTP、DNS等协议。在Linux中,应用程序通过socket API进行...
发送数据时,数据会被封装成UDP或IP包,存储在发送缓存中,接收数据时,DM9000A会检查数据帧的合法性并通知处理器处理接收到的数据。 在Linux环境中,网络驱动程序构建于一个四层架构之上,包括设备驱动层、协议层...
Linux内核是操作系统的核心部分,其中的网络协议栈负责处理所有的网络通信任务,包括数据包的接收、处理和发送。在Linux系统中,网络协议栈遵循分层模型,包括链路层、网络层、传输层和应用层,每一层都有其特定的...
CPU调用网卡驱动注册的硬中断响应程序,网卡硬中断响应程序会为网络数据帧创建内核数据结构sk_buffer,并将网络数据帧拷贝到sk_buffer中。 9. 内核协议栈处理 内核协议栈处理包括ip_rcv函数和tcp_rcv函数等步骤。...
其中,网络内核部分主要处理网络通信,包括协议栈的实现、网络设备驱动、数据包的发送和接收等。 **网络内核结构:** Linux的网络内核结构遵循了分层模型,主要包括以下层次: 1. 链路层(Link Layer):处理物理...
在Linux中,所有网络设备都被抽象为一个net_device结构,其包含一系列设备方法,用于处理接收和发送数据。驱动程序的主要任务是实现这些方法。 【网络设备的初始化】 网络设备的初始化由net_device结构体中的init...