【傳輸平臺】07 共享內存詳解及測試案例(本項目進程間的通信手段)

本項目使用共享內存來進行進程間通信。
**劃重點:**學習Linux的進程間通信(不侷限於進程間通信),我們應該站在Linux內核的角度去看待。
皮毛都不怎麼懂,我還需努力。

01 管道/消息隊列傳遞數據與用共享內存傳遞數據比較

001 用管道/消息隊列傳遞數據
在這裏插入圖片描述
如上圖需要四步:
(1)服務器讀文件;
(2)服務器把數據給內核,內核緩存下來;
(3)客戶端從內核中取數據;
(4)客戶端寫文件。

002 用共享內存傳遞數據
在這裏插入圖片描述
共享內存
進程像使用自己的堆區,棧區一樣使用內核
(1)服務器把文件拷貝到共享內存
(2) 客戶端直接從共享內存拿數據
所以共享內存是最快的IPC 進程間通信方式

02 共享內存理論知識

這塊不是重點: 直接去linux 使用man指令觀看

共享內存:是被多個進程共享的一部分物理內存,共享內存是進程間共享數據的一種最快的方法,一個進程向共享內存區域寫入了數據,共享這個內存區域的所有進程就可以理解看到其中的內容。

共享內存實現實現分爲兩個步驟:
一:創建共享內存,使用shmget函數
二:映射共享內存,將這段創建的共享內存映射到具體的進程空間去,使用shmat函數

創建:
int shmget(key_t key,int size,int shmflg)
key標識共享內存的鍵值:0/IPC_PRIVATE。當key的取值爲IPC_PRIVATE,則函數shmget()將創建一個新的共享內存;如果key的取值爲0,而參數shmflg中又設置IPC_PRIVATE這個標誌,則同樣會創建一塊新的共享內存。
返回值:如果成功則返回內存標識符,如果失敗則返回-1;

映射:
int shmat(int shmid, char *shmaddr,int flag)
參數:
shmid:shmget函數返回的共享內存存儲標識符
flag:決定以後用什麼方式來確定映射地址(通常爲0)
返回值:
如果成功,則返回共享內存到進程中的地址;如果失敗,則返回-1

一個進程不再需要共享內存時,需要把它從進程地址空間中脫離。

03 樣例

我們直接上代碼:

```cpp
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#include <sys/ipc.h>
#include <sys/shm.h>

// int shmget(key_t key, size_t size, int shmflg);
void main40()
{	
	int 		shmid;
	//打開共享內存 若存在,則使用;若不存在 則報錯
	//打開文件 若文件存在,則使用 若文件不存在則報錯
	shmid = shmget(0x2232, 128, 0666 );
	if (shmid < 0)
	{
		printf("獲取共享內存失敗\n");
		return ;
	}
	printf("獲取共享內存ok\n");
	printf("hello....\n");	
}

void main41()
{	
	int 		shmid = 0;
	//打開共享內存 若存在,則使用;若不存在 則創建
	//打開文件 	若文件存在,則使用 若文件不存在,則創建
	shmid = shmget(0x1133, 1024, 0666|IPC_CREAT);
	if (shmid < 0)
	{
		printf("獲取共享內存失敗\n");
		return ;
	}
	printf("創建共享內存成功 ok\n");
	printf("hello....\n");	
}

void main42()
{	
	int 		shmid = 0;
	//打開共享內存 若存在,則報錯;若不存在 則創建
	//打開文件 	若文件存在,報錯 若文件不存在,則創建
	//避免把舊的共享內存
	shmid = shmget(0x4000, 1024, 0666|IPC_CREAT | IPC_EXCL);
	if (shmid < 0)
	{
		printf("獲取共享內存失敗\n");
		return ;
	}
	printf("創建共享內存成功 ok\n");
	printf("hello....\n");	
}

/*

   void *shmat(int shmid, const void *shmaddr, int shmflg);

       int shmdt(const void *shmaddr);
       */
void main()
{	
	int 		shmid = 0;
	//打開共享內存 若存在,則使用;若不存在 則創建
	//打開文件 	若文件存在,則使用 若文件不存在,則創建
	shmid = shmget(0x2222, 1024, 0666|IPC_CREAT);
	if (shmid < 0)
	{
		printf("獲取共享內存失敗\n");
		return ;
	}
	printf("創建共享內存成功 ok\n");
	printf("hello....\n");	
	

	//連接共享內存
	char * p = shmat(shmid, NULL, 0);
	
	strcpy(p, "12345678");
	
	printf("p:%s \n", (char *)p);
	
	
	printf("任意鍵反連接共享內存,並且刪除共享內存\n");
	getchar();
	
	//取消連接共享內存
	shmdt((void *)p);
	
	//刪除共享內存
  	//int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  	shmctl(shmid, IPC_RMID, NULL);
}

(1)使用main40 進行結果演示:
運行之前:
在這裏插入圖片描述
看一下沒有0x2232 鍵的共享內存段,因此結果應該是失敗
在這裏插入圖片描述
注意這塊不存在該鍵的共享內存,以及字節大小小於main40要求的128字節,都會是獲取共享內存失敗。
(2)使用main41 進行結果演示
在這裏插入圖片描述
(3)直接使用上述的代碼不進行更改:就是使用main進行結果演示
代碼步驟是:1》創建共享內存 ; 2》連接共享內存 ;3》向裏面拷貝數據;4》取消連接共享內存;5》關閉共享內存
在這裏插入圖片描述
看到創建了共享內存,並在裏面有一個連接在運行。
當我們輸入任意鍵之後結果如下:
在這裏插入圖片描述
直接刪除。

04 思考一下當3個進程同時使用一塊內存空間,當其中一個進程要求刪除內存空間時會產生什麼結果?

在這裏我們的測試代碼還是使用上述的原代碼不進行更改,在linux中./(運行)三次就可以創建三個進程。
(1)運行前,無共享內存段:
在這裏插入圖片描述
(2)運行三次:
在這裏插入圖片描述
看一下在這個共享內存塊中有三個連接。
(3)讓其中一個刪除共享內存
在這裏插入圖片描述
讓其中一個進程斷掉連接後,刪除共享內存我們會發現連接數變爲2,鍵值變爲0X00000000。
等於說當多個進程連接同一個共享內存段的時候,其中一個連接刪除共享內存,那麼其他的連接會被系統自動轉存到鍵爲0X00000000的共享內存段中去。
我們要知道該鍵值的共享內存段是Linux內核私有的,不能被其它進程進行連接。(這些設計linux內核的大佬真厲害)

05 優缺點:

本篇文章優缺點不做重點,這塊是查看的其他人的博客
https://www.cnblogs.com/wuyepeng/p/9748889.html
(1)優點:我們可以看到使用共享內存進行進程間的通信真的是非常方便,而且函數的接口也簡單,數據的共享還使進程間的數據不用傳送,而是直接訪問內存,也加快了程序的效率。同時,它也不像匿名管道那樣要求通信的進程有一定的父子關係。

(2)缺點:共享內存沒有提供同步的機制,這使得我們在使用共享內存進行進程間通信時,往往要藉助其他的手段來進行進程間的同步工作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章