DllMain中不当操作导致死锁问题的分析--进程对DllMain函数的调用规律的研究和分析
不知道大家是否思考过一个过程:系统试图运行我们写的程序,它是怎么知道程序起始位置的?很多同学想到,我们在编写程序时有个函数,类似Main这样的名字。是的!这就是系统给我们提供的控制程序最开始的地方(注意这儿是提供给我们的,而实际有比这个还要靠前的main)。于是看到DllMain就可以想到它是干嘛的了:Dll的入口点函数。那何时调用这个函数的呢?以及各种调用场景都传给了它什么参数呢?(转载请指明出于breaksoftware的csdn博客)
进程对DLL的载入卸载,以及新线程的创建和退出都会导致对DllMain的调用。于是,我们设计了如下流程
为了尽可能排除一些因素对我们实验的影响,所有线程函数公用一个简单的例程函数
static DWORD WINAPI ThreadRoutine(LPVOID lpParam) {DWORD dwTID = GetCurrentThreadId();PrintLog("Thread%s %u\n", (LPSTR)lpParam, dwTID );Sleep(15000);PrintLog("\nThread%s Will Exit\n", (LPSTR)lpParam );return 0;
}
DllMain函数也是非常简单,两个DLL的DllMain函数99.99%是相同的,只是在最后输出所在DLL时列出了各自的DLL名字,以Dll1为例
BOOL APIENTRY DllMain( HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{string strReason;DWORD TID = GetCurrentThreadId();switch (ul_reason_for_call) {case DLL_PROCESS_ATTACH:{strReason = "DLL_PROCESS_ATTACH";}break;case DLL_PROCESS_DETACH:{strReason = "DLL_PROCESS_DETACH";}break;case DLL_THREAD_ATTACH:{strReason = "DLL_THREAD_ATTACH";}break;case DLL_THREAD_DETACH:{strReason = "DLL_THREAD_DETACH";}break;default:{strReason = "default";}break;}PrintLog("Dll1 TID:%u %s\n", TID, strReason.c_str() );return TRUE;
}
现在我们说下我设计这个流程的考虑:
0 1 这个过程是为了查看Dll加载后,DllMain被调用是否受之前创建的线程影响。如果受到影响,我们应该能看到Dll1中输出的信息中包含有线程A TID的记录。反之则没有记录。
2 这个过程是为了验证创建新线程,对之前加载的Dll的DllMain调用情况。如果Dll1的DllMain输出了线程B TID记录,那么说明新线程创建会让之前加载Dll的DllMain。反之说明创建新线程不会调用之前加载DLL的DllMain。
3 是为了再次验证0,1这个过程得出的结论。
4 是为了再次验证2这个过程得出的结论。
5 创建的线程是为了之后验证线程正常退出和强制关闭之间的影响。
61,62 是为了验证FreeLibrary是否会对之前对此DLL调用DllMain的线程存在影响。也就是想查看之前在创建线程时对Dll调用DllMain的线程,是否会发现要FreeLibrary了,从而对该Dll再调用DllMain做某些处理(比如清理)。该过程导致DllMain中输出的信息包括那些线程TID的记录,则说明存在影响(其他线程调用DllMain),否则说明不存在影响(其他线程不调用DllMain)。
6 验证通过强制关闭线程对DllMain调用的影响。
7 8 9 验证对不同DLL的DllMain调用情况可能存在不同的线程,在退出时,是否会调用DllMain,以及它们对DllMain的调用规律。
10 101 102 103 104等是通过不同方式验证进程退出对DllMain是否存在调用,以及调用的规律。
我们先在主线程中用 1 2 3 4 5 6 7 8 9 10 这个流程,其结果是
MainTid:1056 | 主线程ID是1056 | |
0 | CreatThread A ThreadA 3156 | A线程ID是3156 |
1 | LoadLibraryA Dll1 Dll1 TID:1056 DLL_PROCESS_ATTACH | Dll1加载了,它是主线程(1056)加载的。调用原因是DLL_PROCESS_ATTACH。而它的加载,并不会导致之前创建的A线程对其调用DllMain。 |
2 | CreatThread B Dll1 TID:4784 DLL_THREAD_ATTACH ThreadB 4784 | B线程(4784)在执行到线程函数之前,会去调用之前加载了但还没有卸载的Dll1的DllMain函数。调用原因是DLL_THREAD_ATTACH,而不是之前的DLL_PROCESS_ATTACH。 |
3 | LoadLibraryA Dll2 Dll2 TID:1056 DLL_PROCESS_ATTACH | Dll2加载了,调用其DllMain是主线程。调用原因是DLL_PROCESS_ATTACH。加载后,并不会导致线程A、B去调用其DllMain。 |
4 | CreatThread C Dll1 TID:4052 DLL_THREAD_ATTACH Dll2 TID:4052 DLL_THREAD_ATTACH ThreadC 4052 | C线程(4052)在执行其线程函数之前,会去调用之前在主线程中加载了但还没有卸载的DLL的DllMain函数,调用原因是DLL_THREAD_ATTACH。 |
5 | CreatThread D Dll1 TID:3440 DLL_THREAD_ATTACH Dll2 TID:3440 DLL_THREAD_ATTACH ThreadD 3440 | 同上。 |
6 | TerminateThread D | 强制关闭线程,不会导致任何DllMain的调用。 |
7 | ThreadA Will Exit Dll2 TID:3156 DLL_THREAD_DETACH Dll1 TID:3156 DLL_THREAD_DETACH | 线程A退出之前,会调用之前加载了但还没有卸载的所有DLL的DllMain。注意,此处调用是线程A(3156),而不是主线程(1056)。调用原因是DLL_THREAD_DETACH。 |
8 | ThreadB Will Exit Dll2 TID:4784 DLL_THREAD_DETACH Dll1 TID:4784 DLL_THREAD_DETACH | 同上。 |
9 | ThreadC Will Exit Dll2 TID:4052 DLL_THREAD_DETACH Dll1 TID:4052 DLL_THREAD_DETACH | 同上。 |
10 | Proceess Exit Dll2 TID:1056 DLL_PROCESS_DETACH Dll1 TID:1056 DLL_PROCESS_DETACH | 主线程退出前,会调用所有加载了但还没有卸载的DLL的DllMain。调用原因是DLL_PROCESS_DETACH。 |
为了排除主线程对我们环境的影响我们看下在子线程中执行以上流程的结果(之后我们对流程的修改,都将建立在子线程执行流程的基础之上)
MainTid:5536 | 执行的线程ID是5536 | |
0 | CreatThread A ThreadA 5684 | A线程ID是5684 |
1 | LoadLibraryA Dll1 Dll1 TID:5536 DLL_PROCESS_ATTACH | Dll1加载了,它是执行线程(5536)加载的。调用原因是DLL_PROCESS_ATTACH。而它的加载,并不会导致之前创建的A线程对其调用DllMain。 |
2 | CreatThread B Dll1 TID:4716 DLL_THREAD_ATTACH ThreadB 4716 | B线程(4716)在执行到线程函数之前,会去调用之前加载了但还没有卸载的Dll1的DllMain函数。调用原因是DLL_THREAD_ATTACH,而不是之前的DLL_PROCESS_ATTACH。 |
3 | LoadLibraryA Dll2 Dll2 TID:5536 DLL_PROCESS_ATTACH | Dll2加载了,调用其DllMain是执行线程(5536)。调用原因是DLL_PROCESS_ATTACH。加载后,并不会导致线程A、B去调用其DllMain。 |
4 | CreatThread C Dll1 TID:2620 DLL_THREAD_ATTACH Dll2 TID:2620 DLL_THREAD_ATTACH ThreadC 2620 | C线程(2620)在执行其线程函数之前,会去调用之前在执行线程中加载了但还没有卸载的DLL的DllMain函数,调用原因是DLL_THREAD_ATTACH。 |
5 | CreatThread D Dll1 TID:1016 DLL_THREAD_ATTACH Dll2 TID:1016 DLL_THREAD_ATTACH ThreadD 1016 | 同上。 |
6 | TerminateThread D | 强制关闭线程,不会导致任何DllMain的调用。 |
7 | ThreadA Will Exit Dll2 TID:5684 DLL_THREAD_DETACH Dll1 TID:5684 DLL_THREAD_DETACH | 线程A退出之前,会调用之前加载了但还没有卸载的所有DLL的DllMain。注意,此处调用是线程A(5684),而不是执行线程(5536)。调用原因是DLL_THREAD_DETACH。 |
8 | ThreadB Will Exit Dll2 TID:4716 DLL_THREAD_DETACH Dll1 TID:4716 DLL_THREAD_DETACH | 同上。 |
9 | ThreadC Will Exit Dll2 TID:2620 DLL_THREAD_DETACH Dll1 TID:2620 DLL_THREAD_DETACH | 同上。 |
10 | Dll2 TID:5536 DLL_THREAD_DETACH Dll1 TID:5536 DLL_THREAD_DETACH Proceess Exit Dll2 TID:3904 DLL_PROCESS_DETACH Dll1 TID:3904 DLL_PROCESS_DETACH | 执行线程(5536)在退出时调用了它加载了但还没有卸载的两个DLL的DllMain,调用原因是DLL_THREAD_DETACH。 主线程退出前,会调用所有之前加载了但还没有卸载的DLL的DllMain。调用原因是DLL_PROCESS_DETACH。 |
看了如此一串后,我想很多人都会有点晕,现在我总结一下:
一 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。
我们将过程6替换为过程61,并在子线程中执行,结果大部分相似,我把不一样的地方列出来(执行线程TID是4752)
61 | Dll1 TID:4752 DLL_PROCESS_DETACH | 执行线程(4752)中卸载了Dll1,则执行线程(4752)调用该DLL的DllMain,且原因是DLL_PROCESS_DETACH。 |
6 | TerminateThread D | |
7 | ThreadA Will Exit Dll2 TID:3688 DLL_THREAD_DETACH | 线程A不会对已经卸载了的Dll1调用其DllMain。 |
8 | ThreadB Will Exit Dll2 TID:1872 DLL_THREAD_DETACH | 同上。 |
9 | ThreadC Will Exit Dll2 TID:5600 DLL_THREAD_DETACH | 同上。 |
10 | Dll2 TID:4752 DLL_THREAD_DETACH Proceess Exit Dll2 TID:2364 DLL_PROCESS_DETACH | 同上。 进程退出时,对尚未卸载的DLL调用其DllMain,且原因是DLL_PROCESS_DETACH。 |
基于以上结果,我们将以上四五两点结论再严谨点
四 线程正常退出时,会调用进程中还没卸载的DLL的DllMain,且调用原因是DLL_THREAD_DETACH。
五 进程正常退出时,会调用(不一定是主线程)该进程中还没卸载的DLL的DllMain,且调用原因是DLL_PROCESS_DETACH。
并得出以下结论
七 DLL从进程空间中卸载出去前,会被卸载其的线程调用其DllMain,且调用原因是DLL_PROCESS_DETACH。
如果仔细看过我试验结果的同学,应该看到一个现象:线程A不会对Dll1调用DllMain(DLL_THREAD_ATTACH),而在线程A退出时,却会调用DLL1的DllMain(DLL_THREAD_DETACH)。这种不同步的现象是不是让你内心感觉很疑惑?你说windows为什么要这么设计呢?我不明白。《windows核心编程》也有对该现象的一个描述:虽然当系统将该线程连接到该DLL的时候,不会向该DLL发送DLL_THREAD_ATTACH通知。但是当系统将该线程与DLL解除连接的时候,却会向该DLL发送DLL_THREAD_DETACH通知。由于这个原因,我们在进行与线程相关的清理时必须极其小心。幸运的是,在大多数程序中,调用Loadlibrary的线程与调用Freelibrary的线程是同一个线程。
现在我们再将过程61换成6,并依次用101(TerminateProcess)、102(ExitProcess)、103(TerminateThread)、104(ExitThread)替换10。我列一下不同点
101 | The thread 'Win32 Thread' (0x142c) has exited with code -1 (0xffffffff). The program '[6128] CallDllMain.exe: Native' has exited with code -1 (0xffffffff). | 执行线程(0x142c)和进程退出时未对任何加载的DLL调用DllMain。 没有对主线程退出的捕获。 |
102 | The thread 'Win32 Thread' (0x1214) has exited with code -1 (0xffffffff). Dll2 TID:4660 DLL_PROCESS_DETACH Dll1 TID:4660 DLL_PROCESS_DETACH The program '[2576] CallDllMain.exe: Native' has exited with code -1 (0xffffffff). | 主进程(0x1214) 提前意外关闭,未对任何加载的DLL调用DllMain。 执行线程(4660)退出时对加载了的DLL调用了其DllMain的DLL_PROCESS_DETACH。 |
103 | The thread 'Win32 Thread' (0x81c) has exited with code -1 (0xffffffff). Proceess Exit Dll2 TID:2356 DLL_PROCESS_DETACH Dll1 TID:2356 DLL_PROCESS_DETACH The program '[5860] CallDllMain.exe: Native' has exited with code 0 (0x0). | 执行线程(0x81c)退出时未对任何加载的DLL调用DllMain。 主进程(2356)退出时对加载了的DLL调用了其DllMain的DLL_PROCESS_DETACH。 |
104 | Dll2 TID:5600 DLL_THREAD_DETACH Dll1 TID:5600 DLL_THREAD_DETACH The thread 'Win32 Thread' (0x15e0) has exited with code -1 (0xffffffff). Proceess Exit Dll2 TID:632 DLL_PROCESS_DETACH Dll1 TID:632 DLL_PROCESS_DETACH The program '[284] CallDllMain.exe: Native' has exited with code 0 (0x0). | 执行线程(5600)退出时对加载的DLL调用了DllMain,且原因是DLL_THREAD_DETACH。 主进程(632)退出时对加载了的DLL调用了其DllMain的DLL_PROCESS_DETACH。 |
从以上我们可以看出Terminate(101、103)类型函数比Exit(102、104)类型函数暴力。
102例子中我们看到主线程退出后,子线程还在正常工作的场景,可以想象,可能是ExitProcess是直接TerminateThread主线程了。总结如下:
八 TerminateProcess 将导致线程和进程在退出时不对未卸载的DLL进行DllMain调用。
九 ExitProcess将导致主线程意外退出,子线程对未卸载的DLL进行了DllMain调用,且调用原因是DLL_PROCESS_DETACH。(《windows核心编程》上是说,调用ExitProcess函数的线程将负责执行DllMain函数的代码。(DLL_PROCESS_DETACH))
十 ExitThread是最和平的退出方式,它会让线程退出前对未卸载的DLL调用DllMain。
我们再考虑下DisableThreadLibraryCalls函数对DllMain函数的调用的影响。我们在Dll1的DllMain中加入DisableThreadLibraryCalls(hModule);我们观察下结果
MainTid:7760 | ||
0 | CreatThread A ThreadA 7992 | |
1 | LoadLibraryA Dll1 TID:7760 DLL_PROCESS_ATTACH | 加载DLL1,执行线程调用其DllMain,原因是DLL_PROCESS_ATTACH。 |
2 | CreatThread B ThreadB 6684 | 线程B创建不会对DLL1调用DllMain了。因为DLL1中调用了DisableThreadLibraryCalls。 |
3 | LoadLibraryA Dll2 Dll2 TID:7760 DLL_PROCESS_ATTACH | 加载DLL2。执行线程调用其DllMain,原因是DLL_PROCESS_ATTACH。 |
4 | CreatThread C Dll2 TID:8168 DLL_THREAD_ATTACH ThreadC 8168 | 线程C创建不会对DLL1调用DllMain了。但是会对没有调用过DisableThreadLibraryCalls的DLL2调用DllMain。 |
5 | CreatThread D Dll2 TID:1848 DLL_THREAD_ATTACH ThreadD 1848 | 同上 |
6 | TerminateThread D | |
7 | ThreadA Will Exit Dll2 TID:7992 DLL_THREAD_DETACH | 线程A退出,不会对DLL1调用DllMain了。但是会对没有调用过DisableThreadLibraryCalls的DLL2调用DllMain。 |
8 | ThreadB Will Exit Dll2 TID:6684 DLL_THREAD_DETACH | 同上 |
9 | ThreadC Will Exit Dll2 TID:8168 DLL_THREAD_DETACH | 同上 |
10 | Dll2 TID:7760 DLL_THREAD_DETACH Proceess Exit Dll2 TID:8096 DLL_PROCESS_DETACH Dll1 TID:8096 DLL_PROCESS_DETACH | 执行线程(7760)出前不会对DLL1调用DllMain了。 进程退出前,主线程会对DLL1和DLL2调用DllMain。 |
通过以上我们可以再得出一个结论
十一 线程的创建和退出不会对调用了DisableThreadLibraryCalls的DLL调用DllMain。
最后,我们考虑下LoadLibrary和Freelibrary对DllMain的影响。我将在两个线程中尝试多次LoadLibrary同一个Dll,多次Freelibrary同一个Dll。
PrintLog("LoadLibraryA1\n");
HMODULE hDll1 = LoadLibraryA("DLL1");
WAIT();
PrintLog("LoadLibraryA2\n");
hDll1 = LoadLibraryA("DLL1");
WAIT();
PrintLog("LoadLibraryA3\n");
hDll1 = LoadLibraryA("DLL1");
WAIT();
CreateThread( NULL, NULL, ThreadFun, NULL, 0, NULL );
Sleep(35000);
PrintLog("FreeLibrary1\n");
FreeLibrary(hDll1);
WAIT();
PrintLog("FreeLibrary2\n");
FreeLibrary(hDll1);
WAIT();
PrintLog("FreeLibrary3\n");
FreeLibrary(hDll1);
WAIT();
其结果是
LoadLibraryA1
Dll1 TID:4620 DLL_PROCESS_ATTACH
LoadLibraryA2
LoadLibraryA3
Dll1 TID:5560 DLL_THREAD_ATTACH 子线程创建时调用的
MainTid:5560
……
FreeLibrary1
FreeLibrary2
FreeLibrary3
Dll1 TID:4620 DLL_PROCESS_DETACH
子线程创建时调用的
MainTid:5560
……
FreeLibrary1
FreeLibrary2
FreeLibrary3
Dll1 TID:4620 DLL_PROCESS_DETACH
我们发现第一句LoadLibrary对DllMain产生了调用DLL_PROCESS_ATTACH,而第二三句LoadLibrary不会对DllMain产生任何调用(《windows核心编程》系统不会让进程的主线程用DLL_THREAD_ATTACH值调用DLLMain函数。系统不会让用DLL_PROCESS_ATTACH来调用该DLL的DllMain函数的线程不会得到DLL_THREAD_ATTACH通知);第一二次FreeLibrary对DllMain没有产生调用,而第三次FreeLibrary对DllMain产生了DLL_PROCESS_DETACH调用。
可以见得,在一个线程中对DLL产生了DllMain调用后,就不会因为Loadlibrary再发生DllMain的调用。
我们前两次FreeLibrary不会对DllMain进行调用,而第三次就是DLL_PROCESS_DETACH。同样这个线程中LoadLibraryA也被调用了三次。可以想象LoadLibraryA和FreeLibrary之间存在一个计数器的关系(LoadLibraryA加计数器,FreeLibrary减计数器)。正如《windows核心编程》上所说:当系统第一次将一个DLL映射到进程的地址空间中时……如果之后一个线程在调用Loadlibrary(Ex)来载入一个已经被映射到进程的地址空间的DLL,那么操作系统只不过是递增该DLL的使用计数,而不会再次用DLL_PROCESS_ATTACH来调用DllMain函数。
本文中介绍了经过几轮实验,得出了11条规律。我们之后研究DllMain导致的死锁,将用到这些规律。
相关文章:
力挺Python!同是程序员,为啥同事年前就实现了财务自由?
人红是非多,最近Python就遇到了这样的问题。与技术社区上一片「形势大好」对比鲜明的是,国内技术圈却一直存在对Python,「力挺」和「吃瓜」两派阵营,针锋相对,那么,Python到底有没有用,真相究竟…

C# 判断远程文件是否存在
#region 判断远程文件是否存在/// <summary>/// 判断远程文件是否存在/// </summary>/// <param name"fileUrl"></param>/// <returns></returns>public static bool RemoteFileExists(string fileUrl){HttpWebRequest re null…

DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子
有了前面两节的基础,我们现在切入正题:研究下DllMain为什么会因为不当操作导致死锁的问题。首先我们看一段比较经典的“DllMain中死锁”代码。(转载请指明出于breaksoftware的csdn博客) //主线程中 HMODULE h LoadLibraryA(strD…
性能超FPN!北大、阿里等提多层特征金字塔网络
作者 | Qijie Zhao等编译 | 李杰出品 | AI科技大本营(ID:rgznai100)特征金字塔网络具有处理不同物体尺度变化的能力,因此被广泛应用到one-stage目标检测网络(如DSSD,RetinaNet,RefineDet)和two-…

什么是WIFI
WIFI全称Wireless Fidelity,又称802.11b标准,它的最大优点就是传输速度较高,可以达到11Mbps,另外它的有效距离也很长,同时也与已有的各种802.11DSSS设备兼容。 WIFI是由AP(Access Point)和无线网卡组成的无线网络。…
Android入门——电话拨号器和4种点击事件
关于HelloWorld为,电话拨号程序还AndroidA入门demo,从这个样例我们要理清楚做安卓项目的思路。大体分为三步: 1.理解需求,理清思路 2.设计UI 3.代码实现 电话拨号器 1. 理解需求: *一个文本框——用来接收电话号码 *一个button——用来触发事…

DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子2
本文介绍使用Windbg去验证《DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子》中的结论,调试对象是文中刚开始那个例子。(转载请指明出于breaksoftware的csdn博客) 1 g 让程序运行起来 2 ctrlbreak 中断程序 3 ~ 查看…
从入门到深入:移动平台模型裁剪与优化的技术探索与工程实践
可以看到,通过机器学习技术,软件或服务的功能和体验得到了质的提升。比如,我们甚至可以通过启发式引擎智能地预测并调节云计算分布式系统的节点压力,以此改善服务的弹性和稳定性,这是多么美妙。而对移动平台来说&#…

我在不炎熱也不抑鬱的秋天,依然不抽煙
写过几次电影的观后感,挺过瘾.最近看到my little airport的那张新唱片,再也没有办法保持沉默了 为什么人家的唱片名都起的和小说一样,难得是为了证明听歌的人们都不喜欢动笔吗? 于是,我建了个类别,叫 我也会听歌.很明显,这里面会塞一些和歌相关的东西 这是第一篇

ubuntu安装redis的方法以及PHP安装redis扩展、CI框架sess使用redis的方法
为什么80%的码农都做不了架构师?>>> 再一次被网上那些教程误导后决定自己写一个。真心被那些奇怪的教程误导了好几次,之前研究其它东西的时候也是。蛋疼啊。 安装redis 直接用apt-get命令即可 sudo apt-get install redis-server 安装的时候…

浅谈数据库设计技巧
说到数据库,我认为不能不先谈数据结构。1996年,在我初入大学学习计算机编程时,当时的老师就告诉我们说:计算机程序=数据结构+算法。尽管现在的程序开发已由面向过程为主逐步过渡到面向对象为主,…
避免神经网络过拟合的5种技术(附链接) | CSDN博文精选
作者 | Abhinav Sagar翻译 | 陈超校对 | 王琦来源 | 数据派THU(ID:DatapiTHU)(*点击阅读原文,查看作者更多精彩文章)本文介绍了5种在训练神经网络中避免过拟合的技术。 最近一年我一直致力于深度学习领域。这段时间里,我使用过很多神经网络&a…

DllMain中不当操作导致死锁问题的分析--加载卸载DLL与DllMain死锁的关系
前几篇文章一直没有在源码级证明:DllMain在收到DLL_PROCESS_ATTACH和DLL_PROCESS_DETACH时会进入临界区。这个论证非常重要,因为它是使其他线程不能进入临界区从而导致死锁的关键。我构造了在DLL被映射到进程地址空间的场景,请看死锁时加载DL…

LinearLayout增加divider分割线
2019独角兽企业重金招聘Python工程师标准>>> 在android3.0及后面的版本在LinearLayout里增加了个分割线 android:divider"drawable/shape"<!--分割线图片--> android:showDividers"middle|beginning|end" <!--分割线位置--> 分割线…

JAVA游戏编程之二----j2me MIDlet 手机游戏入门开发--贪吃蛇
作者:雷神 QQ:38929568 QQ群:28048051JAVA游戏编程(满) 28047782(将满) 与前一款扫雷比较,这个游戏多了一个 类,用来显示动画,也是蛇要吃的物品类, 也有了代码…

DllMain中不当操作导致死锁问题的分析——线程中调用GetModuleFileName、GetModuleHandle等导致死锁
之前的几篇文章已经讲解了在DllMain中创建并等待线程导致的死锁的原因。是否还记得,我们分析了半天汇编才知道在线程中的死锁位置。如果对于缺乏调试经验的同学来说,可能发现这个位置有点麻烦。那么本文就介绍几个例子,它们会在线程明显的位置…
如何从菜鸡变成收割机,大厂面试的算法,你懂了吗?
是什么?让大厂面试显得逼格很高,是算法和数据结构吗?是的!!!Google工程师曾总结过,大厂之所以爱考察算法和数据结构是因为:算法能力能够准确辨别一个程序员的技术功底是否扎实&#…

Ejabberd源码解析前奏--配置
一、基本配置 配置文件将在你第一次启动ejabberd时加载,从该文件中获得的内容将被解析并存储到内部的ejabberd数据库中,以后的配置将从数据库加载,并且任何配置文件里的命令都会被添加到数据库里。 需要注意的是:ejabberd从不编辑…

DllMain中不当操作导致死锁问题的分析——DllMain中要谨慎写代码(完结篇)
之前几篇文章主要介绍和分析了为什么会在DllMain做出一些不当操作导致死锁的原因。本文将总结以前文章的结论,并介绍些DllMain中还有哪些操作会导致死锁等问题。(转载请指明出于breaksoftware的csdn博客) DllMain的相关特性 首先列出…
滴滴叶杰平:年运送乘客百亿次,AI如何“服务”出行领域?| BDTC 2019
出品 | AI科技大本营(ID:rgznai100)“如果把北京一天滴滴的轨迹数据放在一起,要覆盖北京所有道路差不多四百次,数据非常大、非常完整。”超5.5亿用户,年运送乘客100亿人次,除了中国地区,滴滴也在…

分析部署无线局域网的关键要素
在部署无线局域网时需要考虑的关键问题包括:确定单个接入点的RF覆盖,保证足够的支持所有用户的容量,以及考虑RF信号损耗因素。 单个AP的覆盖 网络设计师必须通过研究AP的服务范围来决定单个AP的覆盖。数据速率是一种距离函数ÿ…

Delphi调用java开发的WebService,传入参数出错
http://www.cnblogs.com/zhangzhifeng/p/3397053.html 调用没有参数的服务正常,当调用有参数的服务出现以下错误java.util.concurrent.ExecutionException: java.lang.NullPointerException 另外加了RIO.HTTPWebNode.UseUTF8InHeader : True;InvRegistry.RegisterInvokeOptions…
B站收藏6.1w+!这门课拯救你薄弱的计算机基础
作者 | Rocky0429来源 | Python空间大家好,我是 Rocky0429,一个对计算机基础一无所知的蒟蒻...作为一个所谓的计算机科班出身的人来说,特别难为情的是自己的计算机基础很差,比如计算机网络当年一度差点挂掉,多亏当时…

一种不会导致资源泄露的“终止”线程的方法
在项目工程中,我们可能会使用第三方开发的模块。该模块提供一个接口用于完成非常复杂和耗时的工作。我们一般不会将该API放在UI线程中执行,而是启动一个线程,用工作线程去执行这个耗时的操作。(转载请指明出于breaksoftware的csdn…

TCP/IP详解学习笔记(9)-TCP协议概述
终于看到了TCP协议,这是TCP/IP详解里面最重要也是最精彩的部分,要花大力气来读。前面的TFTP和BOOTP都是一些简单的协议,就不写笔记了,写起来也没啥东西。TCP和UDP处在同一层---运输层,但是TCP和UDP最不同的地方是&…

在windows程序中嵌入Lua脚本引擎--使用VS IDE编译Luajit脚本引擎
前些天听到一个需求:某业务方需要我们帮忙清理用户电脑上的一些废弃文件。同事完成这个逻辑的方案便是在我们程序中加入了一个很“独立”的业务逻辑:检索和删除某个程序产生的废弃文件。试想,该“独立”的逻辑之后会如何?被删掉&a…
优酷智能档在大型直播场景下的技术实践
作者 | 阿里文娱高级技术专家 肖文良 本文为阿里文娱高级技术专家肖文良在【阿里文娱2019双11猫晚技术沙龙】中的演讲,主要内容为如何通过优酷智能档,降低用户卡顿尤其是双11直播场景下,提升用户观看体验。具体包括智能档的落地挑战、算法架…

主题:CS0016: 未能写入输出文件“c:#92;WINDOWS#92;Microsoft.NET#92;***.dll”错误处理...
刚装完.NET环境,在编译时出现了如下错误: 编译器错误信息:CS0016: 未能写入输出文件“c:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\Temporary ASP.NET Files\***\*****.dll”--“拒绝访问。” 错误的处理: 出现CS0016的原因一般是…

关于 android 加载 res 图片 out of memory 问题 解决 同样适用于 sd卡图片
2019独角兽企业重金招聘Python工程师标准>>> 发现android 加载res图片如果过多也会崩溃 android 也是使用 Bitmap bm BitmapFactory.decodeResourceStream(res, value, is, pad, opts); 来加载图片,不同他一般不会释放,如果图片太多就崩溃了 不过解决方法就更简…
近期必读的6篇NeurIPS 2019零样本学习论文
来源 | 专知(ID:Quan_Zhuanzhi) 【导读】NeurIPS 是全球最受瞩目的AI、机器学习顶级学术会议之一,每年全球的人工智能爱好者和科学家都会在这里聚集,发布最新研究。NIPS 2019大会已经在12月8日-14日在加拿大温哥华举行,…