1概述
共享存储允许两个或更多进程共享一给定的存储区。因为数据不需要在客户进程和服务器进程之间复制,所以这是最快的一种IPC。使用共享存储时要掌握的唯一窍门是多个进程之间对一给定存储区的同步访问。通常,信号量用来实现对共享存储访问的同步。记录锁也可用于这种场合。
内核为每个共享存储段设置了一个shmid_ds结构 struct shmid_ds { struct ipc_perm shm_perm; size_t shm_segsz;/*共享存储区字节大小*/ pid_t shm_lpid; pid_t shm_cpid; shmatt_t shm_nattch; shmat_t shm_cnattch; time_t shm_atime; time_t shm_dtime; time_t shm_ctime; }; |
2函数
头文件 | #include <sys/shm.h> |
函数 | int shmget(key_t key, size_t size, int flag); |
参数 | key:键值 size:共享存储段的长度,单位字节。通常将其向上取为系统页长的整数倍。 如果正在创建一个新段(一般是在服务器进程中),则必须指定其size。如果正在引用一个现存的段(一个客户进程),则将size指定为0.当创建一新段时,段内的内容初始化为0. flag:标志位,可以为IPC_CREAT、IPC_EXCL、IPC_NOWAIT或三者的或 当只有IPC_CREAT时,若不存在则创建信共享存储段并返回其ID;若存在,则返回其ID 当只有IPC_EXCL时,不管有无共享存储段,都返回-1 当IPC_CREAT | IPC_EXCL时,如果没有共享存储段,则创建并返回其ID;若存在则返回-1 |
返回值 | 若成功则返回共享存储ID,若出错则返回-1 |
功能 | 创建一个新的共享内存区,或访问一个已经存在的内存共享区 |
头文件 | #include <sys/shm.h> |
函数 | int shmctl(int shmid, int cmd, struct shmid_ds *buf); |
参数 | shmid:共享存储段标识符 cmd:有5种命令,IPC_STAT、IPC_SET、IPC_RMID、SHM_LOCK、SHM_UNLOCK,使其在shmid指定的段上执行。 buf:shmid_ds结构地址 |
返回值 | 若成功则返回0,若出错则返回-1 |
功能 | 对共享存储段执行多种操作 |
头文件 | #include <sys/shm.h> |
函数 | void *shmat(int shmid, const void *addr, int flag); |
参数 | shmid:共享存储段标识符 addr:共享存储段连接到调用进程的地址,通常为0,由内核选择地址。 flag:标志位,可为SHM_RDN、SHM_RDONLY、SHM_REMAP、SHM_EXEC,也可为0 |
返回值 | 若成功则返回指向共享存储的指针,若出错则返回-1 |
功能 | 连接共享存储段到进程的地址空间 |
头文件 | #include <sys/shm.h> |
函数 | int shmdt(void *addr); |
参数 | addr:共享存储的指针 |
返回值 | 若成功则返回0,若出错则返回-1 |
功能 | 将共享存储与进程地址空间脱离连接 |
用shmat函数连接一共享存储段,在概念上与用mmap函数可将一个文件的若干部分映射至进程地址空间类似。两者之间的主要区别是,用mmap映射的存储段是与文件相关联的,而System V共享存储段则并无这种关联。
3应用
//shm_server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/stat.h>
#define FILEPATH "/tmp/shm"
#define PAGE_SIZE getpagesize()
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
int semphore_create(int key);
int semphore_delete(int semid);
int semphore_p(int);
int semphore_v(int);
int main(void)
{
int fd;
int shm_id;
int sem_id;
key_t key;
void *addr;
fd = open(FILEPATH, O_RDONLY | O_CREAT, S_IRUSR | S_IRGRP | S_IROTH);
if (fd == -1)
{
printf("open error\n");
exit(1);
}
close(fd);
key = ftok(FILEPATH, 0);
if (key == -1)
{
printf("ftok error\n");
exit(1);
}
/*创建共享存储段,并返回共享存储标识符*/
shm_id = shmget(key, PAGE_SIZE, IPC_CREAT | IPC_EXCL);
if (shm_id == -1)
{
printf("shmget error: %s\n", strerror(errno));
exit(1);
}
/*将共享存储段连接到进程地址空间*/
addr = (char *)shmat(shm_id, 0, 0);
if(-1 == (long)addr)
{
printf("shmat error: %s\n", strerror(errno));
shmctl(shm_id, IPC_RMID, NULL);
exit(1);
}
/*创建信号量并返回信号量标识符*/
sem_id = semphore_create(key);
if (sem_id == -1)
{
printf("semphore init error\n");
goto out;
}
/*信号量保证共享存储段的同步start...*/
if(semphore_p(sem_id) == -1)
{
printf("semphore_p error\n");
goto out;
}
/*对共享存储段的操作start...*/
strncpy(addr,"this is share memory..", 25);
/*对共享存储段的操作end...*/
/*信号量保证共享存储段的同步end...*/
if(semphore_v(sem_id) == -1)
{
printf("semphore_v error");
}
out:
/*解除共享存储段与进程地址空间的联系,但不删除该内存区*/
if(shmdt(addr) == -1)
{
printf("shmdt error\n");
}
exit(0);
}
int semphore_create(int key)
{
int ret;
int sem_id;
union semun semun_val;
/*创建信号量集*/
sem_id = semget(key, 1, IPC_CREAT);
if (sem_id == -1)
{
printf("semget error\n");
return -1;
}
/*设置信号量值*/
semun_val.val = 1;
ret = semctl(sem_id,0,SETVAL, semun_val);
if (ret ==-1)
{
printf("semctl error\n");
return -1;
}
return sem_id;
}
int semphore_delete(int semid)
{
if(semctl(semid, 0, IPC_RMID, NULL) == -1)
{
printf("semctl rmid error\n");
return -1;
}
return 0;
}
int semphore_p(int semid)
{
struct sembuf sem_buf;
sem_buf.sem_num = 0;
sem_buf.sem_op = -1;
sem_buf.sem_flg = SEM_UNDO;
if(semop(semid, &sem_buf, 1) == -1)
return -1;
return 0;
}
int semphore_v(int semid)
{
struct sembuf sem_buf;
sem_buf.sem_num = 0;
sem_buf.sem_op = 1;
sem_buf.sem_flg = SEM_UNDO;
if(semop(semid, &sem_buf, 1) == -1)
return -1;
return 0;
}
//shm_client.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define FILEPATH "/tmp/shm"
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
int semphore_p(int);
int semphore_v(int);
int main(void)
{
int fd;
int shm_id;
int sem_id;
key_t key;
void *addr;
fd = open(FILEPATH, O_RDONLY | O_CREAT, S_IRUSR | S_IRGRP | S_IROTH);
if (fd == -1)
{
printf("open error\n");
exit(1);
}
close(fd);
key = ftok(FILEPATH, 0);
if (key == -1)
{
printf("ftok error\n");
exit(1);
}
/*引用现存共享内存段*/
shm_id = shmget(key, 0, 0);
if(shm_id == -1)
{
printf("shmget error, %s\n", strerror(errno));
exit(1);
}
addr = shmat(shm_id, 0, 0);
if(-1 == (long)addr)
{
printf("shmat error, %s\n", strerror(errno));
exit(1);
}
sem_id = semget(key, 1, 0);
if(sem_id == -1)
{
printf("semget error, %s\n",strerror(errno));
exit(1);
}
if(semphore_p(sem_id) == -1)
{
printf("semphore_p error\n");
exit(1);
}
/*显式共享内存区的内容*/
printf("share memory text: %s\n",addr);
if(semphore_v(sem_id) == -1)
{
printf("semphore_p error\n");
exit(1);
}
if(shmdt(addr) == -1)
{
printf("semphore_p error\n");
exit(1);
}
/*从共享内存区获取内容后,显式删除该共享内存区*/
if(shmctl(shm_id, IPC_RMID, NULL) == -1)
{
printf("shmctl error\n");
exit(1);
}
exit(0);
}
int semphore_p(int semid)
{
struct sembuf sem_buf;
sem_buf.sem_num = 0;
sem_buf.sem_op = -1;
sem_buf.sem_flg = SEM_UNDO;
if(semop(semid, &sem_buf, 1) == -1)
return -1;
return 0;
}
int semphore_v(int semid)
{
struct sembuf sem_buf;
sem_buf.sem_num = 0;
sem_buf.sem_op = 1;
sem_buf.sem_flg = SEM_UNDO;
if(semop(semid, &sem_buf, 1) == -1)
return -1;
return 0;
}
//输出
[root]# ./server
[root]# ./client
share memory text: this is share memory..
[root]#