Linux共享記憶體常見問題分析

2021-06-20 14:46:26 字數 3670 閱讀 3856

這個是接上篇,本來是記錄在一篇草稿上的,但是,內容根本不相關,排版怎麼都覺得不好看,也不方便以後查閱。乾脆再起一篇。

shmget函式用來建立乙個新的,或者訪問乙個已存在的共享記憶體區。

#include #include int shmget(key_t key, size_t size, int oflag);
返回值為整型的共享記憶體區識別符號,用其指代剛建立或已存在的共享記憶體區。

key是ftok()返回的鍵值,size指定記憶體區大小,oflag是一些列讀寫許可權組合。具體不做介紹,請參考《網路程式設計卷二》。

現在的問題是,如果兩個程序以乙個相同的key共享一塊記憶體區,但不同的程序呼叫shmget的size不一樣,是否能成功?

這種關係可能導致的問題:

當多個程序都能建立共享記憶體的時候,如果key出現相同的情況,並且乙個程序需要建立的共享記憶體的大小要比另外乙個程序要建立的共享記憶體小,共享記憶體大的程序先建立共享記憶體,共享記憶體小的程序後建立共享記憶體,小共享記憶體的程序就會獲取到大的共享記憶體程序的共享記憶體,並修改其共享記憶體的大小和內容,從而可能導致大的共享記憶體程序崩潰。

解決方法:

在oflag引數中使用排他性建立,即使用ipc_excl標記;

key值使用ipc_private,然後將最終返回的共享記憶體識別符號用其他ipc方法通知給相關程序;

兩種方法應視具體情況使用。

ftok函式是把乙個已存在的路徑名和乙個整數識別符號轉換成乙個key_t值,稱為ipc鍵。原型如下:

#include key_t ftok(const char * pathname, int id)
該函式將會把pathname匯出的資訊與id組合成乙個整數ipc鍵。在一般的unix實現中,是將pathname所指定的檔案索引節點號取出,前面加上id號得到key_t的返回值。如指定檔案的索引節點號為65538,換算成16進製為0x010002,而你指定的id值為38,換算成16進製為0x26,則最後的key_t返回值為0x26010002。

(具體組合情況可以參考《網路程式設計卷二》3.2節)

下面這段話很重要:

根據pathname指定的檔案(或目錄)名稱,以及id引數指定的數字,ftok函式為ipc物件生成乙個唯一性的鍵值。

在實際應用中,很容易產生的乙個理解是,在id相同的情況下,只要檔案(或目錄)名稱不變,就可以確保ftok返回始終一致的鍵值。然而,這個理解並非完全正確,有可能給應用開發埋下很隱晦的陷阱。

因為ftok的實現存在這樣的風險,即在訪問同一共享記憶體的多個程序先後呼叫ftok函式的時間段中,如果pathname指定的檔案(或目錄)被刪除且重新建立,則檔案系統會賦予這個同名檔案(或目錄)新的i節點資訊,於是這些程序所呼叫的ftok雖然都能正常返回,但得到的鍵值卻並不能保證相同。由此可能造成的後果是,原本這些程序意圖訪問乙個相同的共享記憶體物件,然而由於它們各自得到的鍵值不同,實際上程序指向的共享記憶體不再一致;如果這些共享記憶體都得到建立,則在整個應用執行的過程中表面上不會報出任何錯誤,然而通過乙個共享記憶體物件進行資料傳輸的目的將無法實現。

解決方法:

另外,建議:建立程序在通知其他程序掛接的時候,建議不使用ftok方式來獲取key,而使用檔案或者程序間通訊的方式告知。

當由shmget首次建立共享記憶體時,此時是不能被任何程序所訪問的。為了使共享記憶體區可以被訪問,則必須通過 shmat 函式將其附加( attach )到自己的程序空間中,這樣程序就與共享記憶體建立了連線。該函式原型如下:

#include #include void *shmat(int shmid, const void *shmaddr, int flag);
shmid即shmget()函式返回的共享記憶體識別符號,flag是讀寫許可權組合,比如常見的幾種:

#define shm_rdonly     ***          /* attach read-only else read-write */

#define shm_rnd *** /* round attach address to shmlba */

#define shm_remap *** /* take-over region on attach */

具體情況請參考《網路程式設計卷二》

重要部分:

在程式使用中,因為我們一般不清楚程序中哪些位址沒有被占用,所以不好指定物理空間的記憶體要對映到本程序的虛擬記憶體位址,一般會讓核心自己指定,也就是讓shmaddr設定為null,這樣掛載乙個共享記憶體如果是一次呼叫是沒有問題的,但是

乙個程序是可以對同乙個共享記憶體多次 shmat進行掛載的,物理記憶體是指向同一塊,如果shmaddr為null,則每次返回的線性位址空間都不同。而且指向這塊共享記憶體的引用計數會增加。

也就是程序多塊線性空間會指向同一塊實體地址。這樣,如果之前掛載過這塊共享記憶體的程序的線性位址沒有被shmdt掉,即申請的線性位址都沒有釋放,就會一直消耗程序的虛擬記憶體空間,很有可能會最後導致程序線性空間被使用完而導致下次shmat或者其他操作失敗。

解決方法:

***_***()

當乙個程序完成某個共享記憶體區的使用時,它可呼叫shmdt斷接這個記憶體區。原型如下:

#include #include int shmdt(const void *shmaddr);
shmaddr是shmat函式的返回值,同樣可參考《網路程式設計卷二》。

程序脫離共享記憶體區後,資料結構 shmid_ds 中的 shm_nattch 就會減 1 。但是

共享段記憶體依然存在,只有 shm_attch 為 0 後,即沒有任何程序再使用該共享記憶體區,共享記憶體區才在核心中被刪除。一般來說,當乙個程序終止時,它所附加的共享記憶體區都會自動脫離。

我們同樣也可以通過shmctl函式對共享記憶體進行諸如刪除的操作。原型如下:

int shmctl(int shmid , int cmd , struct shmid_ds *buff);
cmd引數提供了三個命令:

重要部分:

一旦通過shmctl對共享記憶體進行了刪除操作,則該共享記憶體將不能再接受任何新的連線,即使它依然存在於系統中!所以,可以確知,

在對共享記憶體刪除之後不可能再有新的連線,則執行刪除操作是安全的;否則,在刪除操作之後如仍有新的連線發生,則這些連線都將可能失敗!

shmdt和shmctl的區別:

shmdt  是將共享記憶體從程序空間detach出來,使程序中的shmid無效化,不可以使用,但是保留空間;

shmctl(sid,ipc_rmid,0)則是刪除共享記憶體,徹底不可用,釋放空間;

這篇文章介紹的東西,在我之前看過的所有介紹ipc相關的書基本都沒有比較詳細的介紹,也許是我看的書不夠多。但是,上面的這些問題基本都是在程式設計使用中特別容易遇到的問題,這是我自己的親身體驗。所以,當搜到這篇文章時,忍不住多看了兩眼,然後就發現真的是很珍貴的東西。每乙個問題都是經驗知識的積累。再次發次原文連線《linux共享記憶體使用常見陷阱與分析》以示感謝。

記憶體池常見問題

首先,threadcache沒有記憶體,向centralcache申請的時候,是申請乙個批量的記憶體,比如你要1個,那我就一次申請20個,當然20個這個具體數字也不是固定的,你想申請50個,80個也是沒問題的,只是擔心申請多了,用不完 假設要1個,那我就一次申請20個,申請20個需要進行一次加鎖,但...

Linux常見問題

在連線linux上的mysql時出現 communications link failure due to underlying exception last packet sent to the server was 1 ms ago 這個是由於資料庫連線超時造成的。需要修改mysql配置 開啟v...

Linux常見問題

cenos6.7無法上網 解決方法 該解決方法是設定為nat模式 1.將網路連線模式設定成nat模式,不能設定成僅主機模式 2.檢視ip網段與閘道器網段是否一致,如果不一致 將ip網段設定成與網管網段一致 3.將onboot設定成yes 4.將閘道器設定成與非虛擬機器不同的閘道器位址,如 非虛擬機器...