自動(dòng)寫作文網(wǎng)站建站模板免費(fèi)下載
共享內(nèi)存
共享內(nèi)存是一種進(jìn)程間通信(IPC)機(jī)制,它允許多個(gè)進(jìn)程訪問同一塊內(nèi)存區(qū)域。這種方法可以提高效率,因?yàn)閿?shù)據(jù)不需要在進(jìn)程之間復(fù)制,而是可以直接在共享的內(nèi)存空間中讀寫。
?使用共享內(nèi)存的步驟通常包括:
- 創(chuàng)建共享內(nèi)存:一個(gè)進(jìn)程shmget()創(chuàng)建共享內(nèi)存區(qū)域。
- 映射共享內(nèi)存:其他進(jìn)程將該共享內(nèi)存映射到自己的地址空間中的共享區(qū)中。
- 讀寫數(shù)據(jù):進(jìn)程可以在共享內(nèi)存中讀寫數(shù)據(jù),進(jìn)行通信。
- 解除映射和刪除:使用完后,進(jìn)程解除映射并清理共享內(nèi)存。
shmget()創(chuàng)建共享內(nèi)存 獲取標(biāo)識(shí)符
int shmget(key_t key, size_t size, int shmflg);
#include <sys/shm.h>
參數(shù)
- key: 一個(gè)唯一的標(biāo)識(shí)符,用于區(qū)分不同的共享內(nèi)存段。可以使用?
ftok
?函數(shù)生成。- size: 請(qǐng)求的共享內(nèi)存段的大小(以字節(jié)為單位)。
- shmflg: 位圖 控制標(biāo)志,用于指定權(quán)限和其他選項(xiàng)。常見的選項(xiàng)包括:
IPC_CREAT
: 如果共享內(nèi)存段不存在則創(chuàng)建它。如果存在就獲取它IPC_EXCL|?
IPC_CREAT
?:一起使用,表示如果共享內(nèi)存段不存在就創(chuàng)建它,反之,存在則返回錯(cuò)誤。(返回成功一定是創(chuàng)建新的共享內(nèi)存)- 權(quán)限標(biāo)志,例如?
0666
?表示可讀可寫。返回值
- 成功時(shí),返回共享內(nèi)存標(biāo)識(shí)符(非負(fù)整數(shù))。(相當(dāng)于FILE*)
- 失敗時(shí),返回?
-1
,并設(shè)置?errno
?以指示錯(cuò)誤類型。
key作為標(biāo)識(shí)符但不能由系統(tǒng)創(chuàng)建分配給進(jìn)程,而是由用戶自己創(chuàng)建并給進(jìn)程,但要確保key是唯一的。
我們知道key是不同進(jìn)程找到同一個(gè)內(nèi)存空間的關(guān)鍵,要確保它們的key是一樣的。如果是系統(tǒng)分配fey,A進(jìn)程先創(chuàng)建了共享內(nèi)存獲得一個(gè)keg,B進(jìn)程如果想訪問A創(chuàng)建的共享內(nèi)存,就要獲取到key,但A B進(jìn)程相互獨(dú)立不可能從A進(jìn)程中獲取到key。
但如果是用戶給,可以讓用戶先生成唯一的key,再把key作為全局變量寫在AB進(jìn)程的源代碼中,這樣AB進(jìn)程就可以通過同一個(gè)key訪問到同一個(gè)共享內(nèi)存了。
如何生成唯一的key?
ftok()
key_t ftok(const char *pathname, int proj_id); key_t key = ftok("/path/to/file", 'R'); // 'R' 是一個(gè)項(xiàng)目標(biāo)識(shí)符
ftok()函數(shù)是一個(gè)用于生成唯一鍵值的系統(tǒng)調(diào)用,它接受一個(gè)文件路徑和一個(gè)項(xiàng)目標(biāo)識(shí)符(也可以是數(shù)字,通常是一個(gè)字符),并返回一個(gè)唯一的key。這個(gè)方法基于文件的 inode 號(hào),因此只要文件存在且未被刪除,返回的key就會(huì)是唯一的。
ipcs -m命令?顯示所有的共享內(nèi)存信息
ipcs -m 用于顯示系統(tǒng)中當(dāng)前存在的共享內(nèi)存段的信息。
ipcs -m
key shmid owner perms bytes nattch status
0x12345678 12345 user 666 1024 2
這條命令會(huì)列出所有的共享內(nèi)存段,包括它們的 shmid、鍵值、大小和其他信息。
bytes 1024 操作系統(tǒng)申請(qǐng)空間是按塊為單位(4kb...)來申請(qǐng)空間的,4096*x。假設(shè)該操作系統(tǒng)塊的大小為4kb如果你申請(qǐng)4097字節(jié),還是會(huì)申請(qǐng)8kb空間,但只讓你用4097字節(jié)的空間,越界就報(bào)錯(cuò)。
ipcrm -m命令刪除共享內(nèi)存段
ipcrm -m shmid
shmctl() 控制共享內(nèi)存 IPC_RMID
刪除
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
參數(shù)
shmid
:共享內(nèi)存段的標(biāo)識(shí)符,通常是通過?shmget
?函數(shù)獲取的。cmd
:命令,用于指定要執(zhí)行的操作,可以是以下常用值之一:
IPC_STAT
:將共享內(nèi)存段的信息填充到?buf
?指向的結(jié)構(gòu)中。IPC_RMID
:標(biāo)記共享內(nèi)存段以便刪除。SHM_LOCK
:鎖定共享內(nèi)存段。SHM_UNLOCK
:解鎖共享內(nèi)存段。SHM_INFO
:獲取共享內(nèi)存的統(tǒng)計(jì)信息(POSIX擴(kuò)展)。buf
:指向?shmid_ds
?結(jié)構(gòu)的指針,用于存儲(chǔ)共享內(nèi)存的元數(shù)據(jù)。- 成功時(shí)返回 0。
- 失敗時(shí)返回 -1,并設(shè)置?
errno
?以指示錯(cuò)誤原因。
shmat() 將共享內(nèi)存掛接到自己的地址空間中
void* shmat(int shmid, const void* shmaddr, int shmflg);
參數(shù)說明
- shmid: 共享內(nèi)存段的標(biāo)識(shí)符,通常通過?
shmget()
?獲取。- shmaddr: 指定共享內(nèi)存掛接到虛擬地址空間的起始地址。如果為?
NULL
,系統(tǒng)會(huì)選擇一個(gè)合適的地址。- shmflg: 標(biāo)志位: 0默認(rèn)附加模式,進(jìn)程可以讀寫共享內(nèi)存。?
SHM_RDONLY
: 只讀模式,進(jìn)程只能讀取數(shù)據(jù),不能寫入。返回值
- 成功時(shí),返回指向共享內(nèi)存掛接到虛擬地址空間的起始地址。
- 失敗時(shí),返回?
(void*) -1
,并設(shè)置?errno
?指示錯(cuò)誤類型。
注意共享內(nèi)存也是有權(quán)限的,進(jìn)程如果沒有對(duì)應(yīng)的權(quán)限是不能完成掛接的。
shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0666); // 創(chuàng)建共享內(nèi)存,權(quán)限為可讀可寫
在shmget()創(chuàng)建共享內(nèi)存時(shí),我們就可以設(shè)置權(quán)限0666:所有用戶都可以讀寫。
如果不設(shè)置權(quán)限,默認(rèn)權(quán)限是0600:創(chuàng)建者可讀寫 其他人無權(quán)限
shmdt()取消掛載
int shmdt(const void *shmaddr);
const void *shmaddr:掛載到進(jìn)程的虛擬地址空間的起始地址?
shmdt()于從進(jìn)程的地址空間分離已經(jīng)附加的共享內(nèi)存段。它不會(huì)刪除共享內(nèi)存段本身。
shmctl(IPC_RMID) :用于標(biāo)記共享內(nèi)存段為刪除,等待所有附加的進(jìn)程分離后釋放資源。
共享內(nèi)存通信速度最快
1.直接訪問:共享內(nèi)存允許多個(gè)進(jìn)程直接訪問同一塊內(nèi)存區(qū)域,避免了數(shù)據(jù)復(fù)制的開銷。這與其他通信方式(如管道、消息隊(duì)列等)不同,后者通常需要在進(jìn)程之間復(fù)制數(shù)據(jù)
管道:數(shù)據(jù)從外設(shè)讀取到進(jìn)程A的內(nèi)核空間,然后通過系統(tǒng)調(diào)用將數(shù)據(jù)寫入管道。B通過系統(tǒng)調(diào)用從管道中讀取數(shù)據(jù),再將其拷貝到自己的內(nèi)存中。
這種方式總共涉及四次拷貝:一次從外設(shè)到進(jìn)程A的內(nèi)核空間,一次從內(nèi)核空間寫入管道,一次從管道讀取到進(jìn)程B的內(nèi)核空間,最后一次從內(nèi)核空間到進(jìn)程B的內(nèi)存。
共享內(nèi)存 :數(shù)據(jù)從外設(shè)讀取到內(nèi)核空間后,進(jìn)程A和進(jìn)程B可以直接訪問共享的內(nèi)存區(qū)域,避免了多次拷貝。
因此,總的拷貝次數(shù)減少為兩次:一次從外設(shè)讀取到內(nèi)核空間,另一次是進(jìn)程B直接從共享內(nèi)存讀取數(shù)據(jù)。
2.低延遲:由于不涉及操作系統(tǒng)內(nèi)核的上下文切換,共享內(nèi)存通信的延遲較低,特別適合需要高頻率、低延遲的數(shù)據(jù)交換的場景。
3.高吞吐量:共享內(nèi)存能夠支持大量數(shù)據(jù)的快速傳輸,適合處理大規(guī)模數(shù)據(jù)或高并發(fā)的情況。
4.減少系統(tǒng)調(diào)用:其他通信機(jī)制往往需要進(jìn)行系統(tǒng)調(diào)用,而共享內(nèi)存可以減少這種需求,從而提升性能。
消息隊(duì)列
os提供一個(gè)隊(duì)列,A B進(jìn)程都可以看到這個(gè)隊(duì)列,把結(jié)構(gòu)體struct data作為結(jié)點(diǎn)放入隊(duì)列,再讓另一個(gè)進(jìn)程拿該節(jié)點(diǎn),實(shí)現(xiàn)通信。
怎么知道這個(gè)節(jié)點(diǎn)是其他進(jìn)程放的呢?
用戶要自己創(chuàng)建struct data,里面有int type標(biāo)識(shí)符,定義A進(jìn)程標(biāo)識(shí)符1 B為2,這樣就可以區(qū)分拿與自己標(biāo)識(shí)符不同的節(jié)點(diǎn)。
msgget() msgctl()?
這兩個(gè)函數(shù)用法和shmget() shmctl() 差不多
int msgget(key_t key, int msgflg);
參數(shù)說明:
key
: 消息隊(duì)列的鍵值,可以通過?ftok()
?函數(shù)生成。msgflg
: 控制消息隊(duì)列的行為,通常使用以下標(biāo)志:
IPC_CREAT
: 如果消息隊(duì)列不存在則創(chuàng)建一個(gè)。IPC_EXCL
: 如果消息隊(duì)列已經(jīng)存在,調(diào)用失敗。- 權(quán)限位(如?
0666
)控制訪問權(quán)限。返回值:
- 成功時(shí)返回消息隊(duì)列的標(biāo)識(shí)符(非負(fù)整數(shù))。
- 失敗時(shí)返回 -1。
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
參數(shù)說明:
msqid
: 消息隊(duì)列的標(biāo)識(shí)符。cmd
: 控制命令,可以是以下之一:
IPC_STAT
: 獲取消息隊(duì)列的狀態(tài)信息。IPC_SET
: 設(shè)置消息隊(duì)列的屬性。IPC_RMID
: 刪除消息隊(duì)列。buf
: 指向?msqid_ds
?結(jié)構(gòu)的指針,用于存放或設(shè)置屬性。返回值:
- 成功時(shí)返回 0。
- 失敗時(shí)返回 -1。
ipcs -q? ipcrm?-m
ipcs -q 用于顯示當(dāng)前系統(tǒng)中所有的消息隊(duì)列的信息。
------ Message Queues --------
key msqid owner perms used-bytes
0x12345678 12345 user1 666 0
0x87654321 12346 user2 666 0
ipcrm?-m刪除共享內(nèi)存段
ipcrm -m <shmid>
msgsnd() msgrcv() 特有
msgsnd() msgrcv()是消息隊(duì)列特有的函數(shù)
msgsnd() 發(fā)送消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
參數(shù)說明:
msqid
: 消息隊(duì)列的標(biāo)識(shí)符。msgp
: 指向要發(fā)送的消息節(jié)點(diǎn)的指針,消息的結(jié)構(gòu)體struct msgbuf要以標(biāo)識(shí)符long mtype開頭?msgsz
: 消息內(nèi)容的字節(jié)數(shù)(不包括?mtype
)。msgflg
: 自己的標(biāo)識(shí)符,常用值包括?IPC_NOWAIT
(如果隊(duì)列已滿則不等待,直接返回錯(cuò)誤)。0: 默認(rèn)行為,發(fā)送或接收消息時(shí)會(huì)阻塞,直到操作成功。返回值:
- 成功時(shí)返回 0。
- 失敗時(shí)返回 -1。
msgrcv() 接收消息
int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
參數(shù)說明:
msqid
: 消息隊(duì)列的標(biāo)識(shí)符。msgp
: 指向接收消息的緩沖區(qū)的指針。指向struct datamsgsz
: 消息內(nèi)容的最大字節(jié)數(shù)(不包括?mtype
)。msgtyp
: 指定接收消息的標(biāo)識(shí)符mtype,可以是特定類型的消息(如?1
),也可以是?0
(接收任何類型的消息)或?-1
(接收隊(duì)列中最早的消息)。msgflg
: 控制接收行為的標(biāo)志,常用值包括?IPC_NOWAIT
(如果隊(duì)列為空則不等待,直接返回錯(cuò)誤)。返回值:
- 成功時(shí)返回接收到的消息字節(jié)數(shù)。
- 失敗時(shí)返回 -1。
信號(hào)量
信號(hào)量是一種用于進(jìn)程間同步和互斥的機(jī)制,主要用于控制多個(gè)進(jìn)程對(duì)共享資源的訪問。它通過維護(hù)一個(gè)整型計(jì)數(shù)器來實(shí)現(xiàn),計(jì)數(shù)器的值表示可用資源的數(shù)量或狀態(tài)。
我們之前學(xué)過管道,我們把管道的資源看作一個(gè)整體,要寫整個(gè)資源所有地方都可以寫,要讀都可以讀。
現(xiàn)在我們把整個(gè)資源,分成多份,一個(gè)進(jìn)程訪問一份。整個(gè)資源,有部分可能在寫入,還有部分可能在讀,不同進(jìn)程,訪問共享資源,有一定并發(fā)性。
1.但是我們?cè)趺粗勒麄€(gè)資源里面還有多少份沒有進(jìn)程占用?
我們可以用一個(gè)計(jì)算器count來記錄里面剩余個(gè)數(shù),count=16 每進(jìn)入一個(gè)進(jìn)程count--(這種操作稱作P操作),出去count++(V操作)。
P操作(等待):
- 當(dāng)線程或進(jìn)程希望訪問某個(gè)共享資源時(shí),它會(huì)執(zhí)行P操作。這個(gè)操作的作用是檢查信號(hào)量的值:
- 如果信號(hào)量的值大于0,表示資源可用,線程可以繼續(xù)執(zhí)行,并且信號(hào)量的值減1。
- 如果信號(hào)量的值為0,表示資源不可用,線程會(huì)被阻塞,直到信號(hào)量的值變?yōu)檎?/li>
V操作(釋放):
- 當(dāng)線程或進(jìn)程完成對(duì)共享資源的使用后,會(huì)執(zhí)行V操作。這個(gè)操作的作用是將信號(hào)量的值增加1,表示資源現(xiàn)在可用。如果有其他線程在等待這個(gè)資源,執(zhí)行V操作后會(huì)喚醒其中一個(gè)等待的線程。
2.現(xiàn)在有出現(xiàn)一個(gè)問題怎么讓不同進(jìn)程看到同一個(gè)count呢?
可以用共享內(nèi)存 管道 讓進(jìn)程與進(jìn)程間建立聯(lián)系就可以3.但這樣又有一個(gè)新問題,count++這種操作并不是原子性的,多個(gè)線程同時(shí)修改同一數(shù)據(jù)引發(fā)數(shù)據(jù)競爭。
使用二進(jìn)制信號(hào)量(或互斥鎖),可以確保在任何時(shí)刻只有一個(gè)線程或進(jìn)程能夠訪問臨界區(qū)(共享資源)。當(dāng)一個(gè)線程進(jìn)入臨界區(qū)時(shí),它會(huì)調(diào)用 P 操作(等待),如果信號(hào)量值為 0,其他線程會(huì)被阻塞,直到該線程調(diào)用 V 操作(釋放),將信號(hào)量值加 1。
信號(hào)量可以分為兩種類型:
1.二進(jìn)制信號(hào)量。count==1
2.計(jì)數(shù)信號(hào)量。count>1
1.semget()
可以創(chuàng)建nsems個(gè)信號(hào)量,一個(gè)信號(hào)量管理count個(gè)資源。
int semget(key_t key, int nsems, int semflg);
key
: 唯一標(biāo)識(shí)符。nsems
: 信號(hào)量的數(shù)量。semflg
: 權(quán)限標(biāo)志(如?IPC_CREAT
、0666
)。
2.semctl()
控制信號(hào)量集的操作,如獲取、設(shè)置信號(hào)量值或刪除信號(hào)量。
int semctl(int semid, int semnum, int cmd, ...);
semid
: 信號(hào)量集標(biāo)識(shí)符。semnum
: 信號(hào)量在集合中的索引。cmd
: 操作命令(如?SETVAL
、GETVAL
、IPC_RMID
?等)。
semnum:信號(hào)量集合允許我們同時(shí)管理多個(gè)信號(hào)量,
semnum
就是用來指定我們想要操作的具體信號(hào)量。在一個(gè)信號(hào)量集合中,每個(gè)信號(hào)量都有一個(gè)唯一的索引,從 0 開始編號(hào)。例如,如果一個(gè)信號(hào)量集合中有三個(gè)信號(hào)量,索引將是 0、1 和 2。
3.semop()
?執(zhí)行對(duì)信號(hào)量的操作,如 P(wait)和 V(signal)。
int semop(int semid, struct sembuf *sops, size_t nsops);struct sembuf {unsigned short sem_num; // 信號(hào)量在集合中的索引short sem_op; // 要執(zhí)行的操作(正數(shù)表示 V 操作,負(fù)數(shù)表示 P 操作)short sem_flg; // 操作標(biāo)志(如 `IPC_NOWAIT`)
};
semid
: 信號(hào)量集標(biāo)識(shí)符。sops
: 指向?sembuf
?結(jié)構(gòu)數(shù)組的指針,定義了要執(zhí)行的操作。nsops
: 數(shù)組中操作的數(shù)量。(對(duì)nsops個(gè)信號(hào)量進(jìn)行PV操作)
ipcs -s?
顯示當(dāng)前系統(tǒng)中所有的信號(hào)量信息,包括信號(hào)量的標(biāo)識(shí)符、擁有者、權(quán)限和其他相關(guān)信息。
------ Semaphore Arrays --------
KEY ID OWNER PERMS NSEMS
0x12345678 12345 user 666 1
System V怎么實(shí)現(xiàn)IPC的
1.應(yīng)用角度,看IPC屬性
struct shmid_ds里面是共享內(nèi)存的屬性,第一個(gè)成員變量就是struct ipc_prem結(jié)構(gòu)體。而消息隊(duì)列 信號(hào)量同樣也是以struct ipc_prem結(jié)構(gòu)體開頭,struct ipc_prem結(jié)構(gòu)體里面就保存著key值。也就是說不同 IPC 機(jī)制可以通過相同的方式來管理和控制訪問權(quán)限,不同的IPC機(jī)制都有自己的一套key值,所以共享內(nèi)存 消息隊(duì)列 信號(hào)量的key可能重復(fù),但在一種IPC機(jī)制中key是唯一的。
2.從內(nèi)核角度,看IPC結(jié)構(gòu)
有全局的結(jié)構(gòu)體ipc_ids,里面有struct ipc_id_ary* entries指向結(jié)構(gòu)體ipc_id_ary。
結(jié)構(gòu)體ipc_id_ary最后一個(gè)成員變量是柔性數(shù)組,也就是說它可以動(dòng)態(tài)保存kern_ipc_perm指針。kern_ipc_perm結(jié)構(gòu)體里面保存的是IPC不同機(jī)制共同的屬性。
為什么說kern_ipc_perm結(jié)構(gòu)體里面保存的是IPC不同機(jī)制共同的屬性?
因?yàn)樵诓煌琍C機(jī)制的屬性中第一個(gè)成員變量就是kern_ipc_perm結(jié)構(gòu)體,其他成員變量都是根據(jù)不同IPC的實(shí)現(xiàn)機(jī)制特別增加的。
為什么不同IPC機(jī)制的屬性中第一個(gè)成員變量一定是kern_ipc_perm結(jié)構(gòu)體?
因?yàn)榻Y(jié)構(gòu)體ipc_id_ary中柔性數(shù)組保存的是kern_ipc_perm指針,如果共享內(nèi)存 消息隊(duì)列 信號(hào)量它們屬性第一個(gè)成員變量是kern_ipc_perm,那么就可以指向他們的結(jié)構(gòu)體屬性。
這樣ipc_id_ary數(shù)組就可以找到每個(gè)創(chuàng)建的共享內(nèi)存 消息隊(duì)列 信號(hào)量的屬性,進(jìn)而進(jìn)行管理。
其實(shí)我們shmget() msgget() semget()獲取的id就是它們?cè)趇pc_id_ary數(shù)組的下標(biāo)。
和key值一樣,id在一種IPC機(jī)制中是唯一的,但在不同IPC機(jī)制中可能相同。
也就是說一個(gè)ipc_id_ary數(shù)組中指向的全是相同IPC機(jī)制的屬性。
其實(shí)共享內(nèi)存是一種文件
在共享內(nèi)存的屬性中有struct file*的指針,它指向一個(gè)文件,文件有inode,知道它的數(shù)據(jù)塊的物理地址。vm_start vm_end是共享內(nèi)存在虛擬地址空間的起始地址 結(jié)束地址,把它與指向文件的內(nèi)存塊的物理地址建立映射關(guān)系,進(jìn)程可以通過這個(gè)映射直接訪問文件內(nèi)容。