1.简介
共享内存指 (shared memory)在多处理器的计算机系统中,可以被不同中央处理器(CPU)访问的大容量内存。由于多个CPU需要快速访问存储器,这样就要对存储器进行缓存(Cache)。任何一个缓存的数据被更新后,由于其他处理器也可能要存取,共享内存就需要立即更新,否则不同的处理器可能用到不同的数据。共享内存是 Unix下的多进程之间的通信方法 ,这种方法通常用于一个程序的多进程间通信,实际上多个程序间也可以通过共享内存来传递信息。
2.特点
共享内存的特点:
1)共享内存是进程间共享数据的一种最快的方法。
一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。
2)使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。
若一个进程正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据。
3.使用步骤
共享内存使用步骤
① 创建共享内存。也就是从内存中获得一段共享内存区域,这里用到的函数是shmget();
② 映射共享内存。也就是把这段创建的共享内存映射到具体的进程空间中,这里使用的函数是shmat()。到这一步就可以使用这段共享内存了,也就是可以使用不带缓冲的I/O读写命令对其进行操作。(通俗说就是进程和共享内存连接上)
③ 撤销映射。使用完共享内存就需要撤销,用到的函数是shmdt()。(通俗说就是进程和共享内存断开连接)
④ 删除共享内存。
4.函数说明
1)shmget (创建共享内存)
int shmget(key_t key, size_t size,int shmflg);
所需头文件:
#include <sys/ipc.h>
#include <sys/shm.h>
功能:
创建或打开一块共享内存区。
参数:
key:进程间通信键值,ftok() 的返回值。
size:该共享存储段的长度(字节)。
shmflg:标识函数的行为及共享内存的权限,其取值如下:
———– IPC_CREAT:如果不存在就创建
————IPC_EXCL: 如果已经存在则返回失败
位或权限位:共享内存位或权限位后可以设置共享内存的访问权限,格式和 open() 函数的 mode_t 一样(open() 的使用请点此链接),但可执行权限未使用。
返回值:
成功:共享内存标识符。
失败:-1。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define BUFSZ 1024
int main(int argc, char *argv[])
{
int shmid;
key_t key;
key = ftok("./", 2015);
if(key == -1)
{
perror("ftok");
}
//创建共享内存
shmid = shmget(key, BUFSZ, IPC_CREAT|0666);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
return 0;
}
2)shmat(共享内存映射)
void *shmat(int shmid, const void *shmaddr, int shmflg);
所需头文件:
#include <sys/types.h>
#include <sys/shm.h>
功能:
将一个共享内存段映射到调用进程的数据段中。简单来理解,让进程和共享内存建立一种联系,让进程某个指针指向此共享内存。
参数:
shmid:共享内存标识符,shmget() 的返回值。
shmaddr:共享内存映射地址(若为 NULL 则由系统自动指定),推荐使用 NULL。
shmflg:共享内存段的访问权限和映射条件( 通常为 0 ),具体取值如下:
0:共享内存具有可读可写权限。
SHM_RDONLY:只读。
SHM_RND:(shmaddr 非空时才有效)
返回值:
成功:共享内存段映射地址( 相当于这个指针就指向此共享内存 )
失败:-1
3)shmdt(解除共享内存映射)
int shmdt(const void *shmaddr);
所需头文件:
#include <sys/types.h>
#include <sys/shm.h>
功能:
将共享内存和当前进程分离( 仅仅是断开联系并不删除共享内存,相当于让之前的指向此共享内存的指针,不再指向)。
参数:
shmaddr:共享内存映射地址。
返回值:
成功:0
失败:-1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define BUFSZ 512
int main(int argc, char *argv[])
{
int shmid;
int ret;
key_t key;
char *shmadd;
key = ftok("../", 2015); //创建key值
if(key == -1)
{
perror("ftok");
}
system("ipcs -m"); //查看共享内存
//打开共享内存
shmid = shmget(key, BUFSZ, IPC_CREAT|0666);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
shmadd = shmat(shmid, NULL, 0); //映射
if(shmadd < 0)
{
perror("shmat");
exit(-1);
}
//读共享内存区数据
printf("data = [%s]\n", shmadd);
//分离共享内存和当前进程
ret = shmdt(shmadd);
if(ret < 0)
{
perror("shmdt");
exit(1);
}
else
{
printf("deleted shared-memory\n");
}
//删除共享内存
shmctl(shmid, IPC_RMID, NULL);
system("ipcs -m"); //查看共享内存
return 0;
}
4)shmctl(共享内存控制)
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
所需的头文件:
#include <sys/ipc.h>
#include <sys/shm.h>
功能:
共享内存属性的控制。
参数:
shmid:共享内存标识符。
cmd:函数功能的控制,其取值如下:
IPC_RMID:删除。(常用 )
IPC_SET:设置 shmid_ds 参数,相当于把共享内存原来的属性值替换为 buf 里的属性值。
IPC_STAT:保存 shmid_ds 参数,把共享内存原来的属性值备份到 buf 里。
SHM_LOCK:锁定共享内存段( 超级用户 )。
SHM_UNLOCK:解锁共享内存段。
SHM_LOCK 用于锁定内存,禁止内存交换。并不代表共享内存被锁定后禁止其它进程访问。其真正的意义是:被锁定的内存不允许被交换到虚拟内存中。这样做的优势在于让共享内存一直处于内存中,从而提高程序性能。
buf:shmid_ds 数据类型的地址(具体类型请点此链接 ),用来存放或修改共享内存的属性。
返回值:
成功:0
失败:-1
5.示例
写入数据
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include "shmdata.h"
int main(void)
{
int running = 1;
void *shm = NULL;
struct shared_use_st *shared = NULL;
char buffer[BUFSIZ + 1];//用于保存输入的文本
int shmid;
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT); //创建共享内存
if(shmid == -1)
{
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
}
shm = shmat(shmid, (void*)0, 0); //将共享内存连接到当前进程的地址空间
if(shm == (void*)-1)
{
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
printf("Memory attached at %p\n", shm);
//设置共享内存
shared = (struct shared_use_st*)shm; //创建结构体shared,详见sharedata.h头文件
while(running)//向共享内存中写数据
{
//数据还没有被读取,则等待数据被读取,shared->written = 1不能向共享内存中写入文本,此时数据可读不可写
while(shared->written == 1)
{
sleep(1);
printf("Waiting...\n");
}
//向共享内存中写入数据
printf("Enter some text: "); //输入提示
fgets(buffer, BUFSIZ, stdin); //输入数据
strncpy(shared->text, buffer, TEXT_SZ); //写入数据
shared->written = 1; //写完数据,设置written使共享内存段可读,此时数据可读不可写
if(strncmp(buffer, "end", 3) == 0) //输入了end,退出循环(程序)
running = 0;
}
if(shmdt(shm) == -1) //把共享内存从当前进程中分离
{
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}
sleep(2);
exit(EXIT_SUCCESS);
}
读取数据
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/shm.h>
#include "shmdata.h"
int main(void)
{
int running = 1;//程序是否继续运行的标志
void *shm = NULL;//分配的共享内存的原始首地址
struct shared_use_st *shared; //指向shm
int shmid;//共享内存标识符
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT); //创建共享内存
if(shmid == -1)
{
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
}
shm = shmat(shmid, 0, 0); //将共享内存连接到当前进程的地址空间,内存和进程连接
if(shm == (void*)-1)
{
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
printf("\nMemory attached at %p\n", shm);
shared = (struct shared_use_st*)shm; //设置共享内存结构体
shared->written = 0; //可写不可读
while(running)//读取共享内存中的数据
{
//没有进程向共享内存定数据有数据可读取
if(shared->written != 0) //如果shared->written不为0则可读,即为1则可读,为0不可读
{
printf("You wrote: %s", shared->text); //直接输出共享内存内容
sleep(rand() % 3);
//读取完数据,设置written使共享内存段可写
shared->written = 0;
//输入了end,退出循环(程序)
if(strncmp(shared->text, "end", 3) == 0)
running = 0;
}
else //否则shared->written = 0有其他进程在写数据,不能读取数据
sleep(1); //shared->written = 0有其他进程在写数据,等待数据写入
}
if(shmdt(shm) == -1) //把共享内存从当前进程中分离,共享内存和进程断开
{
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}
if(shmctl(shmid, IPC_RMID, 0) == -1) //删除共享内存
{
fprintf(stderr, "shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
//头文件shmdata.h
#ifndef _SHMDATA_H_HEADER
#define _SHMDATA_H_HEADER
#define TEXT_SZ 2048
struct shared_use_st
{
//作为一个标志,非0:表示可读,0表示可写
int written;
//记录写入和读取的文本
char text[TEXT_SZ];
};
#endif
程序流程图