共享内存与消息队列、管道的区别:
1.消息队列、管道中的数据读取之后就没有了;而共享内存中的数据无论读取多少次,都还会在里面;
2.共享内存读取相对于消息队列与管道来说,效率最高,直接对指向共享内存的指针进行读写操作;
共享内存函数
共享内存函数
一、int shmget(key_t key, size_t size, int shmflg);
功能:创建或打开共享内存
参数:
key:键值;
size:要创建的共享内存的字节数;
shmflg:权限(IPC_CREAT | IPC_EXCL|0664)
返回值:
成功:返回shmid;
失败:返回 -1;
二、void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射共享内存,即把指定的共享内存映射到进程的地址空间,用于访问;
参数:
shmid:要映射的共享内存的id号;
shmaddr:共享内存地址的选择方式:1.NULL,系统帮你选一块不碍事儿的地方;2.你自己选,一般不自己选;
shmflg:读写权限:1.SHM_RDONLY,即对共享内存只进行读操作;2.0,可读可写操作;
返回值:
成功:返回完成映射之后的共享内存地址;
失败:返回(void *)-1;
三、int shmdt(const void *shmaddr);
功能:解除当前进程与共享内存的映射关系;
参数:shmaddr:shmat的返回值,共享内存的地址;
返回值:
成功:0
失败:-1
四、int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:对共享内存进行各种操作;
参数:
shmid:共享内存的id号;
cmd:命令:
IPC_STAT:获取shmid的属性信息,存放在第三个参数内;
IPC_SET:设置shmid的属性,要设置的属性放在第三个参数里;
IPC_RMID:删除共享内存,此时第三个参数为NULL;
返回值:
成功:0;
失败:-1;
编程实例:
下面我们来编写一个测试代码:从一个terminal输入到共享内存,另一个terminal从共享内存读取并输出打印;
在终端输入到共享内存:
#include "sem.h"
int main(int argc, const char *argv[])
{
key_t key;
int shmid;
char *p = NULL;
char buf[N];
if( (key = ftok(".", 1)) < 0)
{
perror("ftok error");
exit(1);
}
if( (shmid = shmget(key, 512, IPC_CREAT|IPC_EXCL|0664)) < 0)
{
if(errno == EEXIST)
{
shmid = shmget(key, 512, 0664);
puts("share buffer is exist");
}
else
{
perror("shmget error");
exit(1);
}
}
p = (char *)shmat(shmid, NULL, 0);
if(p == (char *)-1)
{
perror("shmat error");
exit(1);
}
while(fgets(p, N, stdin) != NULL)
{
if(strncmp(p, "quit", 4) == 0)
{
break;
}
}
shmdt(p);
return 0;
}
从共享内存读取数据并打印到终端:
#include "sem.h"
int main(int argc, const char *argv[])
{
key_t key;
int shmid;
char *p = NULL;
char buf[N];
if( (key = ftok(".", 1)) < 0)
{
perror("ftok error");
exit(1);
}
if( (shmid = shmget(key, 512, IPC_CREAT|IPC_EXCL|0664)) < 0)
{
if(errno == EEXIST)
{
shmid = shmget(key, 512, 0664);
puts("share buffer is exist");
}
else
{
perror("shmget error");
exit(1);
}
}
p = (char *)shmat(shmid, NULL, 0);
if(p == (char *)-1)
{
perror("shmat error");
exit(1);
}
while(1)
{
if(strncmp(p, "quit", 4) == 0)
{
break;
}
fputs(p, stdout);
}
shmdt(p);
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
我们运行程序后发现,负责读取的终端会一直进行输出,刷屏了。这是因为共享内存里的数据不会因为对他进行了读取后就把里面的数据清空。
回想无名管道、有名管道、消息队列,都是读取后里面的东东就没有了。同时那三个是在内核空间进行的通信,这个不是在内核空间进行的通信;
要想实现一端输入,另一端读取(只读取一遍,不刷屏),我们还有结合信号灯的使用,让这个共享内存有个先后执行的顺序(也就是同步)。