百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

从网卡到应用层nginx,一个数据包经历了什么?

nanshan 2025-02-20 17:11 17 浏览 0 评论

推荐视频:

手把手带你实现一个nginx模块,更加深入了解nginx(搭建好环境)

16w行的nginx源码,如何分拆模块阅读,让你明白轮子如何造

c/c++ linux服务器开发学习地址:C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂

数据包从网卡到nginx

本文将研究一个数据包从被网卡接收到流出应用层到底经历了什么,并探究在应用层nginx的处理流程。**注:**本文只讨论物理网卡,暂不涉及虚拟网卡。

从网卡到内存

1: 数据包从外面的网络进入物理网卡。如果目的地址不是该网卡,且该网卡没有开启混杂模式,该包会被网卡丢弃。

2: 网卡将数据包通过DMA的方式写入到指定的内存地址,该地址由网卡驱动分配并初始化。注: 老的网卡可能不支持DMA,不过新的网卡一般都支持。

3: 网卡通过硬件中断(IRQ)通知CPU,告诉它有数据来了

4: CPU根据中断表,调用已经注册的中断函数,这个中断函数会调到驱动程序(NIC Driver)中相应的函数

5: 驱动先禁用网卡的中断,表示驱动程序已经知道内存中有数据了,告诉网卡下次再收到数据包直接写内存就可以了,不要再通知CPU了,这样可以提高效率,避免CPU不停的被中断。

6: 启动软中断。这步结束后,硬件中断处理函数就结束返回了。由于硬中断处理程序执行的过程中不能被中断,所以如果它执行时间过长,会导致CPU没法响应其它硬件的中断,于是内核引入软中断,这样可以将硬中断处理函数中耗时的部分移到软中断处理函数里面来慢慢处理。

如下图:

内存-网络模块-协议栈

RPS实现了数据流的hash归类,并把软中断的负载均衡分到各个cpu

7: 内核中的ksoftirqd进程专门负责软中断的处理,当它收到软中断后,就会调用相应软中断所对应的处理函数,对于上面第6步中是网卡驱动模块抛出的软中断,ksoftirqd会调用网络模块的net_rx_action函数

8: net_rx_action调用网卡驱动里的poll函数来一个一个的处理数据包

9: 在pool函数中,驱动会一个接一个的读取网卡写到内存中的数据包,内存中数据包的格式只有驱动知道

10: 驱动程序将内存中的数据包转换成内核网络模块能识别的skb格式,然后调用napi_gro_receive函数

11: napi_gro_receive会处理GRO相关的内容,也就是将可以合并的数据包进行合并,这样就只需要调用一次协议栈。然后判断是否开启了RPS,如果开启了,将会调用enqueue_to_backlog

12: 在enqueue_to_backlog函数中,会将数据包放入CPU的softnet_data结构体的input_pkt_queue中,然后返回,如果input_pkt_queue满了的话,该数据包将会被丢弃,queue的大小可以通过
net.core.netdev_max_backlog来配置

13: CPU会接着在自己的软中断上下文中处理自己input_pkt_queue里的网络数据(调用__netif_receive_skb_core)

14: 如果没开启RPS,napi_gro_receive会直接调用__netif_receive_skb_core

15: 看是不是有AF_PACKET类型的socket(也就是我们常说的原始套接字),如果有的话,拷贝一份数据给它。tcpdump抓包就是抓的这里的包。

16: 调用协议栈相应的函数,将数据包交给协议栈处理。

17: 待内存中的所有数据包被处理完成后(即poll函数执行完成),启用网卡的硬中断,这样下次网卡再收到数据的时候就会通知CPU

如下图:


下面,数据包将交给相应的协议栈函数处理,进入第三层网络层。

IP 层的入口函数在 ip_rcv 函数。该函数首先会做包括 package checksum 在内的各种检查,如果需要的话会做 IP defragment(将多个分片合并),然后 packet 调用已经注册的 Pre-routing netfilter hook ,完成后最终到达 ip_rcv_finish 函数。

ip_rcv_finish 函数会调用 ip_router_input 函数,进入路由处理环节。它首先会调用 ip_route_input 来更新路由,然后查找 route,决定该 package 将会被发到本机还是会被转发还是丢弃:

1、如果是发到本机的话,调用 ip_local_deliver 函数,可能会做 de-fragment(合并多个 IP packet),然后调用 ip_local_deliver 函数。该函数根据 package 的下一个处理层的 protocal number,调用下一层接口,包括 tcp_v4_rcv (TCP), udp_rcv (UDP),icmp_rcv (ICMP),igmp_rcv(IGMP)。对于 TCP 来说,函数 tcp_v4_rcv 函数会被调用,从而处理流程进入 TCP 栈。

2、如果需要转发 (forward),则进入转发流程。该流程需要处理 TTL,再调用 dst_input 函数。该函数会

(1)处理 Netfilter Hook

(2)执行 IP fragmentation

(3)调用 dev_queue_xmit,进入链路层处理流程。

如下图:


在上图中,

  • 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相关的钩子程序,如果通过,数据包将会向下发送到传输层

【文章福利】需要C/C++ Linux服务器架构师学习资料加群812855908(资料包括C/C++,Linux,golang技术,内核,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等)

传输层

1、传输层 TCP包的 处理入口在 tcp_v4_rcv 函数(位于 linux/net/ipv4/tcp ipv4.c 文件中),它会做 TCP header 检查等处理。

2、调用 _tcp_v4_lookup,查找该 package 的 open socket。如果找不到,该 package 会被丢弃。接下来检查 socket 和 connection 的状态。

3、如果socket 和 connection 一切正常,调用 tcp_prequeue 使 package 从内核进入 user space,放进 socket 的 receive queue。然后 socket 会被唤醒,调用 system call,并最终调用 tcp_recvmsg 函数去从 socket recieve queue 中获取 segment。

应用层

1、每当用户应用调用 read 或者 recvfrom 时,该调用会被映射为/net/socket.c 中的 sys_recv 系统调用,并被转化为 sys_recvfrom 调用,然后调用 sock_recgmsg 函数。

2、对于 INET 类型的 socket,/net/ipv4/af inet.c 中的 inet_recvmsg 方法会被调用,它会调用相关协议的数据接收方法。

3、对 TCP 来说,调用 tcp_recvmsg。该函数从 socket buffer 中拷贝数据到 user buffer。

4、对 UDP 来说,从 user space 中可以调用三个 system call recv()/recvfrom()/recvmsg() 中的任意一个来接收 UDP package,这些系统调用最终都会调用内核中的 udp_recvmsg 方法。

整个报文接收的过程如下:

分层:

1、socket 位于传输层协议之上,屏蔽了不同网络协议之间的差异

2、socket 是网络编程的入口,它提供了大量的系统调用,构成了网络程序的主体

3、在Linux系统中,socket 属于文件系统的一部分,网络通信可以被看作是对文件的读取,使得我们对网络的控制和对文件的控制一样方便

nginx处理socket套接字的流程

nginx解析用户配置,在所有端口创建socket并启动监听。

nginx解析配置文件是由各个模块分担处理的,每个模块注册并处理自己关心的配置,通过模块结构体ngx_module_t的字段ngx_command_t *commands实现。

main方法会调用ngx_init_cycle,其完成了服务器初始化的大部分工作,其中就包括启动监听(
ngx_open_listening_sockets)

假设nginx使用epoll处理所有socket事件,ngx_event_core_module模块是事件处理核心模块,初始化此模块时会执行ngx_event_process_init函数,包括将监听事件添加到epoll

  • 结构体ngx_connection_t存储socket连接相关信息;nginx预先创建若干个ngx_connection_t对象,存储在全局变量ngx_cycle->free_connections,称之为连接池;当新生成socket时,会尝试从连接池中获取空闲connection连接,如果获取失败,则会直接关闭此socket。指令worker_connections用于配置连接池最大连接数目,配置在events指令块中,由ngx_event_core_module解析
  • 结构体ngx_http_request_t存储整个HTTP请求处理流程所需的所有信息,字段非常多
  • ngx_http_request.c文件中定义了所有的HTTP头部,存储在ngx_http_headers_in数组,数组的每个元素是一个ngx_http_header_t结构体,解析后的请求头信息都存储在ngx_http_headers_in_t结构体中
  • 从ngx_http_headers_in数组中查找请求头对应ngx_http_header_t对象时,需要遍历,每个元素都需要进行字符串比较,效率低下。因此nginx将ngx_http_headers_in数组转换为哈希表,哈希表的键即为请求头的key,方法ngx_http_init_headers_in_hash实现了数组到哈希表的转换

1、在创建socket启动监听时,会添加可读事件到epoll,事件处理函数为ngx_event_accept,用于接收socket连接,分配connection连接,并调用ngx_listening_t对象的处理函数(ngx_http_init_connection)

2、socket连接成功后,nginx会等待客户端发送HTTP请求,默认会有60秒的超时时间,即60秒内没有接收到客户端请求时,断开此连接,打印错误日志。函数ngx_http_init_connection用于设置读事件处理函数,以及超时定时器。

3、函数
ngx_http_wait_request_handler为解析HTTP请求的入口函数

4、函数ngx_http_create_request创建并初始化ngx_http_request_t对象

5、解析完成请求行与请求头,nginx就开始处理HTTP请求,并没有等到解析完请求体再处理。处理请求入口为ngx_http_process_request。

下面进入nginx http请求处理的11个阶段

绝大多数HTTP模块都会将自己的handler添加到某个阶段(将handler添加到全局唯一的数组ngx_http_phases中),注意其中有4个阶段不能添加自定义handler,nginx处理HTTP请求时会挨个调用每个阶段的handler

typedef enum {
    NGX_HTTP_POST_READ_PHASE = 0, //第一个阶段,目前只有realip模块会注册handler,但是该模块默认不会运行(nginx作为代理服务器时有用,后端以此获取客户端原始ip)
  
    NGX_HTTP_SERVER_REWRITE_PHASE,  //server块中配置了rewrite指令,重写url
  
    NGX_HTTP_FIND_CONFIG_PHASE,   //查找匹配的location配置;不能自定义handler;
    NGX_HTTP_REWRITE_PHASE,       //location块中配置了rewrite指令,重写url
    NGX_HTTP_POST_REWRITE_PHASE,  //检查是否发生了url重写,如果有,重新回到FIND_CONFIG阶段;不能自定义handler;
  
    NGX_HTTP_PREACCESS_PHASE,     //访问控制,比如限流模块会注册handler到此阶段
  
    NGX_HTTP_ACCESS_PHASE,        //访问权限控制,比如基于ip黑白名单的权限控制,基于用户名密码的权限控制等
    NGX_HTTP_POST_ACCESS_PHASE,   //根据访问权限控制阶段做相应处理;不能自定义handler;
  
    NGX_HTTP_TRY_FILES_PHASE,     //只有配置了try_files指令,才会有此阶段;不能自定义handler;
    NGX_HTTP_CONTENT_PHASE,       //内容产生阶段,返回响应给客户端
  
    NGX_HTTP_LOG_PHASE            //日志记录
} ngx_http_phases;

nginx 在ngx_http_block函数中初始化11个阶段的ngx_http_phases数组,把http模块注册到相应的阶段去。注意多个模块可能注册到同一个阶段,因此phases是一个二维数组

  • nginx使用结构体ngx_module_s表示一个模块,其中字段ctx,是一个指向模块上下文结构体的指针(上下文结构体的字段都是一些函数指针)
  • postconfiguration,负责注册本模块的handler到某个处理阶段

使用GDB调试,断点到ngx_http_block方法执行所有HTTP模块注册handler之后,打印phases数组:

p cmcf->phases[*].handlers
p *(ngx_http_handler_pt*)cmcf->phases[*].handlers.elts

11个阶段(7个阶段可注册)以及模块注册的handler如下图:

处理请求的过程

1、HTTP请求的处理入口函数是ngx_http_process_request,其主要调用ngx_http_core_run_phases实现11个阶段的执行流程

2、ngx_http_core_run_phases遍历预先设置好的cmcf->phase_engine.handlers数组,调用其checker函数

3、checker内部就是调用handler,并设置下一步要执行handler的索引

所以综上看来,nginx处理请求的过程可以归纳为:

  1. 初始化 HTTP Request(读取来自客户端的数据,生成 HTTP Request 对象,该对象含有该请求所有的信息)。
  2. 处理请求头。
  3. 处理请求体。
  4. 如果有的话,调用与此请求(URL 或者 Location)关联的 handler。
  5. 依次调用各 phase handler 进行处理。

相关推荐

三种自建KMS激活系统自动激活windows方法

第一种:在windows服务器上搭建主要针对vol版本(win7、win10、win20xx、win2012等等)平台:我自己搭建的windows虚拟机,windows2016的操作系统软件:...

重装系统被收98元?避开Windows付费陷阱的实用指南

重装系统被收98元?避开Windows付费陷阱的实用指南有网友反映,在重装Windows系统后,屏幕突然弹出“激活系统需支付98元服务费”的提示,疑惑自己是不是遭遇了付费陷阱。事实上,微软官方的Wi...

Windows Server2012远程桌面服务配置和授权激活

安装:注意:安装完毕之后需手动重启一下计算机配置终端服务管理工具---远程桌面服务---RD授权诊断程序,查看当前服务器有没有授权授权:运行—>gpedit.msc->计算机配置---管理...

新书速览|Windows Server 2022 系统与网站配置实战

讲述桌面体验、ServerCore/NanoServer,容器与云系统的配置1本书内容《WindowsServer2022系统与网站配置实战》秉持作者一贯理论兼具实践的写作风格,以新版的Wi...

Windows激活全攻略:KMS神钥与专业工具的完美结合!

对于许多Windows用户来说,系统的激活是一个必经的过程。虽然Windows操作系统在未经激活的状态下也可以使用一段时间,但长期来看,未激活的系统会限制某些功能并频繁提示用户激活。以下是两种流行的激...

微软Win9全新激活技术曝光(微软系统激活有什么用)

2014-07-0905:46:00作者:徐日俄罗斯Wzor日前披露了更多关于Windows9的最新消息,据悉,Windows9将会在今年秋季亮相,其宣传口号是“想要开始按钮和开始菜单?如你所...

快速激活Windows 10/11:CMD命令详细教程

#记录我的2024#激活Windows操作系统是确保系统功能和安全更新正常运行的重要步骤。本文将为您分享如何使用命令提示符(CMD)在Windows10和Windows11上进行激活的详细步骤。...

Wndows 2019 RDS应用发布部署(rds的安装和应用程序的发布)

安装前的准备1、需要提供服务器作为应用中心,应用中心的推荐配置如下表所示。规格建议1-10人11-20人21-50人51-100人100+人CPU4核8核16核内存8GB16GB32GB64GB系统盘...

解决 Windows 系统激活难题(如何解决windows激活问题)

今天,一位朋友给我说,他手头有三台电脑,均同时弹出系统未激活的提示。他对此毫无头绪,便急忙将电脑上出现的激活提示信息一股脑发给了我。我看到其中一台显示的是“Windows10企业版LTSC尚...

自建KMS激活服务器(自建kms激活服务器的风险)

自建KMS激活服务器Win10和office安装后,都需要激活才可以使用,一般可以输入购买的MAK激活码进行在线激活,也可以通过KMS激活,网上也有很多激活工具,但这些工具一般都含有病毒或木马程序,容...

30秒免费激活windows和office亲测有效!

“第三方工具有病毒?”“KMS服务器激活总失效?”今天给大家分享一个开源激活工具——MicrosoftActivationScripts(MAS),无需密钥、不装软件,30秒永久激活Window...

「操作系统」Windows 10 LTSC 2019 企业版C大集成更新版

Windows10LTSC企业版CHIANNET集成更新优化整合多镜像版,CHIANNET,是USBOS超级PE维护盘工具箱作者,长久以来一直默默的更新着,USBOSPE软件,电脑城装机及...

一文看懂Windows激活:自查方法+授权类型科普(Win7/Win10通用)

一、如何判断Windows是否永久激活?无论是Win7还是Win10,均可通过以下方法快速验证:命令提示符法(通用):按下Win+R,输入slmgr.vbs/xpr并按回车键运行即可查看是否...

部分Windows Server 2019/2022用户反馈无法运行微软Teams应用

IT之家7月2日消息,科技媒体borncity今天(7月2日)发布博文,报道称在多个WindowsServer版本上,MicrosoftTeams应用近期出现了运行故障。用...

这种Windows激活方式已有20年...(windows现在激活)

2006年微软正式发布WindowsVista,随之而来引入了一项新的激活机制「OEM激活」,这项机制在Vista和Win7上最为流行。其实WindowsServer自2008开始至2025版本一...

取消回复欢迎 发表评论: