目录
1.概念
共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,比如信号量。
2.特点
1)共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,
而不需要任何数据的拷贝
2)为了在多个进程间交换信息,内核专门留出了一块内存区,可以由
需要访问的进程将其映射到自己的私有地址空间
3)进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。
4)由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等
3.步骤
a. 创建key值
b. 创建或打开共享内存
c. 映射共享内存到用户空间
d. 撤销映射
e. 删除共享内存
3.函数接口
a. 创建key值
key_t ftok(const char *pathname, int proj_id);
功能:产生一个独一无二的key值
参数:
Pathname:已经存在的可访问文件的名字
Proj_id:一个字符(因为只用低8位)
返回值:成功:key值
失败:-1
key_t key = ftok("路径地址", '随机一个字符');
if (key < 0)
{
perror("ftok err");
return -1;
}
有关该函数的三个常见问题:
1.pathname是目录还是文件的具体路径,是否可以随便设置
2.pathname指定的目录或文件的权限是否有要求
3.proj_id是否可以随便设定,有什么限制条件
解答:
1、ftok根据路径名,提取文件信息,再根据这些文件信息及project ID合成key,该路径可以随便设置。
2、该路径是必须存在的,ftok只是根据文件inode在系统内的唯一性来取一个数值,和文件的权限无关。
3、proj_id是可以根据自己的约定,随意设置。这个数字,有的称之为project ID; 在UNIX系统上,它的取值是1到255;
b. 创建或打开共享内存
int shmget(key_t key, size_t size, int shmflg);
功能:创建或打开共享内存
参数:
key 键值
size 共享内存的大小
shmflg IPC_CREAT|IPC_EXCL|0777
返回值:成功 shmid
出错 -1
int shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0777);
if (shmid < 0)
{
if (errno == EEXIST)
{
shmid = shmget(key, 128, 0777);
if (shmid < 0)
{
perror("shmget err");
return -1;
}
}
else
{
perror("shmget err");
return -1;
}
}
如果共享内存已存在,函数会报错,更新errno,需要判断errno==EEXIST再执行打开的操作
问题:有可能执行完函数,shmid的值为0 ,在执行完打印一下shmid的值如果为零,就意味着共享内存没有开辟成功,需要删掉,重新执行代码。
在命令行查看共享内存: ipcs -m
删除共享内存:ipcrm -m shmid
c. 映射共享内存到用户空间
void *shmat(int shmid,const void *shmaddr,int shmflg);
功能:映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
参数:
shmid 共享内存的id号
shmaddr 一般为NULL,表示由系统自动完成映射
如果不为NULL,那么有用户指定
shmflg:SHM_RDONLY就是对该共享内存只进行读操作
0 可读可写
返回值:成功:完成映射后的地址,
出错:-1的地址
用法
if((p = (char *)shmat(shmid,NULL,0)) == (char *)-1)
用法2:
void *p = NULL;
p = shmat(shmid, 0, 0);
printf("%d\n",(int)p);
如果出错返回的是-1的地址,但是是void*类型,需要强转成与共享内存的
char *p = (char *)shmat(shmid, NULL, 0);
if (p == (char *)-1)
{
perror("shmat err");
return -1;
}
共享内存的使用
直接使用strcpy向赋值,不能直接向p赋值,那样会改变p的指向
d. 撤销映射
int shmdt(const void *shmaddr);
功能:取消映射
参数:要取消的地址
返回值:成功0
失败的-1
e. 删除共享内存
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
功能:(删除共享内存),对共享内存进行各种操作
参数:
shmid 共享内存的id号
cmd IPC_STAT 获得shmid属性信息,存放在第三参数
IPC_SET 设置shmid属性信息,要设置的属性放在第三参数
IPC_RMID:删除共享内存,此时第三个参数为NULL即可
返回:成功0
失败-1
第二个参数,command是要采取的操作,它可以取下面的三个值 :
IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除共享内存段
用法:
shmctl(shmid,IPC_RMID,NULL);