一摔桦、共享內(nèi)存是什么
在Linux系統(tǒng)中,共享內(nèi)存是一種IPC(進程間通信)方式承疲,它可以讓多個進程在物理內(nèi)存中共享一段內(nèi)存區(qū)域邻耕。
這種共享內(nèi)存區(qū)域被映射到多個進程的虛擬地址空間中,使得多個進程可以直接訪問同一段物理內(nèi)存區(qū)域中的數(shù)據(jù)燕鸽,從而實現(xiàn)進程間的高速數(shù)據(jù)交換和通信兄世。
二、共享內(nèi)存的原理
共享內(nèi)存基于內(nèi)核的支持啊研。在共享內(nèi)存中御滩,內(nèi)核維護了一塊物理內(nèi)存區(qū)域,并將其映射到多個進程的虛擬地址空間中党远。每個進程都可以使用指針來訪問共享內(nèi)存區(qū)域中的數(shù)據(jù)削解,就像它們訪問自己的內(nèi)存一樣。
<img src="https://s2.loli.net/2023/03/18/MpBHsE23jhbCkw1.png" alt="使用共享內(nèi)存示例" style="zoom: 25%;" />
三沟娱、共享內(nèi)存的使用方法
相關函數(shù)介紹
shmget函數(shù)
int shmget(key_t key, size_t size, int shmflg);
用于創(chuàng)建或打開一個共享內(nèi)存區(qū)段氛驮,具體參數(shù)如下:
參數(shù) | 類型 | 說明 |
---|---|---|
key |
key_t |
共享內(nèi)存區(qū)段的關鍵字,用于在多個進程間標識同一個共享內(nèi)存區(qū)段济似。 |
size |
size_t |
共享內(nèi)存區(qū)段的大小矫废,以字節(jié)為單位。 |
shmflg |
int |
共享內(nèi)存區(qū)段的訪問權(quán)限和行為屬性砰蠢。 |
函數(shù)返回值為共享內(nèi)存區(qū)段的標識符 shmid
蓖扑,用于標識已創(chuàng)建或已打開的共享內(nèi)存區(qū)段。
shmat函數(shù)
void *shmat(int shmid, const void *shmaddr, int shmflg);
用于將共享內(nèi)存區(qū)段連接到當前進程的地址空間台舱,具體參數(shù)如下:
參數(shù) | 類型 | 說明 |
---|---|---|
shmid |
int |
共享內(nèi)存區(qū)段的標識符律杠,用于標識已創(chuàng)建或已打開的共享內(nèi)存區(qū)段。 |
shmaddr |
const void* |
共享內(nèi)存區(qū)段連接到當前進程地址空間的起始地址,如果為 NULL柜去,則由系統(tǒng)自動選擇一個地址灰嫉。 |
shmflg |
int |
標志參數(shù),指定共享內(nèi)存區(qū)段的訪問權(quán)限和行為屬性诡蜓。 |
函數(shù)返回值為共享內(nèi)存區(qū)段連接到當前進程地址空間的起始地址,即指向共享內(nèi)存區(qū)段的指針胰挑。
shmdt函數(shù)
int shmdt(const void *shmaddr);
用于斷開進程與共享內(nèi)存區(qū)段的連接蔓罚,具體參數(shù)如下:
參數(shù) | 類型 | 說明 |
---|---|---|
shmaddr |
const void* |
共享內(nèi)存區(qū)段連接到當前進程地址空間的起始地址。 |
函數(shù)返回值為 0 表示成功瞻颂,-1 表示失敗豺谈。
shmctl函數(shù)
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
用于控制共享內(nèi)存區(qū)段的行為,如刪除贡这、獲取茬末、設置共享內(nèi)存區(qū)段的屬性等,具體參數(shù)如下:
參數(shù) | 類型 | 說明 |
---|---|---|
shmid |
int |
共享內(nèi)存區(qū)段的標識符盖矫,用于標識已創(chuàng)建或已打開的共享內(nèi)存區(qū)段丽惭。 |
cmd |
int |
控制命令,指定對共享內(nèi)存區(qū)段的操作類型辈双。 |
buf |
struct shmid_ds* |
指向共享內(nèi)存區(qū)段屬性結(jié)構(gòu)體的指針责掏,用于獲取或設置共享內(nèi)存區(qū)段的屬性。 |
常用的cmd
參數(shù)包括:
- IPC_STAT:獲取共享內(nèi)存的狀態(tài)信息湃望,并將該信息存儲在
buf
參數(shù)指向的結(jié)構(gòu)體中换衬。 - IPC_SET:設置共享內(nèi)存的狀態(tài)信息,
buf
參數(shù)指向要設置的新值证芭。 - IPC_RMID:刪除共享內(nèi)存瞳浦。
函數(shù)返回值為操作成功返回 0,失敗返回 -1废士。
實例演示
以下是一個示例代碼叫潦,其中一個程序用于寫入共享內(nèi)存,另一個程序用于讀取共享內(nèi)存官硝。
寫入程序
/* write.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/shm.h>
#include <string.h>
#define SHM_SIZE 1024
int main()
{
int shmid;
char *shmaddr;
char write_buf[SHM_SIZE];
// 創(chuàng)建共享內(nèi)存段
shmid = shmget((key_t)1234, SHM_SIZE, 0666|IPC_CREAT);
if (shmid == -1) {
perror("shmget failed");
exit(EXIT_FAILURE);
}
// 將共享內(nèi)存段連接到當前進程
shmaddr = (char*)shmat(shmid, 0, 0);
if (shmaddr == (void*)-1) {
perror("shmat failed");
exit(EXIT_FAILURE);
}
// 從標準輸入讀取數(shù)據(jù)并將其寫入共享內(nèi)存
while(1) {
fgets(write_buf, SHM_SIZE, stdin);
strncpy(shmaddr, write_buf, SHM_SIZE);
if (strncmp(write_buf, "exit", 4) == 0) {
break;
}
}
// 斷開共享內(nèi)存連接
if (shmdt(shmaddr) == -1) {
perror("shmdt failed");
exit(EXIT_FAILURE);
}
// 刪除共享內(nèi)存段
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl failed");
exit(EXIT_FAILURE);
}
printf("write exit\n");
return 0;
}
讀取程序
/* read.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/shm.h>
#include <string.h>
#define SHM_SIZE 1024
int main()
{
int shmid;
char *shmaddr;
char read_buf[SHM_SIZE];
// 獲取共享內(nèi)存段
shmid = shmget((key_t)1234, SHM_SIZE, 0666|IPC_CREAT);
if (shmid == -1) {
perror("shmget failed");
exit(EXIT_FAILURE);
}
// 將共享內(nèi)存段連接到當前進程
shmaddr = (char*)shmat(shmid, 0, 0);
if (shmaddr == (void*)-1) {
perror("shmat failed");
exit(EXIT_FAILURE);
}
// 從共享內(nèi)存讀取數(shù)據(jù)并輸出到標準輸出
while(1) {
strncpy(read_buf, shmaddr, SHM_SIZE);
printf("Received message: %s\n", read_buf);
if (strncmp(read_buf, "exit", 4) == 0) {
printf("Received exit\n");
break;
}
sleep(1);
}
// 斷開共享內(nèi)存連接
if (shmdt(shmaddr) == -1) {
perror("shmdt failed");
exit(EXIT_FAILURE);
}
printf("read exit\n");
return 0;
}
- 編譯這兩個程序
gcc -o write write.c
gcc -o read read.c
在一個終端窗口中運行write
, 輸入消息诅挑,在另一個終端窗口中運行read
,查看消息泛源。
要退出程序拔妥,則在運行write
的終端窗口中鍵入"exit",效果如下:
四达箍、 共享內(nèi)存的注意事項
- 使用共享內(nèi)存需要注意以下幾點:
- 同步問題:由于多個進程可以同時訪問共享內(nèi)存没龙,因此必須要使用同步機制來保證數(shù)據(jù)的一致性和正確性。
- 內(nèi)存泄漏:如果一個進程崩潰或者沒有及時解除共享內(nèi)存映射,就有可能導致內(nèi)存泄漏的問題硬纤。
- 安全問題:共享內(nèi)存是多個進程共享的解滓,因此必須要注意數(shù)據(jù)的安全性和隱私性,避免敏感數(shù)據(jù)泄露筝家。
五洼裤、 共享內(nèi)存的使用技巧
- 以下是一些共享內(nèi)存的使用技巧:
- 分配內(nèi)存時使用
shmget()
系統(tǒng)調(diào)用中的IPC_PRIVATE
標記,可以確保共享內(nèi)存的鍵值在系統(tǒng)中是唯一的溪王,避免沖突和安全問題腮鞍。 - 在讀寫共享內(nèi)存之前,使用信號量或互斥鎖等同步機制來保證數(shù)據(jù)的一致性和正確性莹菱。
- 在使用共享內(nèi)存時移国,可以將共享內(nèi)存區(qū)域按照固定大小進行分塊,避免多個進程同時訪問同一塊內(nèi)存區(qū)域的沖突道伟。
小結(jié)
共享內(nèi)存是一種高效迹缀、靈活、方便的進程間通信方式蜜徽,可以用于在多個進程之間共享大量數(shù)據(jù)祝懂。
使用共享內(nèi)存需要注意同步、安全和內(nèi)存泄漏等問題拘鞋,可以使用信號量嫂易、互斥鎖等同步機制來保證數(shù)據(jù)的正確性。
以上掐禁,如果覺得對你有幫助怜械,點個贊再走吧,這樣@知微之見也有更新下去的動力傅事!
也歡迎私信我缕允,一起交流!