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 = ;
FD_ZERO(&fdset);
block = NETSNMP_SNMPBLOCK;
tvp = &timeout;
timerclear(tvp);
snmp_sess_select_info(sessp, &numfds, &fdset, tvp, &block);
if (block == )
tvp = NULL; /* block without timeout */
count = select(numfds, &fdset, , , tvp);
if (count > ) {
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、总结

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

作者:郝峰波

mail : fengbohello@qq.com

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

  1. 我大概知道他在说什么了,是对内存单元的竞争访问吧。Python有GIL,在执行伪码时是原子的。但是伪码之间不保证原子性。 UDP丢包,你是不是做了盲发?没有拥塞控制的情况下,确实会出现丢包严重的情况。你先看看发送速率,还有是否带有拥塞控制。

    我大概知道他在说什么了,是对内存单元的竞争访问吧.Python有GIL,在执行伪码时是原子的.但是伪码之间不保证原子性.   UDP丢包,你是不是做了盲发?没有拥塞控制的情况下,确实会出现丢包严重的情 ...

  2. linux 存在多个版本的情况下,切换python版本

    linux 存在多个版本的情况下 python 命令默认寻找 /usr/bin下的命令 所以先find / -name python* 找一下所有的Python版本 然后 sudo ln /usr/b ...

  3. 采用多线程方式,解决由于查询等待造成winfrom假死问题

    1.这里是触发一个比较耗时的操作,比如一次大数据量的查询: Thread thread = new Thread(new ThreadStart(DoWord)); thread.Start(); 2 ...

  4. Python多版本情况下四种快速进入交互式命令行的操作技巧

    因为工作需求或者学习需要等原因,部分小伙伴的电脑中同时安装了Python2和Python3,相信在Python多版本的切换中常常会遇到Python傻傻分不清楚的情况,今天小编整理了四个操作技巧,以帮助 ...

  5. Java多线程(下)

    线程同步 当多个线程访问一个对象时,有可能会发生污读,即读取到未及时更新的数据,这个时候就需要线程同步. 线程同步: 即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线 ...

  6. 2539-SpringSecurity系列--在有安全验证的情况下做单元测试Test

    在有安全验证的情况下做单元测试Test 版本信息 <parent> <groupId>org.springframework.boot</groupId> < ...

  7. java里的数组和list分别在什么情况下使用?

    数组长度固定,List未限定长度,且支持的功能更多,最常用的ArrayList底层实际上也是使用数组实现. 不需要复杂功能和确定长度的情况下,使用数组效率更高,通常情况建议使用List.

  8. 第一章01-正常情况下Activity的生命周期

    一.Android下能见到的界面 Window Dialog Toast Activity 二.Activity的生命周期分析 典型情况下的生命周期 ​是指在有用户参与的情况下,Activity所经过 ...

  9. 64位CreateProcess逆向:(二)0环下参数的整合即创建进程的整体流程

    转载:https://bbs.pediy.com/thread-207683.htm 点击下面进入总目录: 64位Windows创建64位进程逆向分析(总目录) 在上一篇文章中,我们介绍了Create ...

随机推荐

  1. Jquery获取iframe子/父窗口中的标签

    获取子窗口中的标签: $("#id",document.frames('iframename').document); 获取父窗口中的标签: $('#id', parent.doc ...

  2. HR外包系统 - 员工项目 薪资项目 考勤项目 -管理

    项目管理-包括员工项目 薪资项目 考勤项目 一 后台总公司定义项目-前台分公司选择项目,定义别名-分公司客户选择员工项目,定义别名 分公司下面-新建薪资类别-薪资类别下面选择要的薪资和考勤项目. 二 ...

  3. VR 相关专业词汇

    最近在看 SIGGRAPH2015 有关 VR Display and Interaction 的几篇文章,之前从来没看过有关方面的 paper,一看才发现专业词汇太多了,根本不懂啊,幸亏 Paper ...

  4. document.body / document.ducumentElement /等获取高度和宽度的区别 ----转载

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  5. 【项目经验】之——Controller向View传值

    我们的ITOO进行了一大部分了,整体上来说还是比较顺利的.昨天进行了一次验收,大体上来说,我们新生这块还是可以的.不仅仅进行了学术上的交流,还进行了需求上的更新.也正是由于这一次,我有了解到了一个新的 ...

  6. Understanding Kafka Consumer Groups and Consumer Lag

    In this post, we will dive into the consumer side of this application ecosystem, which means looking ...

  7. 提取Windows用户密钥文件cachedump

    提取Windows用户密钥文件cachedump   Windows系统将用户信息和密钥存储在系统存档文件(System hive)和安全存档(Security hive)中.只要提取这些内容,就可以 ...

  8. php,blade语法

    打印数组 <?php print_r($agreement);die?> <?= ?><?php echo ?><?php printf();die;?> ...

  9. appium定位元素java篇【转】

    1.关于没有name,没有ID的元素的定位---通用篇解题思路:因为没有name,id:其实剩下的选择已不多,要么xpath,要么className.xpath木有好印象(稳定性不高,加之1.0x后需 ...

  10. linux read简单用法

    #!/bin/bash #read chars into var read -n var echo $var #read chars without showing it read -s var ec ...