零基础逆向工程35_Win32_09_临界区_CRITICAL_SECTION结构

1 引入

为什么会存在临界区这中机制呢?是为多线程同时访问全局变量而引入的。也就是上一篇帖子的末尾流出的问题程序的解决办法。

看懂了上面的,那么我们再罗嗦总结一下:
1.多线程访问全局变量时,存在线程安全问题。
2.局部变量不存在线程安全问题。

2 临界区的使用

2.1 创建CRITICAL_SECTION:

CRITICAL_SECTION cs;

2.2 在使用前进行初始化

InitializeCriticalSection(&cs);		

2.3 在函数中使用

DWORD WINAPI 线程A(PVOID pvParam) 		
{		
      EnterCriticalSection(&cs);		
		
      //对全局遍历X的操作		
		
      LeaveCriticalSection(&cs);		
   return(0);		
}		

DWORD WINAPI 线程B(PVOID pvParam) 		
{		
      EnterCriticalSection(&g_cs);		
		
      //对全局遍历X的操作		
		
      LeaveCriticalSection(&g_cs);		
   return(0);		
}		

2.4 删除CRITICAL_SECTION

VOID DeleteCriticalSection(PCRITICAL_SECTION pcs);//当线程不再试图访问共享资源时		

3 CRITICAL_SECTION

3.1 结构

typedef struct _RTL_CRITICAL_SECTION {
    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
    LONG LockCount;
    LONG RecursionCount;
    HANDLE OwningThread;       
    HANDLE LockSemaphore;
    DWORD SpinCount;
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;

LockCount:
它被初始化为数值 -1
此数值等于或大于 0 时,表示此临界区被占用

等待获得临界区的线程数:LockCount - (RecursionCount -1)

RecursionCount:
此字段包含所有者线程已经获得该临界区的次数

OwningThread:
此字段包含当前占用此临界区的线程的线程标识符
此线程 ID 与GetCurrentThreadId 所返回的 ID 相同

3.2 测试代码

#include "stdafx.h"		
#include <windows.h>		
CRITICAL_SECTION cs;		
		
DWORD WINAPI ThreadProc1(LPVOID lpParameter)		
{		
	for(int x=0;x<1000;x++)	
	{	
		  EnterCriticalSection(&cs);
		  
		  Sleep(1000);
		
		  printf("11111:%x %x %x\n",cs.LockCount,cs.RecursionCount,cs.OwningThread);
		  
		  LeaveCriticalSection(&cs);
		
	}	
	return 0;	
}		
		
DWORD WINAPI ThreadProc2(LPVOID lpParameter)		
{		
	for(int x=0;x<1000;x++)	
	{	
		EnterCriticalSection(&cs);
		
		Sleep(1000);
		
		printf("22222:%x %x %x\n",cs.LockCount,cs.RecursionCount,cs.OwningThread);
		
		LeaveCriticalSection(&cs);
		
	}	
		
	return 0;	
}		
		
DWORD WINAPI ThreadProc3(LPVOID lpParameter)		
{		
	for(int x=0;x<1000;x++)	
	{	
		EnterCriticalSection(&cs);
		
		Sleep(1000);
		
		printf("33333:%x %x %x\n",cs.LockCount,cs.RecursionCount,cs.OwningThread);
		
		LeaveCriticalSection(&cs);
		
	}	
		
	return 0;	
}		
		
DWORD WINAPI ThreadProc4(LPVOID lpParameter)		
{		
	for(int x=0;x<1000;x++)	
	{	
		EnterCriticalSection(&cs);
		
		Sleep(1000);
		
		printf("44444:%x %x %x\n",cs.LockCount,cs.RecursionCount,cs.OwningThread);
		
		LeaveCriticalSection(&cs);
		
	}	
		
	return 0;	
}		
		
		
int main(int argc, char* argv[])		
{		
	InitializeCriticalSection(&cs);	
		
	//printf("主线程:%x %x %x\n",cs.LockCount,cs.RecursionCount,cs.OwningThread);	
		
	//创建一个新的线程	
	HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1, 	
		NULL, 0, NULL);
		
	//创建一个新的线程	
	HANDLE hThread2 = ::CreateThread(NULL, 0, ThreadProc2, 	
		NULL, 0, NULL);
		
	//创建一个新的线程	
	HANDLE hThread3 = ::CreateThread(NULL, 0, ThreadProc3, 	
		NULL, 0, NULL);
		
	//创建一个新的线程	
	HANDLE hThread4 = ::CreateThread(NULL, 0, ThreadProc4, 	
		NULL, 0, NULL);
		
	//如果不在其他的地方引用它 关闭句柄	
	::CloseHandle(hThread1);	
	::CloseHandle(hThread2);	
	::CloseHandle(hThread3);	
	::CloseHandle(hThread4);	
		
	Sleep(1000*60*60);	
		
	return 0;	
}

4 使用时容易出错和造成程序不稳定的地方

4.1 怎样使用是合理的

场景1:

//1. 错误
DWORD WINAPI 线程A(PVOID pvParam) 		
{		
   EnterCriticalSection(&cs);
   while(g_nIndex < MAX_TIMES) 		
   {		
      //对全局遍历X的操作				
   }		
   LeaveCriticalSection(&cs);
   return(0);		
}
		
		
DWORD WINAPI 线程B(PVOID pvParam) 		
{		
   EnterCriticalSection(&cs);
   while(g_nIndex < MAX_TIMES) 		
   {		
      //对全局遍历X的操作				
   }		
   LeaveCriticalSection(&cs);
   return(0);		
}	

//2. 正确
DWORD WINAPI 线程A(PVOID pvParam) 		
{		
   		
   while(g_nIndex < MAX_TIMES) 		
   {		
      EnterCriticalSection(&cs);		
      //对全局遍历X的操作		
      LeaveCriticalSection(&cs);		
   }		
   		
   return(0);		
		
		
		
DWORD WINAPI 线程B(PVOID pvParam) 		
{		
   		
   while(g_nIndex < MAX_TIMES) 		
   {		
      EnterCriticalSection(&cs);		
      //对全局遍历X的操作		
      LeaveCriticalSection(&cs);		
   }		
   		
   return(0);		
}		

场景2:

//1. 错误
DWORD WINAPI 线程A(PVOID pvParam) 		
{		
   EnterCriticalSection(&cs);		
      //代码xxxxxx		
      //代码xxxxxx		
		
      //对全局遍历X的操作		
      		
      //代码xxxxxx		
      //代码xxxxxx		
   LeaveCriticalSection(&cs);		
}		
		
		
DWORD WINAPI 线程B(PVOID pvParam) 		
{		
   EnterCriticalSection(&cs);		
      //代码xxxxxx		
      //代码xxxxxx		
		
      //对全局遍历X的操作		
      		
      //代码xxxxxx		
      //代码xxxxxx		
   LeaveCriticalSection(&cs);		
}

//2. 正确
DWORD WINAPI 线程A(PVOID pvParam) 		
{				
      //代码xxxxxx		
      //代码xxxxxx		
	EnterCriticalSection(&cs);	
      //对全局遍历X的操作		
    LeaveCriticalSection(&cs);
      //代码xxxxxx		
      //代码xxxxxx		
}

DWORD WINAPI 线程B(PVOID pvParam) 		
{				
      //代码xxxxxx		
      //代码xxxxxx		
	EnterCriticalSection(&cs);	
      //对全局遍历X的操作		
    LeaveCriticalSection(&cs);
      //代码xxxxxx		
      //代码xxxxxx		
}

场景3:

//错误。应该将所有X全局变量放入临界区,否则是没有意义的
DWORD WINAPI 线程A(PVOID pvParam) 		
{		
		
      //代码xxxxxx		
      //代码xxxxxx		
   EnterCriticalSection(&cs);		
      //对全局遍历X的操作		
   LeaveCriticalSection(&cs);		
      //代码xxxxxx		
      //代码xxxxxx		
		
}		
		
		
DWORD WINAPI 线程B(PVOID pvParam) 		
{		
		
      //代码xxxxxx		
      //代码xxxxxx		
		
      //对全局遍历X的操作		
      		
      //代码xxxxxx		
      //代码xxxxxx		
}		

4.2 应该有多少个CRITICAL_SECTION

全局变量X
全局变量Y
全局变量Z

线程1
DWORD WINAPI ThreadFunc(PVOID pvParam) 
{
   EnterCriticalSection(&g_cs);
   使用X
   使用Y
   LeaveCriticalSection(&g_cs);
   return(0);
}

线程2
DWORD WINAPI ThreadFunc(PVOID pvParam) 
{
   EnterCriticalSection(&g_cs);
   使用X
   使用Z
   LeaveCriticalSection(&g_cs);
   return(0);
}

线程3
DWORD WINAPI ThreadFunc(PVOID pvParam) 
{
   EnterCriticalSection(&g_cs);
   使用Y
   使用X
   LeaveCriticalSection(&g_cs);
   return(0);
}

解决方案:

CRITICAL_SECTION g_csX; 
CRITICAL_SECTION g_csY; 
CRITICAL_SECTION g_csZ; 

线程1
DWORD WINAPI ThreadFunc(PVOID pvParam) 
{
   EnterCriticalSection(&g_csX);
   使用X
   LeaveCriticalSection(&g_csX);
   EnterCriticalSection(&g_csY);
   使用Y
   LeaveCriticalSection(&g_csY);

   return(0);
}

线程2
DWORD WINAPI ThreadFunc(PVOID pvParam) 
{
   EnterCriticalSection(&g_csX);
   使用X
   LeaveCriticalSection(&g_csX);
   EnterCriticalSection(&g_csZ);
   使用Z
   LeaveCriticalSection(&g_csZ);

   return(0);
}

线程3
DWORD WINAPI ThreadFunc(PVOID pvParam) 
{
   EnterCriticalSection(&g_csX);
   使用X
   LeaveCriticalSection(&g_csX);
   return(0);
}

posted @ 2017-11-19 09:42  flatcc  阅读(381)  评论(0编辑  收藏  举报