本文将介绍在Linux系统中,数据包是如何一步一步从网卡传到进程手中的。

本文只讨论以太网的物理网卡,不涉及虚拟设备,并且以一个UDP包的接收过程作为示例.

本示例里列出的函数调用关系来自于kernel 3.13.0,如果你的内核不是这个版本,函数名称和相关路径可能不一样,但背后的原理应该是一样的(或者有细微差别)

网卡到内存

网卡需要有驱动才能工作,驱动是加载到内核中的模块,负责衔接网卡和内核的网络模块,驱动在加载的时候将自己注册进网络模块,当相应的网卡收到数据包时,网络模块会调用相应的驱动程序处理数据。

下图展示了数据包(packet)如何进入内存,并被内核的网络模块开始处理:

                   +-----+|     |                            Memroy
+--------+   1     |     |  2  DMA     +--------+--------+--------+--------+
| Packet |-------->| NIC |------------>| Packet | Packet | Packet | ...... |
+--------+         |     |             +--------+--------+--------+--------+|     |<--------++-----+         ||            +---------------+|                            |3 | Raise IRQ                  | Disable IRQ|                          5 ||                            |↓                            |+-----+                   +------------+|     |  Run IRQ handler  |            || CPU |------------------>| NIC Driver ||     |       4           |            |+-----+                   +------------+|6  | Raise soft IRQ|↓
  1. 数据包从外面的网络进入物理网卡。如果目的地址不是该网卡,且该网卡没有开启混杂模式,该包会被网卡丢弃。
  2. 网卡将数据包通过DMA的方式写入到指定的内存地址,该地址由网卡驱动分配并初始化。注: 老的网卡可能不支持DMA,不过新的网卡一般都支持。
  3. 网卡通过硬件中断(IRQ)通知CPU,告诉它有数据来了
  4. CPU根据中断表,调用已经注册的中断函数,这个中断函数会调到驱动程序(NIC Driver)中相应的函数
  5. 驱动先禁用网卡的中断,表示驱动程序已经知道内存中有数据了,告诉网卡下次再收到数据包直接写内存就可以了,不要再通知CPU了,这样可以提高效率,避免CPU不停的被中断。
  6. 启动软中断。这步结束后,硬件中断处理函数就结束返回了。由于硬中断处理程序执行的过程中不能被中断,所以如果它执行时间过长,会导致CPU没法响应其它硬件的中断,于是内核引入软中断,这样可以将硬中断处理函数中耗时的部分移到软中断处理函数里面来慢慢处理。

内核的网络模块

软中断会触发内核网络模块中的软中断处理函数,后续流程如下

                                            +-----+17      |     |+----------->| NIC ||            |     ||Enable IRQ  +-----+||+------------+                                      Memroy|            |        Read           +--------+--------+--------+--------++--------------->| NIC Driver |<--------------------- | Packet | Packet | Packet | ...... ||                |            |          9            +--------+--------+--------+--------+|                +------------+|                      |    |        skbPoll | 8      Raise softIRQ | 6  +-----------------+|                      |             10       ||                      ↓                      ↓
+---------------+  Call  +-----------+        +------------------+        +--------------------+  12  +---------------------+
| net_rx_action |<-------| ksoftirqd |        | napi_gro_receive |------->| enqueue_to_backlog |----->| CPU input_pkt_queue |
+---------------+   7    +-----------+        +------------------+   11   +--------------------+      +---------------------+|                                                      | 1314 |        + - - - - - - - - - - - - - - - - - - - - - - +↓        ↓+--------------------------+    15      +------------------------+| __netif_receive_skb_core |----------->| packet taps(AF_PACKET) |+--------------------------+            +------------------------+|| 16↓+-----------------+| protocol layers |+-----------------+
  1. 内核中的ksoftirqd进程专门负责软中断的处理,当它收到软中断后,就会调用相应软中断所对应的处理函数,对于上面第6步中是网卡驱动模块抛出的软中断,ksoftirqd会调用网络模块的net_rx_action函数
  2. net_rx_action调用网卡驱动里的poll函数来一个一个的处理数据包
  3. 在pool函数中,驱动会一个接一个的读取网卡写到内存中的数据包,内存中数据包的格式只有驱动知道
  4. 驱动程序将内存中的数据包转换成内核网络模块能识别的skb格式,然后调用napi_gro_receive函数
  5. napi_gro_receive会处理GRO相关的内容,也就是将可以合并的数据包进行合并,这样就只需要调用一次协议栈。然后判断是否开启了RPS,如果开启了,将会调用enqueue_to_backlog
  6. 在enqueue_to_backlog函数中,会将数据包放入CPU的softnet_data结构体的input_pkt_queue中,然后返回,如果input_pkt_queue满了的话,该数据包将会被丢弃,queue的大小可以通过net.core.netdev_max_backlog来配置
  7. CPU会接着在自己的软中断上下文中处理自己input_pkt_queue里的网络数据(调用__netif_receive_skb_core)
  8. 如果没开启RPS,napi_gro_receive会直接调用__netif_receive_skb_core
  9. 看是不是有AF_PACKET类型的socket(也就是我们常说的原始套接字),如果有的话,拷贝一份数据给它。tcpdump抓包就是抓的这里的包。
  10. 调用协议栈相应的函数,将数据包交给协议栈处理。
  11. 待内存中的所有数据包被处理完成后(即poll函数执行完成),启用网卡的硬中断,这样下次网卡再收到数据的时候就会通知CPU

enqueue_to_backlog函数也会被netif_rx函数调用,而netif_rx正是lo设备发送数据包时调用的函数

协议栈

IP层

由于是UDP包,所以第一步会进入IP层,然后一级一级的函数往下调:

          ||↓         promiscuous mode &&+--------+    PACKET_OTHERHOST (set by driver)   +-----------------+| ip_rcv |-------------------------------------->| drop this packet|+--------+                                       +-----------------+||↓
+---------------------+
| NF_INET_PRE_ROUTING |
+---------------------+||↓+---------+|         | enabled ip forword  +------------+        +----------------+| routing |-------------------->| ip_forward |------->| NF_INET_FORWARD ||         |                     +------------+        +----------------++---------+                                                   ||                                                         || destination IP is local                                 ↓↓                                                 +---------------++------------------+                                       | dst_output_sk || ip_local_deliver |                                       +---------------++------------------+||↓+------------------+| NF_INET_LOCAL_IN |+------------------+||↓+-----------+| UDP layer |+-----------+
  • ip_rcv:ip_rcv函数是IP模块的入口函数,在该函数里面,第一件事就是将垃圾数据包(目的mac地址不是当前网卡,但由于网卡设置了混杂模式而被接收进来)直接丢掉,然后调用注册在NF_INET_PRE_ROUTING上的函数
  • NF_INET_PRE_ROUTING:netfilter放在协议栈中的钩子,可以通过iptables来注入一些数据包处理函数,用来修改或者丢弃数据包,如果数据包没被丢弃,将继续往下走
  • routing: 进行路由,如果是目的IP不是本地IP,且没有开启ip forward功能,那么数据包将被丢弃,如果开启了ip forward功能,那将进入ip_forward函数
  • ip_forward:ip_forward会先调用netfilter注册的NF_INET_FORWARD相关函数,如果数据包没有被丢弃,那么将继续往后调用dst_output_sk函数
  • dst_output_sk: 该函数会调用IP层的相应函数将该数据包发送出去,同下一篇要介绍的数据包发送流程的后半部分一样
  • ip_local_deliver:如果上面routing的时候发现目的IP是本地IP,那么将会调用该函数,在该函数中,会先调用NF_INET_LOCAL_IN相关的钩子程序,如果通过,数据包将会向下发送到UDP层

UDP层

          ||↓+---------+            +-----------------------+| udp_rcv |----------->| __udp4_lib_lookup_skb |+---------+            +-----------------------+||↓+--------------------+      +-----------+| sock_queue_rcv_skb |----->| sk_filter |+--------------------+      +-----------+||↓+------------------+| __skb_queue_tail |+------------------+||↓+---------------+| sk_data_ready |+---------------+
  • udp_rcv:udp_rcv函数是UDP模块的入口函数,它里面会调用其它的函数,主要是做一些必要的检查,其中一个重要的调用是__udp4_lib_lookup_skb,该函数会根据目的IP和端口找对应的socket,如果没有找到相应的socket,那么该数据包将会被丢弃,否则继续
  • sock_queue_rcv_skb: 主要干了两件事,一是检查这个socket的receive buffer是不是满了,如果满了的话,丢弃该数据包,然后就是调用sk_filter看这个包是否是满足条件的包,如果当前socket上设置了filter,且该包不满足条件的话,这个数据包也将被丢弃(在Linux里面,每个socket上都可以像tcpdump里面一样定义filter,不满足条件的数据包将会被丢弃)
  • __skb_queue_tail: 将数据包放入socket接收队列的末尾
  • sk_data_ready: 通知socket数据包已经准备好

调用完sk_data_ready之后,一个数据包处理完成,等待应用层程序来读取,上面所有函数的执行过程都在软中断的上下文中。

socket

应用层一般有两种方式接收数据,一种是recvfrom函数阻塞在那里等着数据来,这种情况下当socket收到通知后,recvfrom就会被唤醒,然后读取接收队列的数据;另一种是通过epoll或者select监听相应的socket,当收到通知后,再调用recvfrom函数去读取接收队列的数据。两种情况都能正常的接收到相应的数据包

原文链接:https://segmentfault.com/a/1190000008836467

linux内核网络协议栈--数据包的接收过程(二十)相关推荐

  1. linux内核网络协议栈--数据包的接收过程(二十二)

    与其说这篇文章分析了网卡驱动中中数据包的接收,还不如说基于Kernel:2.6.12,以e100为例,对网卡驱动编写的一个说明.当然,对数据包的接收说的很清楚. 一.从网卡说起 这并非是一个网卡驱动分 ...

  2. linux内核网络协议栈--数据包的发送过程(二十一)

    继上一篇介绍了数据包的接收过程后,本文将介绍在Linux系统中,数据包是如何一步一步从应用程序到网卡并最终发送出去的. socket层 +-------------+| Application |+- ...

  3. linux内核网络协议栈--数据包的接收流程(二十三)

    网卡在接受数据包时会产生中断,即当 有一个以太网帧到来时,网卡向内核产生一次中断: CPU收到中断信号后,执行中断处理程序,中断处理程序会设置 缓冲区地址.DMA 地址等信息: 网卡通过DMA 方式将 ...

  4. linux内核网络协议栈--数据包的网卡缓冲区(二十四)

    程序员可能关心的基本网卡知识 网卡相关介绍:http://www.linuxidc.com/Linux/2012-12/77132.htm 一.什么是网卡? 它是主机的网络设备,本身是LAN(局域网) ...

  5. linux内核网络协议栈--数据包的skb桥转发蓝图(二十六)

    话不多说,先看一张桥转发时函数调用的一个基本蓝图. 这张图中,简单的展示了,数据的接收和发送,其中还包括netfilet的钩子点所处的位置. 需要说明的是: 1.我们先暂时忽略数据包从一开始是怎么从驱 ...

  6. linux内核网络协议栈--数据包的网卡驱动收发包过程(二十五)

    网卡 网卡工作在物理层和数据链路层,主要由PHY/MAC芯片.Tx/Rx FIFO.DMA等组成,其中网线通过变压器接PHY芯片.PHY芯片通过MII接MAC芯片.MAC芯片接PCI总线 PHY芯片主 ...

  7. linux内核网络协议栈--数据包的数据收发简略流程图(二十八)

    基于内核3.17.1版本 原文链接:https://blog.csdn.net/subfate/article/details/53107435

  8. linux内核网络协议栈--数据包的网卡转发流程(二十七)

    原文链接:https://blog.csdn.net/jackywgw/article/details/78321226

  9. Linux内核网络udp数据包发送(二)——UDP协议层分析

    1. 前言 本文分享了Linux内核网络数据包发送在UDP协议层的处理,主要分析了udp_sendmsg和udp_send_skb函数,并分享了UDP层的数据统计和监控以及socket发送队列大小的调 ...

最新文章

  1. virtualbox 在物理机是无线网卡的时候做桥接配置 - juandx - 博客园
  2. 解决Genymotion运行Android 5.0一直卡在开机界面
  3. Mercurial 版本控制服务器(Web Server)的搭建
  4. Action和Func的区别
  5. 纪中C组模拟赛总结(2019.9.7)
  6. 【CodeForces - 349B】Color the Fence (贪心,填数)
  7. 吹毛求疵C#(1)明确赋值检查缺陷
  8. [流体输配管网]古罗马渡槽从水源到城市的落差估计
  9. 天坑-安装salt-api安装的正确姿势
  10. 计算机文化理论基础考试单机版,计算机文化基础授课计划表
  11. python画图颜色代码rgb_python – matplotlib 3D散点图,其标记颜色对应于RGB值
  12. 【渝粤教育】国家开放大学2018年秋季 1070t组织行为学 参考试题
  13. 修改文字处理布局及文字绘制,一个字的感受:太糙
  14. 微信小程序开发(一):小程序代码构成
  15. 汇编语言 王爽 第四版 第一章 检测点1.1
  16. cmd_vel速度话题的使用
  17. Python-3:循环-练习
  18. 智能交通系统计算机技术应用,计算机技术在智能交通系统中的应用.docx
  19. 2022爱分析・消费品零售数字化厂商全景报告 | 爱分析报告
  20. Python-matplotlib用法

热门文章

  1. python基本语法语句-python学习笔记:基本语法
  2. python学精通要多久-精通python需要多久
  3. python填写excel-python读写Excel
  4. 大学计算机基础python-大学计算机基础最新章节_曾一著_掌阅小说网
  5. python好找工作吗2017-2017学什么编程语言好找工作?
  6. python从1到n出现了多少个1-Python解决 从1到n整数中1出现的次数
  7. python使用符号 表示单行注释-Python编程规范之注释
  8. numpy——hsplit()、vsplit()函数的详细使用
  9. 无意间看到Pure-Mvc记录下
  10. Arcgis Server 默认服务端口号修改方法