【20180607】关于TCP队列信息溢出的排查和解释

本文探讨了HTTP及HTTPS请求间歇性丢失的原因,重点分析了TCP队列问题,包括半连接队列(SYN queue)与全连接队列(accept queue)的处理机制,并介绍了如何通过调整Linux内核参数来优化网络性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于TCP队列问题和内核参数的优化
http请求或者https请求间歇性的被丢弃或者访问失败的排查流程
  1. 检查dev 和网卡设备层,是否有error和drop。硬件和系统层面错误。

    • cat /proc/net/dev
      • errors:由设备驱动程序检测到的发送或者接受错误的总数
      • drop:设备驱动程序丢弃包总数
    • ifconfig
  2. 观察socket overflow 和 socket droped。如果应用处理全连接队列(accept queue)过慢则会导致socket overflow,影响半连接队列(syn queue)溢出而导致socket dropped

    • netstat -s | grep -i listen
      • 645870725 times the listen queue of a socket overflowed
      • 645990109 SYNs to LISTEN sockets ignored
      • 隔段时间查看或者说监控SYN socket overflow和socket droped急剧增加的话则说明,TCP的三次握手是存在很大的问题的。
    • 关于TCP三次握手的。
      • client发送syn给server端。
      • server接收到client的syn,这个时候则会将相关信息放到半链接队列(syn queue),并且发送syn+ack发送给client。
      • client接受到server的syn+ack之后,发送一个ack给server告诉server我接收到了。这个时候server就会将相关信息放到全连接队列(accept queue)中。
  3. 检查sysctl内核参数。

    • 内核参数详解sysctl -a
      • fs.file-max:设置系统所有进程一共可以打开的文件数量。和ulimit的区别就是,ulimit设置的是当前shell以及由它启动的进程的资源限制。
      • net.core.somaxconn:表示socket监听(listen)的backlog的上限,即全连接队列上限(accept queue)。backlog就是socket的监听队列,当一个请求(request)尚未被处理或建立时,它会进入backlog。而socket server可以一次性处理backlog中的所有请求,处理后的请求不在位于监听队列中。当server处理请求比较慢,以至于监听队列被填满后,新的请求会被拒绝。
      • net.core.netdev_max_backlog:在每个网络接口接收数据包的速率比内核处理这些数据包快的时候,允许送到队列的数据包的最大数目。
      • net.ipv4.tcp_max_syn_backlog:指定所能接受SYN同步包的最大客户端数量,即半连接队列(syn queue)上限
      • net.ipv4.tcp_syncookies=1:表示开启SYN Cookies。当出现SYN等待队列溢出的时候,启用cookies来处理少量的SYN×××。
      • net.ipv4.tcp_tw_reuse=1:表示开启tcp连接重用。表示允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭。
      • net.ipv4.tcp_tw_recycle=1:表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。若是过NAT,则需要禁用这个参数。因为当同时在server端开启tcp_timestamps和recycle的时候,60s内同一源IP主机的socket connect请求中的timestamp必须是递增的。
      • net.ipv4.tcp_timestamps:TCP时间戳(会在TCP包头增加12个字节)。
    • ss -lnt查询。主要针对的是基于应用或者短裤的半链接当前值和全连接的上限值。
      • Recv-Q:表示的是当前等待server端调用accept完成三次握手的listen backlog数值,也就是说,当客户端发送syn给server端,server端接收到syn存放的半连接队列(SYN queue)的当前值。
      • Send-Q:表示全连接队列(accept queue)上限的数值,min(backlog,somaxconn)的值。
  4. 检查selinux和NetworkManager是否启用,禁用状态。
  5. 抓包判断请求进来后应用处理的情况,是否收到SYN未响应的情况。
验证
  1. 查看全连接队列(accept queue)溢出之后,OS处理设置:
    • cat /proc/sys/net/ipv4/tcp_abort_on_overflow
      • 0:表示如果三次握手第三步的时候全连接队列满了那么server扔掉client发过来的ack(在server端则会认为连接没有建立起来)
      • 1:表示如果三次握手第三步的时候全连接队列满了,server端就会发送一个reset包给client端,表示废弃这个握手过程和这个链接。(在server端也会认为连接没有建立起来)
  2. 设置tcp_abort_on_overflow为1
    • echo 1 > /proc/sys/net/ipv4/tcp_abort_on_overflow
  3. 查看server端的web服务是否存在许多的connection reset peer错误。
  4. 查看sysctl -a内核参数 backlog,somaxconn,file-max和nginx的blocklog参数。
  5. ss -lnt查看服务的Send-Q全队列的上限和Recv-Q的当前半链接的值。
  6. 内核参数优化:
    • net.ipv4.tcp_syncookies = 1
    • net.ipv4.tcp_max_syn_backlog = 16384
    • net.core.somaxconn = 16384
      • 最大极限值是65535。当超过了这个值的话需要转化为二进制,区最后16位,再转化为10进制。
  7. nginx参数优化
    • backlog = 32768
补充: SYN Flood洪水×××

当前最流行的DoS(拒绝服务×××)与DDos(分布式拒绝服务×××)的方式之一,这是一种利用TCP协议缺陷,导致被×××服务器保持大量的SYN_RECV状态的“半链接”,并且会重试默认的5次回应第二个握手包,塞满TCP等待连接队列,耗尽资源(CPU满负载或内存不足),让正常的业务请求连接不进来。

转载于:https://blog.51cto.com/11819159/2126061

<think>好的,我现在需要回答用户关于FreeRTOS中队列信号量的介绍。首先,用户之前问过优先级反转的问题,现在可能是在进一步学习FreeRTOS的任务间通信机制。我需要确保回答结构清晰,符合之前他们提供的格式,比如分点说明,使用代码示例等。 首先,队列信号量都是FreeRTOS中用于任务间通信同步的重要机制。我需要分别介绍它们的基本概念、用途、工作原理以及使用场景,可能还需要对比它们的区别,避免用户混淆。 队列主要用于任务间的数据传输,是FIFO结构,可以传输任意类型的数据,而信号量则用于同步互斥,不传输数据,只是传递状态。信号量又分为二进制信号量、计数信号量、互斥信号量等,这些都需要详细说明。 接下来,我需要考虑如何组织内容。可能分为队列介绍信号量介绍两大部分,每部分再细分为基本概念、运作机制、使用场景、代码示例等。还要注意用户提到的格式要求,比如行内数学表达式用$...$,独立公式用$$...$$,但可能这里用不上太多数学公式,不过代码部分需要用正确的LaTeX语法,比如代码块用三个反引号包裹,并注明语言。 另外,用户需要结构清晰的回答,帮助逐步解决问题,所以可能需要用标题、子标题、列表、表格等形式。例如,队列的部分可以包括创建队列、发送数据、接收数据等步骤,每个步骤给出代码示例。信号量部分则需要说明不同类型的信号量及其应用场景,比如二进制信号量用于同步,互斥信号量用于资源保护等。 还需要注意用户之前的回答中有使用表格来对比不同情况,比如在常见错误排查表中,这里是否也需要类似的对比表格,比如队列与信号量的对比,或者不同信号量类型的对比。这样可以更直观地帮助用户理解区别。 另外,用户可能对如何选择队列还是信号量有疑问,需要给出使用建议,比如什么时候用队列传输数据,什么时候用信号量进行同步或互斥。同时,要提到一些常见的使用错误,比如误用信号量代替队列导致数据丢失,或者队列阻塞时间设置不当导致任务挂起等。 最后,确保语言是中文,技术术语准确,代码示例正确,符合FreeRTOS的API规范。可能需要检查代码中的函数名用法是否正确,比如创建队列使用xQueueCreate,发送接收使用xQueueSendxQueueReceive,而信号量的创建如xSemaphoreCreateBinary等。 总结一下,回答的结构大致如下: 1. 队列介绍:概念、运作机制、使用场景、代码示例 2. 信号量介绍:类型(二进制、计数、互斥)、各自用途、代码示例 3. 队列与信号量的对比表格 4. 使用建议常见错误 在写作过程中,要确保每个部分清晰,使用适当的标题子标题,代码示例正确并注释,最后可能还需要给出实际应用中的注意事项,比如资源管理、阻塞时间设置、优先级继承等与之前问题相关的点,保持连贯性。</think>--- ### **FreeRTOS队列与信号量详解** --- #### **一、队列(Queue)** **核心作用**:任务间**数据传输**,支持任意数据类型的异步/同步通信。 ##### **1. 核心特性** | 特性 | 说明 | |---------------------|-----------------------------| | 数据结构 | 先进先出(FIFO)或后进先出(LIFO) | | 数据传输方式 | 深拷贝(复制数据内容) | | 阻塞机制 | 可设置超时时间(portMAX_DELAY表示无限等待)| | 多任务访问 | 支持多个任务并发读写 | ##### **2. 关键API及示例** **创建队列**: ```c // 创建队列队列长度5,每个元素占4字节) QueueHandle_t xQueue = xQueueCreate(5, sizeof(uint32_t)); ``` **发送数据**: ```c uint32_t data = 42; // 发送到队列尾部(阻塞时间10 ticks) xQueueSend(xQueue, &data, pdMS_TO_TICKS(10)); ``` **接收数据**: ```c uint32_t received_data; // 从队列头部读取(无限等待) xQueueReceive(xQueue, &received_data, portMAX_DELAY); ``` ##### **3. 典型应用场景** - 传感器数据采集任务 → 数据处理任务 - 网络接收线程 → 协议解析线程 - GUI事件生产者 → 事件消费者 --- #### **二、信号量(Semaphore)** **核心作用**:任务间**同步与资源管理**,不传输具体数据,仅传递状态信号。 ##### **1. 信号量分类** | 类型 | 用途 | 关键特性 | |---------------------|-----------------------------|-------------------------| | **二进制信号量** | 事件通知(如中断服务程序通知任务) | 初始值0,释放后置1 | | **计数信号量** | 资源池管理(如内存块计数) | 支持累计计数值(最大255) | | **互斥信号量** | 临界资源保护(防优先级反转) | 支持优先级继承协议 | ##### **2. 关键API及示例** **二进制信号量创建**: ```c SemaphoreHandle_t xBinarySem = xSemaphoreCreateBinary(); ``` **计数信号量创建**: ```c // 最大计数值10,初始值5 SemaphoreHandle_t xCountingSem = xSemaphoreCreateCounting(10, 5); ``` **互斥信号量使用**: ```c // 任务A获取互斥锁 xSemaphoreTake(xMutex, portMAX_DELAY); // 访问共享资源 xSemaphoreGive(xMutex); // 任务B尝试获取 if(xSemaphoreTake(xMutex, pdMS_TO_TICKS(100)) == pdTRUE) { // 成功获取锁 } ``` ##### **3. 典型应用场景** - **二进制信号量**:串口接收完成中断 → 任务处理 - **计数信号量**:管理空闲TCP连接数量 - **互斥信号量**:保护SD卡文件系统操作 --- ### **三、队列与信号量对比** | 维度 | 队列 | 信号量 | |---------------------|-----------------------------|-------------------------| | **数据传输** | 支持任意数据类型传输 | 仅传递信号,无数据负载 | | **内存占用** | 需预分配元素存储空间 | 仅需保存计数值(轻量) | | **阻塞行为** | 满队列发送阻塞/空队列接收阻塞 | 信号量为0时获取阻塞 | | **优先级继承** | 不支持 | 仅互斥信号量支持 | --- ### **四、选择策略** 1. **使用队列的场景**: - 需要传递实际数据(如传感器读数、协议报文) - 生产者消费者速率不一致需要缓冲 2. **使用信号量的场景**: - 仅需事件通知(如任务完成标志) - 资源数量管控(如同时运行的网络连接数) - 保护共享资源(如外设寄存器访问) --- ### **五、常见问题及解决** **问题1:队列溢出** ```c // 检查发送是否成功 if(xQueueSend(xQueue, &data, 0) == errQUEUE_FULL) { // 启动溢出处理策略 } ``` **问题2:信号量误用** ```c // 错误:在中断中使用xSemaphoreTake()(需改用xSemaphoreTakeFromISR()) BaseType_t xHigherPriorityTaskWoken = pdFALSE; xSemaphoreGiveFromISR(xBinarySem, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); ``` **问题3:优先级反转风险** ```c // 必须使用互斥信号量而非二进制信号量进行资源保护 SemaphoreHandle_t xMutex = xSemaphoreCreateMutex(); ``` --- ### **六、性能优化技巧** 1. **队列深度选择**: $$所需队列深度 = \frac{最大生产速率 \times 处理延迟}{1} + 安全余量$$ 例如:每秒100次数据产生,处理延迟10ms → 最小深度2 2. **信号量替代方案**: 高频事件通知可改用直接任务通知(`xTaskNotify()`),减少内核对象开销 3. **零拷贝优化**: 传输大型数据时传递指针(需配合互斥量保护数据生命周期): ```c struct SensorData { int32_t values[8]; }; QueueHandle_t xPointerQueue = xQueueCreate(5, sizeof(struct SensorData*)); // 发送端 struct SensorData *pData = pvPortMalloc(sizeof(struct SensorData)); xQueueSend(xPointerQueue, &pData, 0); // 接收端 struct SensorData *pReceived; xQueueReceive(xPointerQueue, &pReceived, 0); vPortFree(pReceived); // 必须显式释放 ``` --- 通过合理选择队列与信号量,并配合FreeRTOS的任务调度机制,可构建高效可靠的嵌入式实时系统。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值