链路层收包

//当底层设备驱动程序接收一个报文时,就会通过调用netif_receive_skb将报文的SKB上传至网络层。
/*
在netif_rx函数中会调用netif_rx_schedule, 然后该函数又会去调用__netif_rx_schedule
在函数__netif_rx_schedule中会去触发软中断NET_RX_SOFTIRQ, 也即是去调用net_rx_action.
然后在net_rx_action函数中会去调用设备的poll函数, 它是设备自己注册的.
在设备的poll函数中, 会去调用netif_receive_skb函数,  在该函数中有下面一条语句 pt_prev->func, 此处的func为一个函数指针,
因此, 就完成了从链路层上传到网络层的这一个过程了.
*/

/*非NAPI方式,从驱动硬件中断中调用这个netif_rx函数,

而NAPI方式从硬件中断中调用____napi_schedule激活软中断,
*/
/*
            非NAPI方式                                              NAPI方式NAPI方式(NAPI的napi_struct是自己构造的,该结构上的poll钩子函数也是自己定义的。

IRQ
                                         |
                  _______________________|_____________________________
                  |                                                     |
             netif_rx(netif_rx_internal)                napi_schedule
 上半部           |                                                     |
             enqueue_to_backlog                                  __napi_schedule
                  |                                                     |           
            skb加入input_pkt_queuem中                           napi_struct加入poll_list中
         __napi_schedule

    softnet_data->backlog加入poll_list中                                      |
                   |____________________________________________________|
                                             |
                                        net_rx_action
下半部                                       |
                      _______________________|_____________________________
                      |                                                     |
            process_backlog->__netif_receive_skb                驱动poll方法->napi_gro_receive->netif_receive_skb->__netif_receive_skb

*/

static int netif_rx_internal(struct sk_buff *skb)
{
	int ret;

	net_timestamp_check(netdev_tstamp_prequeue, skb);

	trace_netif_rx(skb);
#ifdef CONFIG_RPS
	if (static_key_false(&rps_needed)) {
		struct rps_dev_flow voidflow, *rflow = &voidflow;
		int cpu;

		preempt_disable();
		rcu_read_lock();

		cpu = get_rps_cpu(skb->dev, skb, &rflow);
		if (cpu < 0)
			cpu = smp_processor_id();

		ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail);//这里面的数据在process_backlog

		rcu_read_unlock();
		preempt_enable();
	} else
#endif
	{
		unsigned int qtail;
		ret = enqueue_to_backlog(skb, get_cpu(), &qtail);
		put_cpu();
	}
	return ret;
}

/* 队列中.在中断轮询的时候,软中断总函数do_softirq()直接到达网卡的接收软中断函数net_rx_action(),
   在此函数中调用queue->backlog_dev.poll=process_backlog;即process_backlog()函数,它将queue->input_pkt_queue
   队列中的数据向上层协议传输,比如网络层的ip协议等。
*/
/*
            非NAPI方式                                              NAPI方式NAPI方式(NAPI的napi_struct是自己构造的,该结构上的poll钩子函数也是自己定义的

IRQ
                                         |
                  _______________________|_____________________________
                  |                                                       |
             netif_rx                                            napi_schedule
 上半部      |                                                          |
             enqueue_to_backlog

     ____napi_schedule                                 __napi_schedule
                  |                                                       |           
            skb加入input_pkt_queuem中                           napi_struct加入poll_list中
            softnet_data->backlog加入poll_list中                                      |
                   |____________________________________________________|
                                             |
                                        net_rx_action
下半部                                       |
                      _______________________|_____________________________
                      |                                                     |
            porcess_backlog->__netif_receive_skb                驱动poll方法->napi_gro_receive->netif_receive_skb->__netif_receive_skb

*/

static int enqueue_to_backlog(struct sk_buff *skb, int cpu,
			      unsigned int *qtail)
{
	struct softnet_data *sd;
	unsigned long flags;
	unsigned int qlen;

	sd = &per_cpu(softnet_data, cpu);//获取cpu接口缓存队列(每cpu变量下次写内核同步介绍)

	local_irq_save(flags);//关中断,当该SKB添加到输入队列input_pkt_queue后打开中断,继续从硬件中断中接收输入然后放入该接收队列中

	rps_lock(sd);
	if (!netif_running(skb->dev))
		goto drop;
	qlen = skb_queue_len(&sd->input_pkt_queue);
	if (qlen <= netdev_max_backlog && !skb_flow_limit(skb, qlen)) {如果链路层缓存队列还没有满,则加入队列,否者上层处理严重阻塞,丢失,drop++
		if (qlen) { 队列不为空,说明已经有数据,只需加入队列,等待下次软中断处理
enqueue:
			__skb_queue_tail(&sd->input_pkt_queue, skb);/* 挂softnet_data输入队列 */ //net_rx_action中会对包的个数,以及软中断处理时间进行限制
			input_queue_tail_incr_save(sd, qtail);
			rps_unlock(sd);
			local_irq_restore(flags);//打开中断,当该SKB添加到输入队列input_pkt_queue后打开中断,继续从硬件中断中接收输入然后放入该接收队列中
			return NET_RX_SUCCESS;
		}

		/* Schedule NAPI for backlog device
		 * We can use non atomic operation since we own the queue lock
		 */ /* &sd->backlog加入napi->poll_list,backlog即函数process_backlog */
		if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state)) {
			if (!rps_ipi_queued(sd))//这里就会调用net_dev_init中的->backlog_dev.poll=process_backlog从而到process_backlog中执行                  

for_each_possible_cpu(i) {
        struct softnet_data *sd = &per_cpu(softnet_data, i);

        skb_queue_head_init(&sd->input_pkt_queue);
        skb_queue_head_init(&sd->process_queue);
        INIT_LIST_HEAD(&sd->poll_list);
        sd->output_queue_tailp = &sd->output_queue;
#ifdef CONFIG_RPS
        sd->csd.func = rps_trigger_softirq;
        sd->csd.info = sd;
        sd->cpu = i;
#endif

        sd->backlog.poll = process_backlog;
        sd->backlog.weight = weight_p;
    }

    dev_boot_phase = 0;

    /* The loopback device is special if any other network devices
     * is present in a network namespace the loopback device must
     * be present. Since we now dynamically allocate and free the
     * loopback device ensure this invariant is maintained by
     * keeping the loopback device as the first device on the
     * list of network devices.  Ensuring the loopback devices
     * is the first device that appears and the last network device
     * that disappears.
     */
    if (register_pernet_device(&loopback_net_ops))
        goto out;

    if (register_pernet_device(&default_device_ops))
        goto out;

    open_softirq(NET_TX_SOFTIRQ, net_tx_action);
    open_softirq(NET_RX_SOFTIRQ, net_rx_action);

              ____napi_schedule(sd, &sd->backlog);//加入list 并激活软中断

/*NAPI方式,把dev设备添加到了poll_list链表中。
每个网络设备(MAC层)都有自己的net_device数据结构,这个结构上有napi_struct。每当收到数据包时,网络设备驱动会把自己的napi_struct挂到CPU私有变量上。
这样在软中断时,net_rx_action会遍历cpu私有变量的poll_list,执行上面所挂的napi_struct结构的poll钩子函数,将数据包从驱动传到网络协议栈。

NAPI的napi_struct是自己构造的,该结构上的poll钩子函数也是自己定义的。
非NAPI的napi_struct结构是默认的,也就是per cpu的softnet_data>backlog,起poll钩子函数为process_backlog
*/

/* Called with irq disabled */
static inline void ____napi_schedule(struct softnet_data *sd,
                     struct napi_struct *napi)
{
    list_add_tail(&napi->poll_list, &sd->poll_list);
    __raise_softirq_irqoff(NET_RX_SOFTIRQ);
}

		}
		goto enqueue;
	}

drop:
	sd->dropped++;
	rps_unlock(sd);

	local_irq_restore(flags);

	atomic_long_inc(&skb->dev->rx_dropped);
	kfree_skb(skb);
	return NET_RX_DROP;
}

原文地址:https://www.cnblogs.com/codestack/p/9163165.html

时间: 2024-08-27 04:10:24

链路层收包的相关文章

TCP/IP协议简介(二) 之 链路层介绍

链路层介绍 一.简介 上一节已经介绍过,网络层协议的数据单元是 IP 数据报 ,而数据链路层的工作就是把网络层交下来的 IP 数据报 封装为 帧(frame)发送到链路上,以及把接收到的帧中的数据取出并上交给网络层. 为达到这一目的,数据链路必须具备一系列相应的功能,主要有: 将数据封装为帧(frame),帧是数据链路层的传送单位: 控制帧的传输,包括处理传输差错,调节发送速率与接收方相匹配: 在两个网络实体之间提供数据链路通路的建立.维持和释放的管理. 数据帧的结构是这样的: 二.控制帧的传输

浅谈UDP(数据包长度,收包能力,丢包及进程结构选择)

UDP数据包长度 UDP数据包的理论长度 udp数据包的理论长度是多少,合适的udp数据包应该是多少呢?从TCP-IP详解卷一第11章的udp数据包的包头可以看出,udp的最大包长度是2^16-1的个字节.由于udp包头占8个字节,而在ip层进行封装后的ip包头占去20字节,所以这个是udp数据包的最大理论长度是2^16-1-8-20=65507. 然而这个只是udp数据包的最大理论长度.首先,我们知道,TCP/IP通常被认为是一个四层协议系统,包括链路层.网络层.运输层.应用层.UDP属于运输

(2)链路层介绍

一.简介 网络层协议的数据单元是 IP 数据报 ,而数据链路层的工作就是把网络层交下来的 IP 数据报 封装为 帧(frame)发送到链路上,以及把接收到的帧中的数据取出并上交给网络层. 为达到这一目的,数据链路必须具备一系列相应的功能,主要有: 将数据封装为帧(frame),帧是数据链路层的传送单位: 控制帧的传输,包括处理传输差错,调节发送速率与接收方相匹配: 在两个网络实体之间提供数据链路通路的建立.维持和释放的管理. 数据帧的结构是这样的: 二.控制帧的传输 1.差错控制 通信系统必须具

网络知识扫盲之链路层

说明:为了方便学习,综合了OSI和TCP/IP的优点,得出“五层协议”图 数据链路和帧 链路(物理链路):从一个结点到相邻结点的一段物理线路,而中间没有任何其他的交换结点. 数据链路(逻辑链路):把实现通信协议的硬件和软件加到链路上,就构成了数据链路.现在最常用的方法是网络适配器来实现这些协议和软件.一般的适配器都包括了数据链路层和物理层这两层的功能. 帧:数据链路层的协议数据单元. 数据链路层把网络层交下来的数据构成帧发送到链路上,以及把接收到的帧中的数据去除并交给网络层.在因特网中,网络层协

链路层输入报文的处理

中断服务程序接收报文后都交由__netif_receive_skb处理:根据协议将报文向上传输: packet_type 结构为网络层输入接口:其支持多种协议,每个协议族都会实现一个接收报文的的实例:此结构在链路层和网络层之间起到了桥梁的作用. struct packet_type { __be16 type; /* This is really htons(ether_type). */ struct net_device *dev; /* NULL is wildcarded here */

Bluetooth Low Energy链路层

1. 介绍 1.1 链路状态机 链路层操作可以描述为链路状态机(The Link Layer State Machine) 链路状态机有如下五种状态 - Standby State: 准备,不传输或接受数据包 - Advertising State: 广播, advertiser,发送advertising channel packets,接受来自scanner的响应 - Scanning State: 监听/扫描, scanner,监听来自advertiser的advertising chan

链路层的简介和MTU

链路层杂谈(凭个人理解瞎说的,欢迎拍砖) 链路层,说白了就是把网络层的IP数据处理一下,加点东西,放到物理层上去.    加的东西:源.目的地址和CRC校验值,有的还有类型这个字段,用来区分协议.    处理的部分:就是数据,就是把IP数据报,用指定的方法打个包: 打包的方法有以下几种:    尾部封装:把变长字段都放到最后(CRC之前),主要是为了前面的512整字节的数据整体直接复制到内核中而减小复制次数.    SLIP协议:串行线路IP,就是用END字符作为分隔符,分割数据报.为了防止干扰

协议详解2——链路层

1.1 链路层 链路层作用: 为IP模块发送和接受数据包: 为ARP模块发送ARP请求和接受ARP应答(地址解析协议): 为RARP发送RARP请求和接收RARP应答(逆地址解析协议): 链路层协议: 以太网链路层协议,串行接口链路层协议(SLIP和PPP),回环(loopback)驱动程序 1.2 以太网和IEEE802封装 1.3 SLIP:串行线路IP SLIP:是一种在串行线路上对IP数据报进行封装的简单形式: SLIP协议定义的帧格式: SLIP缺陷: 1)每一端必须知道对方的IP地址

一次刨根问底的socket收包过程(Linux)

Linux会对一个网络包(packet)的收和发做大量的处理.packet在被发送之前会被存在队列中,而在被接受之后也会存在队列中,共有三个队列:reception(接收),transmission(发送)和Backlog.它们都受到spinlock的保护,是为了保证在并发访问时的一致性.言归正传,接下来看看当一个packet到达NIC(网卡)时,linux都会做些什么工作. 先来看一个图(来自论文 Analysis of Linux UDP Sockets Concurrent Perform