关于多线程情况下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,如需转载请自行联系原作者


相关文章
|
26天前
|
存储 NoSQL Redis
Redis 新版本引入多线程的利弊分析
【10月更文挑战第16天】Redis 新版本引入多线程是一个具有挑战性和机遇的改变。虽然多线程带来了一些潜在的问题和挑战,但也为 Redis 提供了进一步提升性能和扩展能力的可能性。在实际应用中,我们需要根据具体的需求和场景,综合评估多线程的利弊,谨慎地选择和使用 Redis 的新版本。同时,Redis 开发者也需要不断努力,优化和完善多线程机制,以提供更加稳定、高效和可靠的 Redis 服务。
30 1
|
1月前
线程CPU异常定位分析
【10月更文挑战第3天】 开发过程中会出现一些CPU异常升高的问题,想要定位到具体的位置就需要一系列的分析,记录一些分析手段。
61 0
|
7天前
|
开发框架 监控 .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
|
4天前
Visual Studio 快速分析 .NET Dump 文件
【11月更文挑战第10天】.NET Dump 文件是在 .NET 应用程序崩溃或出现问题时生成的,记录了应用程序的状态,包括内存对象、线程栈和模块信息。通过分析这些文件,开发人员可以定位和解决内存泄漏、死锁等问题。在 Visual Studio 中,可以通过调试工具、内存分析工具和符号加载等功能来详细分析 Dump 文件。此外,还可以使用第三方工具如 WinDbg 进行更深入的分析。
|
8天前
|
运维 JavaScript jenkins
鸿蒙5.0版开发:分析CppCrash(进程崩溃)
在HarmonyOS 5.0中,CppCrash指C/C++运行时崩溃,常见原因包括空指针、数组越界等。系统提供基于posix信号机制的异常检测能力,生成详细日志辅助定位。本文详解CppCrash分析方法,涵盖异常检测、问题定位思路及案例分析。
29 4
|
8天前
|
运维 监控 JavaScript
鸿蒙next版开发:分析JS Crash(进程崩溃)
在HarmonyOS 5.0中,JS Crash指未处理的JavaScript异常导致应用意外退出。本文详细介绍如何分析JS Crash,包括异常捕获、日志分析和典型案例,帮助开发者定位问题、修复错误,提升应用稳定性。通过DevEco Studio收集日志,结合HiChecker工具,有效解决JS Crash问题。
25 4
|
1月前
|
NoSQL Linux 程序员
进程管理与运行分析
进程管理与运行分析
23 0
|
2月前
|
存储 运维
.NET开发必备技巧:使用Visual Studio分析.NET Dump,快速查找程序内存泄漏问题!
.NET开发必备技巧:使用Visual Studio分析.NET Dump,快速查找程序内存泄漏问题!
|
2月前
|
并行计算 API 调度
探索Python中的并发编程:线程与进程的对比分析
【9月更文挑战第21天】本文深入探讨了Python中并发编程的核心概念,通过直观的代码示例和清晰的逻辑推理,引导读者理解线程与进程在解决并发问题时的不同应用场景。我们将从基础理论出发,逐步过渡到实际案例分析,旨在揭示Python并发模型的内在机制,并比较它们在执行效率、资源占用和适用场景方面的差异。文章不仅适合初学者构建并发编程的基础认识,同时也为有经验的开发者提供深度思考的视角。
|
2月前
|
安全 Java API
Java线程池原理与锁机制分析
综上所述,Java线程池和锁机制是并发编程中极其重要的两个部分。线程池主要用于管理线程的生命周期和执行并发任务,而锁机制则用于保障线程安全和防止数据的并发错误。它们深入地结合在一起,成为Java高效并发编程实践中的关键要素。
30 0

相关实验场景

更多