posix提供了两种在无亲缘关系进程间共享内存区的方法:
1.内存映射文件:由open函数打开,通过mmap函数将得到的描述符映射到当前进程地址空间中的一个文件。
2.共享内存区对象:由shm_open打开一个posix IPC名字,所返回的描述符由mmap函数映射到当前进程的地址空间。
这两种技术都需要调用mmap,差别在于作为mmap的参数之一的描述符的获取手段:通过open或者通过shm_open:
shm_open和shm_unlink函数
posix共享内存区涉及以下两个步骤要求:
1.指定一个名字参数调用shm_open,以创建一个新的共享内存区对象或者打开一个已存在的共享内存区对象。(传递给shm_open的名字参数随后由希望共享该内存区的任何其他进程使用)
2.调用mmap把这个共享内存区映射到调用进程的地址空间。
#include <sys_open> int shm_open(const char *name,int oflag,mode_t mode);//oflag参数包含O_CREAT,O_EXCL,O_TRUNC. mode参数指定权限位。返回值是一个整数描述符,作为mmap参数。 int shm_unlink(const char *name);//删除一个共享内存区对象的名字,删除一个名字不会影响对于其底层支撑对象的现有引用,直到对于该对象的应用全部关闭为止。删除一个名字仅仅防止后续open,mq_open或者sem_open调用取得成功。
shmcreate函数
以某个指定的名字和长度创建一个共享内存区对象:
int main(int argc,char **argv) { int c,fd,flags; char *ptr; off_t length; flags = O_RDWR | O_CREAT; while((c = getopt(argc,argv,"e")) != -1) { switch(c) { flags |= O_EXCL; break; } } if(optind != argc -2) err_quit("usage:shmcreate [-e] <name> <length>"); length = atoi(argv[optind +1]); fd = shm_open(argv[optind],flags,FILE_MODE);//打开一个共享内存区对象 ftruncate(fd,length); //设置长度 ptr = mmap(NULL,length,PROT_READ | PROT_WRITE , MAP_SHARED,fd,0);//将fd映射到调用进程的地址空间 return;//posix共享内存区至少具有随内核的持续性,则本程序终止不会删除该共享内存区。 }
shmunlink程序
int main(int argc,char** argv) { if(argc != 2) err_quit("usage:shmunlink <name>"); shm_unlink(argv[1]); return; }
shmwrite程序
int main(int argc,char** argv) { int i,fd; struct stat stat; unsigned char *ptr; if(argc != 2) err_quit("usage:shmwrite <name>"); fd = shm_open(argv[1],O_RDWR, FILE_MODE); fstat(fd,&stat);//获取信息stat.st_size ptr = mmap(NULL,stat.st_size,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);//从文件fd的0开始取stat.st_size个字节数映射到ptr close(fd); for(i=0;i<stat.st_size;i++) *ptr++ = i%256;//往共享内存区写入数据 return; }
shmread程序
int main(int argc,char** argv) { int i,fd; struct stat stat; unsigned char *ptr; if(argc != 2) err_quit("usage:shmread <name>"); fd = shm_open(argv[1],O_RDONLY, FILE_MODE);//只读模式打开 fstat(fd,&stat);//获取信息stat.st_size ptr = mmap(NULL,stat.st_size,PROT_READ,MAP_SHARED,fd,0);//从文件fd的0开始取stat.st_size个字节数映射到ptr close(fd); for(i=0;i<stat.st_size;i++) { if((c = *ptr++) != (i % 256))//对共享内存区ptr进行读取 err_ret("ptr[%d] = %d",i,c); } return; }
多个客户端向一个服务器发送消息
服务器启动后创建一个共享内存区对象,各个客户进程就在其中放置消息。目的是展示客户端产生的消息没有存放空间可用,但是客户端又不想为此阻塞的情况如何处理。
#define MESGSIZE 256 #define NMESG 16 struct shmstruct { sem_t mutex; sem_t nempty; sem_t nstored; int nput; //存放消息的下一个位置 long noverflow; sem_t noverflowmutex; long msgoff[NMESG];//含有针对msgdata数组的各个偏移 char msgdata[NMESG * MESGSIZE]; }; 服务器程序 int main(int argv,char **argv) { int fd,index,lastnoverflow,temp; long offset; struct shmstruct* ptr; if(argv != 2) err_quit("usage: server2 <name>\n"); shm_unlink(px_ipc_name(argv[1]));//创建前先删除,防止之前存在 fd = shm_open(px_ipc_name(argv[1]),O_RDWR | O_CREAT | OEXCL,FILE_MODE); ptr = mmap(NULL,sizeof(struct shmstruct),PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);//px_ipc_name(argv[1])打开后映射到ptr ftruncate(fd,sizeof(struct shmstruct)); close(fd); for(index = 0;index < NMESG;index++) ptr->msgoff[index] = index * MESGSIZE;//初始化为含有每个消息的位置偏移 sem_init(&ptr->mutex,1,1);第二个参数1表示进程间共享信号量,第三个参数1表示信号量初始值 sem_init(&ptr->nempty,1,NMESG); sem_init(&ptr->nstored,1,0); sem_init(&ptr->noverflowmutex,1,1); index = 0; lastnoverflow = 0; for(;;) { sem_wait(&ptr->nstored);//消费者等待生产成功 sem_wait(&ptr->mutex);//----------------------临界区--- offset = ptr->msgoff[index]; printf("index = %d:%s \n",index,&ptr->msgdata[offset]); if(++index >= NMESG) index = 0; sem_post(&ptr->mutex);//----------------------临界区--- sem_post(&ptr->nempty);//消费者出来后生成一个空位 sem_wait(&ptr->noverflowmutex);//----------------------临界区--- temp = ptr->noverflow; sem_post(&ptr->noverflowmutex);//----------------------临界区--- if(temp != lastnoverflow)//测试是否不同于上一次 { printf("noverflow = %d\n",temp); lastnoverflow = temp; } } return; } 客户端程序 int main(int argc,char **argv) { int fd,i,nloop,nusec; pid_t pid; char mesg[MESGSIZE]; long offset; struct shmstruct *ptr; if(argc != 4) printf("usage: client2 <name> <#loops> <#usec>\n"); nloop = atoi(argv[2]);//服务器存放的消息数 nusec = atoi(argv[3]);//存放消息间隔 fd = shm_open(px_ipc_name(argv[1],O_RDWR,FILE_MODE)); ptr = mmap(NULL,sizeof(struct shmstruct),PROT_READ | PROT_WRITE,MAP_SHARED,fd,0); close(fd); pid = getpid(); for(i = 0;i < nloop; i++) { sleep_us(nusec);//启动多个本程序,每个指定一个停顿,强行造成溢出,验证服务器能处理其。 snprintf(mesg,MESGSIZE."pid %ld:message %d\n",(long)pid,i); if(sem_trywait(&ptr->nempty) == -1)//非阻塞等待,-1表示生产者获取锁失败 { if(erron == EAGAIN) { sem_wait(&ptr->noverflowmutex);//----------------------临界区--- ptr->noverflow++; //槽位满,无法写入,非阻塞,计数增加 sem_post(&ptr->noverflowmutex);//----------------------临界区--- continue;//继续尝试获取锁 } else { printf("sem_trywait error"); return; } } //这边表示生产者已经获取锁 sem_wait(&ptr->mutex);//----------------------临界区--- offset = ptr->msgoff[ptr->nput]; if(++(ptr->nput) >= NMESG) ptr->nput = 0; sem_post(&ptr->mutex);//----------------------临界区--- strcpy(&ptr->msgdata[offset], mesg); sem_post(&ptr->nstored); } return; }