您的位置:首页 > 其它

关于Posix共享内存

2015-08-03 17:12 183 查看
Posix提供了两种在无亲缘关系的进程间共享内存区的方法:

1. 内存映射文件


由open函数打开, 由mmap函数把得到的描述符映射到当前进程的地址空间的一个文件

2. 共享内存区对象

由shm_open打开一个Posix IPC名字, mmap将得到的描述符映射到当前进程的地址空间

两者的差距在于获取描述符的手段不同

操作函数:

int shm_open(const char *name, int oflag, mode_t mode);

shm_open与sem_open, mq_open不同的地方在于, mode总是要指定的. 如果没有指定O_CREAT, 那么mode可以是0

int shm_unlink(const char *name);

shm_unlink与其他unlink函数一样, 直到全部引用关闭才删除. 其中一个作用是防止后续的open调用成功

以下给出使用的实例:



//头文件
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>        /* For mode constants */
#include <fcntl.h>           /* For O_* constants */

#define     MESGSIZE    256        //every message's max size
#define     NMESG        16        //total max size

struct shm_ds{
sem_t     ds_mutex;
sem_t    ds_nempty;
sem_t    ds_nstored;
int     ds_nput;
long     ds_noverflow;            //如果有客户没有空间再存放进共享内存, 那么这个变量加一. 该客户不再等待
sem_t     ds_overflowmutex;        //这变量也是共享的, 需要加锁
long     ds_msgoff[NMESG];        //每个消息的起始位置
char    ds_msgbuf[NMESG * MESGSIZE];    //所有消息的占有空间
};


//服务端代码
#include "scpm.h"

int main(int ac, char *av[])
{
int fd, index;
int over_flows, offset, temp;
struct shm_ds *ptr;

if(ac != 2){
fprintf(stderr, "Usage server <pathname>");
exit(-1);
}

shm_unlink(av[1]);            //保证我们创建的共享内存是新的
fd = shm_open(av[1], O_RDWR | O_CREAT | O_EXCL, (S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH));
if(fd < 0){
perror("shm_open error:");
exit(-1);
}
ftruncate(fd, sizeof(struct shm_ds));    //调整对象的大小

ptr = mmap(NULL, sizeof(struct shm_ds), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(ptr == MAP_FAILED){
perror("mmap error");
exit(-1);
}
close(fd);

for(index=0; index<NMESG; index++)        //初始化每个消息的起始位置, 为了方便以后使用
ptr->ds_msgoff[index] = index * MESGSIZE;

sem_init(&ptr->ds_mutex, 1, 1);        //每个共享变量都是shared的
sem_init(&ptr->ds_nempty, 1, NMESG);
sem_init(&ptr->ds_nstored, 1, 0);
sem_init(&ptr->ds_overflowmutex, 1, 1);

index = 0;
over_flows = 0;
for(;;)                        //下面即为多个生产者一个消费者的模型
{
sem_wait(&ptr->ds_nstored);
sem_wait(&ptr->ds_mutex);
offset = ptr->ds_msgoff[index];
printf("Index : %d . Content is [%s]\n", index, &ptr->ds_msgbuf[offset]);
if(++index >= NMESG)
index = 0;
sem_post(&ptr->ds_mutex);
sem_post(&ptr->ds_nempty);

sem_wait(&ptr->ds_overflowmutex);
temp = ptr->ds_noverflow;
sem_post(&ptr->ds_overflowmutex);
if(temp != over_flows)            //因为没有位置而被放弃的客户个数,发生变化则输出告知
{
printf("now %d are abandoned\n", temp);
over_flows = temp;
}

}
return 0;
}


//客户端代码
#include "scpm.h"

int main(int ac, char *av[])
{
int loops, nusec;
int fd, index, offset;
pid_t pid;
struct shm_ds *ptr;
char buf[MESGSIZE];

if(ac != 4){
fprintf(stderr, "Usage : cspm <pathname> loops usec\n");
exit(-1);
}

loops = atoi(av[2]);
nusec = atoi(av[3]);

fd = shm_open(av[1], O_RDWR, 0);
ptr = mmap(NULL, sizeof(struct shm_ds), PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
close(fd);

pid = getpid();
//这里我们让一个客户端多次输出, 模拟多个客户端的情况
for(index = 0; index < loops; index++)
{
usleep(nusec);
snprintf(buf, MESGSIZE, "pid : %ld : message : %d", pid, index);    //消息内容

//这里我们使用trywait的原因是某些客户端比如HTTP不会愿意等待, 如果没有消息存放位置则选择直接返回
if(sem_trywait(&ptr->ds_nempty) == -1){
if(errno == EAGAIN){
sem_wait(&ptr->ds_overflowmutex);
ptr->ds_noverflow ++;
sem_post(&ptr->ds_overflowmutex);
continue;
}
else{
perror("sem_trywait error");
exit(-1);
}
}
sem_wait(&ptr->ds_mutex);
offset = ptr->ds_msgoff[ptr->ds_nput];
if(++(ptr->ds_nput) >= NMESG)
ptr->ds_nput = 0;
sem_post(&ptr->ds_mutex);            //这里我们让互斥锁之间的critical code尽量的少, 以免互斥锁会影响性能
strcpy(&ptr->ds_msgbuf[offset], buf);
sem_post(&ptr->ds_nstored);
}

return 0;
}


接下来, 就看一下程序的运行结果:

$ gcc -o scpm server_clients_posix_mmap.c -lrt    //服务端
$ gcc -o cspm clients_server_posix_mmap.c -lrt    //客户端
$ ./scpm /haha &
$ ./cspm /haha 5 0
$ ./scpm /haha
Index : 0 . Content is [pid : 5911 : message : 0]        //下面即为后台服务器的输出
Index : 1 . Content is [pid : 5911 : message : 1]
Index : 2 . Content is [pid : 5911 : message : 2]
Index : 3 . Content is [pid : 5911 : message : 3]
Index : 4 . Content is [pid : 5911 : message : 4]
^C
接下来不会再介绍System V的共享内存, 因为两者相差无几, 只是调用的函数换了换
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: