系統編程練習題---利用共用記憶體和信號量集,完成兩個進程之間的互斥通信

来源:https://www.cnblogs.com/fly-home/p/18218764
-Advertisement-
Play Games

目錄題目解析代碼展示process_A.cprocess_B.cprocess_C.c結果展示重要知識點記錄 題目 設計一個程式,作為進程A,進程A專門創建一個信號量集,要求信號量集中有1個信號量,對信號量集合中的信號量進行設置,要求集合中的信號量的初值為1,然後再設計2個程式,分別是進程B和進程C ...


目錄

題目

設計一個程式,作為進程A,進程A專門創建一個信號量集,要求信號量集中有1個信號量,對信號量集合中的信號量進行設置,要求集合中的信號量的初值為1,然後再設計2個程式,分別是進程B和進程C,要求進程B和進程C使用進程A創建的信號量集合中的信號量實現互斥訪問。 提示:進程A、進程B、進程C需要使用共用記憶體作為臨界資源的訪問。

解析

該題目核心設計思路是利用信號量集的P操作和V操作,實現進程B與進程C之間的互斥,避免進程B與進程C同時對共用記憶體段操作。三個進程文件具體分工如下:

  1. 進程A用於創建並初始化信號量集與共用記憶體中的初始數據值
  2. 進程B用於更改共用記憶體段中的數據值
  3. 進程C用於輸出共用記憶體段中的數據值

image

代碼展示

process_A.c

/*******************************************************************
*
*	file name:	process_A.c
*	author	 :  [email protected]
*	date	 :  2024/05/28
*	function :  該案例是掌握進程通信方式,主要是共用記憶體和信號量集的使用
*				進程A需要創建一個信號量集,該信號量集中有一個信號量,
* 				並要求該信號量的初值為1;進程A還需要創建一個共用記憶體段,
*				供給進程B和進程C進行訪問操作
* 	note	 :  None
*   version  :
*
*	CopyRight (c)  2023-2024   [email protected]   All Right Reseverd 
*
* *****************************************************************/
/****************************頭文件**************************************/

#include <stdio.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

/****************************聯合體**************************************/
union semun
{
	int val;
	struct semid_ds *buf;
	unsigned short *array;
} ;


int main(int argc, char const *argv[])
{
	/****創建共用記憶體段,並完成數據的寫入****/
	//1. 申請共用記憶體段
	int shm_id = shmget(ftok(".", 0xffffff01), 4, IPC_CREAT | IPC_EXCL | 0644);
	if(shm_id == -1)
	{
		fprintf(stderr, "shmget error, errno:%d, %s\n", errno, strerror(errno));
		shm_id = shmget(ftok(".", 0xffffff01), 4, 0644);
	}
	//2. 映射共用記憶體到進程A中
	int *pshm = (int *)shmat(shm_id, NULL, 0);
	if(pshm == (void*)-1)
	{
		fprintf(stderr, "shmat error, errno:%d, %s\n", errno, strerror(errno));
		exit(-1);
	}
	//3. 向共用記憶體中寫入數據
	*pshm = 0;
	//4. 分離共用記憶體段
	int flag_dt = shmdt(pshm);
	if(flag_dt == -1)
	{
		fprintf(stderr, "shmdt error, errno:%d, %s\n", errno, strerror(errno));
		exit(-1);
	}
	


	/****創建信號量集,並設置信號量集的信號量初值為1****/
	//1. 創建信號量集
	int sem_id = semget(ftok(".", 0xffffff01), 1, IPC_CREAT | IPC_EXCL | 0644);
	if(sem_id == -1)
	{
		fprintf(stderr, "sem_id error, errno:%d, %s\n", errno, strerror(errno));
		sem_id = semget(ftok(".", 0xffffff01), 1, 0644);
	}
	//2. 先獲取信號集中的數據,再設置信號量初值為1
	union semun arg;
	arg.val = 1;
	semctl(sem_id, 0,SETVAL,arg.val);

	return 0;
}

process_B.c

/*******************************************************************
*
*	file name:	process_B.c
*	author	 :  [email protected]
*	date	 :  2024/05/28
*	function :  該案例是掌握進程通信方式,主要是共用記憶體和信號量集的使用
*				需要將進程A創建的共用記憶體段映射到進程B中,方便後續寫入數據;
*				進程B需要對進程A創建的信號量集交替進行P操作和V操作,來實現
*				對共用記憶體段中的數據互斥修改
* 	note	 :  None
*   version  :
*
*	CopyRight (c)  2023-2024   [email protected]   All Right Reseverd 
*
* *****************************************************************/
/****************************頭文件**************************************/

#include <stdio.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>


int main(int argc, char const *argv[])
{
	while(1)
	{
		/****打開信號量集,完成P操作****/
		//1. 打開信號量集
		int sem_id = semget(ftok(".", 0xffffff01), 1, 0644);
		if(sem_id == -1)
		{
			fprintf(stderr, "sem_id error, errno:%d, %s\n", errno, strerror(errno));
			exit(-1);
		}
		//2. 定義結構體變數,並對結構體變數值完成相應配置
		struct sembuf semop_P;
		semop_P.sem_num = 0;	//該信號量集只有一個信號量,所以從下標0開始
		semop_P.sem_op = -1;	//操作值為負值,代表執行P操作,向信號量集申請資源
		semop_P.sem_flg = 0;	//由於題目要求進程B與進程C互斥,所以標誌設為0,即預設阻塞模式
		//3. 對信號量集完成P操作
		semop(sem_id, &semop_P, 1);


		/****打開共用記憶體段,並完成數據的寫入****/
		//1. 打開共用記憶體段
		int shm_id = shmget(ftok(".", 0xffffff01), 4, 0644);
		if(shm_id == -1)
		{
			fprintf(stderr, "shmget error, errno:%d, %s\n", errno, strerror(errno));
			exit(-2);
		}
		//2. 映射共用記憶體到進程B中
		int *pshm = (int *)shmat(shm_id, NULL, 0);
		if(pshm == (void*)-1)
		{
			fprintf(stderr, "shmat error, errno:%d, %s\n", errno, strerror(errno));
			exit(-3);
		}
		//3. 每次完成P操作後,將共用記憶體中的數據加一
		*pshm += 1;
		//4. 分離共用記憶體段
		int flag_dt = shmdt(pshm);
		if(flag_dt == -1)
		{
			fprintf(stderr, "shmdt error, errno:%d, %s\n", errno, strerror(errno));
			exit(-4);
		}
		
		/****完成對共用記憶體中數據修改後,執行V操作****/
		//1. 定義結構體變數,並對結構體變數值完成相應配置
		struct sembuf semop_V;
		semop_V.sem_num = 0;	//該信號量集只有一個信號量,所以從下標0開始
		semop_V.sem_op = +1;	//操作值為正值,代表執行V操作,向信號量集歸還資源
		semop_V.sem_flg = 0;	//由於題目要求進程B與進程C互斥,所以標誌設為0,即預設阻塞模式
		//2. 對信號量集完成V操作
		semop(sem_id, &semop_V, 1);

		//為了方便觀察結果,延遲一秒在進入迴圈
		sleep(1);
	}

	return 0;
}

process_C.c

/*******************************************************************
*
*	file name:	process_C.c
*	author	 :  [email protected]
*	date	 :  2024/05/28
*	function :  該案例是掌握進程通信方式,主要是共用記憶體和信號量集的使用
*				需要將進程A創建的共用記憶體段映射到進程C中,方便後續讀出數據;
*				進程C需要對進程A創建的信號量集交替進行P操作和V操作,來實現
*				對共用記憶體段中的數據互斥修改
* 	note	 :  
*             由於信號量集中的信號量初值設定為1,所以進程B與進程C的執行順序
*             會影響輸出結果。若是想要從共用記憶體段中數據初值開始輸出,則需要
*             先執行進程C,在執行進程B
*   version  :
*
*	CopyRight (c)  2023-2024   [email protected]   All Right Reseverd 
*
* *****************************************************************/
/****************************頭文件**************************************/

#include <stdio.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>


int main(int argc, char const *argv[])
{
	while(1)
	{
		/****打開信號量集,完成P操作****/
		//1. 打開信號量集
		int sem_id = semget(ftok(".", 0xffffff01), 1, 0644);
		if(sem_id == -1)
		{
			fprintf(stderr, "sem_id error, errno:%d, %s\n", errno, strerror(errno));
            exit(-1);
        }
        //2. 定義結構體變數,並對結構體變數值完成相應配置
		struct sembuf semop_P;
		semop_P.sem_num = 0;	//該信號量集只有一個信號量,所以從下標0開始
		semop_P.sem_op = -1;	//操作值為負值,代表執行P操作,向信號量集申請資源
		semop_P.sem_flg = 0;	//由於題目要求進程B與進程C互斥,所以標誌設為0,即預設阻塞模式
		//3. 對信號量集完成P操作
		semop(sem_id, &semop_P, 1);

		/****打開共用記憶體段,並完成數據的輸出****/
		//1. 打開共用記憶體段
		int shm_id = shmget(ftok(".", 0xffffff01), 4,  0644);
		if(shm_id == -1)
		{
			fprintf(stderr, "shmget error, errno:%d, %s\n", errno, strerror(errno));
            exit(-2);
        }
        //2. 映射共用記憶體到進程B中
		int *pshm = (int *)shmat(shm_id, NULL, 0);
		if(pshm == (void*)-1)
		{
			fprintf(stderr, "shmat error, errno:%d, %s\n", errno, strerror(errno));
			exit(-3);
		}
		//3. 每次完成P操作後,將共用記憶體段中的數據輸出到終端上
        printf("data = %d\n", *pshm);
        // 4. 分離共用記憶體段
        int flag_dt = shmdt(pshm);
		if(flag_dt == -1)
		{
			fprintf(stderr, "shmdt error, errno:%d, %s\n", errno, strerror(errno));
			exit(-4);
		}
		
		/****完成對共用記憶體中數據輸出後,執行V操作****/
		//1. 定義結構體變數,並對結構體變數值完成相應配置
		struct sembuf semop_V;
		semop_V.sem_num = 0;	//該信號量集只有一個信號量,所以從下標0開始
		semop_V.sem_op = +1;	//操作值為正值,代表執行V操作,向信號量集歸還資源
		semop_V.sem_flg = 0;	//由於題目要求進程B與進程C互斥,所以標誌設為0,即預設阻塞模式
		//2. 對信號量集完成V操作
		semop(sem_id, &semop_V, 1);

		//為了方便觀察結果,延遲一秒在進入迴圈
		sleep(1);
	}

	return 0;
}

結果展示

image

重要知識點記錄

修改信號量集中的信號量的初值,需要使用到SETVAL標識,具體操作如下圖:

image


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Web Service 理解:Web Service 是一種基於網路的服務,它使用標準化的消息傳遞協議,最典型的是基於 SOAP(Simple Object Access Protocol)協議。SOAP 使用 XML 格式封裝數據,定義了消息的結構和傳輸方式,因此它是一個重量級的解決方案。Web ...
  • 工廠模式(Factory Pattern)是一種創建型設計模式,它提供了一種創建對象的介面,而不是通過具體類來實例化對象。工廠模式可以將對象的創建過程封裝起來,使代碼更具有靈活性和可擴展性。 工廠模式有幾種常見的實現方式: 簡單工廠模式(Simple Factory Pattern): 簡單工廠模式 ...
  • 一:背景 1. 講故事 前些天群里有一個朋友說他們軟體會偶發崩潰,想分析看看是怎麼回事,所幸的是自己會抓dump文件,有了dump就比較好分析了,接下來我們開始吧。 二:WinDbg 分析 1. 程式為什麼會崩潰 windbg 還是非常強大的,當你雙擊打開的時候會自動幫你定位過去展示崩潰時刻的寄存器 ...
  • StackExchange.Redis 是一個高性能的 Redis 客戶端庫,主要用於 .NET 環境下與 Redis 伺服器進行通信,大名鼎鼎的stackoverflow 網站就使用它。它使用非同步編程模型,能夠高效處理大量請求。支持 Redis 的絕大部分功能,包括發佈/訂閱、事務、Lua 腳本等... ...
  • 爆了,爆了,DeveloperSharp系列近期又被製造業ERP、民航飛行App、建築BIM、電力掌上營業廳、等多家大型採用,站在巨人的肩膀上你能走的更遠。 支持.Net Core2.0及以上,支持.Net Framework4.0及以上 http請求調用是開發中經常會用到的功能。在內,調用自有項目 ...
  • 前言:有時候遠程伺服器的進程你想偷偷去圍觀一下有哪些,或者對一些比較調皮的進程進行封殺,或者對一些自己研發的服務進行遠程手動啟動或者重啟等,又不想打開遠程桌面,只想悄咪咪地執行,那也許下麵的文章會對你有啟發。 前提條件 確保遠程伺服器(服務端)已啟用WinRM。在遠程伺服器上運行以下命令可以啟用和配 ...
  • !!!是的沒錯,胖友們,保姆教程系列又更新了!!! @目錄前言簡介一、磁碟分區二、文件系統三、實際操作1. 使用lsblk命令查看新加入的磁碟信息2. 使用fdisk或者cfdisk分區新磁碟,並將分區標記為Linux文件系統類型(83)3. 格式化新分區,使用mkfs命令4. 創建掛載目錄,使用m ...
  • 1. 為什麼要有線程 我們知道一個集成應用場景需要多個進程同時調度執行各自的功能,那麼多進程的本質就是產生多個執行流,每個執行流執行不同的代碼和功能,但是一個進程由PCB(task_struct)、進程地址空間、頁表、文件描述符表等資源組成,是一個資源集合,創建的開銷較大,那麼為了滿足用戶的多執行流 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...