吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2559|回复: 12
上一主题 下一主题
收起左侧

[系统底层] win32笔记九 互斥体

  [复制链接]
跳转到指定楼层
楼主
huchen 发表于 2024-4-23 11:10 回帖奖励
之前的索引

互斥体

在上一章中,讲了临界区的东西,使用了临界资源,而这个临界资源也仅仅是个全局变量,那如果这个临界资源是一个内核级别的临界资源,比如说是一个跨进程共享的物理页,或者说他本身是个文件,在这种情况下,有两个不同的进程,同时要访问一个内核级的临界资源,那怎么做再能保证是安全的?

上一章的线程锁的方式就不行了,因为他是控制同一个进程中的两个或者多个线程访问临界资源

但是,在上一章中,提到了令牌,在访问资源之前要访问令牌,而这里也是同样的道理,只是不像线程锁的令牌那样是全局变量,这个令牌是放在内核的,这样才能让其他进程进行访问,因为内核是共享的

所以能够放在内核的令牌我们称作为互斥体

这个与线程锁的思路是完全一样的,只是令牌和临界资源的位置不同而已,这里就不多说互斥体使用的方法了

代码演示

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<windows.h>

int main()
{
    //创建一个互斥体
    HANDLE hMutex = CreateMutexA(NULL, FALSE, "huchen");

    //获取令牌
    WaitForSingleObject(hMutex, INFINITE);

    for (int i = 0; i < 10; i++)
    {
        Sleep(1000);
        printf("A进程的X线程:%d\n", i);
        //另一个进程就写B进程的Y线程,其他不变
    }
    //释放令牌
    ReleaseMutex(hMutex);
}

CreateMutex

定义

HANDLE CreateMutex(
  LPSECURITY_ATTRIBUTES lpMutexAttributes,  // SD
  BOOL bInitialOwner,                       // initial owner
  LPCTSTR lpName                            // object name
);
参数解释
lpMutexAttributes

安全描述符,这个见过很多次了,只要是内核级的函数,都有,不多说,填空就进行

bInitialOwner

[in] Specifies the initial owner of the mutex object. If this value is TRUE and the caller created the mutex, the calling thread obtains 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.

[in] 指定互斥对象的初始所有者。如果该值为 TRUE 并且调用者创建了互斥对象,则调用线程获得互斥对象的所有权。否则,调用线程不获得互斥对象的所有权。要确定调用者是否创建了互斥对象,请参阅返回值部分。

可以理解为如果你是TRUE,但是当前这个互斥体是属于这个线程的(原视频说的是进程,不知道是口误还是我理解的不透彻,后面我会写上我的依据的),那么就是有信号,反之就没有,如果是FALSE,不管你是不是属于,就有信号

lpName

互斥体的名字,可以随便起

运行结果

可以看到,当A执行完了,B才能开始,所以,A没有释放令牌,那B就只能处于阻塞的状态

接下来说下第二个参数的TRUE形态

根据上面,对第二个参数的了解,直接代码演示,不多bb

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<windows.h>

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
    HANDLE hMutex = CreateMutexA(NULL, TRUE, "huchen");
    WaitForSingleObject(hMutex, INFINITE);
    for (int i = 0; i < 10; i++)
    {
        Sleep(1000);
        printf("A进程的Z线程:%d\n", i);
    }
    ReleaseMutex(hMutex);
    return 0;
}
int main()
{
    //创建一个互斥体
    HANDLE hMutex = CreateMutexA(NULL, TRUE, "huchen");
    HANDLE hThread;
    hThread=CreateThread(NULL, 0, ThreadProc, 0, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);
    printf("Z完毕\n");

    //获取令牌
    WaitForSingleObject(hMutex, INFINITE);

    for (int i = 0; i < 10; i++)
    {
        Sleep(1000);
        printf("A进程的X线程:%d\n", i);
    }

    //getchar();
    //释放令牌
    ReleaseMutex(hMutex);
}

这里我创建了个线程,把互斥体设置成TRUE,当前互斥体是属于主函数线程的,那Z线程就会阻塞,由于我设置的是无限时间,所以会无限阻塞

验证

验证成功

当变成FALSE,那就会直接输出

如果先执行的是B进程,也会打印出来,不管是不是TRUE,在这里,好像是谁先执行谁就会先拿到令牌。

互斥体与线程锁的区别

  1. 线程锁只能用于单个进程间的线程控制
  2. 互斥体可以设定等待超时,但线程不能
  3. 线程意外终结时,Mutex可以避免无限等待
  4. Mutex效率没有线程锁高

第三个的意思就是在A进程中Sleep后面加上

if(i==5)
    ExitProcess(0);

发现在第五个意外退出了,那么B进程也是可以继续执行的

互斥体之多开问题

关于多开,喜欢玩游戏的应该知道,一个游戏通常只能打开一份,首先防止刷级之类的,其次也没有意义,对开发者来说。所以今天就以互斥体的例子来讲一下如何防止多开。(当然不止互斥体一种防止多开的方法,有许多个)

首先还要回到函数的返回值说明

Return Values

If the function succeeds, the return value is a handle to the mutex object.  If the named mutex object existed before the function call, the function returns  a handle to the existing object and [GetLastError](JavaScript:hhobj_4.Click()) returns  ERROR_ALREADY_EXISTS. Otherwise, the caller created the mutex.

If the function fails, the return value is NULL. To get extended error  information, call GetLastError.

如果函数调用成功,返回值是指向互斥对象的句柄。如果在函数调用之前命名的互斥对象已经存在,函数将返回现有对象的句柄,并且 GetLastError 返回 ERROR_ALREADY_EXISTS。否则,调用者创建了互斥对象。

如果函数调用失败,返回值为 NULL。要获取扩展的错误信息,请调用 GetLastError。

意思就是,假如在A进程里创建了一个互斥体,那么在B进程也创建了一个同名的互斥体的,那么B里的互斥体返回的句柄,跟A一样,也就相当于时声明了,并不是重新创建,并且这个也会被GetLastError函数捕获到错误,错误代码代码为:ERROR_ALREADY_EXISTS

所以代码就可以写成

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<windows.h>

int main()
{
    //创建一个互斥体
    HANDLE hMutex = CreateMutexA(NULL, FALSE, "huchen");

    DWORD Retn = GetLastError();
    if (hMutex)
    {
        if (Retn == ERROR_ALREADY_EXISTS)   //判断是否多开
        {
            CloseHandle(hMutex);
            MessageBoxA(0, "禁止多开", 0, 0);
            return 0;
        }
    }
    else        //因其他原因而退出
    {
        printf("创建失败,程序退出!\n");
        CloseHandle(hMutex);
        return 0;
    }
    while (1)
    {
        Sleep(1000);
        printf("程序执行中……\n");
    }
    return 0;

}

运行结果

总结

  1. 了解新的API:CreateMutex
  2. 了解了与线程锁的区别
  3. 以互斥体来解决多开问题





制作不易,求求大佬们,给孩子个免费评分吧

免费评分

参与人数 10吾爱币 +16 热心值 +9 收起 理由
asciibase64 + 1 我很赞同!
稻海香 + 2 + 1 我很赞同!
wxn2023 + 1 + 1 用心讨论,共获提升!
老司车开机 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
qiaoyong + 1 + 1 热心回复!
janken + 1 + 1 热心回复!
willJ + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
vethenc + 1 + 1 谢谢@Thanks!
hacker1983 + 1 谢谢@Thanks!
Yifan2007 + 2 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
Gxd1703 发表于 2024-4-23 14:09
感谢分享,点赞支持
3#
shetel 发表于 2024-4-23 18:23
4#
Psc7day 发表于 2024-4-23 23:17
5#
HapplyLin 发表于 2024-4-24 08:59
感谢楼主分享
6#
去旅行 发表于 2024-4-24 22:37
好难得,现在研究系统底层的人很少了,都是Web、Java、Python
7#
deffedyy 发表于 2024-4-24 23:01
感谢楼主分享
8#
zhgang908 发表于 2024-4-25 10:50
感谢分享,点赞
9#
sam喵喵 发表于 2024-4-25 11:03
感谢分享,这个排版风格好眼熟啊。。。妹子?
10#
lymanxin 发表于 2024-4-27 18:46
感谢分享!点个赞
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-12 18:03

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表