目录
1.原理
共享内存为多个进程之间共享和传递数据提供了一种有效的方式。共享内存是先在物理内存
上申请一块空间,多个进程可以将其映射到自己的虚拟地址空间中。所有进程都可以访问共享内存
中的地址,就好像它们是由 malloc 分配的一样。如果某个进程向共享内存写入了数据,所做的改
动将立刻被可以访问同一段共享内存的任何其他进程看到。
由于它并未提供同步机制,所以我们通常需要用其他的机制来同步对共享内存的访问。
2.共享内存函数介绍
2.1 shmget
int shmget(key_t key,size_t size,int shmflg);
shmget()用于创建或者获取共享内存,成功返回共享内存的ID,失败返回-1
key:不同的进程使用相同的key值可以获取到一个共享内存,(这里的值和信号量的值一样也没有关系,因为类型不一样)
size:创建共享内存时,指定要申请的共享内存空间大小。
shmflg:IPC_CREAT IPC_EXCL
2.2 shmat
void* shmat(int shmid,const void* shmaddr,int shmflg);
shmat():将申请的共享内存的物理内存映射到当前进程的虚拟地址空间上。成功返回共享内存的首地址,失败返回NULL(像malloc一样给共享空间的起始地址),看帮助手册失败返回的是-1;
shmaddr:一般给NULL,由系统自动选择映射的虚拟地址空间。
shmflg:一般给0(给0就代表的可读可写),可以给SHM_RDONLY为只读模式,其它的为读写。
2.3 shmdt
int shmdt(const void* shmaddr);
shmdt():断开当前进程的shmaddr指向的共享内存映射。成功返回0,失败返回-1。
为什么不是删除共享内存而是断开共享内存呢?因为你不使用了,别的进程可能还在使用这块共享内存。
2.4 shmctl
删除共享内存用shmctl,就是对共享内存做控制,那么可以设置,也可以删除。
int shmctl(int shmid,int cmd,struct shmid_ds* buf);
shmctl():控制共享内存,成功返回0,失败返回-1;
cmd:IPC_RMID(表示删除共享内存段)
buf:是一个结构指针,它指向共享内存模式和访问权限的结构。
删除的时候如果还有人在使用共享内存,这个函数就会延迟删除,等到最后一个进程断开连接它才会删除共享内存
3.共享内存使用
例1
进程a向共享内存写入数据(写一次hello),进程b从共享内存读取数据(读取hello)并显示。
main.c写入数据
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/shm.h>
#include <string.h>
int main()
{
int shmid=shmget((key_t)1234,256,IPC_CREAT|0600);
assert(shmid!=-1);
char *s=(char*)shmat(shmid,NULL,0);
//assert(s!=NULL);
if(s==(char*)-1)//帮助手册上说明返回值是-1
{
exit(1);
}
strcpy(s,"hello");
shmdt(s);
}
test.c读取数据
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/shm.h>
#include <string.h>
int main()
{
int shmid=shmget((key_t)1234,256,IPC_CREAT|0600);
assert(shmid!=-1);
char *s=(char*)shmat(shmid,NULL,0);
//assert(s!=NULL);
if(s==(char*)-1)//帮助手册上说明返回值是-1
{
exit(1);
}
printf("%s",s);
shmdt(s);
shmctl(shmid,IPC_RMID,NULL);
exit(0);
}
运行结果:
如果用完之后没有销毁,用ipcs命令进行查询
可以使用重启,函数shmctl(shmid,IPC_RMID,NULL)等方法进行销毁。
例2
进程a从键盘循环获取数据并拷贝到共享内存中,进程b从共享内存中获取并打印数据。要求进程a输入一次,进程b输出一次,进程a不输入,进程b也不输出。
思路:
利用信号量实现“进程a输入一次,进程b输出一次,进程a不输入,进程b也不输出”,使用信号量时注意:
- 信号量个数
- 信号量的初始值
- p,v操作
代码实现:
include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sem.h>
#define SEM1 0
#define SEM2 1
#define SEM_MAX 2
union semun
{
int val;
};
void sem_init();
void sem_p(int index);
void sem_v(int index);
void sem_destory();
//sem.c
#include "sem.h"
static int semid=-1;
void sem_init()
{
semid=semget((key_t)1234,SEM_MAX,IPC_CREAT|IPC_EXCL|0600);
if(semid==-1)
{
semid=semget((key_t)1234,SEM_MAX,0600);
if(semid==-1)
{
printf("sem failed!\n");
return;
}
}
else
{
union semun a;
int arr[SEM_MAX]={1,0};
for(int i=0;i<SEM_MAX;i++)
{
a.val=arr[i];
if(semctl(semid,i,SETVAL,a)==-1)
{
printf("semctl setval failed\n");
}
}
}
}
void sem_p(int index)
{
if(index<0||index>=SEM_MAX)
{
return;
}
struct sembuf a;
a.sem_num=index;
a.sem_op=-1;
a.sem_flg=SEM_UNDO;
if(semop(semid,&a,1)==-1)
{
printf("semop p error!\n");
}
}
void sem_v(int index)
{
if(index<0||index>=SEM_MAX)
{
return;
}
struct sembuf a;
a.sem_num=index;
a.sem_op=1;
a.sem_flg=SEM_UNDO;
if(semop(semid,&a,1)==-1)
{
printf("semop v error!\n");
}
}
void sem_destory()
{
if(semctl(semid,0,IPC_RMID)==-1)
{
printf("destory error!\n");
}
}
//main.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/shm.h>
#include <string.h>
#include "sem.h"
int main()
{
int shmid=shmget((key_t)1234,256,IPC_CREAT|0600);
assert(shmid!=-1);
char *s=(char*)shmat(shmid,NULL,0);
//assert(s!=NULL);
if(s==(char*)-1)//帮助手册上说明返回值是-1
{
exit(1);
}
// strcpy(s,"hello");
sem_init();
while(1)
{
printf("input:\n");
char buff[128]={0};
fgets(buff,128,stdin);
sem_p(SEM1);
strcpy(s,buff);
sem_v(SEM2);//必须放到30行前,否则获取end之后退出循环,未执行v操作,test进程无法读入end,无法退出
if(strncmp(buff,"end",3)==0)
{
break;
}
}
shmdt(s);
exit(0);
}
//test.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/shm.h>
#include <string.h>
#include "sem.h"
int main()
{
int shmid=shmget((key_t)1234,256,IPC_CREAT|0600);
assert(shmid!=-1);
char *s=(char*)shmat(shmid,NULL,0);
//assert(s!=NULL);
if(s==(char*)-1)//帮助手册上说明返回值是-1
{
exit(1);
}
//printf("%s",s);
sem_init();
while(1)
{
sem_p(SEM2);
if(strncmp(s,"end",3)==0)
{
break;
}
printf("read:%s\n",s);
sem_v(SEM1);
//sleep(1);
}
shmdt(s);
sem_destory();
shmctl(shmid,IPC_RMID,NULL);
exit(0);
}
运行结果: