Linux IPC——簡單應用 共享記憶體(Share Memory)
目錄
一:共享記憶體
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:指定共享儲存段連線到呼叫程序的哪個地址上。
|
||||||
函式功能: | 一旦建立了一個共享儲存段,程序就可以呼叫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);
}