一、共享内存的原理
二、共享内存的函数
2.1、int shmget(key_t key,size_t size,int flags);
返回值:失败返回-1,成功返回共享内存的id。
size:表示要申请的共享内存的大小,一般是4k的整数倍。
flags:IPC_CREAT和IPC_EXCL一起使用,则创建一个新的关系内存,否则返回-1。IPC_CREAT单独使用时返回一个共享内存,有就直接返回,没有就创建。
2.2、void *shmat(int shmid);
shmat的作用是将申请的共享内存挂接在该进程的页表上,是的虚拟内存和物理内存向对应。
返回值:返回这块内存的虚拟地址。
2.3、int shmdt(const void*shmaddr);
shmdt的作用是去挂接,将这块共享内存从页表上剥离下来。
返回值:失败返回-1.
shmaddr:表示这块物理内存的虚拟地址。
2.4、int shmctl(int shmid,int cmd,const void* addr);
shmctl用来设置共享内存的属性。当cmd是IPC_RMID时可以用来删除一块共享内存。
三、命令
ipcs -m //查看系统中创建的共享内存的信息
ipcrm -m shmid //销毁创建的共享内存
四、共享内存中的结构体
ipc_perm这个结构体,在消息队列、信号量、共享内存中都是存在的。
struct ipc_perm {
key_t __key; /* Key supplied to shmget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions + SHM_DEST and
SHM_LOCKED flags */
unsigned short __seq; /* Sequence number */
};
shmid_ds这个结构体的第一个参数就是一个ipc_perm类型的成员。这个结构体用来描述共享内存的id的。
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
shmatt_t shm_nattch; /* No. of current attaches */
...
};
五、共享内存的特点
在匿名管道(pipe),命名管道(mkfifo),消息队列(msgget)、信号量(semget)、共享内存(shmget)这五种进程间通信的方式中,前两种生命周期随进程,后三种生命周期随内核。而共享内存是这五种方式中效率最高的。虽然共享内存提供了进程间通信的方式,但是他没有相应的互斥机制,所以一般共享内存和信号量配合起来使用。
六、共享内存比管道和消息队列效率高的原因
共享内存区是最快的可用IPC形式,一旦这样的内存区映射到共享它的进程的地址空间,这些进程间数据的传递就不再通过执行任何进入内核的系统调用来传递彼此的数据,节省了时间。
共享内存和消息队列,FIFO,管道传递消息的区别:
后者,消息队列,FIFO,管道的消息传递方式一般为
1:服务器得到输入
2:通过管道,消息队列写入数据,通常需要从进程拷贝到内核。
3:客户从内核拷贝到进程
4:然后再从进程中拷贝到输出文件
上述过程通常要经过4次拷贝,才能完成文件的传递。
而共享内存只需要
1:从输入文件到共享内存区
2:从共享内存区输出到文件
上述过程不涉及到内核的拷贝,所以花的时间较少
栗子:利用共享内存,实现,在serve这个进程中向共享内存中写入数据。从client读出数据。
代码:
//makefile
sersrc=serve.c comm.c
clisrc=client.c comm.c
ser=serve
cli=client
commh=comm.h
cc=gcc
.PHONY:all
all:$(ser) $(cli)
$(ser):$(sersrc) $(commh)
$(cc) -o $@ $<
$(cli):$(clisrc) $(commh)
$(cc) -o $@ $<
.PHONY:clean
clean:
rm -r $(ser) $(cli)
//comm.h
#ifndef __COMM_H__
#define __COMM_H__
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/shm.h>
#include<sys/ipc.h>
#define PATHNAME "."
#define PROJID 0666
#define SIZE 4096*1
int creatshm();
int getshm();
char *atshm(int shmid);
int dtshm(const void* shmaddr);
int destroyshm(int shmid);
#endif
//comm.c
#include"comm.h"
static int commshm(int flags)
{
key_t _k=ftok(PATHNAME,PROJID);
if(_k==-1)
{
perror("ftok");
return -1;
}
int shmid=shmget(_k,SIZE,flags|0666);
if(shmid==-1)
{
perror("shmget");
return -1;
}
return shmid;
}
int creatshm()
{
int shmid=commshm(IPC_CREAT | IPC_EXCL);
return shmid;
}
int getshm()
{
int shmid=commshm(IPC_CREAT);
return shmid;
}
char *atshm(int shmid)
{
char* addr=shmat(shmid,NULL,0);
return (char*)addr;
}
int dtshm(const void* shmaddr)
{
return shmdt(shmaddr);
}
int destroyshm(int shmid)
{
if(shmctl(shmid,IPC_RMID,NULL)<0)
{
perror("shmctl");
return -1;
}
return 0;
}
//serve.c
#include"comm.c"
int main()
{
int shmid=creatshm();
char* buf=(char*)atshm(shmid);
int i=0;
while(i<(SIZE-1))
{
buf[i]='A';
i++;
}
buf[SIZE-1]='\0';
dtshm(buf);
// destroyshm(shmid);
return 0;
}
//client
#include"comm.c"
int main()
{
int shmid=getshm();
char* buf=(char*)atshm(shmid);
printf("%s\n",buf);
dtshm(buf);
return 0;
}