Linux使用System?V實(shí)現(xiàn)內(nèi)存共享的最佳實(shí)踐
前言:
System V的共享內(nèi)存是Unix操作系統(tǒng)中一種進(jìn)程間通信(IPC)機(jī)制,它允許不同的進(jìn)程通過(guò)共享一塊物理內(nèi)存區(qū)域來(lái)交換數(shù)據(jù)。共享內(nèi)存提供了高效的進(jìn)程間通信方式,因?yàn)檫M(jìn)程可以直接讀寫共享區(qū)域,而不需要通過(guò)內(nèi)核或其他進(jìn)程的中介。
system V共享內(nèi)存
System V共享內(nèi)存的特點(diǎn):
- 高效性:通過(guò)共享內(nèi)存,不同進(jìn)程可以直接訪問(wèn)和修改數(shù)據(jù),而不需要經(jīng)過(guò)數(shù)據(jù)復(fù)制的過(guò)程,減少了開銷。
- 持久性:共享內(nèi)存段在進(jìn)程退出后仍然存在,直到顯式地由進(jìn)程刪除。
- 權(quán)限控制:共享內(nèi)存段可以設(shè)定訪問(wèn)權(quán)限,控制哪些進(jìn)程可以讀寫共享內(nèi)存。
System V共享內(nèi)存流程:
- 獲取
key值:進(jìn)程通過(guò)ftok系統(tǒng)調(diào)用來(lái)計(jì)算出key值. - 創(chuàng)建共享內(nèi)存段:進(jìn)程使用
shmget系統(tǒng)調(diào)用來(lái)創(chuàng)建或訪問(wèn)共享內(nèi)存段。 - 映射共享內(nèi)存:通過(guò)
shmat系統(tǒng)調(diào)用,將共享內(nèi)存段映射到進(jìn)程的虛擬地址空間中。 - 使用共享內(nèi)存:進(jìn)程可以直接通過(guò)指針訪問(wèn)共享內(nèi)存,就像訪問(wèn)普通內(nèi)存一樣。
- 解除映射:進(jìn)程使用
shmdt來(lái)解除對(duì)共享內(nèi)存段的映射。 - 刪除共享內(nèi)存:當(dāng)不再需要共享內(nèi)存時(shí),進(jìn)程通過(guò)
shmctl來(lái)刪除共享內(nèi)存段。
system V共享內(nèi)存函數(shù):
在Unix系統(tǒng)中,System V共享內(nèi)存的操作由一系列系統(tǒng)調(diào)用來(lái)管理。以下是常用的共享內(nèi)存函數(shù):
1. shmget
功能:創(chuàng)建一個(gè)共享內(nèi)存段或獲取已經(jīng)存在的共享內(nèi)存段。
原型:
int shmget(key_t key, size_t size, int shmflg);
返回值:返回共享內(nèi)存段的標(biāo)識(shí)符(一個(gè)非負(fù)整數(shù)),如果出錯(cuò),返回 -1。
key:共享內(nèi)存段的鍵值,用于標(biāo)識(shí)共享內(nèi)存。如果是新創(chuàng)建的共享內(nèi)存段,key是唯一的。size:共享內(nèi)存段的大?。ㄗ止?jié)數(shù))。shmflg:標(biāo)志位,控制共享內(nèi)存的訪問(wèn)權(quán)限。常用標(biāo)志有:IPC_CREAT:如果共享內(nèi)存段不存在,則創(chuàng)建一個(gè)新段。IPC_EXCL:與IPC_CREAT一起使用,如果共享內(nèi)存段已存在則調(diào)用失敗。0666:設(shè)置權(quán)限(可讀可寫)。
2. shmat
功能:將共享內(nèi)存段映射到進(jìn)程的虛擬地址空間。
原型:
void* shmat(int shmid, const void *shmaddr, int shmflg);
返回值:返回共享內(nèi)存的指針(映射到進(jìn)程地址空間中)。如果失敗,則返回(void*) -1。
shmid:由shmget返回的共享內(nèi)存段標(biāo)識(shí)符。shmaddr:期望的映射地址,通常設(shè)置為NULL,讓操作系統(tǒng)自動(dòng)選擇地址。shmflg:標(biāo)志位,常用的標(biāo)志是0(表示默認(rèn)映射),SHM_RDONLY(只讀映射)。
3. shmdt
功能:解除共享內(nèi)存的映射。
原型:
int shmdt(const void *shmaddr);
返回值:成功返回0,失敗返回-1。
shmaddr:共享內(nèi)存的指針,通常是由shmat返回的指針。
4. shmctl
功能:控制共享內(nèi)存段的各種操作,例如獲取共享內(nèi)存段的狀態(tài)、刪除共享內(nèi)存段等。
原型:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
返回值:成功返回0,失敗返回-1。
shmid:共享內(nèi)存段的標(biāo)識(shí)符。cmd:控制命令,常用的命令有:IPC_STAT:獲取共享內(nèi)存段的狀態(tài)。IPC_RMID:刪除共享內(nèi)存段。SHM_INFO:獲取共享內(nèi)存的統(tǒng)計(jì)信息。
buf:用于存儲(chǔ)共享內(nèi)存信息的結(jié)構(gòu)體,shmid_ds結(jié)構(gòu)體定義了共享內(nèi)存的狀態(tài)信息。
5.ftok
key_t ftok(const char *pathname, int proj_id);
參數(shù):
pathname:文件路徑名(文件或目錄的路徑),該文件必須存在且具有一定的權(quán)限。路徑名用于生成一個(gè)唯一的標(biāo)識(shí)符。proj_id:一個(gè)項(xiàng)目標(biāo)識(shí)符,通常是一個(gè)字符值(例如 ‘A’、‘B’ 等)。這用于區(qū)分同一文件的不同用途。
返回值:
- 成功時(shí)返回一個(gè)
key_t類型的唯一鍵值,可以用于標(biāo)識(shí) IPC 對(duì)象。 - 失敗時(shí)返回
-1,并設(shè)置errno為相應(yīng)的錯(cuò)誤代碼。
shmid_ds結(jié)構(gòu)體
shmctl函數(shù)返回的shmid_ds結(jié)構(gòu)體用于獲取共享內(nèi)存段的信息。它通常包含以下字段:
struct shmid_ds {
struct ipc_perm shm_perm; // 權(quán)限
size_t shm_segsz; // 共享內(nèi)存段大小
time_t shm_atime; // 最近一次附加時(shí)間
time_t shm_dtime; // 最近一次分離時(shí)間
time_t shm_ctime; // 創(chuàng)建時(shí)間
pid_t shm_cpid; // 創(chuàng)建該共享內(nèi)存段的進(jìn)程ID
pid_t shm_lpid; // 最后操作該共享內(nèi)存段的進(jìn)程ID
shmatt_t shm_nattch; // 附加到共享內(nèi)存的進(jìn)程數(shù)
};
觀察shmif_ds結(jié)構(gòu)體
#include"share.hpp" // 引入頭文件,假設(shè)這是包含共享內(nèi)存相關(guān)函數(shù)聲明的頭文件。
using namespace std; // 使用標(biāo)準(zhǔn)命名空間,簡(jiǎn)化代碼書寫。
int main()
{
// 獲取共享內(nèi)存段的標(biāo)識(shí)符
int shmid = Getshmid();
// 使用shmat函數(shù)將共享內(nèi)存映射到進(jìn)程的地址空間
char* shaddr = (char*)shmat(shmid, nullptr, 0);
// 定義一個(gè)shmid_ds結(jié)構(gòu)體,用于存儲(chǔ)共享內(nèi)存段的狀態(tài)信息
struct shmid_ds shmds;
// 調(diào)用shmctl函數(shù)獲取共享內(nèi)存段的狀態(tài)
int ret = shmctl(Getshmid(), IPC_STAT, &shmds);
// 輸出共享內(nèi)存的權(quán)限
cout << "權(quán)限: " << shmds.shm_perm.mode << endl;
// 輸出共享內(nèi)存的大?。ㄗ止?jié)數(shù))
cout << "共享內(nèi)存大小: " << shmds.shm_segsz << endl;
// 輸出創(chuàng)建共享內(nèi)存的進(jìn)程ID
cout << "創(chuàng)建共享內(nèi)存的進(jìn)程ID: " << shmds.shm_perm.mode << endl;
// 輸出共享內(nèi)存的key值以及當(dāng)前程序自定義的key值
cout << "共享內(nèi)存的key值: " << shmds.shm_perm.__key << " 自己創(chuàng)建的值:" << Get_key() << endl;
// 使用shmdt解除共享內(nèi)存的映射
shmdt((void*)shaddr);
return 0; // 結(jié)束程序
}
代碼的工作流程:
- 獲取共享內(nèi)存標(biāo)識(shí)符 (
shmid):通過(guò)Getshmid()獲取共享內(nèi)存的標(biāo)識(shí)符。Getshmid()應(yīng)該是用戶自己定義的一個(gè)函數(shù),返回有效的共享內(nèi)存段ID。 - 映射共享內(nèi)存:通過(guò)
shmat函數(shù)將共享內(nèi)存段映射到當(dāng)前進(jìn)程的地址空間。這里使用的是nullptr,表示操作系統(tǒng)會(huì)自動(dòng)選擇一個(gè)合適的地址來(lái)映射共享內(nèi)存。 - 獲取共享內(nèi)存信息:調(diào)用
shmctl函數(shù)并使用IPC_STAT命令來(lái)獲取共享內(nèi)存段的狀態(tài),保存在shmid_ds結(jié)構(gòu)體中。這個(gè)結(jié)構(gòu)體包含了共享內(nèi)存的權(quán)限、大小、創(chuàng)建進(jìn)程ID、key值等信息。 - 輸出共享內(nèi)存的信息:輸出共享內(nèi)存的權(quán)限、大小、創(chuàng)建進(jìn)程ID以及
key值。注意,在輸出創(chuàng)建共享內(nèi)存的進(jìn)程ID時(shí),代碼中有個(gè)小錯(cuò)誤,應(yīng)改為shmds.shm_cpid,而不是shmds.shm_perm.mode,因?yàn)?code>mode字段是權(quán)限信息。 - 解除共享內(nèi)存映射:調(diào)用
shmdt函數(shù)解除映射,防止進(jìn)程泄露資源。

總結(jié)
shmget用于創(chuàng)建和訪問(wèn)共享內(nèi)存段。shmat用于將共享內(nèi)存段映射到進(jìn)程的地址空間。shmdt用于解除共享內(nèi)存的映射。shmctl用于控制共享內(nèi)存段,例如刪除或查看信息。
共享內(nèi)存通信實(shí)例(管道控制同步)
1. 構(gòu)建管道類
class FIFO
{
public:
// 構(gòu)造函數(shù):創(chuàng)建命名管道(FIFO)
FIFO()
{
// 調(diào)用 mkfifo 創(chuàng)建命名管道,MYFIFO_PATH 是管道的路徑,MODE 是文件權(quán)限
int n = mkfifo(MYFIFO_PATH, MODE);
// 檢查 mkfifo 是否成功,若返回值為 -1,則表示創(chuàng)建失敗
if (n == -1)
{
// 如果創(chuàng)建失敗,記錄錯(cuò)誤日志并退出程序
log(fatal, "mkfifo failure: %s", strerror(errno)); // 輸出錯(cuò)誤日志,顯示錯(cuò)誤原因
exit(4); // 返回 4 表示創(chuàng)建管道失敗
}
// 如果創(chuàng)建成功,記錄成功日志
log(info, "mkfifo success"); // 輸出成功日志
}
// 析構(gòu)函數(shù):刪除命名管道
~FIFO()
{
// 調(diào)用 unlink 刪除命名管道
int n = unlink(MYFIFO_PATH);
// 檢查 unlink 是否成功,若返回值為 -1,則表示刪除失敗
if (n == -1)
{
// 如果刪除失敗,記錄錯(cuò)誤日志并退出程序
log(error, "unlink failure: %s", strerror(errno)); // 輸出錯(cuò)誤日志,顯示錯(cuò)誤原因
exit(4); // 返回 4 表示刪除管道失敗
}
// 如果刪除成功,記錄成功日志
log(info, "unlink success"); // 輸出成功日志
}
};
FIFO 類用于創(chuàng)建和刪除命名管道(FIFO)。
構(gòu)造函數(shù):在對(duì)象創(chuàng)建時(shí),調(diào)用 mkfifo 創(chuàng)建命名管道。如果創(chuàng)建失敗,記錄錯(cuò)誤并退出程序。
析構(gòu)函數(shù):在對(duì)象銷毀時(shí),調(diào)用 unlink 刪除命名管道。如果刪除失敗,記錄錯(cuò)誤并退出程序。
2. 獲取key值
- 獲取
key值:進(jìn)程通過(guò)ftok系統(tǒng)調(diào)用來(lái)計(jì)算出key值.
key_t GetKey()
{
// 調(diào)用 ftok 函數(shù),使用 pathname 和 proj_id 生成唯一的 IPC 鍵值
key_t key = ftok(pathname.c_str(), proj_id);
// 檢查 ftok 調(diào)用是否成功,key < 0 表示失敗
if (key < 0)
{
// 如果 ftok 失敗,記錄錯(cuò)誤信息到日志,并返回 1
log(fatal, "ftok failure:%s", strerror(errno)); // 錯(cuò)誤日志輸出,顯示 errno 對(duì)應(yīng)的錯(cuò)誤信息
return 1; // 返回 1 表示錯(cuò)誤
}
// 如果成功,記錄成功信息到日志
log(info, "GetKey success"); // 成功日志輸出
// 返回生成的唯一 IPC 鍵值
return key;
3.創(chuàng)建共享內(nèi)存段
- 創(chuàng)建共享內(nèi)存段:進(jìn)程使用
shmget系統(tǒng)調(diào)用來(lái)創(chuàng)建或訪問(wèn)共享內(nèi)存段。
int GetShare_mm(int flag)
{
// 使用獲取的 IPC 鍵值(通過(guò) GetKey 函數(shù))和共享內(nèi)存的大?。⊿IZE_MM)來(lái)調(diào)用 shmget 創(chuàng)建或獲取共享內(nèi)存
int shmid = shmget(GetKey(), SIZE_MM, flag);
// 檢查 shmget 調(diào)用是否成功,如果返回值是 -1 表示創(chuàng)建或獲取共享內(nèi)存失敗
if (shmid == -1)
{
// 如果 shmget 失敗,記錄錯(cuò)誤信息到日志,并返回 2 作為錯(cuò)誤標(biāo)識(shí)
log(fatal, "shmget failure: %s", strerror(errno)); // 錯(cuò)誤日志輸出,顯示 errno 對(duì)應(yīng)的錯(cuò)誤信息
return 2; // 返回 2 表示共享內(nèi)存獲取失敗
}
// 如果 shmget 成功,記錄成功信息到日志
log(info, "shmget success"); // 成功日志輸出
// 返回獲取的共享內(nèi)存標(biāo)識(shí)符
return shmid;
}
4.映射、使用和銷毀共享內(nèi)存
服務(wù)端:
int main()
{
// 創(chuàng)建一個(gè)命名管道 FIFO
FIFO init; // 假設(shè) FIFO 是一個(gè)類,它的構(gòu)造函數(shù)用于初始化命名管道。
// 打開命名管道,打開模式是只讀
int fd = open("myfifo", O_RDONLY);
if (fd == -1)
{
// 如果打開管道失敗,記錄錯(cuò)誤并退出
log(error, "open failure: %s", strerror(errno)); // 錯(cuò)誤日志輸出,顯示 errno 對(duì)應(yīng)的錯(cuò)誤信息
exit(5); // 返回 5 表示出現(xiàn)錯(cuò)誤并退出
}
// 創(chuàng)建共享內(nèi)存并獲取共享內(nèi)存標(biāo)識(shí)符
int shmid = CreateShmid(); // 假設(shè) CreateShmid() 是創(chuàng)建共享內(nèi)存的函數(shù),它返回共享內(nèi)存段的標(biāo)識(shí)符。
// 將共享內(nèi)存映射到進(jìn)程的地址空間
char* shmaddr = (char*)shmat(shmid, nullptr, 0);
if ((void*)shmaddr == (void*)-1)
{
// 如果共享內(nèi)存映射失敗,記錄錯(cuò)誤并返回
log(fatal, "shmat failure: %s", strerror(errno)); // 錯(cuò)誤日志輸出
return 1; // 返回 1 表示出錯(cuò)
}
// 進(jìn)入通信循環(huán)
while (true)
{
char ch;
- 創(chuàng)建管道:使用
FIFO類創(chuàng)建命名管道。 - 打開管道:調(diào)用
open("myfifo", O_RDONLY)打開管道進(jìn)行讀取。如果失敗,記錄錯(cuò)誤并退出。 - 創(chuàng)建共享內(nèi)存:使用
CreateShmid()創(chuàng)建共享內(nèi)存。、 - 映射共享內(nèi)存:使用
shmat()將共享內(nèi)存映射到進(jìn)程地址空間,檢查映射是否成功。 - 通信循環(huán):從管道讀取字符,輸出共享內(nèi)存內(nèi)容,直到讀取結(jié)束。
- 解除映射與清理:使用
shmdt()解除共享內(nèi)存映射,調(diào)用DeleteShare()刪除共享內(nèi)存,釋放資源。
客戶端:
#include "share.hpp"
int main()
{
// 打開命名管道 "myfifo" 進(jìn)行寫操作 (O_WRONLY)
int fd = open("myfifo", O_WRONLY);
if (fd == -1) // 如果打開管道失敗,fd 會(huì)是 -1
{
// 記錄管道打開失敗的錯(cuò)誤信息并退出程序
log(error, "open failure: %s", strerror(errno));
exit(5); // 退出程序,返回錯(cuò)誤碼 5
}
// 獲取共享內(nèi)存的標(biāo)識(shí)符
int shmid = Getshmid();
// 將共享內(nèi)存映射到進(jìn)程地址空間
char* shmaddr = (char*)shmat(shmid, nullptr, 0);
if ((void*)shmaddr == (void*)-1) // 檢查映射是否成功
{
// 如果映射失敗,記錄錯(cuò)誤信息并退出程序
log(fatal, "shmat failure: %s", strerror(errno));
return 1; // 返回錯(cuò)誤碼 1
}
// 進(jìn)入通信循環(huán)
while (true)
{
// 提示用戶輸入
std::cout << "Please Enter@ ";
// 從標(biāo)準(zhǔn)輸入讀取數(shù)據(jù)并存儲(chǔ)到共享內(nèi)存
fgets(shmaddr, SIZE_MM, stdin);
// 向管道寫入字符 'x',表示有新的消息
int n = write(fd, "x", sizeof(char));
}
// 解除共享內(nèi)存映射
shmdt((void*)shmaddr);
return 0; // 正常退出
}
- 打開管道:通過(guò)
open("myfifo", O_WRONLY)打開管道進(jìn)行寫操作。如果失敗,記錄錯(cuò)誤并退出。 - 獲取共享內(nèi)存:使用
Getshmid()獲取共享內(nèi)存標(biāo)識(shí)符,使用shmat()將共享內(nèi)存映射到進(jìn)程地址空間。 - 通信循環(huán):不斷提示用戶輸入,將用戶輸入存儲(chǔ)到共享內(nèi)存中,并向管道寫入字符
'x'表示有新的數(shù)據(jù)。、 - 解除映射并退出:調(diào)用
shmdt()解除共享內(nèi)存映射,程序正常退出。

以上就是Linux使用System V實(shí)現(xiàn)內(nèi)存共享的最佳實(shí)踐的詳細(xì)內(nèi)容,更多關(guān)于Linux System V內(nèi)存共享的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Apache實(shí)現(xiàn)多端口多主機(jī)名配置
這篇文章主要介紹了Apache實(shí)現(xiàn)多端口多主機(jī)名配置的相關(guān)資料,需要的朋友可以參考下2018-08-08
Linux查看系統(tǒng)版本最實(shí)用的三種方法
在Linux系統(tǒng)管理和日常運(yùn)維中,準(zhǔn)確獲取操作系統(tǒng)的版本信息是執(zhí)行軟件安裝、腳本兼容性判斷、系統(tǒng)升級(jí)以及問(wèn)題排查等任務(wù)的基石,本文將深入探討并詳細(xì)對(duì)比在Linux環(huán)境下查看系統(tǒng)版本的幾種主流方法,需要的朋友可以參考下2025-10-10
Linux上使用telnet連接本機(jī)IP地址端口方式
用戶需配置iptables允許本機(jī)TCP 80端口響應(yīng),輸入規(guī)則設(shè)源IP為本機(jī)以限制外部訪問(wèn),輸出規(guī)則設(shè)目標(biāo)IP為本機(jī)以確保本地連接,但telnet實(shí)際使用23端口,80端口可能為其他服務(wù),需確認(rèn)端口及規(guī)則方向2025-07-07
Apache下開啟SSI配置使html支持include包含的方法
這篇文章主要介紹了Apache下開啟SSI配置使html支持include包含的方法,需要的朋友可以參考下2016-10-10
Ubuntu 18.04中截圖工具shutter的編輯按鈕不可用的解決辦法
Shutter是一個(gè)由第三方提供的在Ubuntu上運(yùn)行的截圖工具。這篇文章主要介紹了Ubuntu 18.04中截圖工具shutter的編輯按鈕不可用的解決辦法及Ubuntu18.04 截圖工具推薦,需要的朋友可以參考下2018-08-08
Linux VPS 自動(dòng)備份數(shù)據(jù)到遠(yuǎn)程主機(jī)FTP的方法
推薦GD的免費(fèi)空間,10G,并且長(zhǎng)期穩(wěn)定。備份建議設(shè)置為每天執(zhí)行一次,時(shí)間在凌晨2點(diǎn)至凌晨5點(diǎn)之間2011-11-11
CentOS 7.0服務(wù)器安裝配置LAMP服務(wù)器(Apache+PHP+MariaDB)
這篇文章主要介紹了CentOS 7.0服務(wù)器安裝配置LAMP服務(wù)器(Apache+PHP+MariaDB),需要的朋友可以參考下2016-10-10

