目录
1.共享内存
1.1概念
共享内存指的是操作系统在物理内存中申请的一块空间,应用程序可以映射到这块空间,进行直接读写操作
1.2特点
1.共享内存是一种最为高效的进程通信方式,进程可以直接读写内存,而不需要任何数据的拷贝
2.为了在多个进程间交换信息,内核专门流出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间
3.进程可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高效率
4.由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量
1.3步骤
1.创建唯一key值,ftok
2.创建或打开共享内存
3.映射共享内存
4.取消映射
5.删除共享内存
1.4函数接口
1.4.1创建key值
#include <sys/types.h>#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
功能:创建key值
参数:
pathname:文件名
proj_id:取整型数的低8位数值
返回值:
成功:key值
失败:-1
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
int main(int argc, char const *argv[])
{
key_t key;
key = fork("./app",'a');
if (key < 0)
{
perror("fork err");
return -1;
}
printf("%#x\n",key);
return 0;
}
补充:
key值是由pathname的inode号和proj_id的低8位组合而成的,这样形成的唯一标识符用于在进程间通信中标识消息队列、信号量或共享内存等系统资源。
在进程间通信(IPC)中,系统需要一种方法来唯一地标识一个资源,例如消息队列、信号量或共享内存。为此,系统V IPC使用了键值(key),它通过ftok函数生成。这个键值是基于指定的文件(由pathname给出)的索引节点号(inode number)和项目标识符(proj_id)的低8位组合而成。
从结构上来看,ftok函数接受两个参数:一个是文件名(pathname),另一个是项目标识符(proj_id)。该函数利用这两个参数生成一个键值,这个值是唯一的,并且可以被用来识别某个特定的IPC资源。具体来说,ftok函数会提取pathname参数所指定的文件的索引节点信息,并将这个信息与proj_id参数的低8位组合起来,形成一个32位的键值。
综上所述,键值的生成不仅依赖于文件系统的索引节点号,还依赖于proj_id的低8位。这意味着同一个文件可以产生多达256个不同的键值(从0x01到0xFF),为不同的IPC资源提供唯一性。这种设计使得在同一个项目中可以创建多个不同的IPC资源,而不会因为键值冲突导致无法区分。
1.4.2创建共享内存
int shmget ( key_t key , size_t size , int shmflg );功能:创建或打开共享内存
参数:
key 键值
size 共享内存的大小
创建 检测错误
shmflg IPC_CREAT|IPC_EXCL|0777 创建共享内存的时候的权限
返回值:
成功 shmid 共享内存的id
出错 -1
查看创建的共享内存的命令:ipcs -m
示例:
int shmid = shmget(key, 64, IPC_CREAT | IPC_EXCL | 06666);
if (shmid <= 0)
{
if (errno == EEXIST)
{
printf("文件已存在\n");
shmid = shmget(key, 64, 0666);
}
else
{
perror("shmget err");
return -1;
}
}
1.4.3映射共享内存
void * shmat ( int shmid , const void * shmaddr , int shmflg );功能:映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
参数:
shmid 共享内存的id号
shmaddr 一般为NULL,表示由系统自动完成映射
如果不为NULL,那么有用户指定
shmflg:SHM_RDONLY就是对该共享内存只进行读操作
0 可读可写
返回值:
成功:完成映射后的地址,
出错:(void *)-1的地址
示例:
//映射
char *p = shmat(shmid, NULL, 0);//此处的0为可读可写
if (p == (void *) - 1)
{
perror("shat err");
return -1;
}
//向共享内中存放数据
scanf("%s",p);
printf("%s\n",p);
return 0;
1.4.4取消映射
int shmdt ( const void * shmaddr );功能:取消映射
参数:要取消的地址
返回值:
成功0
失败的-1
//取消映射
shmat(p);
getchar();
1.4.5删除共享
int shmctl ( int shmid , int cmd , struct shmid_ds * buf );功能:(删除共享内存),对共享内存进行各种操作
参数:
shmid 共享内存的id号
cmd IPC_STAT 获得shmid属性信息,存放在第三参数
IPC_SET 设置shmid属性信息,要设置的属性放在第三参数
IPC_RMID:删除共享内存,此时第三个参数为NULL即可
struct shmid_ds *buf:是一个结构体指针,但我们是删除共享内存,所以并没有意义,我们直接设置为NULL就可以了
返回:
成功0
失败-1
示例:
shmctl(shmid, IPC_RMID, NULL);
1.5操作指令
ipcs -m:查看系统中创建的共享内存
ipcrm -m [shmid]:删除创建的共享内存
练习:
通过共享内存实现进程间通信,一个进程从终端输入数据,另一个进程打印数据,循环执行,当输入quit时循环结束
输入数据函数
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
struct data
{
int flag; // 标志位,用于判断数据是否已写入共享内存
char buf[32]; // 存储输入的字符串
};
int main(int argc, char const *argv[])
{
key_t key; // 定义一个key_t类型的变量,用于存储生成的键值
key = ftok("./app", 'a'); // 使用ftok函数生成一个键值,参数为文件路径和字符
struct data *p = NULL; // 定义一个指向data结构体的指针
int shmid = shmget(key, 64, IPC_CREAT | IPC_EXCL | 0666); // 创建共享内存,大小为64字节
if (key < 0) // 如果生成键值失败
{
perror("ftok err"); // 输出错误信息
return -1; // 返回错误代码
}
if (shmid <= 0) // 如果共享内存创建失败
{
if (errno == EEXIST) // 如果错误原因是共享内存已存在
{
printf("file exits"); // 输出提示信息
shmid = shmget(key, 64, 066); // 重新获取共享内存的ID
}
else // 如果错误原因不是共享内存已存在
{
perror("shmid err"); // 输出错误信息
return -1; // 返回错误代码
}
}
// 映射共享内存到当前进程的地址空间
p = shmat(shmid, NULL, 0);
if (p == (void *)-1) // 如果映射失败
{
perror("shat err"); // 输出错误信息
}
p->flag = 0; // 初始化标志位为0
while (1) // 无限循环
{
if (p->flag == 0) // 如果标志位为0,表示共享内存中的数据未被修改
{
scanf("%s", p->buf); // 从标准输入读取字符串并存入共享内存
p->flag = 1; // 将标志位设为1,表示数据已被修改
if (!strcmp(p->buf, "quit")) // 如果输入的字符串为"quit"
break; // 跳出循环
}
}
// 取消映射共享内存
shmdt(p);
return 0; // 程序正常结束
}
输出数据函数
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
struct data
{
int flag; // 标志位,用于判断数据是否已写入共享内存
char buf[32]; // 存储输入的字符串
};
int main(int argc, char const *argv[])
{
key_t key; // 定义一个key_t类型的变量,用于存储生成的键值
key = ftok("./app", 'a'); // 使用ftok函数生成一个键值,参数为文件路径和字符
struct data *p = NULL; // 定义一个指向data结构体的指针
int shmid = shmget(key, 64, IPC_CREAT | IPC_EXCL | 0666); // 创建共享内存,大小为64字节
if (key < 0) // 如果生成键值失败
{
perror("ftok err"); // 输出错误信息
return -1; // 返回错误代码
}
if (shmid <= 0) // 如果共享内存创建失败
{
if (errno == EEXIST) // 如果错误原因是共享内存已存在
{
printf("file exits"); // 输出提示信息
shmid = shmget(key, 64, 066); // 重新获取共享内存的ID
}
else // 如果错误原因不是共享内存已存在
{
perror("shmid err"); // 输出错误信息
return -1; // 返回错误代码
}
}
// 映射共享内存到当前进程的地址空间
p = shmat(shmid, NULL, 0);
if (p == (void *)-1) // 如果映射失败
{
perror("shat err"); // 输出错误信息
}
p->flag = 0; // 初始化标志位为0
while (1) // 无限循环
{
if (p->flag == 1) // 如果标志位为1,表示共享内存中的数据已被修改
{
if (!strcmp(p->buf, "quit")) // 如果输入的字符串为"quit"
break; // 跳出循环
printf("p:%s", p->buf); // 输出共享内存中的字符串
p->flag = 1; // 将标志位设为1,表示数据已被修改
}
}
// 取消映射共享内存
shmdt(p);
return 0; // 程序正常结束
}