引言
在多進(jìn)程編程中,進(jìn)程間通信(IPC)是一項關(guān)鍵技術(shù),它使得不同的進(jìn)程能夠相互交換數(shù)據(jù)和協(xié)調(diào)行為。而在眾多的IPC機(jī)制中,System V共享內(nèi)存是一種高效且強大的通信方式。通過使用共享內(nèi)存,我們可以將數(shù)據(jù)從一個進(jìn)程快速地傳遞給另一個進(jìn)程,避免了復(fù)制數(shù)據(jù)的開銷,提高了數(shù)據(jù)傳輸?shù)男?。同時,由于共享內(nèi)存是在進(jìn)程之間共享的內(nèi)存區(qū)域,進(jìn)程可以直接在該內(nèi)存區(qū)域進(jìn)行讀寫操作,從而實現(xiàn)了高效的數(shù)據(jù)交換。本文將深入探討System V共享內(nèi)存的原理、使用方法以及相關(guān)函數(shù),并結(jié)合實際場景介紹如何利用共享內(nèi)存進(jìn)行進(jìn)程間通信。無論您是初學(xué)者還是有經(jīng)驗的開發(fā)者,本文都將為您提供全面的指導(dǎo),幫助您更好地掌握System V共享內(nèi)存,并應(yīng)用于您的項目中。讓我們一起深入研究進(jìn)程間通信的精髓,開啟高效、協(xié)同的多進(jìn)程編程之旅吧!??
一、system V的概念
System V是指AT&T公司開發(fā)的一系列UNIX操作系統(tǒng)版本,它是UNIX歷史上最著名和廣泛使用的一個分支。System V在1979年發(fā)布,其名稱中的“V”代表著羅馬數(shù)字5,表明它是UNIX的第五個主要版本。
System V引入了許多重要的功能和概念,其中之一就是System V IPC(進(jìn)程間通信)。System V IPC是一組用于在不同進(jìn)程之間傳遞數(shù)據(jù)和協(xié)調(diào)行為的機(jī)制。它包括三種主要的IPC機(jī)制:共享內(nèi)存(Shared Memory)、消息隊列(Message Queues)和信號量(Semaphores)
二、共享內(nèi)存
(1) 概念
??在Linux系統(tǒng)中,System V共享內(nèi)存是一種用于進(jìn)程間通信的機(jī)制,它允許多個進(jìn)程訪問同一塊物理內(nèi)存,從而實現(xiàn)數(shù)據(jù)共享。
??共享內(nèi)存區(qū)是最快的IPC形式。一旦這樣的內(nèi)存映射到共享它的進(jìn)程的地址空間,這些進(jìn)程間數(shù)據(jù)傳遞不再涉及到內(nèi)核,換句話說是進(jìn)程不再通過執(zhí)行進(jìn)入內(nèi)核的系統(tǒng)調(diào)用來傳遞彼此的數(shù)據(jù)
(2) 共享內(nèi)存示意圖
(3) 共享內(nèi)存數(shù)據(jù)結(jié)構(gòu)
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
__kernel_time_t shm_atime; /* last attach time */
__kernel_time_t shm_dtime; /* last detach time */
__kernel_time_t shm_ctime; /* last change time */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current attaches */
unsigned short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */
};
struct shmid_ds
是 System V 共享內(nèi)存的狀態(tài)信息結(jié)構(gòu)體,在 Linux 系統(tǒng)中用來描述共享內(nèi)存段的屬性和狀態(tài)。下面是 struct shmid_ds
結(jié)構(gòu)體中各個成員的含義:
成員 | 含義 |
---|---|
struct ipc_perm shm_perm | 用于描述共享內(nèi)存的操作權(quán)限。 |
int shm_segsz | 共享內(nèi)存段的大小,以字節(jié)為單位。 |
__kernel_time_t shm_atime | 最后一次附加(attach)該共享內(nèi)存的時間。 |
__kernel_time_t shm_dtime | 最后一次分離(detach)該共享內(nèi)存的時間。 |
__kernel_time_t shm_ctime | 最后一次修改該共享內(nèi)存的時間。 |
__kernel_ipc_pid_t shm_cpid | 創(chuàng)建者進(jìn)程的進(jìn)程號(PID)。 |
__kernel_ipc_pid_t shm_lpid | 最后一次操作該共享內(nèi)存的進(jìn)程號(PID)。 |
unsigned short shm_nattch | 當(dāng)前附加(attach)該共享內(nèi)存的進(jìn)程數(shù)。 |
unsigned short shm_unused | 未使用的字段,用于兼容性。 |
void* shm_unused2 | 未使用的字段,由 DIPC(Distributed Inter-Process Communication)使用。 |
void* shm_unused3 | 未使用的字段。 |
??通過這些狀態(tài)信息,我們可以獲取共享內(nèi)存段的大小、權(quán)限、最后訪問時間、創(chuàng)建者信息以及當(dāng)前附加進(jìn)程的數(shù)量等相關(guān)信息。這些信息對于管理和監(jiān)控共享內(nèi)存非常有用,能夠幫助我們更好地理解和控制共享內(nèi)存的狀態(tài)。
三、共享內(nèi)存的使用
1. 共享內(nèi)存的使用步驟
(1)包含頭文件
首先,在程序中包含 <sys/ipc.h>
、<sys/shm.h>
和 <sys/types.h>
頭文件,以便能夠使用共享內(nèi)存相關(guān)的函數(shù)和數(shù)據(jù)結(jié)構(gòu)。
(2)獲取鍵值(ftok函數(shù))
ftok
函數(shù)是一個用于生成 System V IPC(進(jìn)程間通信)鍵值的函數(shù),它通常用于創(chuàng)建消息隊列、共享內(nèi)存和信號量的標(biāo)識符。其原型如下:
key_t ftok(const char *pathname, int proj_id);
-
pathname
參數(shù)是一個指向路徑名的指針,它引用一個現(xiàn)有的目錄項。 -
proj_id
是一個用戶定義的整數(shù),用來區(qū)分不同的 IPC 對象。
ftok
函數(shù)會將給定的路徑名和項目 ID 轉(zhuǎn)換成一個唯一的 key 值,以便在創(chuàng)建或獲取 IPC 對象時使用。這個 key 值是一個標(biāo)識 IPC 對象的關(guān)鍵參數(shù),確保了不同的 IPC 對象具有不同的標(biāo)識符。
需要注意的是,ftok
函數(shù)并不是一個完美的方法來生成唯一的 IPC 鍵值,因為它對路徑名和項目 ID 的要求比較嚴(yán)格,而且在不同的系統(tǒng)上可能會有一些限制。另外,由于 ftok
使用的是低位 8 位作為唯一標(biāo)識,所以在某些情況下可能存在沖突。(可以類比C++中的哈希函數(shù))
(3)創(chuàng)建共享內(nèi)存段(shmget函數(shù))
shmget
函數(shù)用于創(chuàng)建或獲取一個共享內(nèi)存段。它的原型如下:
int shmget(key_t key, size_t size, int shmflg);
-
key
是通過ftok
函數(shù)生成的鍵值,用于標(biāo)識共享內(nèi)存段。 -
size
是共享內(nèi)存段的大小,以字節(jié)為單位。 -
shmflg
是用來指定權(quán)限和行為的標(biāo)志位。
shmget
函數(shù)的功能如下:
- 如果以給定的鍵值
key
找到了一個共享內(nèi)存段,則返回該共享內(nèi)存段的標(biāo)識符(非負(fù)整數(shù))。 - 如果以給定的鍵值
key
沒有找到對應(yīng)的共享內(nèi)存段,則根據(jù)shmflg
參數(shù)的設(shè)置來創(chuàng)建一個新的共享內(nèi)存段,并返回該共享內(nèi)存段的標(biāo)識符。 - 當(dāng)創(chuàng)建一個新的共享內(nèi)存段時,需要指定共享內(nèi)存段的大小
size
。如果創(chuàng)建成功,操作系統(tǒng)會分配足夠的內(nèi)存空間來容納這個大小的共享內(nèi)存段,并返回其標(biāo)識符。 -
shmflg
參數(shù)可以用來指定共享內(nèi)存段的權(quán)限和行為,例如讀寫權(quán)限、創(chuàng)建新的共享內(nèi)存段還是獲取已有的共享內(nèi)存段等。
需要注意的是,共享內(nèi)存段在整個系統(tǒng)中是全局可見的,可以被多個進(jìn)程同時訪問。因此,在使用共享內(nèi)存時,需要確保進(jìn)程之間的同步和互斥,以避免數(shù)據(jù)競爭和不一致性的問題。
(4)將共享內(nèi)存連接到當(dāng)前進(jìn)程的地址空間(shmat函數(shù))
shmat
函數(shù)用于將共享內(nèi)存段連接到當(dāng)前進(jìn)程的地址空間,以便進(jìn)程可以訪問共享內(nèi)存中的數(shù)據(jù)。它的原型如下:
void *shmat(int shmid, const void *shmaddr, int shmflg);
-
shmid
是共享內(nèi)存段的標(biāo)識符,通常是由shmget
函數(shù)返回的值。 -
shmaddr
是用來指定將共享內(nèi)存連接到進(jìn)程地址空間的地址,通常設(shè)為 NULL,表示由系統(tǒng)自動選擇合適的地址。 -
shmflg
是用來指定連接行為的標(biāo)志位。
shmat
函數(shù)的功能如下:
- 將指定標(biāo)識符的共享內(nèi)存段連接到當(dāng)前進(jìn)程的地址空間,并返回一個指向共享內(nèi)存段起始地址的指針。
- 如果
shmaddr
參數(shù)設(shè)為 NULL,則系統(tǒng)會自動選擇合適的地址將共享內(nèi)存連接到當(dāng)前進(jìn)程的地址空間。 -
shmflg
參數(shù)可以用來指定連接的行為,例如是否只讀、讀寫權(quán)限等。
?在調(diào)用 shmat
函數(shù)后,程序員可以通過返回的指針來訪問共享內(nèi)存中的數(shù)據(jù)。
(5)訪問共享內(nèi)存
連接成功后,就可以通過返回的指針來訪問共享內(nèi)存中存儲的數(shù)據(jù)了。
(6)分離共享內(nèi)存(shmdt函數(shù))
shmdt
函數(shù)用于將共享內(nèi)存段從當(dāng)前進(jìn)程的地址空間分離,以釋放資源并防止內(nèi)存泄漏。它的原型如下:
int shmdt(const void *shmaddr);
-
shmaddr
是指向共享內(nèi)存段起始地址的指針,需要與之前調(diào)用shmat
函數(shù)時返回的指針相同。
shmdt
函數(shù)的功能如下:
- 將共享內(nèi)存段從當(dāng)前進(jìn)程的地址空間分離。
- 分離后,當(dāng)前進(jìn)程將無法再訪問共享內(nèi)存段中的數(shù)據(jù)。
下面是 shmdt
函數(shù)的示例用法:
#include <sys/types.h>
#include <sys/shm.h>
#include <stdio.h>
int main()
{
void *ptr = ...; // 已知指向共享內(nèi)存的指針
int status = shmdt(ptr); // 將共享內(nèi)存從當(dāng)前進(jìn)程的地址空間分離
if (status == -1)
{
perror("shmdt");
return 1;
}
printf("Shared memory detached\n");
return 0;
}
需要注意的是,分離共享內(nèi)存后,當(dāng)前進(jìn)程將無法再通過指針 ptr
訪問共享內(nèi)存中的數(shù)據(jù)。為避免內(nèi)存泄漏,使用完共享內(nèi)存后應(yīng)該及時調(diào)用 shmdt
函數(shù)分離共享內(nèi)存。
2. 共享內(nèi)存的使用示例
進(jìn)程1:創(chuàng)建共享內(nèi)存并寫入數(shù)據(jù)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
int main() {
key_t key = ftok("shmfile", 'R'); // 通過 ftok 函數(shù)生成鍵值
int size = 1024; // 共享內(nèi)存段的大小
int shmflg = IPC_CREAT | 0666; // 創(chuàng)建共享內(nèi)存段的權(quán)限
int shmid = shmget(key, size, shmflg); // 獲取共享內(nèi)存的標(biāo)識符
if (shmid == -1) {
perror("shmget");
return 1;
}
printf("Shared memory segment ID: %d\n", shmid);
char *shmaddr = shmat(shmid, NULL, 0); // 將共享內(nèi)存連接到當(dāng)前進(jìn)程的地址空間
if (shmaddr == (char *)-1) {
perror("shmat");
return 1;
}
strcpy(shmaddr, "Hello, shared memory!"); // 向共享內(nèi)存寫入數(shù)據(jù)
printf("Data written to shared memory: %s\n", shmaddr);
shmdt(shmaddr); // 將共享內(nèi)存從當(dāng)前進(jìn)程的地址空間分離
return 0;
}
進(jìn)程2:連接共享內(nèi)存并讀取數(shù)據(jù)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
int main() {
key_t key = ftok("shmfile", 'R'); // 通過 ftok 函數(shù)生成鍵值
int size = 1024; // 共享內(nèi)存段的大小
int shmflg = 0666; // 訪問共享內(nèi)存的權(quán)限
int shmid = shmget(key, size, shmflg); // 獲取共享內(nèi)存的標(biāo)識符
if (shmid == -1) {
perror("shmget");
return 1;
}
char *shmaddr = shmat(shmid, NULL, 0); // 將共享內(nèi)存連接到當(dāng)前進(jìn)程的地址空間
if (shmaddr == (char *)-1) {
perror("shmat");
return 1;
}
printf("Data read from shared memory: %s\n", shmaddr); // 從共享內(nèi)存讀取數(shù)據(jù)
shmdt(shmaddr); // 將共享內(nèi)存從當(dāng)前進(jìn)程的地址空間分離
// 在實際應(yīng)用中,可能需要在完成讀取數(shù)據(jù)后刪除共享內(nèi)存段,使用 shmctl 函數(shù)執(zhí)行刪除操作
// shmctl(shmid, IPC_RMID, NULL);
return 0;
}
四、共享內(nèi)存的優(yōu)缺點
優(yōu)點:
-
快速高效: 由于共享內(nèi)存直接映射到進(jìn)程的地址空間,進(jìn)程可以直接讀寫共享內(nèi)存中的數(shù)據(jù),因此在性能上非常高效。
-
方便: 共享內(nèi)存提供了一個簡單的機(jī)制,允許多個進(jìn)程共享相同的數(shù)據(jù),而無需進(jìn)行復(fù)制或傳輸。這樣可以方便地實現(xiàn)進(jìn)程間的數(shù)據(jù)共享。
-
靈活性: 共享內(nèi)存可以用于任意類型的數(shù)據(jù),包括結(jié)構(gòu)化數(shù)據(jù)、對象等,因此非常靈活。
缺點:
-
同步問題: 由于多個進(jìn)程可以同時訪問共享內(nèi)存,因此需要額外的同步機(jī)制來確保數(shù)據(jù)的一致性和完整性,比如信號量、互斥鎖等。
-
安全性: 共享內(nèi)存的使用需要特別小心,因為在沒有適當(dāng)?shù)耐胶捅Wo(hù)機(jī)制的情況下,可能會導(dǎo)致競爭條件和數(shù)據(jù)損壞。
-
復(fù)雜性: 在設(shè)計和實現(xiàn)中,共享內(nèi)存可能會引入更多的復(fù)雜性,需要開發(fā)人員自行管理內(nèi)存分配、釋放、同步等問題,容易出現(xiàn)錯誤。
-
可移植性: 共享內(nèi)存的實現(xiàn)可能受限于操作系統(tǒng),因此在不同的平臺上可能存在一定的可移植性問題。
溫馨提示
感謝您對博主文章的關(guān)注與支持!如果您喜歡這篇文章,可以點贊、評論和分享給您的同學(xué),這將對我提供巨大的鼓勵和支持。另外,我計劃在未來的更新中持續(xù)探討與本文相關(guān)的內(nèi)容。我會為您帶來更多關(guān)于Linux以及C++編程技術(shù)問題的深入解析、應(yīng)用案例和趣味玩法等。如果感興趣的話可以關(guān)注博主的更新,不要錯過任何精彩內(nèi)容!文章來源:http://www.zghlxwxcb.cn/news/detail-751808.html
再次感謝您的支持和關(guān)注。我們期待與您建立更緊密的互動,共同探索Linux、C++、算法和編程的奧秘。祝您生活愉快,排便順暢!文章來源地址http://www.zghlxwxcb.cn/news/detail-751808.html
到了這里,關(guān)于【探索Linux】—— 強大的命令行工具 P.15(進(jìn)程間通信 —— system V共享內(nèi)存)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!