概念
共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。
共享内存优缺点
由于进程可以直接读写内存,所以效率高。像匿名管道和命名管道通信方式,需要在用户空间和内核空间之间进行数据拷贝,而共享内存通信方式只在内存中操作,要高效得多。
另外一方面,由于多个进程对同一段内存区域都具有读写权限,在共享内存通信中,进程间的同步问题就显得尤为重要。必须保证同一时刻只有一个进程对共享内存进行写操作,否则会导致数据被覆盖。而共享内存通信又没有提供同步机制,需要使用诸如信号量等手段进行同步控制,这又增加了其复杂性。
写端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main()
{
// 创建共享内存
// int shmget(key_t key, size_t size,int shmflg);
// key:进程间通信键值
// size:该共享存储段的长度(字节)
// shmflg:标识函数的行为及共享内存的权限,其取值如下:
// IPC_CREAT:如果不存在就创建
// IPC_EXCL: 如果已经存在则返回失败
// 位或权限位:共享内存位或权限位后可以设置共享内存的访问权限,
// 格式和 open() 函数的 mode_t 一样,但可执行权限未使用。
int shmid = shmget((key_t)123, BUFSIZ, 0666|IPC_CREAT);
printf("shmid: %u\n", shmid);
if(shmid < 0)
{
perror("shmget error!");
exit(EXIT_FAILURE);
}
// 内存映射
// void *shmat(int shmid, const void *shmaddr, int shmflg);
// shmid:共享内存标识符,shmget() 的返回值。
// shmaddr:共享内存映射地址(若为 NULL 则由系统自动指定),推荐使用 NULL。
// shmflg:共享内存段的访问权限和映射条件( 通常为 0 ),具体取值如下:
// 0:共享内存具有可读可写权限。
// SHM_RDONLY:只读。
// SHM_RND:(shmaddr 非空时才有效)
void *shm_addr = shmat(shmid, NULL, 0);
if(shm_addr == (void*)-1)
{
perror("shmat error!");
exit(EXIT_FAILURE);
}
char buf[BUFSIZ];
bzero(buf, BUFSIZ);
sprintf(buf, "PID: %u\n", (unsigned int)getpid());
// 拷贝数据到共享内存
memcpy(shm_addr, buf, strlen(buf));
printf("send: %s\n", buf);
usleep(1000);
// 解除共享内存映射
// int shmdt(const void *shmaddr);
// shmaddr:共享内存映射地址
if(shmdt(shm_addr) == -1)
{
printf("shmdt error!\n");
exit(1);
}
return 0;
}
读端
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/shm.h>
int main()
{
// 创建共享内存
int shmid = shmget((key_t)123, BUFSIZ, IPC_CREAT);
printf("shmid: %u\n", shmid);
if(shmid == -1)
{
perror("shmget error!");
exit(EXIT_FAILURE);
}
// 内存映射
void *shmaddr = shmat(shmid, NULL, 0);
if(shmaddr == (void*)-1)
{
perror("shmatt error!");
exit(EXIT_FAILURE);
}
char buf[BUFSIZ];
bzero(buf, BUFSIZ);
// 读取共享内存数据
memcpy(buf, shmaddr, BUFSIZ);
printf("read: %s\n", buf);
usleep(1000);
// 解除共享内存映射
if(shmdt(shmaddr) == -1)
{
printf("shmdt error!\n");
exit(EXIT_FAILURE);
}
// 删除共享内存
// int shmctl(int shmid, int cmd, struct shmid_ds *buf);
// shmid:共享内存标识符
// cmd:函数功能的控制,其取值如下:
// IPC_RMID:删除(常用 )
// IPC_SET:设置 shmid_ds 参数,相当于把共享内存原来的属性值替换为 buf 里的属性值
// IPC_STAT:保存 shmid_ds 参数,把共享内存原来的属性值备份到 buf 里
// SHM_LOCK:锁定共享内存段( 超级用户 )
// SHM_UNLOCK:解锁共享内存段
// SHM_LOCK 用于锁定内存,禁止内存交换。并不代表共享内存被锁定后禁止其它进程访问
// 其真正的意义是:被锁定的内存不允许被交换到虚拟内存中
// 这样做的优势在于让共享内存一直处于内存中,从而提高程序性能
// buf:shmid_ds 数据类型的地址,用来存放或修改共享内存的属性
if(shmctl(shmid, IPC_RMID, 0) == -1)
{
printf("shmctl error!\n");
exit(EXIT_FAILURE);
}
return 0;
}
参考
https://blog.csdn.net/tennysonsky/article/details/46425485
https://blog.csdn.net/xiejingfa/article/details/50888870