进程间通信之共享内存
共享内存
一. 什么是共享内存
共享内存就是允许两个或多个进程共享一片存储区,是操作系统在实际物理内存开辟一块空间,当一个进程往该空间写入内容时,另外一进程会访问该空间,得到写入的值,即实现了进程间的通信。
共享内存做到数据不需要在客户机和服务器端之间来回复制,数据直接写到内存中,不需要多次数据拷贝,是进程间最有效的方式
二. 共享内存有关函数
1.获取key
功能 : 获取一个独一无二的key,作为传给共享内存的一个参数
#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
key_t key = ftok("./read", 'a');
pathname:提前创建的可访问文件的文件名,可以随意写
proj_id:任意一个字符返回值:成功则返回生成的key值,失败则返回-1
2.打开创建共享内存对象 - shmget
功能:创建/打开一个共享内存对象
#include <sys/ipc.h>
#include <sys/shm.h>//在内核上创建共享内存
int shemid = shmget(key, 1024, IPC_CREAT | 0666);
int shmget(key_t key, size_t size, int shmflg);key:表示要打开或者创建一个对象的“密钥”可以写0或者IPC_PRIVATE:表示共享内存对象是私有的
size:要创建的共享内存大小
shmflg:打开或者创建时的权限IPC_CREAT:不存在则创建IPC_EXCL:存在(如果加上了IPC_CREAT)就报错0666
返回值:成功返回ID号,失败返回-1
3.映射空间地址 - shmat
功能:把内核中的共享内存空间映射到用户空间
#include <sys/types.h>
#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);
char *shmaddr = shmat(shmid, NULL, 0);
void *shmat(int shmid, const void *shmaddr, int shmflg);shmid:共享内存的id号
shmaddr:可以指定要映射的空间地址NULL:表示系统决定0x...:表示指定要把内核空间映射到这个地址
shmflg:表示共享内存的操作权限(读写)SHM_RDONLY:表示只读(不用)0:可读可写
返回值:成功返回映射后的用户空间地址,失败返回(void *)-1
4.取消映射 - shmdt
功能:把shmat映射后的空间取消掉,释放进程地址空间
#include <sys/types.h>
#include <sys/shm.h>int shmdt(const void *shmaddr);
shmaddr:shmat返回的地址(映射地址)
返回值:成功返回0,失败返回-1
5.删除共享内存对象 - shmctl
功能:整体控制共享内存对象
#include <sys/ipc.h>
#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);shmctl(shmid, IPC_RMID, NULL);
shmid:共享内存对象ID号
cmd:要执行的操作IPC_STAT (获取对象属性)IPC_SET (设置对象属性)IPC_RMID (删除对象)
buf:用于设置或者获取对象的属性,如果是删除对象,写NULL
返回值:成功返回0,失败返回-1注:删除对象并不是直接删除,而是标记这个对象为删除状态
三. 实例
write.c依次往共享内存中输入字符a-z, read.c读取共享内存中的数据,并打印出来
write.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>int main()
{key_t key = ftok("./read", 'a');if(key < 0){perror("ftok");return -1;}// 创建共享内存int shmid = shmget(key, 1024*4, IPC_CREAT | 0666);if(shmid < 0){perror("shmget");return -1;}// 映射空间地址char *shmaddr = shmat(shmid, NULL, 0);if(shmaddr == (void *)-1){perror("shmat");return -1;}char c='a';for( ; c <= 'z'; c++){printf("%c\\n",c);shmaddr[c-'a']=c;sleep(1);}shmdt(shmaddr);shmctl(shmid, IPC_RMID, NULL);
}
read.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>int main()
{key_t key = ftok("./read", 'a');if(key < 0){perror("ftok");return -1;}//在内核上创建共享内存int shmid = shmget(key, 1024*4,IPC_CREAT|0666);if(shmid < 0){perror("shmget");return -1;}//映射空间地址char *shmaddr = shmat(shmid, NULL, 0);if(shmaddr == (void *)-1){perror("shmat");return -1;}int i = 20;while(i){ printf("result : %s\\n", shmaddr);sleep(1);i--;}shmdt(shmaddr);shmctl(shmid, IPC_RMID, NULL);}
注意 : 当运行程序时,先运行read.c,程序启动就直接读取共享内存中的数据,此时并没有往共享内存中写入数据,
但是read.c并没有阻塞
四. 注意事项
1.查看当前系统的共享内存
ipcs -m
2.当两个进程间ftok参数不一样时,shmid也不一样,共享内存不是同一个空间
write.c
read.c
3.释放共享内存
当我结束两个进程时,如果代码中没有shmctl,那么共享内存还会一直存在,会造成内存泄漏,所以当我们使用shmget创建共享内存后,如果不在使用该共享内存,应当即时释放