内核级临界资源怎么办?
- 如果这个临界资源是内核级临界资源,例如是跨进程共享的物理页,或者是一个文件。
- 这种情况下,不同进程同时访问内核级临界资源,这种情况下如何保证访问是安全的?
互斥体的使用
CreateMutex
创建一个互斥体。
HANDLE CreateMutexA(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
LPCSTR lpName
);
Parameters
lpMutexAttributes
A pointer to a SECURITY_ATTRIBUTES structure. If this parameter is NULL, the handle cannot be inherited by child processes.
The lpSecurityDescriptor member of the structure specifies a security descriptor for the new mutex. If lpMutexAttributes is NULL, the mutex gets a default security descriptor. The ACLs in the default security descriptor for a mutex come from the primary or impersonation token of the creator. For more information, see Synchronization Object Security and Access Rights.
bInitialOwner
If this value is TRUE and the caller created the mutex, the calling thread obtains initial ownership of the mutex object. Otherwise, the calling thread does not obtain ownership of the mutex. To determine if the caller created the mutex, see the Return Values section.
lpName
The name of the mutex object. The name is limited to MAX_PATH characters. Name comparison is case sensitive.
If lpName matches the name of an existing named mutex object, this function requests the MUTEX_ALL_ACCESS access right. In this case, the bInitialOwner parameter is ignored because it has already been set by the creating process. If the lpMutexAttributes parameter is not NULL, it determines whether the handle can be inherited, but its security-descriptor member is ignored.
If lpName is NULL, the mutex object is created without a name.
If lpName matches the name of an existing event, semaphore, waitable timer, job, or file-mapping object, the function fails and the GetLastError function returns ERROR_INVALID_HANDLE. This occurs because these objects share the same namespace.
The name can have a "Global" or "Local" prefix to explicitly create the object in the global or session namespace. The remainder of the name can contain any character except the backslash character (). For more information, see Kernel Object Namespaces. Fast user switching is implemented using Terminal Services sessions. Kernel object names must follow the guidelines outlined for Terminal Services so that applications can support multiple users.
The object can be created in a private namespace. For more information, see Object Namespaces.
A.cpp
#include <iostream>
#include <Windows.h>
int main(int argc, char* argv[])
{
//创建一个互斥体
HANDLE g_hMutex = CreateMutex(NULL, FALSE, (LPCWSTR)"XYZ");
//获取令牌
WaitForSingleObject(g_hMutex, INFINITE);
for (int i = 0; i < 10; i++)
{
Sleep(1000);
std::cout << "进程A的X线程:" << i << "\n";
}
//释放令牌
ReleaseMutex(g_hMutex);
return 0;
}
B.cpp
#include <iostream>
#include <Windows.h>
int main(int argc, char* argv[])
{
//创建一个互斥体
HANDLE g_hMutex = CreateMutex(NULL, FALSE, (LPCWSTR)"XYZ");
//获取令牌
WaitForSingleObject(g_hMutex, INFINITE);
for (int i = 0; i < 10; i++)
{
Sleep(1000);
std::cout << "进程B的Y线程:" << i << "\n";
}
//释放令牌
ReleaseMutex(g_hMutex);
return 0;
}
互斥体与线程锁的区别
- 线程锁只能用于单个进程的线程控制。
- 互斥体可以设定等待超时,但线程锁不能。
- 线程意外终结时,互斥体可以避免无限等待。
- 互斥体效率没有线程锁高。
互斥体实现防止程序多开
详情见MSDN文档,CreateMutex() 的返回值:
Return value
如果函数成功,则返回值是新创建的互斥对象的句柄。
如果函数失败,则返回值为NULL。要获取扩展错误信息,请调用GetLastError。
如果互斥体是一个已经命名的互斥体,并且对象在此函数调用之前存在,则返回值是现有对象的句柄,GetLastError 函数返回 ERROR_ALREADY_EXISTS。
#include <iostream>
#include <Windows.h>
int main(int argc, char* argv[])
{
//创建一个互斥体
HANDLE hMutex = CreateMutex(NULL, FALSE, (LPCWSTR)"OVER");
DWORD dwRet = GetLastError();
if (hMutex)
{
if (ERROR_ALREADY_EXISTS == dwRet)
{
std::cout << "禁止多开, 按任意键退出!\n";
CloseHandle(hMutex);
std::cin.get();
return 0;
}
}
else
{
std::cout << "创建互斥体失败, 按任意键退出!\n";
CloseHandle(hMutex);
std::cin.get();
return 0;
}
while (1)
{
Sleep(1000);
std::cout << "执行中...\n" ;
}
return 0;
}