c共享内存附加/分离/解除分配

时间:2012-01-04 20:52:15

标签: c client posix shared-memory

我对使用POSIX系统调用的c中的共享内存分段有疑问。我是从客户端和服务器分离和删除段,还是只需要从服务器中删除它?

考虑我有2个程序

一个用于服务器,一个用于客户端

the steps for the server

1)create memory segment
2)attach
3)detach
4)remove

steps for the client

1)create
2)attach
3)detach
4)remove

这是我的代码:

//server

#include<stdlib.h>
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/shm.h>

#define SHMSZ 100
int main()
{

key_t key;
char c;
int shmid;
char *shm;

key=1025;

//locate
if((shmid=shmget(key,SHMSZ,0666 | IPC_CREAT))<0)
{
perror("shmget");
exit(-1);
}



//attach
if((shm=shmat(shmid,NULL,0))==(char*)-1)
{
perror("shmat");
exit(-1);
}


sprintf(shm,"Hi there");

//shm="Hi There";

while(*shm!='*');
sleep(1);

//detach
shmctl(shmid,IPC_RMID,NULL);
return 0;
}

这是客户端

//client

#include<stdlib.h>
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/shm.h>

#define SHMSZ 100

int main()
{


key_t key;
int shmid;
char c;
char *shm, *s;

key=1025;
//locate
if((shmid=shmget(key,SHMSZ,0666 | IPC_CREAT))<0)
{
perror("shmget");
exit(-1);
}

//attach
if((shm=shmat(shmid,NULL,0))==(char*)-1)
{
perror("shmat");
exit(-1);
}

printf("%s\n",shm);

*shm='*';

shmdt(&shmid);
shmctl(shmid, IPC_RMID,NULL);

return 0;
}

1 个答案:

答案 0 :(得分:6)

由于您使用的是System V IPC而不是POSIX IPC,因此请检查与共享内存段ID相关联的数据结构中shm_nattch的值。您可以通过使用shmctl标记调用IPC_STAT来获取此值。调用shmdt会将此值减一,调用此函数的最后一个过程会将shm_nattach的值设置为0。一旦值清零,您就可以安全地调用shmctl来删除内存段。

因此,在客户端和服务器代码中,如果服务器不能保证服务器的使用寿命,则应在调用shm_nattch后单独调用shmctl来检查shmdt的值。查看访问共享内存段的进程数是否已减少到零。您还应确保对此IPC_STAT调用的结果进行错误检查,以避免因两个单独的进程调用shmdt而将shm_nattch的值减小为零的竞争条件,但是实际上最后调用shmdt的进程被OS挂起,而另一个进程看到shm_nattch的值为零并删除了内存段。由于检查和删除共享内存段都需要调用shm_ctl,如果共享内存段的ID无效,则该调用将失败,理论上如果你仅在单个进程删除共享内存段后调用shm_ctlshmdt。您想要避免的事情是在删除之后访问指向共享内存段的指针。检查shm_ctl的失败呼叫将帮助您避免这些类型的情况。换句话说,如果调用失败,那么你就不能再安全地访问指针了。

如果另一方面,您的服务器保证比任何客户端寿命长,那么服务器可以安全地进行调用以删除共享内存段,因为它将是使用它的最后一个进程...所有其他客户端不需要删除内存段,只需从中删除即可。