Windows共享内存与死锁

news2024/12/23 12:43:59

实验一

一、实验内容或题目:

利用共享内存完成一个生产者进程和一个消费者进程的同步。

二、实验目的与要求:

1、编写程序,使生产者进程和消费者进程通过共享内存和mutex来完成工作同步。
2、了解通过操作系统接口调用,实现通过共享内存进行数据交换。

三、实验步骤:

1、分别编写生产者进程和消费者进程,并创建同名的共享内存和mutex
2、生产者加上mutex则向共享内存中增加一条数据,消费者加上mutex则从共享内存中尝试消费一条数据,两者放掉mutex后随机Sleep若干毫秒
3、若生产者接受到一个sigint中断,则向共享内存中写入一个标识后再退出,消费者消费完所有的数据后若发现此标识被置上则同时退出。

需要使用的api:
CreateMutex, CreateFileMapping,MapViewOfFile
共享内存相关example参照:https://docs.microsoft.com/en-us/windows/win32/memory/creating-named-shared-memory
提示:
共享内存的相关数据结构可以采用最简单的两个int,第一个int表示数据的数量,第二个int表示生产者是否已经退出。

四、实验结果:

在这里插入图片描述
Producer:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Sighandler(中断信号CTRL + C处理):
在这里插入图片描述
在这里插入图片描述
Consumer:
在这里插入图片描述
在这里插入图片描述
Main:
在这里插入图片描述
结果:
在这里插入图片描述

五、总结:

总的来说这个实验还是很难的,花费了很长时间,也遇到了很多问题。首先为什么要映射,这其实是操作系统内存管理的知识,MapViewOfFile就是为了将此内存空间映射到进程的地址空间中。如果两个进程使用相同的DLL,只把DLL的代码页装入内存一次,其他所有映射这个DLL的进程只要共享这些代码页就可以了,如果利用消息机制实现IPC有交换的数据量小、携带的信息少等缺点。
在这里插入图片描述
在这里插入图片描述
在实验中每次向共享内存放入值后,都必须加“_getch”,否则会出现“Could not open file mapping object”,或者可能导致concumer或producer迟迟无法使用共享内存。这是因为,如果不加“_getch”锁住那个瞬间,那么映射对象将会关闭。
在这里插入图片描述
如果你在设定共享内存空间为Global…也是会出错的。这是因为Global保证创建的对象是全局的,对权限要求比较高,而使用local可以保证服务对象仅对当前用户中的进程和线程可见。
在这里插入图片描述
在捕获中断信号后,理论上应该让线程1关闭,但是,经过我的尝试,无法实现只关闭进程1而不关闭进程2。之后只能设定一个全局flag,来标志producer已经退出。

六、源码

共享内存:

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <process.h>
#include <signal.h>

#define BUF_SIZE 4096
#pragma comment(lib, "user32.lib")
TCHAR szName[] = TEXT("Local\\MyFileMappingObject");
HANDLE hEvent;
HANDLE hMutex;	//定义互斥对象句柄
HANDLE hThreads[2];
// 告诉consumer,producer已经中断退出
int flag = 0;
// 中断处理
void sighandler(int);

unsigned  __stdcall producer(void* lpVoid)
{
    HANDLE hMapFile;
    LPCTSTR pBuf;
    int a[2] = {5, 0};
    int i = 0;

    // 创建共享文件句柄 
    hMapFile = CreateFileMapping(
        INVALID_HANDLE_VALUE,    // 物理文件句柄
        NULL,                    // 默认安全级别
        PAGE_READWRITE,          // 可读可写
        0,                       // 高位文件大小
        BUF_SIZE,               // 地位文件大小
        szName);                 // 共享内存名称

    if (hMapFile == NULL)
    {
        _tprintf(TEXT("concumer:Could not create file mapping object (%d).\n"), GetLastError());
        return 1;
    }

    // 映射缓存区视图 , 得到指向共享内存的指针
    pBuf = (LPTSTR)MapViewOfFile(
        hMapFile,   // 共享内存的句柄
        FILE_MAP_ALL_ACCESS, // 可读写许可
        0,
        0,
        BUF_SIZE);
    if (pBuf == NULL)
    {
        _tprintf(TEXT("Could not map view of file (%d).\n"), GetLastError());
        CloseHandle(hMapFile);
        return 1;
    }

    // 设置互斥量,向共享区放入值
    WaitForSingleObject(hMutex, INFINITE);
    memcpy((int*)pBuf, a, 40);
    _getch();
    ReleaseMutex(hMutex);

    // 捕获中断信号
    signal(SIGINT, sighandler);

    while (TRUE)
    {
        if (flag == 0)
        {
            WaitForSingleObject(hMutex, INFINITE);
            // 从共享空间拿值
            int* p = (int*)pBuf;
            printf("producer:%d  %d\n", p[0], p[1]);
            p[0]++;
            // 向共享空间放值
            memcpy((int*)pBuf, p, 40);
            _getch();
            ReleaseMutex(hMutex);
            Sleep(1000);
        }
    }
    // 解除文件映射
    UnmapViewOfFile(pBuf);
    // 关闭内存映射文件对象句柄
    CloseHandle(hMapFile);
    return 0;
}

void sighandler(int signum)
{
    printf("捕获信号 %d,准备跳出...\n", signum);

    HANDLE hMapFile;
    LPCTSTR pBuf;
    hMapFile = OpenFileMapping(
        FILE_MAP_ALL_ACCESS,   // read/write access
        FALSE,                 // do not inherit the name
        szName);               // name of mapping object

    if (hMapFile == NULL)
    {
        _tprintf(TEXT("producer:Could not open file mapping object (%d).\n"), GetLastError());
        return;
    }
    pBuf = (LPTSTR)MapViewOfFile(hMapFile, // handle to map object
        FILE_MAP_ALL_ACCESS,  // read/write permission
        0,
        0,
        BUF_SIZE);
    if (pBuf == NULL)
    {
        _tprintf(TEXT("Could not map view of file (%d).\n"), GetLastError());
        CloseHandle(hMapFile);
        return;
    }
    WaitForSingleObject(hMutex, INFINITE);
    int* p = (int*)pBuf;
    printf("concumer:%d  %d\n", p[0], p[1]);
    // 标志producer已经中断
    p[1] = 1;
    memcpy((int*)pBuf, p, 40);
    _getch();
    ReleaseMutex(hMutex);
    // 通过全局变量告知consumer自己已退出
    flag = 1;
    //WaitForSingleObject(hThreads[0], INFINITE);
    //_endthreadex(0);
    //WaitForSingleObject(hThreads[0], INFINITE);
    //WaitForSingleObject(hThreads[1], INFINITE);
    //TerminateThread(hThreads[0], 0);
    //ExitThread((DWORD)hThreads[0]);
    //exit(0);
    CloseHandle(hThreads[0]);
}



unsigned  __stdcall concumer(void* lpVoid)
{
    HANDLE hMapFile;
    LPCTSTR pBuf;

    hMapFile = OpenFileMapping(
        FILE_MAP_ALL_ACCESS,   // read/write access
        FALSE,                 // do not inherit the name
        szName);               // name of mapping object

    if (hMapFile == NULL)
    {
        _tprintf(TEXT("producer:Could not open file mapping object (%d).\n"), GetLastError());
        return 0;
    }

    pBuf = (LPTSTR)MapViewOfFile(hMapFile, // handle to map object
        FILE_MAP_ALL_ACCESS,  // read/write permission
        0,
        0,
        BUF_SIZE);

    if (pBuf == NULL)
    {
        _tprintf(TEXT("Could not map view of file (%d).\n"), GetLastError());
        CloseHandle(hMapFile);
        return 0;
    }


    while (TRUE)
    {
        WaitForSingleObject(hMutex, INFINITE);

        int *p = (int*)pBuf;
        printf("concumer:%d  %d\n", p[0], p[1]);
        p[0]--;
        
        memcpy((int*)pBuf, p, 40);
        _getch();

        ReleaseMutex(hMutex);
        Sleep(1000);
        // 彻底退出进程
        if (p[0] == -1)
        {
            UnmapViewOfFile(pBuf);
            CloseHandle(hMapFile);
            exit(0);
        }
    }
    return 0;
}

int main()
{
    hEvent = CreateEvent(
        NULL,               // default security attributes
        FALSE,               // manual-reset event
        FALSE,              // initial state is nonsignaled
        TEXT("WriteEvent_two")  // object name
    );
    if (hEvent == NULL)
    {
        printf("CreateEvent failed (%d)\n", GetLastError());
        return 0;
    }
    
    //创建进程
    //HANDLE hThreads[2];
    hThreads[0] = (HANDLE)_beginthreadex(NULL, 0, &producer, NULL, 0, NULL);
    Sleep(1000);
    hThreads[1] = (HANDLE)_beginthreadex(NULL, 0, &concumer, NULL, 0, NULL);
    // 无线等待进程结束
    WaitForMultipleObjects(2, hThreads, TRUE, INFINITE);

    CloseHandle(hThreads[0]);
    CloseHandle(hThreads[1]);
    CloseHandle(hEvent);
}

实验二

一、实验内容或题目:

实现哲学家用餐问题,观察死锁

二、实验目的与要求:

1、使用多线程和mutex模拟实现哲学家用餐问题(课本习题3.14)
2、测试并观察线程数量以及用餐/不用餐时间比对出现死锁概率的影响

三、实验步骤:

1、创建N个mutex,进行相应的编号
2、创建N个线程,每个线程获得左右两边的mutex后,进行m1时间的用餐(用sleep模拟),放掉mutex后,进行m2时间的休息,再重复上述过程
3、观察死锁的出现
4、调节N以及m1/m2的数值,观察死锁出现的概率与这两个数值的关系

需要使用的api:
CreateMutex, ReleaseMutex

四、实验结果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

五、总结

根据实验结果可以观察出m1/m2越大,越容易出现死锁,用餐时间越长越容易出现死锁,思考时间越长越不容易发生死锁。

六、源码

死锁:

#include <Windows.h>
#include <iostream>
#include <cstdio>
#include <stdlib.h>
#include <time.h>
using namespace std;

HANDLE chop[5];
HANDLE ph[5];
HANDLE mutex;
int nums = 0;


DWORD WINAPI phthread(LPVOID param) {
	nums++;
	int id = nums;
	int lc = id;
	int rc = (id + 1) % 5;
	int times = 0;
	while (true)
	{
		// 思考
		Sleep(10);
		WaitForSingleObject(chop[rc], INFINITE);
		WaitForSingleObject(chop[lc], INFINITE);
		WaitForSingleObject(mutex, INFINITE);
		printf("哲学家%d号拿到两只筷子开始吃第%d顿饭。\n", id, times + 1);
		// 吃饭
		Sleep(1);
		ReleaseMutex(mutex);
		times++;
		ReleaseMutex(chop[lc]);
		ReleaseMutex(chop[rc]);
		Sleep(10000);
	}
}

int main()
{
	for (int i = 0; i < 5; ++i)
	{
		chop[i] = CreateMutex(NULL, false, NULL);
	}
	for (int i = 0; i < 5; ++i)
	{
		int j = i + 1;
		ph[i] = CreateThread(NULL, 0, phthread, NULL, 0, NULL);
	}
	Sleep(10000);//释放句柄
	for (int i = 0; i < 5; ++i)
	{
		CloseHandle(ph[i]);
	}
	CloseHandle(mutex);
	Sleep(500);
	system("pause");
	return 0;
}

实验三

一、实验内容或题目:

在实验二的基础上,破坏掉一个死锁的必要条件,使死锁得到避免

二、实验目的与要求:

1.理解课本上死锁出现的必要条件
2.破坏掉其中的一个必要条件,使哲学家用餐可以不死锁的持续运行下去,可以尝试以下之一:
1)破坏掉部分分配(拿不到第二个mutex则放掉第一个)
2)破坏掉环路条件(某一个哲学家和其余的取mutex顺序不同)

三、实验步骤:

1.设计方法规定,奇数号哲学家只能先拿左边的筷子,而偶数号哲学家相反
2.设计方案实现,保证一个哲学家拿筷子时,将所有的筷子锁住,保证同一时间只有一个哲学家拿筷子。

需要使用的api:
CreateMutex, ReleaseMutex

四、实验结果:

方案一:
在这里插入图片描述
在这里插入图片描述
方案二:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

五、总结

通过实验可以发现通过方案奇数号的哲学家先拿起右边的筷子再拿起左边的筷子,偶数号哲学家先拿起左边的筷子,再拿起右边的筷子;保证一个哲学家拿筷子时,将所有的筷子锁住,保证同一时间只有一个哲学家拿筷子,这样都能有效避免哲学家死锁问题。

六、源码

方案一:

#include <Windows.h>
#include <iostream>
#include <cstdio>
#include <stdlib.h>
#include <time.h>
/*
(1)奇数号的哲学家先拿起右边的筷子再拿起左边的筷子。

(2)偶数号哲学家先拿起左边的筷子,再拿起右边的筷子。
*/
using namespace std;

HANDLE chop[5];
HANDLE ph[5];
HANDLE mutex;
int nums = 0;

DWORD WINAPI phthread(LPVOID param) {
	nums++;
	int id = nums;
	int lc = id;
	int rc = (id + 1) % 5;
	int times = 0;
	while (true)
	{
		// 思考
		Sleep(10);
		if (id % 2 == 0)
		{
			WaitForSingleObject(chop[rc], INFINITE);
			WaitForSingleObject(chop[lc], INFINITE);
			WaitForSingleObject(mutex, INFINITE);
			printf("哲学家%d号拿到两只筷子开始吃第%d顿饭。\n", id, times + 1);
			// 吃饭
			Sleep(10);
			ReleaseMutex(mutex);
			times++;
			ReleaseMutex(chop[lc]);
			ReleaseMutex(chop[rc]);
		}
		else
		{
			WaitForSingleObject(chop[lc], INFINITE);
			WaitForSingleObject(chop[rc], INFINITE);
			WaitForSingleObject(mutex, INFINITE);
			printf("哲学家%d号拿到两只筷子开始吃第%d顿饭。\n", id, times + 1);
			// 吃饭
			Sleep(10);
			ReleaseMutex(mutex);
			times++;
			ReleaseMutex(chop[lc]);
			ReleaseMutex(chop[rc]);
		}
	}
}

int main()
{
	for (int i = 0; i < 5; ++i)
	{
		chop[i] = CreateMutex(NULL, false, NULL);
	}
	for (int i = 0; i < 5; ++i)
	{
		int j = i + 1;
		ph[i] = CreateThread(NULL, 0, phthread, NULL, 0, NULL);
	}
	Sleep(10000);//释放句柄
	for (int i = 0; i < 5; ++i)
	{
		CloseHandle(ph[i]);
	}
	CloseHandle(mutex);
	Sleep(500);
	system("pause");
	return 0;
}

方案二:

#include <Windows.h>
#include <iostream>
#include <cstdio>
#include <stdlib.h>
#include <time.h>
/*
(1)保证一个哲学家拿筷子时,将所有的快走锁住,保证同一时间只有一个哲学家拿筷子
*/
using namespace std;

HANDLE chop[5];
HANDLE ph[5];
HANDLE mutex;
int nums = 0;

DWORD WINAPI phthread(LPVOID param) {
	nums++;
	int id = nums;
	int lc = id;
	int rc = (id + 1) % 5;
	int times = 0;
	while (true)
	{
		// 思考
		Sleep(10);
		WaitForSingleObject(mutex, INFINITE);
		WaitForSingleObject(chop[lc], INFINITE);
		WaitForSingleObject(chop[rc], INFINITE);
		ReleaseMutex(mutex);			
		printf("哲学家%d号拿到两只筷子开始吃第%d顿饭。\n", id, times + 1);
		// 吃饭
		Sleep(10);
		times++;
		ReleaseMutex(chop[lc]);
		ReleaseMutex(chop[rc]);
	}
}

int main()
{

	for (int i = 0; i < 5; ++i)
	{
		chop[i] = CreateMutex(NULL, false, NULL);
	}
	for (int i = 0; i < 5; ++i)
	{
		int j = i + 1;
		ph[i] = CreateThread(NULL, 0, phthread, NULL, 0, NULL);
	}
	Sleep(10000);//释放句柄
	for (int i = 0; i < 5; ++i)
	{
		CloseHandle(ph[i]);
	}
	CloseHandle(mutex);
	Sleep(500);
	system("pause");
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/482030.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux字符设备驱动

前言 代码结构简单&#xff0c;旨在用最简单的原理理解最主要的框架逻辑&#xff0c;细节需要自行延伸。 -----------------学习的基础底层逻辑 基础步骤 开发linux内核驱动需要以下4个步骤&#xff1a; 编写驱动代码编写makefile编译和加载驱动编写应用程序测试驱动 由于硬…

Android9.0 系统Framework发送通知流程分析

1.前言 在android 9.0的系统rom定制化开发中,在systemui中一个重要的内容就是系统通知的展示,在状态栏展示系统发送通知的图标,而在 系统下拉通知栏中展示接收到的系统发送过来的通知,所以说对系统framework中发送通知的流程分析很重要,接下来就来分析下系统 通知从frame…

开发攻城狮必备的Linux虚拟机搭建指南|原创

hi&#xff0c;我是阿笠&#xff01; 这篇文章主要面对的是不常搭建Linux操作系统环境的开发同学&#xff0c;文中介绍了基本操作步骤并且提供了相关云盘资源&#xff0c;都是为了节约时间&#xff01; 因为从我自身来讲&#xff0c;作为一名后端开发&#xff0c;经常需要练习一…

c#笔记-内置类型

内置类型 内置类型是一些有关键字表示的类型。关键字具有非常高的优先级&#xff0c;可以让你在没有别的配置的情况下&#xff0c; 只要用的是c#就可以使用。这也意味着这些类型是非常重要&#xff0c;或是基本的东西。 整数&#xff1a;byte, sbyte, short, ushort, int, ui…

【Python入门】搭建开发环境-安装Pycharm开发工具

前言 &#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;致力于C/C、Java、Python等多编程语言&#xff0c;热爱跑步&#xff0c;喜爱音乐的一位博主。 &#x1f4d7;本文收录于Python零基础入门系列&#xff0c;本专栏主要内容为Python基础语法、判断、循环语句、函…

【数据结构】线性表之单链表(讲解实现——带动图理解)

文章目录 单链表单链表主体结构单链表操作函数介绍单链表操作函数实现单链表的初始化&#xff1a;打印函数单链表插入函数&#xff1a;头插尾插指定结点后插入和查找函数单链表结点之前插入数据 单链表删除函数头删尾删指定结点后删除指定结点删除 销毁单链表 文件分类test.cLi…

【STM32】基础知识 第十课 CubeMx

【STM32】基础知识 第十课 CubeMx STM32 CubeMX 简介安装 JAVACubeMX 安装新建 STM32 CubeMX 工程步骤新建工程时钟模块配置GPIO 配置生成源码 main.c STM32 CubeMX 简介 CubeMX (全称 STM32CubeMX) 是 ST 公司推出的一款用于 STM32 微控制器配置的图形化工具. 它能帮助开发者…

「Bug」解决办法:Could not switchto this profil,无法使用节点的解决方法,彻底解决

♥️作者&#xff1a;白日参商 &#x1f935;‍♂️个人主页&#xff1a;白日参商主页 ♥️坚持分析平时学习到的项目以及学习到的软件开发知识&#xff0c;和大家一起努力呀&#xff01;&#xff01;&#xff01; &#x1f388;&#x1f388;加油&#xff01; 加油&#xff01…

二十五、OSPF高级技术——开销值、虚链路、邻居建立、LSA、静默接口

文章目录 调试指令&#xff08;三张表&#xff09;1、邻居表&#xff1a;dis ospf peer brief2、拓扑表&#xff08;链路状态数据库&#xff09;&#xff1a;dis ospf lsdb3、路由表&#xff1a;dis ip routing-table 一、OSPF 开销值/度量值&#xff08;cost&#xff09;1、co…

Python基础合集 练习15(内置函数 匿名函数)

匿名函数 以lambda开头表示这是匿名函数&#xff0c;之后的x,y是函数参数 def sub(a,b): return a-b print(sub(10,3)) print(lambda x,y:x-y) sublambda x,y:x-y print(sub(8,4)) def game(math,chinese,english): “”" 功能&#xff1a;计算三科的成绩 math&#xf…

谈谈多线程的上线文切换

大家好&#xff0c;我是易安&#xff01; 我们知道&#xff0c;在并发程序中&#xff0c;并不是启动更多的线程就能让程序最大限度地并发执行。线程数量设置太小&#xff0c;会导致程序不能充分地利用系统资源&#xff1b;线程数量设置太大&#xff0c;又可能带来资源的过度竞争…

【C++】隐式转换与explicit关键字、运算符及其重载、this关键字

C隐式转换与explicit关键字 隐式构造函数 隐含的意思是不会明确告诉你要做什么 隐式转换 C允许编译器对代码执行一次隐式转换&#xff0c;而不需要使用casr强制转换 例1 #include <iostream> #include <string>class Entity { private:std::string m_Name;in…

13 SQL——数值函数

1 ceil() 数值向上取整&#xff08;前提是小数位不是0&#xff09; select ceil(1.2);2 floor() 数值向下取整&#xff08;前提是小数位不是0&#xff09;select floor(1.8);3 mod() 取&#xff08;x%y&#xff09;的模运算&#xff08;求余数运算&#xff09; select …

10. hr 综合面试题汇总

10. hr 综合面试题汇总 C++软件与嵌入式软件面经解析大全(蒋豆芽的秋招打怪之旅) 本章讲解知识点 1.1 HR心理复盘1.2 HR常问问题——学校的表现怎么样啊?1.3 HR常问问题——了解我们公司吗?1.4 HR常问问题——个人情况1.5 HR常问问题——业余生活1.6 HR常问问题——薪资待…

【源码角度】为什么AQS这样设计

AQS&#xff08;AbstractQueuedSynchronizer&#xff0c;抽象同步队列器&#xff09;是 一个基于 FIFO的双端队列。它分为独占模式和共享模式&#xff0c;本文主要围绕独占模式进行讲解&#xff0c;共享模式的原理和独占模式相似&#xff0c;最后会提一嘴。 场景代入 其实AQS模…

云计算基础(持续更新)

文章目录 云计算云计算的定义第1关&#xff1a;云计算定义第2关&#xff1a;云计算的基本原理 云计算出现的背景第1关&#xff1a;云计算出现的背景第2关&#xff1a;云计算的特征第3关&#xff1a;云计算的优势与劣势 虚拟化的类型第1关&#xff1a;虚拟化的定义第2关&#xf…

第六章结构型模式—代理模式

文章目录 代理模式解决的问题概念结构 静态代理动态代理织入的概念JDK 动态代理JDK 动态代理分析 CGLIB 动态代理 三种代理的对比JDK 和 CGLIB 的区别动态代理和静态代理的对比代理模式的优缺点使用场景 结构型模式描述如何将类或对象按某种布局组成更大的结构&#xff0c;有以…

浅谈springboot启动过程

1. 知识回顾 为了后文方便&#xff0c;我们先来回顾一下spring的一些核心概念。 spring最核心的功能无非是ioc容器&#xff0c;这个容器里管理着各种bean。ioc容器反映在java类上就是spring的核心类ApplicationContext。ApplicationContext有众多的子接口和子类&#xff0c;不…

SAP重复制造入门到放弃系列之基本配置

目录 前言 主要配置清单&#xff1a; REM参数文件&#xff1a; 计划订单计划参数文件 维护输入项参数 维护行选择 确认和物流信息系统全局设置 定义确认处理 操作方法表 其他 前言 重复制造中的配置步骤包括创建重复制造配置文件、为运行计划数量&#xff08;计划订单&a…

Excel技能之打印,19+技巧超省纸

颜色太多&#xff0c;重新打印。 没有边框&#xff0c;重新打印。 少了几列&#xff0c;重新打印。 整个工作表打印出来&#xff0c;拿剪刀把自己需要的数据剪下来&#xff0c;用胶水贴到另一张新的A4纸。 你上班打印资料&#xff0c;浪费了多少纸&#xff0c;认真算一下&…