关于多线程情况下Net-SNMP v3 版本导致进程假死情况的跟踪与分析

简介:

1、问题描述

  在使用net-snmp对交换机进行扫描的时候经常会出现进程假死的情况(就是进程并没有死掉,但是看不到它与外界进行任何的数据交互)。这时候不知道进程内部发生了什么,虽然有日志信息,但进程已经很长时间没有动静,根本不知道这段时间做了什么。用gdb att进去发现,进行snmp发送的线程已经被阻塞了。但是阻塞的情况并不是每次都发生,而是经常发生,这就导致很难捕捉问题。通过观察日志和 tcpdump 抓包,发现这种情况只在v3版本的时候出现,那就是v3版本有什么特别的地方。


 

2、调试跟踪

  观察 gdb att 后的情况,发现每次都会挂在 recvmsg() 这个函数上。刚开始以为是在进行接收的时候出的问题,多看了几层栈才发现,原来在发送函数里面就卡住了,伤心啊……

  下面是gdb看到的栈调用情况:

  在snmp v3 协议中,要想能够请求数据,首先需要获取SNMP 协议引擎,也就是engineID号,然后根据这个再去请求索要的数据。目前遇到的情况就是在请求这个引擎ID的时候卡住了。看了下net-snmp的源码,在请求引擎ID的时候是没有设置超时的,也就是死等……

在 snmp_client.c 源文件中的 snmp_sess_synch_response() 函数中的源代码:

复制代码
numfds = 0;
        FD_ZERO(&fdset);
        block = NETSNMP_SNMPBLOCK;
        tvp = &timeout;
        timerclear(tvp);
        snmp_sess_select_info(sessp, &numfds, &fdset, tvp, &block);
        if (block == 1)
            tvp = NULL;         /* block without timeout */
        count = select(numfds, &fdset, 0, 0, tvp);
        if (count > 0) {
            snmp_sess_read(sessp, &fdset);
        } 
复制代码

而且是用的 select !!!我们发送和接收用的可是多线程呀……

然后搜到这么一篇讨论 net-snmp 对多线程支持情况的文章:《Is Net-SNMP thread safe?

译文在这里:《Net-SNMP是线程安全的吗

文章开篇是这么说的,我忍不住要截图呀 !!!

问:Net-SNMP是线程安全的吗?

答:确切的说,不是!

我勒个去,多么干脆的回答,简直不忍直视……

虽然在文末给出了v3不支持多线程的原因,我还是感到不开心……

Unfortunately, the SNMPv3 support was added about the same time as the thread support and since they occurred in parallel the SNMPv3 support was never checked for multi-threading correctness. It is most likely that it is not thread-safe at this time.


 

3、分析多线程下net-snmp v3出现卡死的原因

  在多线程下,因为我的程序对交换机的发送和接收是在不同线程的,导致一个很严重的问题就是线程间的同步。

  我们假设有这样一种情况:

  我们有两个线程,发送线程T1 和 接收线程 T2。T1 只负责调用 net-snmp 的发送函数接口,向交换机请求信息;T2只负责进行接收,并且是select 方式进行接收:

  步骤:

    1)T1线程 发送一个请求,然后就不管了,接收工作就交给T2线程 select吧

    2)那么,此时这个发送线程 T1 可以继续向另一个snmp v3版本的交换机发送请求,在发送的过程中需要首先请求SNMP引擎,及请求engineID

    3)T1线程发送完了,进行阻塞调用 select,直到有消息返回才会结束阻塞状态

  注意,此时就出现问题了。我们发送请求和接收请求的时候使用两个不同的线程,也就是说,那个只负责接收的线程一直在进行select。这个时候就要看是谁能够接收到这个请求engineID的UDP包了,如果是此时的这个发送线程T1,那么万事大吉,程序继续向下走。如果这个请求engineID的包被只负责接收的线程 T2 收到了呢?那 T1 线程就没得接收了,那就只好等待了,由于没有设置超时,还是以阻塞方式进行调用的,结果就是死等……

  所以现在遇到的问题是,发送线程 T1 不仅仅只是进行了发送,还进行了接收,并且在发送过程中出现的接收动作不是我们能控制的。当然我们也可以修改源代码,增加线程锁,但那需要更多的精力去研究 net-snmp 更多的代码,还不如在自己代码里加锁。


 

4、解决办法

  还能有什么解决办法?只能加线程锁了。加锁的目的在于,使发送线程 T1 的发送过程和接收线程 T2 的select过程互斥的进行,不允许接收线程 T2 抢夺发送线程 T1 的数据。

  解决方法:

    1)在 T1 线程进行发送之前先加锁,发送完成后解锁

    2)在 T2 线程 select 之前加锁,seelct结束之后解锁

  结果:运行了很长事件了,没有再出现v3卡死的情况

  但目前这种处理还是比较粗糙的,会导致T2线程在没有接收到返回数据时进行超时等待处理,一次超时等待需要3s,这对性能的损耗还是比较大的。

 


 

5、总结

  开发的时候总会遇到各种各样的奇闻异事,习惯了就好了。今天把问题的发现及解决过程记录一下,按照茨威格在《昨日的世界》中所说的“倘若你想完全领悟伟大的杰作,你不仅要看到过它们的成品,而且必须了解到它们形成的过程”。所以我要记录下每一个问题的过程,不是因为这是什么杰作,而是因为这是我生命中的一件故事,当我垂垂暮年,回首往事,看到自己一路上记录的点滴,想起自己年轻时努力的身影,也许,这才是我送给未来自己最好的礼物吧。

 


本文转自郝峰波博客园博客,原文链接:http://www.cnblogs.com/fengbohello/p/4363542.html,如需转载请自行联系原作者


相关文章
|
存储 Linux API
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
|
弹性计算 运维 监控
基于进程热点分析与系统资源优化的智能运维实践
智能服务器管理平台提供直观的可视化界面,助力高效操作系统管理。核心功能包括运维监控、智能助手和扩展插件管理,支持系统健康监控、故障诊断等,确保集群稳定运行。首次使用需激活服务并安装管控组件。平台还提供进程热点追踪、性能观测与优化建议,帮助开发人员快速识别和解决性能瓶颈。定期分析和多维度监控可提前预警潜在问题,保障系统长期稳定运行。
635 17
|
数据采集 Java 数据处理
Python实用技巧:轻松驾驭多线程与多进程,加速任务执行
在Python编程中,多线程和多进程是提升程序效率的关键工具。多线程适用于I/O密集型任务,如文件读写、网络请求;多进程则适合CPU密集型任务,如科学计算、图像处理。本文详细介绍这两种并发编程方式的基本用法及应用场景,并通过实例代码展示如何使用threading、multiprocessing模块及线程池、进程池来优化程序性能。结合实际案例,帮助读者掌握并发编程技巧,提高程序执行速度和资源利用率。
782 0
|
调度 开发者
核心概念解析:进程与线程的对比分析
在操作系统和计算机编程领域,进程和线程是两个基本而核心的概念。它们是程序执行和资源管理的基础,但它们之间存在显著的差异。本文将深入探讨进程与线程的区别,并分析它们在现代软件开发中的应用和重要性。
636 4
|
并行计算 数据处理 调度
Python中的并发编程:探索多线程与多进程的奥秘####
本文深入探讨了Python中并发编程的两种主要方式——多线程与多进程,通过对比分析它们的工作原理、适用场景及性能差异,揭示了在不同应用需求下如何合理选择并发模型。文章首先简述了并发编程的基本概念,随后详细阐述了Python中多线程与多进程的实现机制,包括GIL(全局解释器锁)对多线程的影响以及多进程的独立内存空间特性。最后,通过实例演示了如何在Python项目中有效利用多线程和多进程提升程序性能。 ####
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
416 5
|
运维 JavaScript jenkins
鸿蒙5.0版开发:分析CppCrash(进程崩溃)
在HarmonyOS 5.0中,CppCrash指C/C++运行时崩溃,常见原因包括空指针、数组越界等。系统提供基于posix信号机制的异常检测能力,生成详细日志辅助定位。本文详解CppCrash分析方法,涵盖异常检测、问题定位思路及案例分析。
694 4
|
运维 监控 JavaScript
鸿蒙next版开发:分析JS Crash(进程崩溃)
在HarmonyOS 5.0中,JS Crash指未处理的JavaScript异常导致应用意外退出。本文详细介绍如何分析JS Crash,包括异常捕获、日志分析和典型案例,帮助开发者定位问题、修复错误,提升应用稳定性。通过DevEco Studio收集日志,结合HiChecker工具,有效解决JS Crash问题。
839 4
|
开发框架 Java .NET
.net core 非阻塞的异步编程 及 线程调度过程
【11月更文挑战第12天】本文介绍了.NET Core中的非阻塞异步编程,包括其基本概念、实现方式及应用示例。通过`async`和`await`关键字,程序可在等待I/O操作时保持线程不被阻塞,提高性能。文章还详细说明了异步方法的基础示例、线程调度过程、延续任务机制、同步上下文的作用以及如何使用`Task.WhenAll`和`Task.WhenAny`处理多个异步任务的并发执行。
441 1
Visual Studio 快速分析 .NET Dump 文件
【11月更文挑战第10天】.NET Dump 文件是在 .NET 应用程序崩溃或出现问题时生成的,记录了应用程序的状态,包括内存对象、线程栈和模块信息。通过分析这些文件,开发人员可以定位和解决内存泄漏、死锁等问题。在 Visual Studio 中,可以通过调试工具、内存分析工具和符号加载等功能来详细分析 Dump 文件。此外,还可以使用第三方工具如 WinDbg 进行更深入的分析。
1313 1