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

簡介
共享內存是一種非常重要且常用的進程間通信方式,相對于其它IPC機制,因其速度最快、效率最高,被廣泛應用于各類軟件產品及應用開發中 。System V IPC 為Unix平臺上的共享內存應用制定了統一的API標準,從而為在UNIX/Linux平臺上進行跨平臺開發提供了極大的便利;開發人員基于一套基本相同的源代碼,便可開發出同時支持AIX、Solaris、HP-UX、Linux等平臺的產品 。
然而,各個平臺對System V 標準的API在實現上各有差異,由此對相關應用開發帶來影響,甚至引入難以調試的問題 。本文將結合作者在Tivoli產品開發中的實際經驗,對這些平臺相關的問題,以及具有共性的問題,逐一進行分析,并提出解決方法 。
1. System V共享內存概述
System V 進程間通信(IPC)包括3種機制:消息隊列、信號量、共享內存 。消息隊列和信號量均是內核空間的系統對象,經由它們的數據需要在內核和用戶空間進行額外的數據拷貝;而共享內存和訪問它的所有應用程序均同處于用戶空間,應用進程可以通過地址映射的方式直接讀寫內存,從而獲得非常高的通信效率 。
System V 為共享內存定義了下列API接口函數:
# include
# include
# include
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.

推薦閱讀