1. 程式人生 > >Linux IPC——簡單應用 共享記憶體(Share Memory)

Linux IPC——簡單應用 共享記憶體(Share Memory)


目錄

 

一:共享記憶體

1.1定義

1.2優缺點

1.2.1 優點

1.2.2缺點

1.3共享記憶體結構維護

1.4共享記憶體的通訊原理

二:相關函式介面

2.1 Linux命令

2.1.1檢視系統中的共享儲存段

 2.1.2刪除系統中的共享儲存段  

2.2 函式

2.2.1shmget()建立共享記憶體函式

2.2.2shmctl()銷燬等 函式

2.2.3shmat()脫接共享記憶體函式

2.2.4shmdt()去關聯共享記憶體函式

三:程式碼測試

3.1shma.c檔案

3.2shmb.c檔案


一:共享記憶體

1.1定義

    核心管理一片實體記憶體,允許不同的程序同時對映(訪問),多個程序可以對映同一塊記憶體。被多個程序同時對映的實體記憶體:即共享記憶體。(允許多進       程同步訪問給定的儲存區域)。

    對映實體記憶體-》掛接 ; 用完之後解除對映-》脫接

1.2優缺點

1.2.1 優點

    因為資料不需要在客戶程序和伺服器程序之間複製,所以這是一種最快的IPC

1.2.2缺點

    核心沒有提供對共享記憶體的互斥訪問

。(訊號量,互斥量等)

1.3共享記憶體結構維護

    核心為每個共享記憶體都維護相關資訊:

 

//共享記憶體的維護資訊
struct shmid_ds{
    struct ipc_perm     shm_perm;    //許可權控制
    size_t              shm_segsz;   //size of segement in bytes 位元組數
    pid_t               shm_lpid;    //pid of last shomp() 最後執行shomp()的程序ID
    pid_t               shm_cpid;    //建立該共享記憶體的ID
    shmatt_t            shm_nattch;  //number of current attaches,現有連線數
    time_t              shm_time;    //最後被連線時間
    time_t              shm_dtime;   //最後刪除連線的時間
    time_t              shm_ctime;   //最後修改時間
     .
     .
}   

1.4共享記憶體的通訊原理

    Linux,中每個程序都有自己的PCB(程序控制塊)和Addr Space(地址空間),該虛擬地址空間通過多級頁表(這裡以一級頁表為例)和實體地址空間進行對映,通過記憶體管理單元(MMU)進行管理。兩個不同的虛擬地址通過頁表對映到物理空間的同一區域,它們所指向的這個塊區域即:共享記憶體。

     對於一個共享記憶體,實現採用的是引用計數的原理,當程序脫接共享儲存區後,計數器減一;掛接則加一,直至計數器變為0,該共享記憶體才被刪除。當程序終止的時間,它所附加的共享儲存區都會自動脫離。

    這裡還可以解決一個問題:為什麼共享記憶體速度最快?

    上圖中:Proc A程序在給共享區域寫資料,Proc B 程序從其中讀取資料,再次其中一共發生了兩次資料的複製:

                      (1):Prco A 到共享記憶體             (2) : 共享記憶體到 Prec B

    因為直接在記憶體上操作,所以共享記憶體的速度也就提高了。

二:相關函式介面

2.1 Linux命令

2.1.1檢視系統中的共享儲存段

ipcs -m

 2.1.2刪除系統中的共享儲存段  

​ipcrm -m [shmid]​

2.2 函式

2.2.1shmget()建立共享記憶體函式

函式標頭檔案

函式原型:

#include<sys/shm.h>

int shmget ( key_t key , size_t size , int flag );

函式引數:

1. key:將key變換成一個識別符號

2.size_t:建立:共享儲存段的長度,位元組為單位。引用:設定為0

3.flag : 控制位。

函式功能:

若key不存在,建立一個;否則獲取。

成功返回 共享儲存的ID,失敗返回-1;

2.2.2shmctl()銷燬等 函式

函式原型: int shmctl ( int shmid , int cmd , struct shmid_ds *buf);
函式引數:

1.shmid:指定共享記憶體

2.cmd :在shmid上執行某些命令:IPC_STAT IPC_SET IPC_RMID SHM_LOCK PID_UNLOCK

函式功能:

函式對共享儲存段執行多種操作。

成功返回0,失敗返回-1.

2.2.3shmat()脫接共享記憶體函式

函式原型: void* shmat ( int shmid , const void *addr , int flag );
函式引數:

addr:指定共享儲存段連線到呼叫程序的哪個地址上。

addr為0 則此段連線到由核心選擇的第一個可用地址上,推薦使用方式。
addr為非0

並且內有指定 SHM_RND(取整的意思),則此連線段到addr所指定的地址上。

 

addr為非0 指定了SHM_RND,連線到(addr-(addr mod SHMLBA))所表示的地址上。(SHMLBA低邊界地址倍數)

 

函式功能: 一旦建立了一個共享儲存段,程序就可以呼叫shmat將其連線到它的地址空間中

2.2.4shmdt()去關聯共享記憶體函式

函式原型和返回值:

int shmdt ( const void *shmaddr );

成功返回0,並將shmid_ds結構體中的shm_nattch計數器減一;出錯返回-1。

引數: 連線以後返回的地址
功能: 當一個程序不需要共享記憶體的時間,就需要 去關聯。該函式並不刪除所指定的共享儲存區,而是將之前用shmat函式連線好的共享記憶體區 脫離目前程序。

三:程式碼測試

sem:連結https://blog.csdn.net/genzld/article/details/83095577

3.1shma.c檔案

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>

#include <sys/shm.h>  // shm ==> shared  memory
#include "./sem.h"

int main()
{
	int shmid = shmget((key_t)1234, 512, 0666 | IPC_CREAT);//指定共享記憶體空間的大小
	assert(shmid != -1);

	char *ptra = (char *)shmat(shmid, NULL, 0);//推薦給NULL,使得系統自動選取虛擬地址
	//ptra指向的是核心物件的空間

	assert(ptra != (char*)-1);

	int semid = sem_init(123);//初始化一個訊號量,



	while(1)
	{
		//sem_p(semid, 0);
		printf("please input: ");
		fflush(stdout);

		memset(ptra, 0, 512);//核心物件中的內容全部清空為0
		fgets(ptra, 128, stdin);//往共享記憶體中存取資訊
		ptra[strlen(ptra) - 1] = 0;//回車替換

		if(strcmp(ptra, "end") == 0)
		{
			break;
		}

	//	sem_v(semid, 0);
	}

	shmdt(ptra);//shmdt(void* addr)//給shmat的返回值,將虛擬地址和記憶體空間斷開
}

3.2shmb.c檔案

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>

#include <sys/shm.h>  // shm ==> shared  memory
#include "./sem.h"

int main()
{
	int shmid = shmget((key_t)1234, 512, 0666 | IPC_CREAT);
	assert(shmid != -1);

	char *ptrb = (char *)shmat(shmid, NULL, 0);//自動查詢

	assert(ptrb != (char*)-1);

	int semid = sem_init(123);

	while(1)
	{
		//sem_p(semid, 0);
		if(strcmp(ptrb, "end") == 0)
		{
			break;
		}

		int i = 0;
		for(; i < strlen(ptrb); ++i)//讀取ptrb中的內容
		{
			printf("%c\n", ptrb[i]);
			sleep(1);
		}

		//sem_v(semid, 0);
	}

	shmdt(ptrb);
}

參考:https://www.cnblogs.com/xcywt/p/5128430.html