linux入门到精通-第十一章-进程间通信(共享存储映射)

参考

视频教程

概述

存储映射I/O(Memory-mapped I/O)使一个磁盘文件与存储空间中的一个缓冲区相映射。
在这里插入图片描述
于是当从缓冲区中取数据,就相当于读文件中的相应字节。于此类似,将数据存入缓冲区,则相应的字节就自动写入文件。这样,就可在不适用read和write函数的情况下,使用地址 (指针)完成I/O操作。
共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式,因为进程可以直接读写内存,而不需要任何数据的拷贝。

存储映射函数

mmap函数

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

功能:
	一个文件或者其它对象映射进内存
参数:
	addr :指定映射的起始地址,通常设为NULL,由系统指定
	length:映射到内存的文件长度
	prot:映射区的保护方式,最常用的 :
		a): PROT_READ
		b): PROT_WRITE
		c) 读写: PROT_READ | PROT_WRITE
	flag:映射区的特性,可以是
		a) MAP_SHARED : 写入映射区的数据会复制回文件,且允许其他映射该文件的进程共享
		b) MAP PRIVATE:对映射区的写入操作会产生一个映射区的复制(copy - on - write),对此区域所做的修改不会写回原文件。
	fd:由open返回的文件描述符,代表要映射的文件
	offset:以文件开始处的偏移量,必须是4k的整数倍(内存页大小),通常为0,表示从文件头开始映射
返回值:
	成功:返回创建的映射区首地址
	失败:MAP_FAILED宏

关于mmap函数的使用总结

  • 1)、第一个参数写成NULL
  • 2)、第二个参数要映射的文件大小 > 0
  • 3)、第三个参数:PROT_READ 、PROT_WRITE
  • 4)、第四个参数:MAP_SHARED 或者 MAP_PRIVATE
  • 5)、第五个参数:打开的文件对应的文件描述符
  • 6)、第六个参数:4的整数倍,通常为0

munmap

#include <sys/mman.h>
int munmap(void addr, size_t length);
功能
	释放内存映射区
参数:
	addr: 使用mmap函数创建的映射区的首地址
	length: 映射区的大小
返回值
	成功:  0
	失败: -1

代码测试mmap

注意test.txt要提前创建,并加入内容,否则内存拷贝会段错误。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
#define SIZE 1024

//以只写的方式打开管道2
int main(void)
{
    int fd = -1;
    void* addr = NULL;
    // 1.打开文件
    fd = open("test.txt",O_RDWR | O_CREAT,0644);
    if (-1 == fd)
    {
        perror("open");
        return 1;
    }
    printf("fd = %d\n",fd);

    // 2.将文件映射到内存
    addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if( addr == MAP_FAILED )
    {
        perror("mmap");
        return 1;
    }
    printf("文件存储映射 ok \n");
    // 3.关闭文件
    close(fd);

    // 4.写文件
    memcpy(addr,"1234567890", 10);
    // write(addr,"1234567890", 10);

    // 5.断开存储映射
    munmap(addr, 1024);
    return 0;
}

共享存储映射代码测试

父子进程间通信

test.txt要有数据

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#define SIZE 1024

//以只写的方式打开管道2
int main(void)
{
    int fd = -1;
    pid_t pid;
    void* addr = NULL;
    // 1.打开文件
    fd = open("test.txt",O_RDWR | O_CREAT,0644);
    if (-1 == fd)
    {
        perror("open");
        return 1;
    }
    printf("fd = %d\n",fd);

    // 2.将文件映射到内存
    addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if( addr == MAP_FAILED )
    {
        perror("mmap");
        return 1;
    }
    printf("文件存储映射 ok \n");
    // 3.关闭文件
    close(fd);

    // 4.创建一个子进程
    pid = fork();
    if(pid <0)
	{
		// 没有创建成功
		perror("fork");
		return 0;

	}
	if (0 == pid)
	{
		//子进程
		// 4.写文件
        memcpy(addr,"1234567890", 10);
	}
	else
	{
		//父进程
        // 等待子进程结束
        wait(NULL);
        printf("addr:%s\n", (char*)addr);

	}
    // 5.断开存储映射
    munmap(addr, 1024);
    return 0;
}

进程间通信

write.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
#define SIZE 1024

//以只写的方式打开管道2
int main(void)
{
    int fd = -1;
    void* addr = NULL;
    // 1.打开文件
    fd = open("test.txt",O_RDWR | O_CREAT,0644);
    if (-1 == fd)
    {
        perror("open");
        return 1;
    }
    printf("fd = %d\n",fd);

    // 2.将文件映射到内存
    addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if( addr == MAP_FAILED )
    {
        perror("mmap");
        return 1;
    }
    printf("文件存储映射 ok \n");
    // 3.关闭文件
    close(fd);

    // 4.写文件
    memcpy(addr,"1234567890", 10);
    // write(addr,"1234567890", 10);

    // 5.断开存储映射
    munmap(addr, 1024);
    return 0;
}

read.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
#define SIZE 1024

//以只写的方式打开管道2
int main(void)
{
    int fd = -1;
    void* addr = NULL;
    // 1.打开文件
    fd = open("test.txt",O_RDWR | O_CREAT,0644);
    if (-1 == fd)
    {
        perror("open");
        return 1;
    }
    printf("fd = %d\n",fd);

    // 2.将文件映射到内存
    addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if( addr == MAP_FAILED )
    {
        perror("mmap");
        return 1;
    }
    printf("文件存储映射 ok \n");
    // 3.关闭文件
    close(fd);

    // 4.读文件
    printf("addr:%s\n", (char*)addr);

    // 5.断开存储映射
    munmap(addr, 1024);
    return 0;
}

Makefile

all: read write


read:read.c
	gcc $< -o $@
write:write.c
	gcc $< -o $@

.PHONY:clean
clean:
	rm -rf read write

运行

# 创建文件
touch test.txt
echo "aaaaaaaaaaaaaaaa " > test.txt
# 编译
make
# 执行

匿名映射实现父子进程间通信

通过使用我们发现,使用映射区来完成文件读写操作十分方便,父子进程间通信也较容易。但缺陷是,每次创建映射区一定要依赖一个文件才能实现。
通常为了建立映射区要open一个temp文件,创建好了再unlink、close掉,比较麻烦。可以直接使用匿名映射来代替。
其实Linux系统给我们提供了创建匿名映射区的方法,无需依赖一个文件即可创建映射区。同样需要借助标志位参数flags来指定。
使用MAP_ANONYMOUS或(MAP_ANON)。

int *p = mmap(NULL4,PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,-1,0);
  • 4”随意举例,该位置表示映射区大小,可依实际需要填写
  • MAP_ANONYMOUS和MAP_ANON这两个宏是Linux操作系统特有的宏。在类Unix系统中如无该宏定义,可使用如下两步来完成匿名映射区的建立。

示例代码:

创建匿名内存映射区
int len = 4096;
void *ptr = mmap(NULL,len,PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,-1,0)
if (ptr == MAP_FAILED)
{
	perror("mmap error");
	exit(1);
}
// 创建子进程
pid_t pid = fork();
if (pid > 0)
{
	//父进程
	// 也可以用 memcpy(ptr,"1234567890", 10);
	wait(NULL);
	// 读数据
	printf("父进程:%s n"(char*)ptr);
}
else if (pid == 0)
{
	//子进程
	// 写数据
	strcpy((char*)ptr,"he11o mike!!");
}

// 释放内存映射区
int ret = munmap(ptr, len);
if (ret == -1)
{
	perror("munmaperror");
	exit(1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值