[转]DllMain中不当操作导致死锁问题的分析——DllMain中要谨慎写代码(完结篇)
在CSDN中发现这篇文章,讲解的比较详细,所以在这里备份一个。原文链接:http://blog.csdn.net/breaksoftware/article/details/8167641
DllMain的相关特性
首先列出《DllMain中不当操作导致死锁问题的分析--进程对DllMain函数的调用规律的研究和分析》中论证的11个特性:
- Dll的加载不会导致之前创建的线程调用其DllMain函数。
- 线程创建后会调用已经加载了的DLL的DllMain,且调用原因是DLL_THREAD_ATTACH。(DisableThreadLibraryCalls会导致该过程不被调用)
- TerminateThread方式终止线程是不会让该线程去调用该进程中加载的Dll的DllMain。
- 线程正常退出时,会调用进程中还没卸载的DLL的DllMain,且调用原因是DLL_THREAD_DETACH。
- 进程正常退出时,会调用(不一定是主线程)该进程中还没卸载的DLL的DllMain,且调用原因是DLL_PROCESS_DETACH。
- 加载DLL进入进程空间时(和哪个线程LoadLibrary无关),加载它的线程会调用DllMain,且调用原因是DLL_PROCESS_ATTACH。
- DLL从进程空间中卸载出去前,会被卸载其的线程调用其DllMain,且调用原因是DLL_PROCESS_DETACH。
- TerminateProcess 将导致线程和进程在退出时不对未卸载的DLL进行DllMain调用。
- ExitProcess将导致主线程意外退出,子线程对未卸载的DLL进行了DllMain调用,且调用原因是DLL_PROCESS_DETACH。
- ExitThread是最和平的退出方式,它会让线程退出前对未卸载的DLL调用DllMain。
- 线程的创建和退出不会对调用了DisableThreadLibraryCalls的DLL调用DllMain。
不要在DllMain中做的事情
一、直接或者间接调用LoadLibrary(Ex)
假如我们在A.dll中的DllMain收到DLL_PROCESS_ATTACH时,加载了B.dll;而B.dll中的DllMain在收到DLL_PROCESS_ATTACH时又去加载A.dll。则产生了循环依赖。但是注意不要想当然认为这个过程是A.dll的DllMain调用了B.dll的DllMain,B.dll的DllMain再调用了A.dll的DllMain这样的死循环。即使不出现循环依赖,如果出现《DllMain中不当操作导致死锁问题的分析——线程中调用GetModuleFileName、GetModuleHandle等导致死锁》中第三个例子的情况,也会死锁的。
二、使用CoInitializeEx
在CoInitializeEx底层会调用LoadLibraryEx,原因同A。
三、使用CreateProcess
CreateProcess在底层执行了加载DLL的操作。我用IDA查看Kernel32中的CreateProcess可以发现其底层调用的CreateProcessInternalW中有
四、使用User32或Gdi32中的函数
User32和Gdi32中部分函数在调用的底层会加载其他DLL。
五、使用托管代码
运行托管代码需要加载其他DLL。
六、与其他线程同步执行
由《DllMain中不当操作导致死锁问题的分析--加载卸载DLL与DllMain死锁的关系》、《DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子》和《DllMain中不当操作导致死锁问题的分析--线程退出时产生了死锁》可知,进程创建和销毁以及DLL的加载都要进入PEB的LoadLock临界区。如果占用了LoaderLock临界区的线程在等待一个需要经过临界区才能结束的线程时,就发生了死锁。以上3篇博文中均有案例。
七、同步对象
如果该同步对象的释放需要获得PEB中的LoaderLock,而占用该临界区的线程又要去等待这个同步对象,则会死锁。其实F中的线程也算是个同步对象。案例详见《DllMain中不当操作导致死锁问题的分析——线程中调用GetModuleFileName、GetModuleHandle等导致死锁》中例子。
八、使用CreateThread
理由同六。
九、使用ExitThread
理由同六。
十、寄希望于DisableThreadLibraryCalls解决死锁问题
由《DllMain中不当操作导致死锁问题的分析--DisableThreadLibraryCalls对DllMain中死锁的影响》可知。DisableThreadLibraryCalls的实现逻辑是:找到PEB结构中用于保存加载器信息的结构体对象Ldr。
Ldr对象的InMemoryOrderModuleList用户保存已经加载的DLL的链表。
它遍历这个链表,找到调用DisableThreadLibraryCalls的DLL的信息,将该信息中的Flags字段设置或上0x40000。
而创建线程在底层将调用LdrpInitializeThread(详见《DllMain中不当操作导致死锁问题的分析--DisableThreadLibraryCalls对DllMain中死锁的影响》)。该函数一开始便进入了PEB中LoaderLock临界区,在该临界区中根据PEB中LDR的InMemoryOrderModuleList遍历加载的DLL,然后判断该DLL信息的Flags字段是否或上了0x40000。如果或上了,就不调用DllMain。如果没或上,就调用DllMain。这说明DisableThreadLibraryCalls对创建线程时是否进入临界区无关。
在退出线程时底层将调用LdrShutdownThread(详见《DllMain中不当操作导致死锁问题的分析--线程退出时产生了死锁》)。该函数逻辑和LdrpInitializeThread相似,只是在调用DllMain时传的是DLL_THREAD_DETACH。所以DisableThreadLibraryCalls对LdrShutdownThread是否进入临界区也是没有影响的。
[转]DllMain中不当操作导致死锁问题的分析——DllMain中要谨慎写代码(完结篇)的更多相关文章
- VS·调试过程中某个操作导致调试突然退出之解决方案
阅文时长 | 0.11分钟 字数统计 | 232字符 主要内容 | 1.引言&背景 2.声明与参考资料 『VS·调试过程中某个操作导致调试突然退出之解决方案』 编写人 | SCscHero 编 ...
- VS中,添加完Web引用(WebServer引用/Web服务引用),写代码时引用不到
VS中,添加完Web引用(WebServer引用/Web服务引用),写代码时引用不到 添加完之后要等一会儿 等一会儿 等一会儿 就有了
- [经验分享] MySQL Innodb表导致死锁日志情况分析与归纳【转,纯学习】
在定时脚本运行过程中,发现当备份表格的sql语句与删除该表部分数据的sql语句同时运行时,mysql会检测出死锁,并打印出日志. 两个sql语句如下: (1)insert into backup_ta ...
- MySQL Innodb表导致死锁日志情况分析与归纳
发现当备份表格的sql语句与删除该表部分数据的sql语句同时运行时,mysql会检测出死锁,并打印出日志 案例描述在定时脚本运行过程中,发现当备份表格的sql语句与删除该表部分数据的sql语句同时 ...
- 多个程序对sql server中的表进行查询和插入操作导致死锁
最近在做一个项目,是要用多个程序对sql server中的相同的数据库进行操作(查询和插入),所以在开始的时候常会出现死锁问题,后来在网上进行了咨询,发现了一些解决方法,留作大家参考: 并发去操纵一张 ...
- BroadCastReceiver中耗时操作导致ANR
現象:廣播接收器中進行耗時的I/O操作導致ANR. 查資料發現每次广播到来时 , 会重新创建 BroadcastReceiver 对象 , 并且调用 onReceive() 方法 , 执行完以后 该对 ...
- [译]async/await中使用阻塞式代码导致死锁 百万数据排序:优化的选择排序(堆排序)
[译]async/await中使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Cleary的 ...
- [译]async/await中使用阻塞式代码导致死锁
原文:[译]async/await中使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Clea ...
- RocketMQ中Broker的HA策略源码分析
Broker的HA策略分为两部分①同步元数据②同步消息数据 同步元数据 在Slave启动时,会启动一个定时任务用来从master同步元数据 if (role == BrokerRole.SLAVE) ...
随机推荐
- Failed to load libGL.so in android
使用命令:find / -name libGL.so 得到: /usr/lib/i386-linux-gnu/libGL.so /usr/lib/i386-linux-gnu/mesa/libGL.s ...
- UVa 1617 Laptop (贪心)
题意:有n个长度为1的线段,确定它们的起点,使得第i个线段在[ri,di]之间,输出空隙数目的最小值. 析:很明显的贪心题,贪心策略是这样的,先把所有的区间排序,原则是按右端点进行排序,如果相等再按左 ...
- c++,windows中的字符问题
string与char*的转换方法 string a; char *b=a.c_str(); string a=new String(b); a=b; LPCWSTR是unicode的字符串,LPCS ...
- PHP时间格式化封装函数
/*格式化时间戳为小时,分钟,秒,几天前等 */function dgmdate($timestamp, $format = 'dt', $timeoffset = '9999', $uformat ...
- bmp格式解析
最近一直在写图像处理的作业,好多啊 bmp格式简介 a.格式组成 1:位图头文件数据结构,它包含BMP图像文件的类型.显示内容等信息: 2:位图信息数据结构,它包含有BMP图像的宽.高.压缩方法,以及 ...
- 结构类模式(四):装饰(Decorator)
定义 动态地将责任附加到对象上.若要扩展功能,装饰者提供了比继承更有弹性的替代方案. 它是通过创建一个包装对象,也就是装饰来包裹真实的对象. 特点 装饰对象和真实对象有相同的接口.这样客户端对象就能以 ...
- Jmeter_初步认识随笔
1. 简介 Apache JMeter是100%纯java桌面应用程序,被设计用来测试客户端/服务器结构的软件(例如web应用程序).它可以用来测试包括基于静态和动态资源程序的性能,例如静态文件,Ja ...
- 如何在协作开发安卓项目中打jar包给合作人
一般情况下,id都是安卓自动生成的.使用时只要用R.id.xx就可以了.但是,在合作开发安卓时,需要将自己开发的代码部分打成jar包,甚至做混淆. 这就需要使用java的反射机制.在取id时使用如下类 ...
- 在Button的click事件中引起客户端JavaScript
void action1_Execute(object sender, SimpleActionExecuteEventArgs e) { WebWindow.CurrentRequestWindow ...
- Apache开启伪静态后报500错误.
参考:http://blog.163.com/lgh_2002/blog/static/44017526201051452939761/ 加载Rewrite模块: 在conf目录下httpd.conf ...