共享内存
1、共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。
2、为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一块内存而不需要进行数据的拷贝,从而大大提高效率。
3、由于多个进程共享一段内存,因此也需要依靠某种同步机制。
在linux中我们可以通过下面的指令来查看共享内存
ipcs -m
也可以通过下面的命令删除共享内存
ipcrm -m 【shmid】
一、共享内存相关函数
1、shmget函数
函数原型
int shmget(key_t key,size_t size,int shmflg);
所需头文件
#include <sys/ipc.h>
#include <sys/shm.h>
返回值
成功:返回所创建的共享内存的文件描述符
失败:-1
参数
key标识共享内存的键值(就像文件的标识是文件名):0 / IPC_PRIVATE
size即为所创建共享内存的大小(字节)
shmflg即为对共享内存的权限还包含有效的标志,包括IPC_CREAT 和IPC_EXCL,他们的功能与open()的O_CREAT和O_EXCL相当
IPC_CREAT 如果共享内存不存在,则创建一个共享内存,否则直接打开已存在的
IPC_EXCL 只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误
其中,当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;如果key的取值为0,而参数shmflg中设置了IPC_CREATE这个标志,则同样创建一块新的共享内存。通过这种方式分配的共享内存,一般用来亲缘关系的进程间通信,非亲缘关系之间我们一般是通过ftok这个函数获取键值
(1)当key为ftok所获取的
ftok函数原型
key_t ftok(const char *pathname,int proj_id);
所需头文件
#include <sys/shm.h>
#include <sys/ipc.h>
返回值
成功:返回key
失败:-1
参数
pathname就是你指定的文件名(已经存在的文件名),一般使用当前目录
proj_id是子序号,它是一个8bit的整数。即范围是0~255
当key为ftok所获取时如:
#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>
int main()
{
key_t key = ftok(".",123);
if(key<0)
{
printf("ftok fail\n");
return key;
}
else
{
printf("ftok success\n");
}
int shmid = shmget(key,128,IPC_CREAT|0777);
if(shmid < 0)
{
printf("creat share memory fail\n");
return shmid;
}
else
{
printf("creat share memory success\n");
}
return 0;
}
(2)当key为IPC_PRIVATE时如
#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>
int main()
{
int shmid = shmget(IPC_PRIVATE,128,0777);
if(shmid < 0)
{
printf("creat share memory fail\n");
return shmid;
}
else
{
printf("creat share memory success\n");
}
return 0;
}
2、shmat(共享内存的映射)
函数原型
void *shmat(int shmid,const void *shmadd,int shmflg);
所需头文件
#include <sys/types.h>
#include <sys/shm.h>
返回值
成功:调用成功放回映射后的地址
失败:出错放回(void 指针)-1
参数
shmid:要映射的共享内存区标识符
shmaddr:将共享内存映射到指定地址(若为NULL,则表示由系统自动完成映射)
shmflg:SHM_RDONLY : 共享内存只读默认 ; 0:共享内存可读写。
例如:
#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>
int main()
{
key_t key = ftok(".",123);
if(key<0)
{
printf("ftok fail\n");
return key;
}
else
{
printf("ftok success\n");
}
int shmid = shmget(key,128,IPC_CREAT|0777);
if(shmid < 0)
{
printf("creat share memory fail\n");
return shmid;
}
else
{
printf("creat share memory success\n");
}
char *adr = (char *)shmat(shmid,NULL,0);
if(adr == NULL)
{
printf("shmat fail\n");
return -2;
}
else
{
printf("shmat success , address = %x\n",adr);
}
printf("input:");
fgets(adr,128,stdin);
printf("output:%s",adr);
return 0;
}
3、shmdt(取消共享内存与用户进程之间的映射)
函数原型
int *shmdt(const void *shmaddr);
所需头文件
#include <sys/types.h>
#include <sys/shm.h>
返回值
成功:0
失败:-1
注意:当一个进程不再需要共享内存段时,它将调用shmdt()系统调用取消这个段,但是,这并不是从内核真正地删除这个段,而是把相关shmid_ds结构的shm_nattch域的值减1,当这个值为0时,内核才从物理上删除这个共享段
参数
shmaddr是shmat映射成功放回的地址
例如:
#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>
int main()
{
key_t key = ftok(".",123);
if(key<0)
{
printf("ftok fail\n");
return key;
}
else
{
printf("ftok success\n");
}
int shmid = shmget(key,128,IPC_CREAT|0777);
if(shmid < 0)
{
printf("creat share memory fail\n");
return shmid;
}
else
{
printf("creat share memory success\n");
}
char *adr = (char *)shmat(shmid,NULL,0);
if(adr == NULL)
{
printf("shmat fail\n");
return -2;
}
else
{
printf("shmat success , address = %x\n",adr);
}
printf("input:");
fgets(adr,128,stdin);
printf("output:%s",adr);
shmdt(adr);
return 0;
}
4、shmctl(控制共享内存)
函数原型
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
所需头文件
#include <sys/ipc.h>
#include <sys/shm.h>
返回值
成功:0
失败:-1
参数
shmid : 共享内存标识ID
cmd :
IPC_STAT得到共享内存的状态
IPC_SET改变共享内存的状态
IPC_RMID删除共享内存
buf :是一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定,如果要删除共享内存则设置为NULL即可
linux命令ipcrm -m 【shmid】就为shmctl实现
例如:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
int main(int argc,char *argv[])
{
if(argc<3)
{
printf("input two parameters\n");
return argc-1;
}
int shmid;
if(strcmp(argv[1],"-m") == 0)
{
printf("delete share memory\n");
}
shmid = atoi(argv[2]);
shmctl(shmid,IPC_RMID,NULL);
system("ipcrm -m");
return 0;
}
二、进程间通讯
1、有亲缘关系通讯:
#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/ipc.h>
void myfun10(int signum); //accept 10 sig
void myfun11(int signum); //accept 11 sig
int main()
{
//creat share memory
int shmid = shmget(IPC_PRIVATE,128,0777);
if(shmid == -1)
{
printf("creat share memory fail\n");
return shmid;
}
//creat fork
pid_t pid = fork();
if(pid < 0)
{
printf("creat fork fail\n");
return pid;
}
//son process
if(pid == 0)
{
char *p = shmat(shmid,NULL,0);
signal(11,myfun11);
while(1)
{
pause();
printf("son process read : %s", p);
shmdt(p);
kill(getppid(),10);
}
}
//parent process
if(pid > 0)
{
char *p = (char *)shmat(shmid,NULL,0);
signal(10,myfun10);
while(1)
{
printf("parent process write : ");
fgets(p,128,stdin);
kill(pid,11);
pause();
}
}
return 0;
}
void myfun11(int signum)
{
return ;
}
void myfun10(int signum)
{
return ;
}
2、无亲缘关系的进程通讯
客户端和服务端的通讯
服务端:
#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <signal.h>
void myfun(int signum) //accept SIGSUR2 signal's function
{
return ;
}
typedef struct shmbuf //struct
{
pid_t pid;
char buf[124];
}shmbuf;
int main()
{
//creat key
key_t key = ftok(".",123);
if(key<0)
{
printf("creat key fail\n");
return -1;
}
else
{
printf("creat key success\n");
}
//creat share memory
int shmid = shmget(key,128,IPC_CREAT|0777);
if(shmid == -1)
{
printf("shmget fail\n");
return -2;
}
else
{
printf("shmget success , shmid = %d\n",shmid);
}
//creat shmat
struct shmbuf *p = (struct shmbuf *)shmat(shmid,NULL,0);
if(p == NULL)
{
printf("shmat fail\n");
return -3;
}
else
{
printf("shmat success\n");
}
//accept SIGUSR2 signal
signal(SIGUSR2,myfun);
//write server's pid to client
p->pid = getpid();
//sleep
pause();
//read client's pid
pid_t pid = p->pid;
//prompt connect success
printf("server connect client success\n");
while(1)
{
printf("server process write :");
fgets(p->buf,124,stdin);
kill(pid,SIGUSR1);
pause();
}
return 0;
}
客户端
#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <signal.h>
void myfun(int signum)
{
return ;
}
typedef struct shmbuf
{
pid_t pid;
char buf[124];
}shmbuf;
int main()
{
key_t key = ftok(".",123);
if(key<0)
{
printf("creat key fail\n");
return -1;
}
else
{
printf("creat key success\n");
}
int shmid = shmget(key,128,IPC_CREAT|0777);
if(shmid == -1)
{
printf("shmget fail\n");
return -2;
}
else
{
printf("shmget success , shmid = %d\n",shmid);
}
struct shmbuf *p = (struct shmbuf *)shmat(shmid,NULL,0);
if(p == NULL)
{
printf("shmat fail\n");
return -3;
}
else
{
printf("shmat success\n");
}
pid_t pid = p->pid;
p->pid = getpid();
kill(pid,SIGUSR2);
signal(SIGUSR1,myfun);
printf("client connect server success\n");
while(1)
{
pause();
printf("client process read : %s",p->buf);
kill(pid,SIGUSR2);
}
return 0;
}