UNIX 共享内存应用中的问题及解决方法( 五 )


如果仍有别的进程与该共享内存保持连接,则调用IPC_RMID子命令后,该共享内存并不会被立即从系统中删除,而是被设置为IPC_PRIVATE状态,并被标记为"已被删除";直到已有连接全部断开,该共享内存才会最终从系统中消失 。
于是,存在这样的一种状态:
N个进程(进程1至进程N)已经与某共享内存区连接;
进程1已完成对此共享内存的操作,断开连接后,调用shmctl的IPC_RMID子命令,企图删除该共享内存;
由于进程2至进程N仍保持与该共享内存的连接,因此在它们全部断开连接之前,这个共享内存区毫无疑问地会依然存在 。
此时,如果有其它的进程(比如第N 1号进程)想建立对这个共享内存的连接,是否能够成功呢?
类似的状态,在Windows上同样存在,只是程序借助的API有所不同,比如通过CreateFileMapping函数创建共享内存,通过MapViewOfFile函数建立连接,通过UnmapViewOfFile函数断开连接,通过CloseHandle函数删除共享内存等 。在Windows上,对此问题的回答是肯定的;也就是说,只要共享内存依然存在,则进程总是可以建立对它的连接,而无论之前是否有进程对其执行过删除操作 。
然而,对于包括AIX、Solaris、HP-UX等在内的UNIX平台,答案却是否定的!这也正是本节所讨论的使用shmctl中的风险所在;通过以下test03.P1.c和test03.P2.c两个例程,我们可以很直观地得到答案:
test03.P1.c: 创建共享内存,并建立连接,保持10秒后(在此期间,test03.P2将反复连接、并删除该共享内存),断开连接,并最后再次尝试连接以验证该共享内存是否已被真正删除;
test03.P2.c: 反复连接由test03.P1创建的共享内存,并在期间通过shmctl的IPC_RMID 子命令删除该共享内存,以观察共享内存被执行删除操作之后,在被彻底销毁之前是否还能接受连接;
/******* test03.P1.c ********/
#include
#include
#include
#include
#include
#include
int main(int argc, char* argv[])
{
key_tmem_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);" if
on Solaris
shmdt((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
#include
#include
#include
#include
int main(int argc, char* argv[])
{
key_t mem_key;
longmem_id;
void*mem_addr;
inti, isAttached;
mem_key = ftok("/tmp/mykeyfile", 1);
mem_id = shmget(mem_key, 0, 0);
// repeated attaching & detaching
for (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 [d],
errno:%dn",
 argv[0], i, errno);
else {
isAttached = 1;
 printf("%s,.Successfully attached shared memory, times
[d]n",argv[0], i);
}

推荐阅读