Linux使用System?V實現(xiàn)內(nèi)存共享的最佳實踐
前言:
System V的共享內(nèi)存是Unix操作系統(tǒng)中一種進程間通信(IPC)機制,它允許不同的進程通過共享一塊物理內(nèi)存區(qū)域來交換數(shù)據(jù)。共享內(nèi)存提供了高效的進程間通信方式,因為進程可以直接讀寫共享區(qū)域,而不需要通過內(nèi)核或其他進程的中介。
system V共享內(nèi)存
System V共享內(nèi)存的特點:
- 高效性:通過共享內(nèi)存,不同進程可以直接訪問和修改數(shù)據(jù),而不需要經(jīng)過數(shù)據(jù)復制的過程,減少了開銷。
- 持久性:共享內(nèi)存段在進程退出后仍然存在,直到顯式地由進程刪除。
- 權限控制:共享內(nèi)存段可以設定訪問權限,控制哪些進程可以讀寫共享內(nèi)存。
System V共享內(nèi)存流程:
- 獲取
key
值:進程通過ftok
系統(tǒng)調(diào)用來計算出key
值. - 創(chuàng)建共享內(nèi)存段:進程使用
shmget
系統(tǒng)調(diào)用來創(chuàng)建或訪問共享內(nèi)存段。 - 映射共享內(nèi)存:通過
shmat
系統(tǒng)調(diào)用,將共享內(nèi)存段映射到進程的虛擬地址空間中。 - 使用共享內(nèi)存:進程可以直接通過指針訪問共享內(nèi)存,就像訪問普通內(nèi)存一樣。
- 解除映射:進程使用
shmdt
來解除對共享內(nèi)存段的映射。 - 刪除共享內(nèi)存:當不再需要共享內(nèi)存時,進程通過
shmctl
來刪除共享內(nèi)存段。
system V共享內(nèi)存函數(shù):
在Unix系統(tǒng)中,System V共享內(nèi)存的操作由一系列系統(tǒng)調(diào)用來管理。以下是常用的共享內(nèi)存函數(shù):
1. shmget
功能:創(chuàng)建一個共享內(nèi)存段或獲取已經(jīng)存在的共享內(nèi)存段。
原型:
int shmget(key_t key, size_t size, int shmflg);
返回值:返回共享內(nèi)存段的標識符(一個非負整數(shù)),如果出錯,返回 -1。
key
:共享內(nèi)存段的鍵值,用于標識共享內(nèi)存。如果是新創(chuàng)建的共享內(nèi)存段,key
是唯一的。size
:共享內(nèi)存段的大?。ㄗ止?jié)數(shù))。shmflg
:標志位,控制共享內(nèi)存的訪問權限。常用標志有:IPC_CREAT
:如果共享內(nèi)存段不存在,則創(chuàng)建一個新段。IPC_EXCL
:與IPC_CREAT
一起使用,如果共享內(nèi)存段已存在則調(diào)用失敗。0666
:設置權限(可讀可寫)。
2. shmat
功能:將共享內(nèi)存段映射到進程的虛擬地址空間。
原型:
void* shmat(int shmid, const void *shmaddr, int shmflg);
返回值:返回共享內(nèi)存的指針(映射到進程地址空間中)。如果失敗,則返回(void*) -1
。
shmid
:由shmget
返回的共享內(nèi)存段標識符。shmaddr
:期望的映射地址,通常設置為NULL
,讓操作系統(tǒng)自動選擇地址。shmflg
:標志位,常用的標志是0
(表示默認映射),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)存段的標識符。cmd
:控制命令,常用的命令有:IPC_STAT
:獲取共享內(nèi)存段的狀態(tài)。IPC_RMID
:刪除共享內(nèi)存段。SHM_INFO
:獲取共享內(nèi)存的統(tǒng)計信息。
buf
:用于存儲共享內(nèi)存信息的結構體,shmid_ds
結構體定義了共享內(nèi)存的狀態(tài)信息。
5.ftok
key_t ftok(const char *pathname, int proj_id);
參數(shù):
pathname
:文件路徑名(文件或目錄的路徑),該文件必須存在且具有一定的權限。路徑名用于生成一個唯一的標識符。proj_id
:一個項目標識符,通常是一個字符值(例如 ‘A’、‘B’ 等)。這用于區(qū)分同一文件的不同用途。
返回值:
- 成功時返回一個
key_t
類型的唯一鍵值,可以用于標識 IPC 對象。 - 失敗時返回
-1
,并設置errno
為相應的錯誤代碼。
shmid_ds結構體
shmctl
函數(shù)返回的shmid_ds
結構體用于獲取共享內(nèi)存段的信息。它通常包含以下字段:
struct shmid_ds { struct ipc_perm shm_perm; // 權限 size_t shm_segsz; // 共享內(nèi)存段大小 time_t shm_atime; // 最近一次附加時間 time_t shm_dtime; // 最近一次分離時間 time_t shm_ctime; // 創(chuàng)建時間 pid_t shm_cpid; // 創(chuàng)建該共享內(nèi)存段的進程ID pid_t shm_lpid; // 最后操作該共享內(nèi)存段的進程ID shmatt_t shm_nattch; // 附加到共享內(nèi)存的進程數(shù) };
觀察shmif_ds結構體
#include"share.hpp" // 引入頭文件,假設這是包含共享內(nèi)存相關函數(shù)聲明的頭文件。 using namespace std; // 使用標準命名空間,簡化代碼書寫。 int main() { // 獲取共享內(nèi)存段的標識符 int shmid = Getshmid(); // 使用shmat函數(shù)將共享內(nèi)存映射到進程的地址空間 char* shaddr = (char*)shmat(shmid, nullptr, 0); // 定義一個shmid_ds結構體,用于存儲共享內(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)存的權限 cout << "權限: " << shmds.shm_perm.mode << endl; // 輸出共享內(nèi)存的大小(字節(jié)數(shù)) cout << "共享內(nèi)存大小: " << shmds.shm_segsz << endl; // 輸出創(chuàng)建共享內(nèi)存的進程ID cout << "創(chuàng)建共享內(nèi)存的進程ID: " << shmds.shm_perm.mode << endl; // 輸出共享內(nèi)存的key值以及當前程序自定義的key值 cout << "共享內(nèi)存的key值: " << shmds.shm_perm.__key << " 自己創(chuàng)建的值:" << Get_key() << endl; // 使用shmdt解除共享內(nèi)存的映射 shmdt((void*)shaddr); return 0; // 結束程序 }
代碼的工作流程:
- 獲取共享內(nèi)存標識符 (
shmid
):通過Getshmid()
獲取共享內(nèi)存的標識符。Getshmid()
應該是用戶自己定義的一個函數(shù),返回有效的共享內(nèi)存段ID。 - 映射共享內(nèi)存:通過
shmat
函數(shù)將共享內(nèi)存段映射到當前進程的地址空間。這里使用的是nullptr
,表示操作系統(tǒng)會自動選擇一個合適的地址來映射共享內(nèi)存。 - 獲取共享內(nèi)存信息:調(diào)用
shmctl
函數(shù)并使用IPC_STAT
命令來獲取共享內(nèi)存段的狀態(tài),保存在shmid_ds
結構體中。這個結構體包含了共享內(nèi)存的權限、大小、創(chuàng)建進程ID、key值等信息。 - 輸出共享內(nèi)存的信息:輸出共享內(nèi)存的權限、大小、創(chuàng)建進程ID以及
key
值。注意,在輸出創(chuàng)建共享內(nèi)存的進程ID時,代碼中有個小錯誤,應改為shmds.shm_cpid
,而不是shmds.shm_perm.mode
,因為mode
字段是權限信息。 - 解除共享內(nèi)存映射:調(diào)用
shmdt
函數(shù)解除映射,防止進程泄露資源。
總結
shmget
用于創(chuàng)建和訪問共享內(nèi)存段。shmat
用于將共享內(nèi)存段映射到進程的地址空間。shmdt
用于解除共享內(nèi)存的映射。shmctl
用于控制共享內(nèi)存段,例如刪除或查看信息。
共享內(nèi)存通信實例(管道控制同步)
1. 構建管道類
class FIFO { public: // 構造函數(shù):創(chuàng)建命名管道(FIFO) FIFO() { // 調(diào)用 mkfifo 創(chuàng)建命名管道,MYFIFO_PATH 是管道的路徑,MODE 是文件權限 int n = mkfifo(MYFIFO_PATH, MODE); // 檢查 mkfifo 是否成功,若返回值為 -1,則表示創(chuàng)建失敗 if (n == -1) { // 如果創(chuàng)建失敗,記錄錯誤日志并退出程序 log(fatal, "mkfifo failure: %s", strerror(errno)); // 輸出錯誤日志,顯示錯誤原因 exit(4); // 返回 4 表示創(chuàng)建管道失敗 } // 如果創(chuàng)建成功,記錄成功日志 log(info, "mkfifo success"); // 輸出成功日志 } // 析構函數(shù):刪除命名管道 ~FIFO() { // 調(diào)用 unlink 刪除命名管道 int n = unlink(MYFIFO_PATH); // 檢查 unlink 是否成功,若返回值為 -1,則表示刪除失敗 if (n == -1) { // 如果刪除失敗,記錄錯誤日志并退出程序 log(error, "unlink failure: %s", strerror(errno)); // 輸出錯誤日志,顯示錯誤原因 exit(4); // 返回 4 表示刪除管道失敗 } // 如果刪除成功,記錄成功日志 log(info, "unlink success"); // 輸出成功日志 } };
FIFO
類用于創(chuàng)建和刪除命名管道(FIFO)。
構造函數(shù):在對象創(chuàng)建時,調(diào)用 mkfifo
創(chuàng)建命名管道。如果創(chuàng)建失敗,記錄錯誤并退出程序。
析構函數(shù):在對象銷毀時,調(diào)用 unlink
刪除命名管道。如果刪除失敗,記錄錯誤并退出程序。
2. 獲取key值
- 獲取
key
值:進程通過ftok
系統(tǒng)調(diào)用來計算出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 失敗,記錄錯誤信息到日志,并返回 1 log(fatal, "ftok failure:%s", strerror(errno)); // 錯誤日志輸出,顯示 errno 對應的錯誤信息 return 1; // 返回 1 表示錯誤 } // 如果成功,記錄成功信息到日志 log(info, "GetKey success"); // 成功日志輸出 // 返回生成的唯一 IPC 鍵值 return key;
3.創(chuàng)建共享內(nèi)存段
- 創(chuàng)建共享內(nèi)存段:進程使用
shmget
系統(tǒng)調(diào)用來創(chuàng)建或訪問共享內(nèi)存段。
int GetShare_mm(int flag) { // 使用獲取的 IPC 鍵值(通過 GetKey 函數(shù))和共享內(nèi)存的大?。⊿IZE_MM)來調(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 失敗,記錄錯誤信息到日志,并返回 2 作為錯誤標識 log(fatal, "shmget failure: %s", strerror(errno)); // 錯誤日志輸出,顯示 errno 對應的錯誤信息 return 2; // 返回 2 表示共享內(nèi)存獲取失敗 } // 如果 shmget 成功,記錄成功信息到日志 log(info, "shmget success"); // 成功日志輸出 // 返回獲取的共享內(nèi)存標識符 return shmid; }
4.映射、使用和銷毀共享內(nèi)存
服務端:
int main() { // 創(chuàng)建一個命名管道 FIFO FIFO init; // 假設 FIFO 是一個類,它的構造函數(shù)用于初始化命名管道。 // 打開命名管道,打開模式是只讀 int fd = open("myfifo", O_RDONLY); if (fd == -1) { // 如果打開管道失敗,記錄錯誤并退出 log(error, "open failure: %s", strerror(errno)); // 錯誤日志輸出,顯示 errno 對應的錯誤信息 exit(5); // 返回 5 表示出現(xiàn)錯誤并退出 } // 創(chuàng)建共享內(nèi)存并獲取共享內(nèi)存標識符 int shmid = CreateShmid(); // 假設 CreateShmid() 是創(chuàng)建共享內(nèi)存的函數(shù),它返回共享內(nèi)存段的標識符。 // 將共享內(nèi)存映射到進程的地址空間 char* shmaddr = (char*)shmat(shmid, nullptr, 0); if ((void*)shmaddr == (void*)-1) { // 如果共享內(nèi)存映射失敗,記錄錯誤并返回 log(fatal, "shmat failure: %s", strerror(errno)); // 錯誤日志輸出 return 1; // 返回 1 表示出錯 } // 進入通信循環(huán) while (true) { char ch;
- 創(chuàng)建管道:使用
FIFO
類創(chuàng)建命名管道。 - 打開管道:調(diào)用
open("myfifo", O_RDONLY)
打開管道進行讀取。如果失敗,記錄錯誤并退出。 - 創(chuàng)建共享內(nèi)存:使用
CreateShmid()
創(chuàng)建共享內(nèi)存。、 - 映射共享內(nèi)存:使用
shmat()
將共享內(nèi)存映射到進程地址空間,檢查映射是否成功。 - 通信循環(huán):從管道讀取字符,輸出共享內(nèi)存內(nèi)容,直到讀取結束。
- 解除映射與清理:使用
shmdt()
解除共享內(nèi)存映射,調(diào)用DeleteShare()
刪除共享內(nèi)存,釋放資源。
客戶端:
#include "share.hpp" int main() { // 打開命名管道 "myfifo" 進行寫操作 (O_WRONLY) int fd = open("myfifo", O_WRONLY); if (fd == -1) // 如果打開管道失敗,fd 會是 -1 { // 記錄管道打開失敗的錯誤信息并退出程序 log(error, "open failure: %s", strerror(errno)); exit(5); // 退出程序,返回錯誤碼 5 } // 獲取共享內(nèi)存的標識符 int shmid = Getshmid(); // 將共享內(nèi)存映射到進程地址空間 char* shmaddr = (char*)shmat(shmid, nullptr, 0); if ((void*)shmaddr == (void*)-1) // 檢查映射是否成功 { // 如果映射失敗,記錄錯誤信息并退出程序 log(fatal, "shmat failure: %s", strerror(errno)); return 1; // 返回錯誤碼 1 } // 進入通信循環(huán) while (true) { // 提示用戶輸入 std::cout << "Please Enter@ "; // 從標準輸入讀取數(shù)據(jù)并存儲到共享內(nèi)存 fgets(shmaddr, SIZE_MM, stdin); // 向管道寫入字符 'x',表示有新的消息 int n = write(fd, "x", sizeof(char)); } // 解除共享內(nèi)存映射 shmdt((void*)shmaddr); return 0; // 正常退出 }
- 打開管道:通過
open("myfifo", O_WRONLY)
打開管道進行寫操作。如果失敗,記錄錯誤并退出。 - 獲取共享內(nèi)存:使用
Getshmid()
獲取共享內(nèi)存標識符,使用shmat()
將共享內(nèi)存映射到進程地址空間。 - 通信循環(huán):不斷提示用戶輸入,將用戶輸入存儲到共享內(nèi)存中,并向管道寫入字符
'x'
表示有新的數(shù)據(jù)。、 - 解除映射并退出:調(diào)用
shmdt()
解除共享內(nèi)存映射,程序正常退出。
以上就是Linux使用System V實現(xiàn)內(nèi)存共享的最佳實踐的詳細內(nèi)容,更多關于Linux System V內(nèi)存共享的資料請關注腳本之家其它相關文章!
相關文章
Linux簡介及最常用命令(簡單易學,但能解決95%以上的問題)
這篇文章主要介紹了Linux簡介及最常用命令(簡單易學,但能解決95%以上的問題),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-08-08Linux下rsync遠程數(shù)據(jù)同步命令的詳細介紹
rsync是一款開源的、快速的、多功能的、可實現(xiàn)全量及增量的本地或遠程數(shù)據(jù)同步備份的優(yōu)秀工具。rsync軟件適用于unix/linux/windows等多種操作系統(tǒng)平臺。下面這篇文章主要介紹了Linux下rsync命令的相關資料,需要的朋友可以參考借鑒。2017-02-02Linux安裝Python虛擬環(huán)境virtualenv的方法
下面小編就為大家?guī)硪黄狶inux安裝Python虛擬環(huán)境virtualenv的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-01-01centos7 無線網(wǎng)卡驅動的安裝及無線網(wǎng)絡的配置詳解
本篇文章主要介紹了centos7 無線網(wǎng)卡驅動的安裝及無線網(wǎng)絡的配置詳解,具有一定的參考價值,有興趣的可以了解一下。2017-03-03