`
simohayha
  • 浏览: 1404176 次
  • 性别: Icon_minigender_1
  • 来自: 火星
社区版块
存档分类
最新评论

内核中接收网络帧的处理

阅读更多
我这里描述的只是2层的处理。

首先,我们来看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;
}






  • 大小: 33.7 KB
  • 大小: 49.1 KB
  • 大小: 44.7 KB
0
0
分享到:
评论

相关推荐

    Linux系统内核接收以太帧的处理程序

    总结,Linux系统内核的以太帧处理机制是通过packet_type结构体和对应的处理函数来实现的。开发者可以通过自定义packet_type并注册处理函数,来扩展对特定以太帧类型的支持,从而实现对网络数据包的定制化处理。这一...

    Linux网络体系结构 Linux内核中网络协议的设计与实现

    内核空间则包含了网络协议处理的核心模块,负责数据包的接收、处理和发送。 1. **物理层**:这一层主要处理数据的物理传输,如以太网、令牌环、光纤等。在Linux内核中,驱动程序实现了对硬件设备的访问,如网卡驱动...

    《linux内核网络栈源代码情景分析》.(曹桂平).[PDF].&ckook;.pdf.zip

    在Linux内核中,网络层由ip_rcv函数处理,它负责接收IP包,并根据路由表决定如何转发或交付给目标进程。 3. 传输层:主要涉及TCP(传输控制协议)和UDP(用户数据报协议)。TCP提供面向连接的服务,确保数据的可靠...

    Linux网络体系结构:Linux内核中网络协议的设计与实现(english)

    - 驱动程序在硬件层面接收和发送帧,而网络核心负责帧的进一步处理。 3. **网络层**: - 主要协议是IP,Linux内核中的`net/core/ip_input.c`处理IP数据包的接收,`ip_output.c`处理发送。 - 路由选择是网络层的...

    ipv4_linux内核skb处理流程图_

    总结来说,Linux内核中的skb处理流程是一个复杂而精细的过程,涉及了网络协议栈的各个层次,确保了数据包从网络到应用的正确传输。通过理解这一流程,开发者能更好地优化网络应用性能,排查问题,以及实现特定的网络...

    Linux 内核802.11 无线网络协议栈的设计与实现

    该协议栈位于Linux内核的网络子系统中,负责处理遵循IEEE 802.11标准的无线局域网(WLAN)通信。它不仅需要处理MAC层的协议细节,还要与硬件设备进行交互,并与用户空间的配置工具进行沟通。 802.11协议栈的设计...

    linux内核知识系列:网络子系统

    Linux内核中的网络子系统是操作系统的核心组成部分,它负责处理所有与网络相关的任务,包括数据包的接收、发送、协议栈的实现以及网络接口的管理等。本篇将深入探讨这个复杂的子系统的各个方面。 首先,网络子系统...

    LINUX-2.4.0内核网络栈实现源代码分析

    《LINUX-2.4.0内核网络栈实现源代码分析》这本书深入探讨了Linux 2.4.0版本内核中的网络栈实现,它为理解Linux内核的网络处理机制提供了宝贵的参考资料。网络栈是操作系统的核心部分,负责网络数据的接收、处理和...

    linux内核协议栈源码解析(2.6.18内核)

    Linux内核协议栈是操作系统核心的一部分,负责处理所有与网络相关的任务,包括数据包的接收、处理和发送。在2.6.18这个版本中,协议栈已经相当成熟,支持多种网络协议,如TCP/IP(传输控制协议/互联网协议),这是...

    linux内核-网络数据包传送通道解析

    在Linux内核中,网络数据包的传送是一个复杂的多层次过程,涉及到硬件、驱动程序、中断处理、各个网络层次协议栈的处理等多个方面。下面,我们将详细解析Linux内核中的网络数据包传送通道。 首先,我们来看数据包的...

    Linux网络内核分析与开发

    在Linux系统中,网络功能是操作系统的核心组成部分之一,它负责处理数据的发送和接收,实现不同网络间的通信。通过分析Linux网络内核,我们可以深入了解TCP/IP协议栈的实现,包括网络接口层、网络层、传输层以及应用...

    linux内核协议栈分析

    Linux内核协议栈是Linux操作系统中负责处理网络数据包传输的核心组件。它按照TCP/IP协议模型的分层设计,将网络通信划分为链路层、网络层、运输层和应用层四个层次,并为每层提供了相应的协议实现与数据结构。 在...

    基于LINUX内核下的二层收发包机制

    在Linux操作系统中,二层(Layer 2)收发包机制是网络通信的基础,它涉及到数据链路层的协议处理,如以太网、令牌环等。本文将深入探讨Linux内核下的二层socket(L2socket)及其工作原理。 首先,理解Linux中的...

    Linux 内核源码剖析-TCP/IP实现(上册)

    在Linux内核中,这些层的实现涉及多个关键模块,如socket接口、协议处理函数和网络设备驱动。 1. **应用层**:这一层是用户程序与网络通信的接口,如HTTP、FTP、DNS等协议。在Linux中,应用程序通过socket API进行...

    嵌入式系统的以太网接口设计及linux内核网络设备驱动可用.pdf

    发送数据时,数据会被封装成UDP或IP包,存储在发送缓存中,接收数据时,DM9000A会检查数据帧的合法性并通知处理器处理接收到的数据。 在Linux环境中,网络驱动程序构建于一个四层架构之上,包括设备驱动层、协议层...

    《深入理解Linux网络技术内幕》.rar

    Linux内核是操作系统的核心部分,其中的网络协议栈负责处理所有的网络通信任务,包括数据包的接收、处理和发送。在Linux系统中,网络协议栈遵循分层模型,包括链路层、网络层、传输层和应用层,每一层都有其特定的...

    聊聊Netty那些事儿之从内核角度看IO模型.doc

    CPU调用网卡驱动注册的硬中断响应程序,网卡硬中断响应程序会为网络数据帧创建内核数据结构sk_buffer,并将网络数据帧拷贝到sk_buffer中。 9. 内核协议栈处理 内核协议栈处理包括ip_rcv函数和tcp_rcv函数等步骤。...

    Linux_net_internal.rar_linux 内核详解_linux驱动编程

    其中,网络内核部分主要处理网络通信,包括协议栈的实现、网络设备驱动、数据包的发送和接收等。 **网络内核结构:** Linux的网络内核结构遵循了分层模型,主要包括以下层次: 1. 链路层(Link Layer):处理物理...

    嵌入式系统的以太网接口设计及linux内核网络设备驱动.pdf

    在Linux中,所有网络设备都被抽象为一个net_device结构,其包含一系列设备方法,用于处理接收和发送数据。驱动程序的主要任务是实现这些方法。 【网络设备的初始化】 网络设备的初始化由net_device结构体中的init...

Global site tag (gtag.js) - Google Analytics