亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

UNIX 共享內(nèi)存應(yīng)用中的問(wèn)題及解決方法

瀏覽:11日期:2024-06-14 08:04:26

簡(jiǎn)介

共享內(nèi)存是一種非常重要且常用的進(jìn)程間通信方式,相對(duì)于其它IPC機(jī)制,因其速度最快、效率最高,被廣泛應(yīng)用于各類(lèi)軟件產(chǎn)品及應(yīng)用開(kāi)發(fā)中。System V IPC 為Unix平臺(tái)上的共享內(nèi)存應(yīng)用制定了統(tǒng)一的API標(biāo)準(zhǔn),從而為在UNIX/Linux平臺(tái)上進(jìn)行跨平臺(tái)開(kāi)發(fā)提供了極大的便利;開(kāi)發(fā)人員基于一套基本相同的源代碼,便可開(kāi)發(fā)出同時(shí)支持AIX、Solaris、HP-UX、Linux等平臺(tái)的產(chǎn)品。

然而,各個(gè)平臺(tái)對(duì)System V 標(biāo)準(zhǔn)的API在實(shí)現(xiàn)上各有差異,由此對(duì)相關(guān)應(yīng)用開(kāi)發(fā)帶來(lái)影響,甚至引入難以調(diào)試的問(wèn)題。本文將結(jié)合作者在Tivoli產(chǎn)品開(kāi)發(fā)中的實(shí)際經(jīng)驗(yàn),對(duì)這些平臺(tái)相關(guān)的問(wèn)題,以及具有共性的問(wèn)題,逐一進(jìn)行分析,并提出解決方法。

1. System V共享內(nèi)存概述

System V 進(jìn)程間通信(IPC)包括3種機(jī)制:消息隊(duì)列、信號(hào)量、共享內(nèi)存。消息隊(duì)列和信號(hào)量均是內(nèi)核空間的系統(tǒng)對(duì)象,經(jīng)由它們的數(shù)據(jù)需要在內(nèi)核和用戶(hù)空間進(jìn)行額外的數(shù)據(jù)拷貝;而共享內(nèi)存和訪問(wèn)它的所有應(yīng)用程序均同處于用戶(hù)空間,應(yīng)用進(jìn)程可以通過(guò)地址映射的方式直接讀寫(xiě)內(nèi)存,從而獲得非常高的通信效率。

System V 為共享內(nèi)存定義了下列API接口函數(shù):

# 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函數(shù)用于生成一個(gè)鍵值:key_t key,該鍵值將作為共享內(nèi)存對(duì)象的唯一性標(biāo)識(shí)符,并提供給為shmget函數(shù)作為其輸入?yún)?shù);ftok 函數(shù)的輸入?yún)?shù)包括一個(gè)文件(或目錄)路徑名:pathname,以及一個(gè)額外的數(shù)字:proj_id,其中pathname所指定的文件(或目錄)要求必須已經(jīng)存在,且proj_id不可為0;shmget函數(shù)用于創(chuàng)建(或者獲取)一個(gè)由key鍵值指定的共享內(nèi)存對(duì)象,返回該對(duì)象的系統(tǒng)標(biāo)識(shí)符:shmid;shmat函數(shù)用于建立調(diào)用進(jìn)程與由標(biāo)識(shí)符shmid指定的共享內(nèi)存對(duì)象之間的連接;shmdt函數(shù)用于斷開(kāi)調(diào)用進(jìn)程與共享內(nèi)存對(duì)象之間的連接;shmctl函數(shù)用于對(duì)已創(chuàng)建的共享內(nèi)存對(duì)象進(jìn)行查詢(xún)、設(shè)值、刪除等操作;

2. ftok的陷阱

根據(jù)pathname指定的文件(或目錄)名稱(chēng),以及proj_id參數(shù)指定的數(shù)字,ftok函數(shù)為IPC對(duì)象生成一個(gè)唯一性的鍵值。在實(shí)際應(yīng)用中,很容易產(chǎn)生的一個(gè)理解是,在proj_id相同的情況下,只要文件(或目錄)名稱(chēng)不變,就可以確保ftok返回始終一致的鍵值。然而,這個(gè)理解并非完全正確,有可能給應(yīng)用開(kāi)發(fā)埋下很隱晦的陷阱。因?yàn)閒tok的實(shí)現(xiàn)存在這樣的風(fēng)險(xiǎn),即在訪問(wèn)同一共享內(nèi)存的多個(gè)進(jìn)程先后調(diào)用ftok函數(shù)的時(shí)間段中,如果pathname指定的文件(或目錄)被刪除且重新創(chuàng)建,則文件系統(tǒng)會(huì)賦予這個(gè)同名文件(或目錄)新的i節(jié)點(diǎn)信息,于是這些進(jìn)程所調(diào)用的ftok雖然都能正常返回,但得到的鍵值卻并不能保證相同。由此可能造成的后果是,原本這些進(jìn)程意圖訪問(wèn)一個(gè)相同的共享內(nèi)存對(duì)象,然而由于它們各自得到的鍵值不同,實(shí)際上進(jìn)程指向的共享內(nèi)存不再一致;如果這些共享內(nèi)存都得到創(chuàng)建,則在整個(gè)應(yīng)用運(yùn)行的過(guò)程中表面上不會(huì)報(bào)出任何錯(cuò)誤,然而通過(guò)一個(gè)共享內(nèi)存對(duì)象進(jìn)行數(shù)據(jù)傳輸?shù)哪康膶o(wú)法實(shí)現(xiàn)。

AIX、Solaris、HP-UX均明確指出,key文件被刪除并重建后,不保證通過(guò)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沒(méi)有提供類(lèi)似的明確聲明,但我們可以通過(guò)下面的簡(jiǎn)單例程test01.c,得到相同的印證:

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

將上述例程在Red Hat Enterprise Linux AS release 4平臺(tái)上編程成可執(zhí)行程序test01,并且通過(guò)touch命令在 /tmp目錄下創(chuàng)建一個(gè)新文件mykeyfile,然后為該文件生成鍵值:

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

然后,將/tmp/mykeyfile刪除,并且通過(guò)vi命令重新創(chuàng)建該文件,再次生成鍵值:

# ./test01 /tmp/mykeyfileKey generated by ftok: 0x1010017

我們可以看到,雖然文件名稱(chēng)都是 /tmp/mykeyfile,并未改變,但由于中間發(fā)生了文件刪除并重新創(chuàng)建的操作,前后兩次所得到的鍵值已經(jīng)不再相同。

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

3. AIX中shmat的問(wèn)題

AIX系統(tǒng)中,System V各類(lèi)進(jìn)程間通信機(jī)制在使用中均存在限制。區(qū)別于其它UNIX操作系統(tǒng)對(duì)IPC機(jī)制的資源配置方式,AIX使用了不同的方法;在AIX中定義了 IPC 機(jī)制的上限, 且是不可配置的。就共享內(nèi)存機(jī)制而言,在4.2.1及以上版本的AIX系統(tǒng)上,存在下列限制:

對(duì)于64位進(jìn)程,同一進(jìn)程可連接最多268435456個(gè)共享內(nèi)存段;

對(duì)于32位進(jìn)程,同一進(jìn)程可連接最多11個(gè)共享內(nèi)存段,除非使用擴(kuò)展的shmat;

上述限制對(duì)于64位應(yīng)用不會(huì)帶來(lái)麻煩,因?yàn)榭晒┻B接的數(shù)量已經(jīng)足夠大了;但對(duì)于32位應(yīng)用,卻很容易帶來(lái)意外的問(wèn)題,因?yàn)樽畲蟮倪B接數(shù)量只有11個(gè)。在某些事件觸發(fā)的多線程應(yīng)用中,新的線程不斷地為進(jìn)行事件處理而被創(chuàng)建,這些線程如果都需要去連接特定的共享內(nèi)存,則極有可能造成該進(jìn)程連接的共享內(nèi)存數(shù)量超過(guò)11個(gè),事實(shí)上同時(shí)擁有幾十個(gè)甚至上百個(gè)處理線程的應(yīng)用并不少見(jiàn)。一旦超個(gè)這個(gè)限制值,則所有后續(xù)的處理線程都將無(wú)法正常工作,從而導(dǎo)致應(yīng)用運(yùn)行失敗。

下面的例程test02.c演示了這個(gè)問(wèn)題,為了精簡(jiǎn)代碼,它反復(fù)連接的是同一個(gè)共享內(nèi)存對(duì)象;實(shí)際上,無(wú)論所連接的共享內(nèi)存對(duì)象是否相同,該限制制約的是連接次數(shù):

#include <stdio.h>#include <errno.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h>#define MAX_ATTACH_NUM 15void main(int argc, char* argv[]){key_t  mem_key;longmem_id;void*  mem_addr[MAX_ATTACH_NUM];int i;if ( ( mem_key = ftok("/tmp/mykeyfile", 1) ) == (key_t)(-1) ) {printf("Failed to generate shared memory access key, ERRNO=%dn",errno);goto MOD_EXIT;}if ( ( mem_id = shmget(mem_key, 256, IPC_CREAT) ) == (-1) ) {printf("Failed to obtain shared memory ID, ERRNO=%dn", errno);goto MOD_EXIT;}for ( i=1; i<=MAX_ATTACH_NUM; i++ ) {if ( ( mem_addr[i] = (void *)shmat(mem_id, 0, 0) ) == (void *)(-1) )printf("Failed to attach shared memory, times [%02d], errno:%dn", i,errno);elseprintf("Successfully attached shared memory, times [%02d]n", i);}MOD_EXIT:shmctl(mem_id, IPC_RMID, NULL);}

在AIX系統(tǒng)上,我們將其編譯為test02,并運(yùn)行,可以看到如下輸出:

Successfully attached shared memory, times [01]Successfully attached shared memory, times [02]Successfully attached shared memory, times [03]Successfully attached shared memory, times [04]Successfully attached shared memory, times [05]Successfully attached shared memory, times [06]Successfully attached shared memory, times [07]Successfully attached shared memory, times [08]Successfully attached shared memory, times [09]Successfully attached shared memory, times [10]Successfully attached shared memory, times [11]Failed to attach shared memory, times [12], errno:24Failed to attach shared memory, times [13], errno:24Failed to attach shared memory, times [14], errno:24Failed to attach shared memory, times [15], errno:24

說(shuō)明超出11個(gè)連接之后,所有后續(xù)的共享內(nèi)存連接都將無(wú)法建立。錯(cuò)誤碼24的定義是EMFILE,AIX給予的解釋是:

The number of shared memory segments attached to the calling process exceeds the system-imposed limit。

解決這個(gè)問(wèn)題的方法是,使用擴(kuò)展的shmat;具體而言就是,在運(yùn)行相關(guān)應(yīng)用之前(確切地說(shuō),是在共享內(nèi)存被創(chuàng)建之前),首先在shell中設(shè)置EXTSHM環(huán)境變量,通過(guò)它擴(kuò)展shmat,對(duì)于源代碼本身無(wú)需作任何修改:

export EXTSHM=ON

值得注意的是,雖然設(shè)置環(huán)境變量,在程序中也可通過(guò)setenv函數(shù)來(lái)做到,比如在程序的開(kāi)始,加入下列代碼:

setenv("EXTSHM", "ON", 1);

但實(shí)踐證明這樣的方法在解決這個(gè)問(wèn)題上是無(wú)效的;也就是說(shuō)唯一可行的辦法,就是在shell中設(shè)置EXTSHM環(huán)境變量,而非在程序中。

在AIX上配置32位DB2實(shí)例時(shí),也要求確保將環(huán)境變量 EXTSHM 設(shè)為 ON,這是運(yùn)行 Warehouse Manager 和 Query Patroller 之前必需的操作:

export EXTSHM=ONdb2set DB2ENVLIST=EXTSHMdb2start

其原因即來(lái)自我們剛剛介紹的AIX中32位應(yīng)用連接共享內(nèi)存時(shí),存在最大連接數(shù)限制。這個(gè)問(wèn)題同樣普遍存在于AIX平臺(tái)上Oracle等軟件產(chǎn)品中。

4. HP-UX中shmget和shmat的問(wèn)題

4.1 32位和64位應(yīng)用兼容問(wèn)題

在HP-UX平臺(tái)上,如果同時(shí)運(yùn)行32位應(yīng)用和64位應(yīng)用,而且它們?cè)L問(wèn)的是一個(gè)相同的共享內(nèi)存區(qū),則會(huì)遇到兼容性問(wèn)題。

在HP-UX中,應(yīng)用程序設(shè)置IPC_CREAT標(biāo)志調(diào)用shmget,所創(chuàng)建的共享內(nèi)存區(qū),只可被同類(lèi)型的應(yīng)用所訪問(wèn);即32位應(yīng)用程序所創(chuàng)建的共享內(nèi)存區(qū)只可被其它的32位應(yīng)用程序訪問(wèn),同樣地,64位應(yīng)用程序所創(chuàng)建的共享內(nèi)存區(qū)只可被其它的64位應(yīng)用程序訪問(wèn)。

如果,32位應(yīng)用企圖訪問(wèn)一個(gè)由64位應(yīng)用創(chuàng)建的共享內(nèi)存區(qū),則會(huì)在調(diào)用shmget時(shí)失敗,得到EINVAL錯(cuò)誤碼,其解釋是:

A shared memory identifIEr exists for key but is in 64-bit address space and the process performing the request has been compiled as a 32-bit executable.

解決這一問(wèn)題的方法是,當(dāng)64位應(yīng)用創(chuàng)建共享內(nèi)存時(shí),合并IPC_CREAT標(biāo)志,同時(shí)給定IPC_SHARE32標(biāo)志:

shmget(mem_key, size, 0666 | IPC_CREAT | IPC_SHARE32)

對(duì)于32位應(yīng)用,沒(méi)有設(shè)定IPC_SHARE32標(biāo)志的要求,但設(shè)置該標(biāo)志并不會(huì)帶來(lái)任何問(wèn)題,也就是說(shuō)無(wú)論應(yīng)用程序?qū)⒈痪幾g為32位還是64位模式,都可采用如上相同的代碼;并且由此解決32位應(yīng)用和64位應(yīng)用在共享內(nèi)存訪問(wèn)上的兼容性問(wèn)題。

4.2 對(duì)同一共享內(nèi)存的連接數(shù)限制

在HP-UX上,應(yīng)用進(jìn)程對(duì)同一個(gè)共享內(nèi)存區(qū)的連接次數(shù)被限制為最多1次;區(qū)別于上面第3節(jié)所介紹的AIX上的連接數(shù)限制,HP-UX并未對(duì)指向不同共享內(nèi)存區(qū)的連接數(shù)設(shè)置上限,也就是說(shuō),運(yùn)行在HP-UX上的應(yīng)用進(jìn)程可以同時(shí)連接很多個(gè)不同的共享內(nèi)存區(qū),但對(duì)于同一個(gè)共享內(nèi)存區(qū),最多只允許連接1次;否則,shmat調(diào)用將失敗,返回錯(cuò)誤碼EINVAL,在shmat的man幫助中,對(duì)該錯(cuò)誤碼有下列解釋?zhuān)?/p>

shmid is not a valid shared memory identifier, (possibly because the shared memory segment was already removed using shmctl(2) with IPC_RMID), or the calling process is already attached to shmid.

這個(gè)限制會(huì)對(duì)多線程應(yīng)用帶來(lái)無(wú)法避免的問(wèn)題,只要一個(gè)應(yīng)用進(jìn)程中有超過(guò)1個(gè)以上的線程企圖連接同一個(gè)共享內(nèi)存區(qū),則都將以失敗而告終。

解決這個(gè)問(wèn)題,需要修改應(yīng)用程序設(shè)計(jì),使應(yīng)用進(jìn)程具備對(duì)同一共享內(nèi)存的多線程訪問(wèn)能力。相對(duì)于前述問(wèn)題的解決方法,解決這個(gè)問(wèn)題的方法要復(fù)雜一些。

作為可供參考的方法之一,以下介紹的邏輯可以很好地解決這個(gè)問(wèn)題:

基本思路是,對(duì)于每一個(gè)共享內(nèi)存區(qū),應(yīng)用進(jìn)程首次連接上之后,將其鍵值(ftok的返回值)、系統(tǒng)標(biāo)識(shí)符(shmid,shmget調(diào)用的返回值)和訪問(wèn)地址(即shmat調(diào)用的返回值)保存下來(lái),以這個(gè)進(jìn)程的全局?jǐn)?shù)組或者鏈表的形式留下記錄。在任何對(duì)共享內(nèi)存的連接操作之前,程序都將先行檢索這個(gè)記錄列表,根據(jù)鍵值和標(biāo)志符去匹配希望訪問(wèn)的共享內(nèi)存,如果找到匹配記錄,則從記錄中直接讀取訪問(wèn)地址,而無(wú)需再次調(diào)用shmat函數(shù),從而解決這一問(wèn)題;如果沒(méi)有找到匹配目標(biāo),則調(diào)用shmat建立連接,并且為新連接上來(lái)的共享內(nèi)存添加一個(gè)新記錄。

記錄條目的數(shù)據(jù)結(jié)構(gòu),可定義為如下形式:

typedef struct _Shared_Memory_Record{key_tmem_key;// key generated by ftok()intmem_id;// id returned by shmget()void*mem_addr;// access address returned by shmat()intnattach;// times of attachment} Shared_Memory_Record;

其中,nattach成員的作用是,記錄當(dāng)前對(duì)該共享內(nèi)存區(qū)的連接數(shù)目;每一次打開(kāi)共享內(nèi)存的操作都將對(duì)其進(jìn)行遞增,而每一次關(guān)閉共享內(nèi)存的操作將其遞減,直到nattach的數(shù)值降到0,則對(duì)該共享內(nèi)存區(qū)調(diào)用shmdt進(jìn)行真正的斷開(kāi)連接。

打開(kāi)共享內(nèi)存的邏輯流程可參考如下圖一:

圖一

關(guān)閉共享內(nèi)存的邏輯流程可參考如下圖二:

圖二

5. Solaris中的shmdt函數(shù)原型問(wèn)題

Solaris系統(tǒng)中的shmdt調(diào)用,在原型上與System V標(biāo)準(zhǔn)有所不同,

Default int shmdt(char *shmaddr);

即形參shmaddr的數(shù)據(jù)類(lèi)型在Solaris上是char *,而System V定義的是void * 類(lèi)型;實(shí)際上Solaris上shmdt調(diào)用遵循的函數(shù)原型規(guī)范是SVID-v4之前的標(biāo)準(zhǔn);以Linux系統(tǒng)為例,libc4和libc5 采用的是char * 類(lèi)型的形參,而遵循SVID-v4及后續(xù)標(biāo)準(zhǔn)的glibc2及其更新版本,均改為采用void * 類(lèi)型的形參。

如果仍在代碼中采用System V的標(biāo)準(zhǔn)原型,就會(huì)在Solaris上編譯代碼時(shí)造成編譯錯(cuò)誤;比如:

Error: Formal argument 1 of type char* in call to shmdt(char*)is being passed void*.

解決方法是,引入一個(gè)條件編譯宏,在編譯平臺(tái)是Solaris時(shí),采用char * 類(lèi)型的形參,而對(duì)其它平臺(tái),均仍采用System V標(biāo)準(zhǔn)的void * 類(lèi)型形參,比如:

#ifdef _SOLARIS_SHARED_MEMORYshmdt((char *)mem_addr);#else shmdt((void *)mem_addr);#endif

6. 通過(guò)shmctl刪除共享內(nèi)存的風(fēng)險(xiǎn)

當(dāng)進(jìn)程斷開(kāi)與共享內(nèi)存區(qū)的連接后,一般通過(guò)如下代碼刪除該共享內(nèi)存:

shmctl(mem_id, IPC_RMID, NULL);

從HP-UX上shmctl函數(shù)的man幫助,我們可以看到對(duì)IPC_RMID操作的說(shuō)明:

IPC_RMID Remove the shared memory identifier specified by shmid from the system and destroy the shared memory segment and data structure associated with it. If the segment is attached to one or more processes, then the segment key is changed to IPC_PRIVATE and the segment is marked removed. The segment disappears when the last attached process detaches it.

其它UNIX平臺(tái)也有類(lèi)似的說(shuō)明。關(guān)于shmctl的IPC_RMID操作,其使用特點(diǎn)可簡(jiǎn)述如下:

如果共享內(nèi)存已經(jīng)與所有訪問(wèn)它的進(jìn)程斷開(kāi)了連接,則調(diào)用IPC_RMID子命令后,系統(tǒng)將立即刪除共享內(nèi)存的標(biāo)識(shí)符,并刪除該共享內(nèi)存區(qū),以及所有相關(guān)的數(shù)據(jù)結(jié)構(gòu);

如果仍有別的進(jìn)程與該共享內(nèi)存保持連接,則調(diào)用IPC_RMID子命令后,該共享內(nèi)存并不會(huì)被立即從系統(tǒng)中刪除,而是被設(shè)置為IPC_PRIVATE狀態(tài),并被標(biāo)記為"已被刪除";直到已有連接全部斷開(kāi),該共享內(nèi)存才會(huì)最終從系統(tǒng)中消失。

于是,存在這樣的一種狀態(tài):

N個(gè)進(jìn)程(進(jìn)程1至進(jìn)程N(yùn))已經(jīng)與某共享內(nèi)存區(qū)連接;

進(jìn)程1已完成對(duì)此共享內(nèi)存的操作,斷開(kāi)連接后,調(diào)用shmctl的IPC_RMID子命令,企圖刪除該共享內(nèi)存;

由于進(jìn)程2至進(jìn)程N(yùn)仍保持與該共享內(nèi)存的連接,因此在它們?nèi)繑嚅_(kāi)連接之前,這個(gè)共享內(nèi)存區(qū)毫無(wú)疑問(wèn)地會(huì)依然存在。

此時(shí),如果有其它的進(jìn)程(比如第N+1號(hào)進(jìn)程)想建立對(duì)這個(gè)共享內(nèi)存的連接,是否能夠成功呢?

類(lèi)似的狀態(tài),在Windows上同樣存在,只是程序借助的API有所不同,比如通過(guò)CreateFileMapping函數(shù)創(chuàng)建共享內(nèi)存,通過(guò)MapViewOfFile函數(shù)建立連接,通過(guò)UnmapViewOfFile函數(shù)斷開(kāi)連接,通過(guò)CloseHandle函數(shù)刪除共享內(nèi)存等。在Windows上,對(duì)此問(wèn)題的回答是肯定的;也就是說(shuō),只要共享內(nèi)存依然存在,則進(jìn)程總是可以建立對(duì)它的連接,而無(wú)論之前是否有進(jìn)程對(duì)其執(zhí)行過(guò)刪除操作。

然而,對(duì)于包括AIX、Solaris、HP-UX等在內(nèi)的UNIX平臺(tái),答案卻是否定的!這也正是本節(jié)所討論的使用shmctl中的風(fēng)險(xiǎn)所在;通過(guò)以下test03.P1.c和test03.P2.c兩個(gè)例程,我們可以很直觀地得到答案:

test03.P1.c: 創(chuàng)建共享內(nèi)存,并建立連接,保持10秒后(在此期間,test03.P2將反復(fù)連接、并刪除該共享內(nèi)存),斷開(kāi)連接,并最后再次嘗試連接以驗(yàn)證該共享內(nèi)存是否已被真正刪除;

test03.P2.c: 反復(fù)連接由test03.P1創(chuàng)建的共享內(nèi)存,并在期間通過(guò)shmctl的IPC_RMID 子命令刪除該共享內(nèi)存,以觀察共享內(nèi)存被執(zhí)行刪除操作之后,在被徹底銷(xiāo)毀之前是否還能接受連接;

/******* test03.P1.c ********/#include <stdio.h>#include <unistd.h>#include <errno.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h>int main(int argc, char* argv[]){key_t  mem_key;long mem_id;void* mem_addr;intisAttached = 0;mem_key = ftok("/tmp/mykeyfile", 1);mem_id = shmget(mem_key, 256, IPC_CREAT);if ( ( mem_addr = (void *)shmat(mem_id, 0, 0) ) == (void *)(-1) ) printf("%s, Failed to attach shared memory, errno:%dn", argv[0], errno);else {isAttached = 1;printf("%s, +.Successfully attached shared memoryn", argv[0]);  }/* sleep 10 seconds, to wait test03.P2 to run */sleep(10);if (isAttached) {// Attention: the following line should be "shmdt((char *)mem_addr);" ifon Solarisshmdt((void *)mem_addr);printf("%s, -.Successfully detached shared memoryn", argv[0]);}/* try to attach the shared memory which has been removed! */if ( ( mem_addr = (void *)shmat(mem_id, 0, 0) ) == (void *)(-1) )printf("%s, Failed to attach the removed shared memory, errno:%dn",argv[0], errno); return 0;}/******* test03.P2.c ********/#include <stdio.h>#include <errno.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h>int main(int argc, char* argv[]){key_t mem_key;  long  mem_id;void*  mem_addr;inti, isAttached;mem_key = ftok("/tmp/mykeyfile", 1);mem_id = shmget(mem_key, 0, 0);// repeated attaching & detachingfor (i=1; i<10; i++) {isAttached = 0;if ( ( mem_addr = (void *)shmat(mem_id, 0, 0) ) == (void *)(-1) )printf("%s, Failed to attach shared memory, times [%02d],errno:%dn", argv[0], i, errno);  else {  isAttached = 1; printf("%s, +.Successfully attached shared memory, times[%02d]n",argv[0], i);}  if (isAttached) { // Attention: the following line should be "shmdt((char*)mem_addr);", if on Solarisshmdt((void *)mem_addr);  printf("%s, -.Successfully detached, times [%02d]n", argv[0], i);  }// purposely remove the shared memory at times [5]if (i==5) {shmctl(mem_id, IPC_RMID, NULL);printf("%s, *.Remove executed, times [%02d], errno=%dn",argv[0], i, errno);}}return 0;}

上述程序均可在AIX、HP-UX、Linux平臺(tái)上編譯通過(guò);在Solaris平臺(tái)上只需按注釋提示的要求,將shmdt的參數(shù)強(qiáng)制為char *類(lèi)型也可編譯通過(guò)(第5節(jié)中已介紹過(guò))。

將test03.P1.c、test03.P2.c各自編譯為可執(zhí)行程序test03.P1、test03.P2,并通過(guò)下面的shell腳本:runtest,運(yùn)行它們:

#!/bin/sh./test03.P1&sleep 2./test03.P2

在Linux平臺(tái)(Red Hat 8.0)上的運(yùn)行結(jié)果如下:

[root@localhost tmp]# ./runtest./test03.P1, +.Successfully attached shared memory./test03.P2, +.Successfully attached shared memory, times [01]./test03.P2, -.Successfully detached, times [01]./test03.P2, +.Successfully attached shared memory, times [02]./test03.P2, -.Successfully detached, times [02]./test03.P2, +.Successfully attached shared memory, times [03]./test03.P2, -.Successfully detached, times [03]./test03.P2, +.Successfully attached shared memory, times [04]./test03.P2, -.Successfully detached, times [04]./test03.P2, +.Successfully attached shared memory, times [05]./test03.P2, -.Successfully detached, times [05]./test03.P2, *.Remove executed, times [05], errno=0./test03.P2, +.Successfully attached shared memory, times [06]./test03.P2, -.Successfully detached, times [06]./test03.P2, +.Successfully attached shared memory, times [07]./test03.P2, -.Successfully detached, times [07]./test03.P2, +.Successfully attached shared memory, times [08]./test03.P2, -.Successfully detached, times [08]./test03.P2, +.Successfully attached shared memory, times [09]./test03.P2, -.Successfully detached, times [09][root@localhost tmp]# ./test03.P1, -.Successfully detached shared memory./test03.P1, Failed to attach the removed shared memory, errno:22

根據(jù)運(yùn)行結(jié)果,我們可以看到,在Linux平臺(tái)上,即便對(duì)共享內(nèi)存執(zhí)行了刪除操作(在第5次連接之后,test03.P2進(jìn)程調(diào)用了shmctl的IPC_RMID刪除操作),只要該共享內(nèi)存依然存在(test03.P1進(jìn)程保持著連接,因此共享內(nèi)存不會(huì)被立即刪除),則它仍然是可連接的(test03.P2進(jìn)程的第6到第9次連接均是成功的)。

然而,在AIX、HP-UX、Solaris平臺(tái)上的運(yùn)行結(jié)果卻不同于Linux:

# ./runtest./test03.P1, +.Successfully attached shared memory./test03.P2, +.Successfully attached shared memory, times [01]./test03.P2, -.Successfully detached, times [01]./test03.P2, +.Successfully attached shared memory, times [02]./test03.P2, -.Successfully detached, times [02]./test03.P2, +.Successfully attached shared memory, times [03]./test03.P2, -.Successfully detached, times [03]./test03.P2, +.Successfully attached shared memory, times [04]./test03.P2, -.Successfully detached, times [04]./test03.P2, +.Successfully attached shared memory, times [05]./test03.P2, -.Successfully detached, times [05]./test03.P2, *.Remove executed, times [05], errno=0./test03.P2, Failed to attach shared memory, times [06], errno:22./test03.P2, Failed to attach shared memory, times [07], errno:22./test03.P2, Failed to attach shared memory, times [08], errno:22./test03.P2, Failed to attach shared memory, times [09], errno:22# ./test03.P1, -.Successfully detached shared memory./test03.P1, Failed to attach the removed shared memory, errno:22

根據(jù)結(jié)果,可以發(fā)現(xiàn),test03.P2進(jìn)程的第6到第9次連接都是失敗的,也就說(shuō)明,在AIX、HP-UX、Solaris平臺(tái)上一旦通過(guò)shmctl對(duì)共享內(nèi)存進(jìn)行了刪除操作,則該共享內(nèi)存將不能再接受任何新的連接,即使它依然存在于系統(tǒng)中!

而且,上面的運(yùn)行結(jié)果,也證明了,對(duì)共享內(nèi)存進(jìn)行了刪除操作之后,當(dāng)已有的連接全部斷開(kāi),該共享內(nèi)存將被系統(tǒng)自動(dòng)銷(xiāo)毀(運(yùn)行結(jié)果的最后一行,說(shuō)明該共享內(nèi)存已經(jīng)不存在了)。

本節(jié)的目的在于說(shuō)明,在AIX、HP-UX、Solaris平臺(tái)上調(diào)用shmctl的IPC_RMID刪除操作,是存在潛在風(fēng)險(xiǎn)的,需要足夠的謹(jǐn)慎。

如果,可以確知,在刪除之后不可能再有新的連接,則執(zhí)行刪除操作是安全的;

否則,在刪除操作之后如仍有新的連接發(fā)生,則這些連接都將失敗!

7. 結(jié)論

對(duì)共享內(nèi)存的操作,往往是產(chǎn)品或者應(yīng)用中數(shù)據(jù)傳輸?shù)幕A(chǔ),對(duì)其可靠性和性能至關(guān)重要;而且作為底層的IPC機(jī)制,相關(guān)代碼具有不易調(diào)試的特點(diǎn),由其造成的問(wèn)題往往關(guān)鍵卻不容易解決。

本文從應(yīng)用實(shí)現(xiàn)的角度上,對(duì)在UNIX/Linux平臺(tái)上使用共享內(nèi)存可能會(huì)遇到的問(wèn)題,進(jìn)行了全面的介紹和分析,并給出了解決方法或建議,可供相關(guān)的應(yīng)用開(kāi)發(fā)人員參考。

標(biāo)簽: Unix系統(tǒng)
主站蜘蛛池模板: 亚洲精品一区二区不卡 | 亚洲春色在线视频 | 99综合久久| 亚洲狠狠97婷婷综合久久久久 | 欧美毛片 | 夜色网 | 色综合久久精品中文字幕 | 麻豆回家视频区一区二 | 欧美一区二区高清 | 午夜精品久久久久 | 自拍偷拍欧美亚洲 | 亚洲精品一区激情区偷拍 | 91视频聊天网 | 一区二区三区免费视频 www | 12306播播影视播播影院午夜 | 精品一区二区高清在线观看 | 91精彩视频| 久香草视频在线观看免费 | 国产精美视频 | 久久一区二区免费播放 | 福利在线一区 | 亚洲欧洲精品视频在线观看 | 国产 另类 在线 欧美日韩 | 最新大黄网站免费 | 国产91丝袜在线播放九色 | 免费欧美日韩 | 国产最新精品精品视频 | 国产欧美中文字幕 | 免费艹逼视频 | 欧美成人片在线 | 欧美特级毛片a够爽 | 看亚洲a级一级毛片 | 亚洲爱爱图片 | 久久久久免费精品国产小说 | 成人在线观看网址 | 成人午夜影院 | 亚洲伊人精品综合在合线 | 美国一级黄色大片 | 色婷婷综合欧美成人 | 亚洲午夜久久久久影院 | 欧美精品久久久久久久久大尺度 |