共享內存應用中的問題及解決方法

System V 進程間通信(IPC)包括3種機制:消息隊列、信號量、共享內存。消息隊列和信號量均是內核空間的系統對象,經由它們的數據需要在內核和用戶空間進行額外的數據拷貝;而共享內存和訪問它的所有應用程序均同處於用戶空間,應用進程可以通過地址映射的方式直接讀寫內存,從而獲得非常高的通信效率。

System V 爲共享內存定義了下列API接口函數:

# include <sys/types.h>
# include <sys/ipc.h>
# include <sys/shm.h>

key_t	 ftok(const char *pathname, int proj_id);
int	 shmget(key_t key, int size, int shmflg);
void*	 shmat(int shmid, const void *shmaddr, int shmflg);
int	 shmdt(void *shmaddr);
int	 shmctl(int shmid, int cmd, struct shmid_ds *buf);
ftok 函數用於生成一個鍵值:key_t key,該鍵值將作爲共享內存對象的唯一性標識符,並提供給爲shmget函數作爲其輸入參數;ftok 函數的輸入參數包括一個文件(或目錄)路徑名:pathname,以及一個額外的數字:proj_id,其中pathname所指定的文件(或目錄)要求必須已經存在,且proj_id不可爲0;
shmget 函數用於創建(或者獲取)一個由key鍵值指定的共享內存對象,返回該對象的系統標識符:shmid;
shmat 函數用於建立調用進程與由標識符shmid指定的共享內存對象之間的連接;
shmdt 函數用於斷開調用進程與共享內存對象之間的連接;
shmctl 函數用於對已創建的共享內存對象進行查詢、設值、刪除等操作;

2. ftok的陷阱

根據pathname指定的文件(或目錄)名稱,以及proj_id參數指定的數字,ftok函數爲IPC對象生成一個唯一性的鍵值。在實際應用中,很容易產生的一個理解是,在proj_id相同的情況下,只要文件(或目錄)名稱不變,就可以確保ftok返回始終一致的鍵值。然而,這個理解並非完全正確,有可能給應用開發埋下很隱晦的陷阱。因爲ftok的實現存在這樣的風險,即在訪問同一共享內存的多個進程先後調用ftok函數的時間段中,如果pathname指定的文件(或目錄)被刪除且重新創建,則文件系統會賦予這個同名文件(或目錄)新的i節點信息,於是這些進程所調用的ftok雖然都能正常返回,但得到的鍵值卻並不能保證相同。由此可能造成的後果是,原本這些進程意圖訪問一個相同的共享內存對象,然而由於它們各自得到的鍵值不同,實際上進程指向的共享內存不再一致;如果這些共享內存都得到創建,則在整個應用運行的過程中表面上不會報出任何錯誤,然而通過一個共享內存對象進行數據傳輸的目的將無法實現。

AIX、Solaris、HP-UX均明確指出,key文件被刪除並重建後,不保證通過ftok得到的鍵值不變,比如AIX上ftok的man幫助信息即聲明:

Attention: If the Path parameter of the ftok subroutine names a file that has been removed while keys still refer to it, the ftok subroutine returns an error. If that file is then re-created, the ftok subroutine will probably return a key different from the original one.

Linux沒有提供類似的明確聲明,但我們可以通過下面的簡單例程test01.c,得到相同的印證:


#include <stdio.h>
#include <sys/ipc.h>
void main(int argc, char* argv[])
{
if (argc !=2 ) {
		printf("Usage: %s KeyFile/n e.g. %s /tmp/mykeyfile/n", argv[0], argv[0]);
		return;
	}
	printf("Key generated by ftok:  0x%x/n", ftok(argv[1], 1));
}

將上述例程在Red Hat Enterprise Linux AS release 4平臺上編程成可執行程序test01,並且通過touch命令在 /tmp目錄下創建一個新文件mykeyfile,然後爲該文件生成鍵值:


# touch  /tmp/mykeyfile
# ./test01 /tmp/mykeyfile
Key generated by ftok:  0x101000b

然後,將/tmp/mykeyfile刪除,並且通過vi命令重新創建該文件,再次生成鍵值:


# ./test01 /tmp/mykeyfile
Key generated by ftok:  0x1010017

我們可以看到,雖然文件名稱都是 /tmp/mykeyfile,並未改變,但由於中間發生了文件刪除並重新創建的操作,前後兩次所得到的鍵值已經不再相同。

避免此類問題最根本的方法,就是採取措施保證pathname所指定的文件(或目錄)在共享內存的使用期間不被刪除,不要使用有可能被刪除的文件;或者乾脆直接指定鍵值,而不借助ftok來獲取鍵值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章