目錄
共享內存的原理
共享內存的創建
代碼實現創建
共享內存的管理指令
我們今天來學習共享內存!!!
共享內存的原理
兩個進程同時使用內存中開辟的共享空間進行通信就是建立并使用共享內存進行進程間的通信。System V 共享內存(Shared Memory)允許不同的進程共享一塊內存區域。
那它和命名管道有什么區別呢?
所以共享內存不是文件,這個本質區別!!!
接下來我們講一下共享內存通信的原理:
兩個進程通過各自的mm_struct和頁表的映射,映射到各自的物理內存上的一塊空間形成物理地址(空間的頭地址),那兩個進程要進行通信就需要同一個共享內存,所以為了實現通信,內存創建一個共享內存(本質不是由操作系統創建,應該是其中一個進程要求然后自己先創建了),我們暫且任務需要OS創建,這個共同的內存通過頁表映射到各自進程的虛表的共享區然后這個過程叫掛接到進程的地址空間中,解除共享狀態就是去關聯。管道如果只讀打開的話會卡在創建管道的之前,而共享內存就沒有這個問題。
共享內存的創建
使用shmget函數創建共享內存空間。
作為一個系統函數,三個參數,我們先看返回值以及參數說明吧。
size就是設置共享內存的字節大小,shmfig就是創建這個共享內存的創建標志,這個參數只能有以下三種傳遞方式:
前面這個key,類型就是這樣的,這個是用來標志共享內存的唯一標識符,這個一般是用戶自己傳進去的,也就是進程創建時自己提供,為什么不能操作系統直接提供呢?
共享內存在任何時刻可以在OS內部存在多個,那么個的共享內存操作系統肯定是需要管理的,怎么管理呢先描述再組織,又是這句話,共享內存=共享內存的內核數據結構+內存塊,操作系統將這個struct shm加載到操作系統中時,如果自行創建key那進程A進來創建共享內存得到操作系統的key,進程B要與之通信,可是如果此時進程B還沒有創建怎么辦,A和B毫不相干,這樣進程B進入操作系統就不知道key值不知道要訪問哪個共享內存了,如果這個是由jincA創建的key那到時候A只需要將其放在AB的共享區(都能看到的區域)這樣B就可以看到了!!!這個A創建的過程本質還是讓不同的進程看到同一份資源。
這個key值是隨便創建的嗎,A進程隨便給key不會和其他進程沖突嗎,會,所以操作系統認為你既然自己創建不好,我就自己提供一個函數創建一個key給你用。
ftok
是一個 System V IPC 中的函數,用于根據指定的路徑名和一個項目ID(通常是一個字符)生成一個唯一的鍵值(key_t
)。這個鍵值通常用于共享內存、消息隊列或信號量等 IPC 資源的標識符。
ftok根據你傳入的公共路徑(會被解析成數字)和公共項目的ID進行聯合根據某種算法得出唯一key值
這種算法得出的key值原則上還是會有極小概率的沖突,可以忽略不計的,就是比自己創建準。
代碼實現創建
依舊是創建兩個進程和之前一樣,然后都可訪問公共頭文件comm.hpp,makefile里面就是能同時編譯兩個.cc,和上一篇博客里面寫的一樣的。
我們得先創建一個key,所以先使用ftok進行創建,默認server.cc里面進行創建,然后知道了公共路徑和id就知道了key,這個公共路徑是隨便起的自己系統里有這個路徑就行,id也是隨便起的,我們這里使用16進制數,所以將公共路徑和id寫入公共.hpp讓兩個進程都可以看到。
我們這個key值非常大,并且由于路徑和id一致所以沒次運行都是一樣的。
那光有key不行呀,我們是不是還要判斷創建key成功了沒有,并且有了key之后就可以創建shm了對吧,創建共享內存的地點還是在server,不過創建多大字節的空間就需要公共出來。
由于每次創建的都是最新的,已經存在了共享內存就會報錯!!!
這個我們的使用代碼進行創建的流程就完了,至于實現通信就是下節課的事了,由上面可以看到第一次運行了之后由于server進程結束就退出了,但是第二次運行時那個共享空間還在內核里面,所以共享內存的生命周期是不隨單個進程的,而是隨著操作系統的生命周期,所以要銷毀管道只能是將兩個互通的進程都關掉然后操作系統認為這個內存沒有意義了,就自己釋放了,這個過程屬于用戶讓操作系統釋放,方法2是讓OS自己重啟,make clean是代碼層面的釋放,我們還可以使用指令創建,釋放。
共享內存的管理指令
ipcs指令可以看到各自通信模式,有消息隊列,共享內存和管道,由于我們之前沒有釋放共享內存所以就顯示有一個,
nattch是該共享內存的附加進程數,就是有多少個進程在進行通信,當 nattch == 0
時,意味著當前沒有任何進程使用該共享內存,但內存段仍然存在,直到 shmctl(IPC_RMID)
刪除它。因為我們只是創建還沒有使用所以是0。
ipcs -m選項直接可以看到共享進程。
ipcrm
是 Linux 上的 IPC(進程間通信)資源管理命令,用于刪除共享內存、信號量或消息隊列。其中,ipcrm -m
用于刪除共享內存段。
可以看到要刪除一個共享內存需要-m之后再指定上可以標志該內存段的ID,就是刪除這個指定的內存段,為什么不使用key而使用shmid(shmget的返回值)。
還有一個原因是key也是有可能重復的對吧,即使使用了那種算法。
這個shmid有點像文件描述符對吧,shmid可以指明一個獨一的共享內存。
如果我們一直創建共享內存然后刪掉,再創建,發現這個shmid從0開始自增到n,n可以等于2.。。。,所以這個shmid就是數組的下標,我們可以認為shmid數組表的下標存放了shmid值,然后內容指向了共享內存,這個結構和文件描述符表是大致一樣的,但是文件的是從3開始創建的,所以共享內存這部分和它又有所不同(后面會細講)。
接著講指令創建:
shmctl()
是 Linux System V 共享內存的管理函數,用于獲取共享內存信息、修改權限、刪除共享內存等。
這個函數的功能有點多,可以用于創建共享內存等等。
就是將指定唯一的shmid的共享內存創建/修改/刪除,到底執行哪一種就取決于cmd的傳入,這個cmd是像flag有特定傳入限制的(有提供不需要自己寫),然后如果是創建/修改就需要使用buf,刪除填個nullptr就可以了,不關心buf指向的共享內存的信息。
buf指針指向struct shmid_ds這個裝有共享內存信息的結構體,用于修改/創建。我就說共享內存空間有類似于PCB的內核數據結構吧。
我們進行函數級的刪除,再使用ipcs -m進行追蹤。
刪除成功!!!
咦不對呀,這個shmctl不是指令呀,怎么寫在這里,我后學的這個還不行嗎???