1. 共享内存的优缺点
共享内存是在系统内核分配的一块缓冲区,多个进程都可以访问该缓冲区。共享内存通信方式的优点是进程间传递信息最快,因为客户进程和服务进程传递的数据直接从内存里存取,数据不需要在两进程间复制。另外,共享内存的生命周期随内核,即所有访问共享内存区域对象的进程都已经正常结束,共享内存区域对象仍然在内核中存在(除非显式删除共享内存区域对象),在内核重新引导之前,对该共享内存区域对象的任何改写操作都将一直保留。简单地说,共享内存区域对象的生命周期跟系统内核的生命周期是一致的,而且共享内存区域对象的作用域范围就是在整个系统内核的生命周期之内。
但是,共享内存也并不完美,系统没有为共享内存通信方式提供同步机制,即在一个服务进程结束对共享内存的写操作之前,并没有自动机制可以阻止另一个进程(客户进程)开始对它进行读取。这明显还达不到我们想要的,我们不单是在两进程间交互数据,还想实现多个进程对共享内存的同步访问。基于此,我们通常会用信号量来实现对共享内存同步访问控制。
2. 与内存共享相关的函数用法
(1) shmget()函数
功能:创建共享内存,函数用法如下表所示。
(2) shmctl()函数
功能:用于控制共享内存,函数用法如下表所示。
(3) shmat()函数
功能:将共享内存段连接到进程地址空间。
(4) shmdt()函数
功能:将共享内存段与当前进程脱离。该函数不从系统中删除标识符及其数据结构,要显示调用shmctl(带命令IPC_RMID)才能删除它。
3. 使用内存共享实现进程间通信
生成共享内存示例代码,shmcreat.c。
include<stdio.h>
#include<stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<string.h>
#include<errno.h>
typedef struct _Teacher {
char name[64];
int age;
}Teacher;
int main(int argc, char *argv[])
{
int ret = 0;
int shmid;
//创建共享内存 ,相当于打开文件,文件不存在则创建
shmid = shmget(0x12345, sizeof(Teacher), IPC_CREAT | 0666);
if (shmid == -1) {
printf("shmget error.\n");
return errno;
}
printf("shmid:%d \n", shmid);
Teacher *p = NULL;
//将共享内存段连接到进程地址空间
p = shmat(shmid, NULL, 0);// 第二个参数为NULL,核心自动选择一个地址
if (p == (void *)-1 ) {
printf("shmget error.\n");
return errno;
}
strcpy(p->name, "aaaa");
p->age = 33;
//将共享内存段与当前进程脱离
shmdt(p);
printf("键入1 删除共享内存,其他不删除\n");
int num;
scanf("%d", &num);
if (num == 1) {
//用于控制共享内存
ret = shmctl(shmid, IPC_RMID, NULL);//IPC_RMID为删除内存段
if (ret < 0) {
printf("rm error.\n");
}
}
return 0;
}
编译运行程序,如下图所示,在键入1之前,执行ipcs -m可以看到key=0x12345的共享内存信息。
当键入1之后,key=0x12345的共享内存被删除,如下图所示。如果键入非1,则key=0x12345的共享内存在内核中一直存在。
获取共享内存信息源程序shmgetinfo.c。
include<stdio.h>
#include<stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<string.h>
#include<errno.h>
typedef struct _Teacher {
char name[64];
int age;
}Teacher;
int main(int argc, char *argv[])
{
int ret = 0;
int shmid;
//打开获取共享内存
shmid = shmget(0x12345, 0, 0);
if (shmid == -1) {
printf("shmget error.\n");
return errno;
}
printf("shmid:%d \n", shmid);
Teacher *p = NULL;
//将共享内存段连接到进程地址空间
p = shmat(shmid, NULL, 0);
if (p == (void *)-1 ) {
printf("shmget error.\n");
return errno;
}
printf("name:%s\n", p->name);
printf("age:%d \n", p->age);
//将共享内存段与当前进程脱离
shmdt(p);
return 0;
}
编译运行如下图所示。
运行./shmcreat创建shmid为24的共享内存,然后键入2程序运行结束。
运行./shmgetinfo从共享内存中读出./shmcreat写入共享内存的信息。
再次运行./shmgetinfo从共享内存中读出相同信息。