和Nginx相关的TCP/IP中反向代理系统解析与调优

news2024/11/22 11:57:50

文章目录

    • 前言
    • 反向代理系统分析
    • 消耗资源分析
      • CPU
      • Memory
      • 网卡
    • 压测与调优实战
      • 压测数据对比
      • 什么是TIME_WAIT
      • keepalive开启前后数据对比
    • 小结
    • 关于后续
      • 端口不足,限制连接扩展
      • 协议层面无法充分做到连接的复用
    • TCP优化分享相关文章:

前言

本文介绍aeproxy这个应用以及他的职责,它是一个域名代理的应用,负责将对应域名的url经过分析、代理转发给相应的业务系统去处理;
在这里插入图片描述
本文以此为背景,介绍一下具体的调试方式。

反向代理系统分析

关键消耗定位
在实测开始之前,先针对这个应用的消耗做一下简单的分析;分析的思路与上文一样,依然是从RT入手;上文详细分析过代理转发场景下RT的具体组成:
_

我们这里再针对RT进行一下分析,可以简单分为三部分:

前端代理转发请求时间消耗
主请求处理请求和响应时间
主子请求处理连接时间(建立、关闭)
后端服务处理请求时间消耗
网络传输时间消耗
用户端与反向代理应用之间的外部网络消耗
反向代理应用与后端服务直接的内部网络消耗
这三部分消耗其中第二部分完全取决于后端服务集群的处理能力,而第三部分取决于传输过程当中的时延以及网络、路由器、交换机的处理能力等等外部因素,并不在反向代理系统的控制范围之内,因此不做具体分析;

第一部分前端代理转发请求时间消耗当中,主请求处理请求和响应时间具体在aeproxy这个应用上,只有TMD和sysguard,我们暂时忽略他们的影响;因此实际反向代理应用的消耗关键在于主子请求处理连接时间;

消耗资源分析

下面我们就从各个计算机资源来看,主子请求处理连接对他们的消耗是怎么样的:

CPU

由于Nginx本身使用IO多路复用以及事件驱动的并发模型,进程切换和调度成本都是比较小的;但是连接的建立socket(),监听listen(),发起connect(),接收accpet(),发送数据send(),接收数据recv(),关闭close()等等网络套接字操作所造成的消耗却是不可忽略的;前面分析过,反向代理应用的RT消耗关键在于主子请求处理连接时间,因此网络连接操作越频繁,CPU消耗就越大;

Memory

内存消耗主要来源是在于网络连接层面的消耗,Nginx自身的进程数消耗内存较少,也并不运行缓存等消耗内存的模块,但是每个网络连接自身的栈空间和传输的buffer空间的消耗还是有的,虽然每个连接消耗较小,但是反向代理应用连接数较高,因此这部分内存消耗会随着连接数的不断攀升,而增长的;因此内存消耗是与连接数有必然的联系;

网卡

网卡是一个系统网络的入口和出口,针对网页内容的反向代理应用,网页大小一般在几十KB左右,因此一个千兆网卡能支撑的QPS输出大概几w左右,网卡是物理层面的瓶颈,是系统优化往往是不能解决的问题;

网络连接是关键,keepalive是调优的重点
前面提到将消耗资源简单分析之后,我们看到在网络连接是反向代理系统的关键;
能否降低网络连接数,能否减少网络连接操作的次数,用较少的连接数和网络连接操作,输出较高的QPS,是我们最关心的问题;

而就目前而言,keepalive是解决这些问题当中比较有效的手段;

keepalive可以复用网络连接,避免反复创建、关闭等系统操作带来的消耗; (这里注意keepalive实际上并不会降低网络连接数,这个是常见的误区,后面会讲到)
keepalive从TCP层面降低了三次握手和四次挥手的网络时延;
keepalive也可以降低TCP慢启动等无谓的拥塞控制;
针对反向代理应用,主请求是客户端与代理应用之间的连接,而子请求是代理应用与后端业务系统之间的连接;所以keepalive涉及到两部分,主请求目前一般都是开启的,而子请求的keepalive(即upstream keepalive)往往都是忽略的;

压测与调优实战

我们首先以1200并发压测单台aeproxy,对比是否开启子请求keepalive的效果;

压测数据对比

未开启keepalive压测数据:

no_ka_ot7_13

开启keepalive压测数据:

ka_ot7_13_port_error

开启keepalive之后,整体的QPS有所下降,并且cpu使用率和load都飙升:

ka_8_ot7_port_error_cpu

ka_8_ot7_port_error_load

按照之前的分析开启keepalive是应该提升性能和容量的,但是貌似适得其反。。。

端口不足怎么办?
继续排查发现,在Nginx错误日志当中有这样的错误:

2015/02/26 01:38:28 [crit] 27076#0: *150847 connect() to 172.20.21.92:80 failed (99: Cannot assign requested address) while connecting to upstream, client: 172.20.196.37, server: www.aliexpress.com, request: "GET /api/getFreightCountrySearchList.vhtml HTTP/1.1", upstream: "http://172.20.21.92:80/api/getFreightCountrySearchList.vhtml", host: "www.aliexpress.com"

“Cannot assign requested address”,这个是端口不足的错误信息;针对反向代理应用,主请求是被动接收连接,在代理应用上的连接使用的端口都是复用80端口,而子请求是主动发起连接,使用的是代理应用用于TCP传输的端口;

在linux系统当中,端口是16进制的一个数值,因此他的最大不能超过65536;而1024以下端口都是为系统服务预留的端口(如:HTTP、SSH),子请求只能只用临时端口(临时端口一般是提供给客户服务器通讯的端口。这种端口是临时的,并且仅在应用程序使用协议建立通讯联系的周期中有效);

查看系统配置“net.ipv4.ip_local_port_range”,这个配置是配可以使用的临时端口的范围;系统默认配置的是:

net.ipv4.ip_local_port_range = 32768 61000

这意味着子请求只可以使用32768到61000之间的端口(大概2.8w个端口),因此建议变更配置为:

net.ipv4.ip_local_port_range = 1024 65535

改动之后,再进行压测,端口不足的错误不见了,cpu使用率和load也恢复正常;

TIME_WAIT连接过多怎么办?
端口不足的问题解决了,但是TCP连接数还是比不开keepalive增长了将近6倍,看一下TCP相关监控数据:
ka_8_ot7_port_error_tcp

TCP连接数超过18.3w,其中TIME-WAIT连接就占18w;

什么是TIME_WAIT

TCP在关闭连接是需要经过四次握手,如下图:
在这里插入图片描述

因为TCP连接是双向的,所以在关闭连接的时候,两个方向各自都需要关闭。先发FIN包的一方执行的是主动关闭;后发FIN包的一方执行的是被动关闭。主动关闭的一方会进入TIME_WAIT状态,并且在此状态停留两倍的MSL时长。

MSL指的是报文段的最大生存时间,如果报文段在网络活动了MSL时间,还没有被接收,那么会被丢弃;协议当中建议MSL设置为2分钟;不过实际上不同的操作系统可能有不同的设置,以Linux为例,通常是30秒,两倍的MSL就是1分钟,并且这个数值是硬编码在内核中的,除非编译内核,否则无法修改;

缩短TIME_WAIT等待时间
TIME_WAIT等待时间长达60秒,霸占了系统有限的端口和连接,尝试缩短这个时间,看压测效果;

集团重新编译过内核,增加了net.ipv4.tcp_tw_timeout选项,通过改动这个选项的值,可以调整TIME_WAIT的等待时间;

默认值是(单位是秒):

net.ipv4.tcp_tw_timeout = 60

我们将这个值调小:

net.ipv4.tcp_tw_timeout = 3

调整之后进行压测,发现TCP连接数下降至8k+,大幅度降低连接和端口的使用;

为什么需要TIME_WAIT
缩短了等待时间,效果很明显,但是这个可以这么做吗?我们要追本溯源,看为什么需要TIME_WAIT,TIME_WAIT等待时间设置长短所产生的影响;

TIME_WAIT存在的原因是什么?
简单的讲,有以下两点原因:

1)可靠的终止连接

当主动关闭的一方收到被动关闭的一方发出的FIN包后,回应ACK包,同时进入TIME_WAIT状态,但是因为网络传输时延,主动关闭的一方发送的这个ACK包有可能延迟,从而触发被动连接一方重传FIN包。这之间一去一回,最长会达到两倍的MSL时长。
如果主动关闭的一方跳过TIME_WAIT直接进入CLOSED,或者在TIME_WAIT停留的时长不足两倍的MSL,那么当被动关闭一方早先发出的延迟包到达时,TCP连接可能已经关闭了,只能发送RST(复位报文段),而被动关闭一方将视其为错误;

2)保证让迟来的TCP报文段有足够的时间被识别并丢弃

在Linux系统当中,一个TCP端口是不能被同时打开两次及其以上的,当一个TCP连接处于TIME_WAIT状态时,我们将无法立即使用该连接占用着的端口来建立一个新连接;反过来说,如果不存在TIME_WAIT状态,则应用程序有可能立即建立一个和刚关闭的连接相同端口的新连接,这个新连接就有可能收到属于原来的连接的、携带应用程序数据的TCP报文段(即迟到的报文段),这是明显会造成问题的;

在针对aeproxy的场景,变更TIME_WAIT等待时间会影响主子请求,分别看一下各自的情况:

针对子请求的连接,如果出现上面的状况,有可能产生程序处理错误或数据混乱的,但是由于子请求是在机房内部之间进行网络传输,网络时延都比较可控而且相对较低,因此针对子请求,TIME_WAIT等待时间是可以适当缩短的;

同样针对主请求的连接,也会产生程序处理错误或数据混乱的,但是主请求是客户端与服务器之间的连接,涉及外部网络的传输,相对子请求的网络时延更加严重,而且AE作为跨境网站,这种传输时延十分不可控,因此造成处理错误或数据混乱的概率会加大很多;

因此,可以看出能否缩短TIME_WAIT等待时间,很大程度上依赖网络传输的时延;而针对类似AE跨境网站,网络延迟较高,缩短TIME_WAIT等待时间有一定风险;

开启upstream keepalive为什么TIME_WAIT连接数更多?
缩短TIME_WAIT等待时间,是可以减少TIME_WAIT次数的,但是为什么开启子请求的keepalive会导致TIME_WAIT连接增多呢?

打开子请求upstream的keepalive,子请求出现TIME_WAIT的可能就是代理应用主动关闭了连接,导致TIME_WAIT状态出现。那么子请求关闭连接这块Nginx是怎么处理呢?

Nginx如何管理upstream keepalive连接
Nginx是使用free队列和cache队列来进行upstream长连接的管理的。

free队列相当于upstream keepalive的配额池,配置初始化是会按照upstream keepalive到配置数量,创建相应长度的free队列;free队列当中是存储连接地址信息的item元素;应用初始化时,free队列当中存储的都是空的item元素;而cache队列存储的是已经缓存了的连接地址信息,cache队列的长度和free队列相同。

当一个请求到来是,先从cache队列查找是否有已经缓存的未使用的连接地址信息可以使用,如果有,则直接使用这个连接,不再创建新的连接;

如果没有,则新建一个连接(ngx_socket),用这个新建连接处理当前请求;当请求处理完毕时,从free队列当中获取一个空的item元素,将连接信息设置到此元素当中,将这个item元素放到cache队列当中;流程如下图:

nginxupstream_

但是当free队列当中没有可以使用的配额时,Nginx处理方式会发生改变,流程如下图:

nginxupstream_

当free队列为空,证明没有可以使用的配额(即upstream keepalive连接达到了配置的上限),这时Nginx选择从cache队列当中找到最近最少使用的连接并关闭他,再将新连接放置到cache队列当中;Nginx源码如下:

if (ngx_queue_empty(&kp->conf->free)) { // free队列判空

q = ngx_queue_last(&kp->conf->cache); // 从cache队列取最近最少使用的item元素

ngx_queue_remove(q);

item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);

ngx_http_upstream_keepalive_close(item->connection); // 关闭最近最少使用的连接

}

item->connection = c;

ngx_queue_insert_head(&kp->conf->cache, q); // 将item元素放置到cache队列

结合aeproxy具体情况分析upstream keepalive情况
当Nginx配置的upstream连接数不够用时,Nginx会出现主动关闭连接的情况;我们再结合tcpdump信息排查一下主动关闭连接时的发包情况:
Snip20150313_8

这个case是在压测tcpdump信息当中筛选的主动关闭连接的case,可以看到在编号为24783的记录,是172.20.200.57(aeproxy)主动发出FIN包(结束报文段),主动关闭连接;这个包距离上一个编号为23696的包,有400ms的时间差,有1000+包的间隔,与前面分析关闭最近最少使用的连接场景很相近;

查看aeproxy upstream配置:

 upstream wholesale_proxy {
    server us-wholesale.en.vip.alidc.net;
    keepalive 8;
}
upstream aedetail_proxy {
    server us-aedetail.en.vip.alidc.net;
    keepalive 8;
}
upstream aedetailsubsite_proxy {
    server us-aedetailsubsite.en.vip.alidc.net;
    keepalive 8;
}
upstream store_proxy {
    server store.vip.scl.en.alidc.net;
    keepalive 8;
}
upstream usi18n_proxy {
    server us-wsi18n.en.vip.alidc.net;
    keepalive 8;
}

keepalive设置为8,将配置调大,压测试一下

合理配置upstream keepalive压测效果对比
为了将问题简化,单独针对一个upstream进行压测,upstream keepalive配置为512和8,压测数据对比:
Snip20150316_9

TCP连接数下降2/3,确定TCP连接数上涨就是这个问题;

关于upstream keepalive如何配置,配置多少合适?如果需要比较精确度量,可以从较低一个数值,逐步往大调整,并观察TCP连接数量;当keepalive配置扩大,而连接数维持基本稳定时,此时的配置最为合适;
(推荐大家使用ss查看网络连接,ss比netstat快很多,大并发下netstat就挂了)

keepalive开启前后数据对比

经过前面问题的排查和优化,最后再来看一下开启upstream keepalive和不开启upstream keepalive的数据对比;
(三种数据横向对比,第一个是upstream keepalive 512,第二个是upstream keepalive 8,第三个是不开启upstream keepalive)

CPU使用率:
Snip20150316_11

load:
Snip20150316_12

TCP连接数:
Snip20150316_13

可以清晰看到在CPU、load、TCP连接数三个方面,合理开启upstream keepalive都有一定幅度的提升,而且这个数据会随着压测并发加大,效果会更加明显;
(考虑到对后端集群服务的影响,并未将压力压到极限)

这里再强调一个问题,就是前面提到keepalive可以减少连接数的问题;这个地方容易形成误区;keepalive实际上只是将TCP连接进行复用,并不能减少连接数;在HTTP协议下,一个TCP连接同一时间只能服务于一个请求,因此在请求量相同的场景下,实际使用的连接数并不会减少;其实只是TCP协议当中,TIME_WAIT有一段等待时间,造成大量连接处于等待关闭状态,因此才造成可以减少连接数的假象;

实际压测据我观察,ESTABLISHED连接,反倒是由于连接充分复用,比不开启keepalive增长了一些;
Snip20150316_14

小结

前面分析很多东西,我们汇总一下几个重点结论:

  1. 针对反向代理应用,大量子请求需要申请临时端口,因此将临时端口扩大是很有必要的;相关配置如下:

net.ipv4.ip_local_port_range = 1024 65535

  1. 针对TIME_WAIT连接过多的情况,类似AE这种跨境网站,网络时延较大的场景,降低TIME_WAIT等待时间,有可能导致连接错误终止,甚至是迟到TCP报文不能被识别和抛弃,因此缩短TIME_WAIT等待时间要谨慎,有可能导致上面的相关问题出现;

  2. 针对upstream keepalive的配置,需要结合测试,进行合理配置,否则会因为配额较小,导致频繁主动关闭连接,连接数猛增,keepalive效果变差;

  3. keepalive开启之后,复用连接,降低相关网络操作,因此在cpu和连接数方面都有一定幅度的提升;

  4. keepalive并不能减少连接数,只是复用连接而已;

关于后续

前面消耗资源分析和整个调优的过程当中发现,网卡等物理硬件因素除外,反向代理应用的最关键的问题是端口和连接;

端口不足,限制连接扩展

前面提到过Linux操作系统端口最多只有65535个,而反向代理应用子请求的连接都需要使用代理机器的临时端口,而当网卡升级之后,更多的请求发送过来时,随着连接数的不断增长,6w多个肯定是不够用的;

一个连接是通过四个元素进行确认的,源IP地址、源端口、目的IP地址、目的端口,前面提到过反向代理应用源端口是受限的,但是我们可以通过扩展源IP地址方式解决这个问题;Linux当中可以提供一个网卡绑定多个IP的,这样源IP地址也是可以扩展的,这样连接的数量也就可以扩展了,从而规避了端口受限的问题;

协议层面无法充分做到连接的复用

TCP/IP协议是基于连接的、可靠的、全双工网络协议,而目前基于TCP/IP协议之上的HTTP1.1协议,针对一个连接,同一时间只能完成一次请求、应答交互,没有发挥出全双工通信的优势,无法复用连接,从而导致大量连接创建和维护;
目前新的HTTP2.0协议已经正式定稿,新的HTTP2.0协议接纳了SPDY作为原型,支持在一个网络连接同时进行多个请求和应答,在连接层面做到了充分的复用,降低连接创建和维护的成本;

TCP优化分享相关文章:

  • 通过accesslog和tcpdump分析请求响应时间
  • 反向代理系统分析与调优
  • AE网站TCP优化项目介绍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/158524.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IB地理科学什么?

IB地理科是一门很特别的科目,能帮助同学掌握技巧认识和了解这世界,而这课程分为两部分,包括自然环境和人文社会。IB地理科两部分 首先是自然环境,包括生态系统、气候,地壳活动等等 ;另外是人文社会&#x…

Nosql和Redis介绍,Redis五大数据类型及操作,跳跃表

NoSQL数据库是用来解决性能问题的,分很多类。redis是NoSQL的一种。 NoSQL的引入: 随着Web2.0时代的到来。可以进行网络请求的不仅限与电脑。用户还可以通过手机端,平板甚至汽车等来进行网络请求。网络请求极具增加,增加了服务器…

【代码随想录】LC 102. 二叉树的层序遍历

目录 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解 三、知识风暴 一、题目 1、原题链接 力扣 2、题目描述 给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问…

linux下miniconda环境的配置以及软件的安装

miniconda 我们需要在自己的目录下安装conda环境,所以需要自定义安装位置 mkdir /share/nas6/wangyq/biosoft/miniconda wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O /share/nas6/wangyq/biosoft/miniconda/miniconda.sh注意…

070-JAVA项目实训:仿QQ即时通讯软件系列讲座五(讲解用户注册功能)

【上一讲】069-JAVA项目实训:仿QQ即时通讯软件讲座四(讲解系统登录功能)_CSDN专家-赖老师(软件之家)的博客-CSDN博客 本文主要内容是实现注册QQ用户功能,自动获取本机IP地址,与系统用户判断端口是否唯一,使用的主要技术如下: 1.使用数据库技术完成注册功能; 2.自动…

Linux系统编程——锁

目录线程同步:锁的使用:使用matex(互斥量、互斥锁)一般步骤:restrict关键字:死锁:读写锁:条件变量实现生产者-消费者代码信号量实现消费者生产者线程同步: 协同步调,对公共区域数据按序访问。防止数据混乱,产生与时间有关的错误。 锁的使用…

原来这些技术标准,是字节跳动人参与制定的

我们常常遇到这类问题:因为不同厂商产品标准不同、接口不一致,导致 A 产品无法兼容 B 产品,C 接口和 D 接口无法连接,实在令人困扰。其实,技术从业者们一直在试图解决这些不统一、不兼容的问题,他们的方法是…

ceres学习笔记(一)

本来还想着先对照着官方doc来学习的。突然在csdn里面搜了下,发现了几篇高质量的文章,就先对应这几篇文章学习,来快速入门。 一、ceres求解问题一般步骤 使用ceres-solver求解优化问题一般分为下面三步: 1.第一步:构…

aws cdk 配置 lambda 函数的金丝雀发布

之前的文章介绍了使用sam框架完成lambda函数的金丝雀发布,这里使用cdk创建lambda函数项目实现此功能 Building CI/CD pipelines for lambda canary deployments using AWS CDK 项目的结构如下图所示 lambda堆栈示例 应用程序和环境配置 #!/usr/bin/env python3 im…

数据结构与算法0—大纲

数据结构:数据结构是计算机存储、组织数据的方式。是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组. 算法:是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法…

Vue动态路由、动态路由如何进行参数的传递、$router和$route的区别、命名路由、命名视图

一、什么是动态路由 ​ 将URL地址中可变的内容设置成参数,根据不同的参数渲染不同的组件。(组件可以复用) 二、动态路由如何进行参数的传递 ​ 1. 如何设置URL地址中的参数:“/url/:参数名” ​ 2. 在组…

Linux环境下配置Nginx

文章目录安装环境配置安装Nginx需要安装第三方的开发包安装Nginx启动并访问Nginx安装完毕Linux——centos7版本 安装环境配置 Nginx是C语言编写的,所以需要配置C语言编译环境(要联网) 安装gcc环境 [roota ~]# yum install gcc-c 已加载插件…

【Unity3D编辑器扩展】Unity3D中解决Text的清晰度问题

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、前言 在程序开发中,常常会遇到Unity中Text文字不清晰的问…

中兴服务器迈络思NS312网卡down掉网口物理指示灯不熄灭解决方法--NS312型号ConnectX-4 Lx

中兴服务器迈络思NS312网卡down掉网口物理指示灯不熄灭解决方法–NS312型号ConnectX-4 Lx 中兴服务器做网卡倒卡测试时,出现如下问题: 中兴服务器迈络思网卡,卡片NS312,型号ConnectX-4 Lx,万兆网卡在执行ifdown或ip l…

SmartZone ICR/OCR Professional 7.2 NET Crack

SmartZone ICR/OCR 专业版 v7.2 采集 by Ω578867473 检测并报告字体属性、字体大小和大写字母高度。特征 检测并报告字体属性、字体大小、大写字母高度和基线详细信息,作为 Microsoft Windows 上单个字符识别结果的一部分。检测和报告图像方向并识别图像&#xff0…

Spring的生命周期

Spring作为当前Java最流行、最强大的轻量级框架,受到了程序员的热烈欢迎。准确的了解Spring Bean的生命周期是非常必要的。我们通常使用ApplicationContext作为Spring容器。这里,我们讲的也是 ApplicationContext中Bean的生命周期。而实际上BeanFactory也…

【云原生进阶之容器】第四章Operator原理4.1节--定制资源(Custom Resource)

1 定制资源概述 定制资源(Custom Resource) 是对 Kubernetes API 的扩展。 本页讨论何时向 Kubernetes 集群添加定制资源,何时使用独立的服务。 1.1 定制资源 资源(Resource) 是 Kubernetes API 中的一个端点, 其中存储的是某个类别的 API 对象的一个集合。 例如内置的 …

Revit中墙连接方式和墙连接显示及修改问题

关于Revit墙连接方式、显示及其调整修改的理解,首先我们要来理清楚几个问题 1、什么是Revit墙连接? 在Revit里墙与墙之间的连接就是墙连接,它可以是同类型墙之间的连接,也可以是不同类型墙之间的连接,可以是2堵墙、3…

【链表】leetcode面试题 02.07. 链表相交(C/C++/Java/Js)

leetcode面试题 02.07. 链表相交1 题目2 思路2.1 思路一2.2 思路二(强推--5行代码)3 代码3.1 C版本3.1.1思路一:3.1.2 思路二3.2 C版本3.2.1 思路一3.2.2 思路二3.3 Java版本3.3.1 思路一3.3.2 思路二3.4 JavaScript版本4 总结1 题目 给你两个单链表的头…

【进阶C语言】动态内存管理+柔性数组

文章目录1.动态内存的开辟内存的布局内存池内存碎片内存泄漏2.动态内存函数malloc功能函数calloc功能函数realloc功能函数开辟时遇到的两种情况free功能函数3.  建议4.柔性数组特性:定义使用优点1.动态内存的开辟 内存的布局 我们常用的内存开辟函数都是在堆区开辟…