共享内存
本文将研究一下进程通信的另一种方式:共享内存
共享内存:其实就是两个不相关的进程访问同一个逻辑内存,共享内存是在两个正在运行的进程之间共享和传递数据的一种有效方式。不同的近进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有的进程都可以访问共享内存中的地址,但是如果某个进程向共享内存中写入数据,所做的改动会立即影响到可以访问同一段共享内存的其他进程。
这一改动也就说明了共享内存是不安全的,因为它没有提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,没有任何行为阻止第二个进程对它进行读取,我们通常要用其他机制来同步对共享内存的访问,如:信号量
下面用一副图来更直观的介绍共享内存:
我们可以看到上图。蓝色线条表示的是正常的映射关系,而绿色的线条表示:两个进程指向了同一个物理地址,从而该物理地址就是这两个进程的共享内存。
查看共享内存相关指令:
查看共享内存:ipcs -m
- key栏中列出的信息是应用程序定义的键值
- shmid栏中列出共享内存的ID,这个值是唯一的.
- owner栏中列出创建共享内存的用户
- perms栏中列出共享内存的权限.
- bytes栏中列出这块共享内存的大小,
- nattch栏中列出连接在关联的共享内存段的进程数.
删除共享内存
注意:没有进程映射时才可以删除,有的话则不能删除。
我们在上图可以看到最后有一块共享内存没有进程映射,我们演示删除该进程
指令: ipcrm -m +id
我们看到在我们执行删除指令之后,再次查看,就已经删除成功啦。
共享内存相关函数
打开和创建
//打开和创建
int shmget(key_t key,
size_t size, //多大空间,创建时指定大小,写时0
int shmflg);//IPC_CREAT|0644
参数解释:
key:程序需要提供一个参数key(非0整数),它可以有效的为共享内存段命名
size:以字节为单位指定需要共享的内存大小
shmflg:是权限的标志位,在创建时可以与IPC_CREAT做或操作,如:IPC_CREAT|0644
返回值:失败返回-1,成功返回一个与key相关的共享内存标识符(非负整数)
代码实现
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<string.h>
struct stu
{
int id;
int age;
char name[20];
};
int main()
{
int id=shmget(1234,sizeof(struct stu),IPC_CREAT|0644);
if(id==-1)
perror("shmget"),exit(1);
printf("creat ok!");
}
运行该程序,我们在使用查看共享内存指令,查看创建的共享内存
我们可以看到圈出来的是我们刚刚创建的共享内存,此时连接该共享内存的进程数为0。
下面我们来看一下如何将进程挂到该自己的地址空间。
将共享内存挂到自己的地址空间
void *shmat(int shmid, const void *shmaddr, int shmflg//通常不用,填0
);
参数解释:
shmid:由shmget函数返回的共享内存的标识符
shmaddr:指定共享内存连接到当前进程的地址空间,通常写NULL,让系统来选择共享内存的地址
shmflg:一组标志位,通常为0.
返回值:失败返回-1,成功时返回一个指向共享内存第一个字节的指针
代码实现:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<string.h>
struct stu
{
int id;
int age;
char name[20];
};
int main()
{
int id=shmget(1234,0,0);//将共享内存打开
if(id==-1)
perror("shmget"),exit(1);
struct stu*p= shmat(id,NULL,0);//挂在自己的内存空间
p->id=9527;
p->age=77;
strcpy(p->name,"xiaohong");
}
运行结果:
我们可以看到最后一行我们已经成功的将该进程挂在了指定的共享内存中。
将共享内存从当前进程中分离
注:将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用
int shmdt(const void *shmaddr);
参数:shmaddr是shmat函数返回的地址指针,调用成功时返回0,失败时返回-1.
代码实现:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<string.h>
struct stu
{
int id;
int age;
char name[20];
};
int main()
{
int id=shmget(1234,0,0);//将共享内存打开
if(id==-1)
perror("shmget"),exit(1);
struct stu*p= shmat(id,NULL,0);//挂在自己的内存空间
p->id=9527;
p->age=77;
strcpy(p->name,"xiaohong");
sleep(10);
shmdt(p);//卸载,只是解除共享内存的关系,并没有删除
}
这个程序是我们将该进程挂在指定共享内存中,睡眠10s后,分离。
运行结果:
我们打开两个终端在一个终端运行该程序,然后在另一终端通过指令查看共享内存,十秒后再次查看
当我们10s后再次查看时当前共享内存连接的进程数已经变为0了。
控制共享内存(主要用于删除)
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数解释:
key:是shmget函数返回的共享内存标识符。
cmd 是要采取的操作,它可以取下面的三个值 :
IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除共享内存段buf是一个结构指针,它指向共享内存模式和访问权限的结构。(一般填NULL)
共享内存的优缺点:
优点:我们可以看到使用共享内存进行进程间的通信真的是非常方便,而且函数的接口也简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,也加快了程序的效率。同时,它也不像匿名管道那样要求通信的进程有一定的父子关系。
缺点:共享内存没有提供同步的机制,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段来进行进程间的同步工作。