69-Linux_共享内存
共享内存
一.什么是共享内存
共享内存是先在物理内存上申请一块空间,多个进程可以将其映射到自己的虚拟地址空间中。 所有进程都可以访问共享内存中的地址,就好像它们是由 malloc 分配的一样。如果某个进程向共享内存写入了数据,所做的改动将立刻被可以访问同一段共享内存的任何其他进程看到。由于它并未提供同步机制,所以我们通常需要用其他的机制来同步对共享内存的访问。
共享内存为多个进程之间共享和传递数据提供了一种有效的方式。
二.共享内存的接口
1.shmget
int shmget(key_t key, size_t size, int shmflg);
shmget()用于创建或者获取共享内存
shmget()成功返回共享内存的 ID, 失败返回-1
key: 不同的进程使用相同的 key值可以获取到同一个共享内存,(注意:这里的值和信号量的值一样也没有关系,因为类型不一样;
size:
创建共享内存时,指定要申请的共享内存空间大小
shmflg: IPC_CREAT IPC_EXCL
2.shmat
void* shmat(int shmid, const void *shmaddr, int shmflg);
shmat()将申请的共享内存的物理内存映射到当前进程的虚拟地址空间上
shmat()成功返回返回共享内存的首地址,失败返回 NULL(像malloc一样给共享空间的起始地址),看帮助手册失败返回的是(void *)-1;
shmaddr:一般给 NULL,由系统自动选择映射的虚拟地址空间 shmflg: 一般给 0(给0就代表的可读写),可以给 SHM_RDONLY为只读模式,其他的为读写
3.shmat
int shmdt(const void *shmaddr);
shmdt()断开当前进程的 shmaddr 指向的共享内存映射
shmdt()成功返回 0, 失败返回-1
4.shmctl
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmctl()控制共享内存
shmctl()成功返回 0,失败返回-1
cmd: IPC_RMID
三.例题
1.进程 a 向共享内存中写入数据,进程 b 从共享内存中读取数据并显示
//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);if(s==(char*)-1){exit(1);}strcpy(s,"Hello");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>int main()
{int shmid=shmget((key_t)1234,256,IPC_CREAT|0600);assert(shmid!=-1);char* s=(char*)shmat(shmid,NULL,0);if(s==(char*)-1){exit(1);}printf("%s\\n",s);shmdt(s);shmctl(shmid,IPC_RMID,NULL);exit(0);
}
2.进程 a 从键盘循环获取数据并拷贝到共享内存中,进程 b 从共享内存中获取并打印数据。要求进程 a 输入一次,进程 b 输出一次,进程 a 不输入,进程 b 也不输出。
思路:
//sem.h
#include<stdio.h>
#include<unistd.h>
#include<sys/sem.h>union semun
{int val;
};void sem_init();
void sem_p(int index);
void sem_v(int index);
void sem_destroy();
//sem.c
#include"sem.h"#define SEM_NUM 2
static int semid=-1;void sem_init()//创建信号量,并初始化
{semid=semget((key_t)1234,SEM_NUM,IPC_CREAT|IPC_EXCL|0600);if(semid==-1){semid=semget((key_t)1234,SEM_NUM,0600);if(semid==-1){perror("semget error\\n");}}else{int arr[SEM_NUM]={1,0};for(int i=0;i<SEM_NUM;i++){//初始化union semun a;a.val=arr[i];if(semctl(semid,i,SETVAL,a)==-1)//全新创建成功,就初始化{perror("semctl init error");} } }
}void sem_p(int index)//p操作
{if(index<0||index>=SEM_NUM){return;}struct sembuf buf;buf.sem_num=index;buf.sem_op=-1;//p操作buf.sem_flg=SEM_UNDO;if(semop(semid,&buf,1)==-1){perror("p errro\\n");}
}void sem_v(int index)
{if(index<0||index>=SEM_NUM){return ;}struct sembuf buf;buf.sem_num=index;buf.sem_op=1;//v操作buf.sem_flg=SEM_UNDO;if(semop(semid,&buf,1)==-1){perror("v error\\n");}
}void sem_destroy()
{if(semctl(semid,0,IPC_RMID)==-1){perror(" destroy sem error\\n");}
}
//a.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/shm.h>
#include<assert.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);if(s==(char*)-1){exit(1);}sem_init();while(1){printf("Input:");char buff[128]={0};fgets(buff,128,stdin);sem_p(0);strcpy(s,buff);sem_v(1);if(strncmp(buff,"end",3)==0){break;}}shmdt(s);exit(0);
}
//b.c
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/shm.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);if(s==(char*)-1){exit(1);}sem_init();while(1){sem_p(1);if(strncmp(s,"end",3)==0){break;}printf("read:%s",s);sleep(1);sem_v(0);}shmdt(s);sem_destroy();shmctl(shmid,IPC_RMID,NULL);exit(0);
}
运行结果: