关于多线程情况下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 = ;
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 版本导致进程假死情况的跟踪与分析的更多相关文章
- 我大概知道他在说什么了,是对内存单元的竞争访问吧。Python有GIL,在执行伪码时是原子的。但是伪码之间不保证原子性。 UDP丢包,你是不是做了盲发?没有拥塞控制的情况下,确实会出现丢包严重的情况。你先看看发送速率,还有是否带有拥塞控制。
我大概知道他在说什么了,是对内存单元的竞争访问吧.Python有GIL,在执行伪码时是原子的.但是伪码之间不保证原子性. UDP丢包,你是不是做了盲发?没有拥塞控制的情况下,确实会出现丢包严重的情 ...
- linux 存在多个版本的情况下,切换python版本
linux 存在多个版本的情况下 python 命令默认寻找 /usr/bin下的命令 所以先find / -name python* 找一下所有的Python版本 然后 sudo ln /usr/b ...
- 采用多线程方式,解决由于查询等待造成winfrom假死问题
1.这里是触发一个比较耗时的操作,比如一次大数据量的查询: Thread thread = new Thread(new ThreadStart(DoWord)); thread.Start(); 2 ...
- Python多版本情况下四种快速进入交互式命令行的操作技巧
因为工作需求或者学习需要等原因,部分小伙伴的电脑中同时安装了Python2和Python3,相信在Python多版本的切换中常常会遇到Python傻傻分不清楚的情况,今天小编整理了四个操作技巧,以帮助 ...
- Java多线程(下)
线程同步 当多个线程访问一个对象时,有可能会发生污读,即读取到未及时更新的数据,这个时候就需要线程同步. 线程同步: 即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线 ...
- 2539-SpringSecurity系列--在有安全验证的情况下做单元测试Test
在有安全验证的情况下做单元测试Test 版本信息 <parent> <groupId>org.springframework.boot</groupId> < ...
- java里的数组和list分别在什么情况下使用?
数组长度固定,List未限定长度,且支持的功能更多,最常用的ArrayList底层实际上也是使用数组实现. 不需要复杂功能和确定长度的情况下,使用数组效率更高,通常情况建议使用List.
- 第一章01-正常情况下Activity的生命周期
一.Android下能见到的界面 Window Dialog Toast Activity 二.Activity的生命周期分析 典型情况下的生命周期 是指在有用户参与的情况下,Activity所经过 ...
- 64位CreateProcess逆向:(二)0环下参数的整合即创建进程的整体流程
转载:https://bbs.pediy.com/thread-207683.htm 点击下面进入总目录: 64位Windows创建64位进程逆向分析(总目录) 在上一篇文章中,我们介绍了Create ...
随机推荐
- hdu 4751 2013南京赛区网络赛 二分图判断 **
和以前做过的一个二分图颇为相似,以前的是互相不认识的放在一组,这个是互相认识的,本质上是相同的 是 hdu 2444 #include<cstdio> #include<iostre ...
- mysql的启动
1.直接用mysqld手工启动 [root@ora11g bin]# ./mysqld --defaults-file=../my.cnf :: [ERROR] Fatal error: Please ...
- memcached的最佳实践方案
基本问题 1.memcached的基本设置 1)启动Memcache的服务器端 # /usr/local/bin/memcached -d -m 10 -u root -l 192.168.0.200 ...
- linux下vim配置以及一些常用的快捷键
一些常用的vim编辑器快捷键: h」.「j」.「k」.「l」,分别控制光标左.下.上.右移一格. 按「ctrl」+「b」:屏幕往“后”移动一页. 按「ctrl」+「f」:屏幕往“前”移动一页. 按「c ...
- 两种方法获取shadow ssdt
ULONG GetShadowSsdtCurrentAddresses( PSSDT_ADDRESS AddressInfo, PULONG Length ) { PSYSTEM ...
- cocos2dx游戏开发——微信打飞机学习笔记(二)——游戏框架
一.游戏的基本框架: WelcomeScene ——> GameScene ——> GameOverScene || ...
- 关于初级java面试问题的一些整理 (部分转自他人)
1.面向对象的特征有哪些方面 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部 ...
- 《DSP using MATLAB》示例Example4.12
上代码: b = [0, 1, 1]; a = [1, -0.9, 0.81]; % [R, p, C] = residuez(b,a); Mp = (abs(p))' Ap = (angle(p)) ...
- ajax乱码
ajax提交请求,参数在data上依然乱码,并且已经做了过滤转码, 其他请求没有问题,此请求有问题建议使用下述方式处理: 前端:encodeURIComponent(fileName)或者encode ...
- The Unique MST(次小生成树)
Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 22335 Accepted: 7922 Description Give ...