深入理解mmap函数

函数简介

在Linux操作系统中,进程是资源分配的基本单位。所以说每个进程间有各自独立的存储空间,但是在某些情况下各进程要相互配合来完成特定任务,这样就使得进程间通信变得非常必要。进程间通信方式有多种,当然这不是我们这节要讨论的重点,这节主要说的是mmap函数,它是通过将一块物理内存映射到多个进程的虚拟地址空间上,来完成多个进程对同一块物理内存的读写从而使得进程间能够实现通信。

先看一下mmap函数的函数原型,如下:

参数说明:
                  addr:映射区的开始地址,设置为NULL时表示由系统决定映射区的起始地址。
                  length:映射区的长度。//长度单位是 以字节为单位,不足一内存页按一内存页处理
                  prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起
                            PROT_READ //页内容可以被读取
                            PROT_WRITE //页可以被写入
                  flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体
                            MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。
                            MAP_PRIVATE //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。
                            MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联,简写为:MAP_ANON。
                  fd:有效的文件描述词。
                  offset:被映射对象内容的偏移量(4K的整数倍)。

返回值说明:
                  成功执行时,mmap()返回被映射区的指针,返回MAP_FAILED(其值为(void *)-1)。

程序案例

案例一:进程通过fork函数产生子进程时,我们知道父进程除了打开的文件描述符被子进程共享外,还有就是父进程通过调用mmap函数产生的映射地址空间被父子进程共享。看一个案例如下:

/**************************************
作   者 : lijd
生成日期 : 2021年02月06日
功能描述 : mmap函数案例测试
**************************************/ 
#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

int main(int argc,char **argv)
{
    int ret, inum = 0;
#if 0    // 该段用匿名映射区代替
    int fd = open("mytest.txt", O_CREAT|O_RDWR, 0644);
	
    if(fd < 0)
    {
	perror("open feild!");
	return -1;
    }
	
    ret = ftruncate(fd, 4);
    if(ret == -1)
    {
	perror("ftruncate feild!");
	return -1;
    }
    char *ptr = mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if(ptr == MAP_FAILED)
    {
	perror("mmap feild!");
	return -1;
    }
    close(fd);
#else
    char *ptr = mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
    if(ptr == MAP_FAILED)
    {
	perror("mmap feild!");
	return -1;
    }
#endif
    *(int *)ptr = 1234;
    printf("ptr : %d, inum : %d\n", *(int *)ptr, inum);
	
    pid_t pid = fork();
    if(pid == 0)	// son 
    {
	*(int *)ptr = 5678;
	inum = 6666;
	printf("ptr : %d, inum : %d\n", *(int *)ptr, inum);
    }

    if(pid > 0)	// father
    {
	wait(NULL);	
	printf("ptr : %d, inum : %d\n", *(int *)ptr, inum);

	ret = munmap(ptr, 4);
	if(ret == -1)
	{
		perror("munmap feild!");
		return -1;
	}
    }

    return 0;
}

执行结果如下:

分析上图的执行结果:刚开始指针ptr指向的内存空间调用到mmap函数申请到空间后,父进程使其存储的值为:1234,而变量inum的值为0,当fork创建子进程后,在子进程总将指针ptr执行的内容值修改为:5678,变量inum的值变为6666。当子进程执行完毕后父进程中的指针ptr中的内容变成了子进程修改后的值,而变量inum还是之前的0。因为父子进程共享mmap函数创建的内存,而当子进程修改inum变量时子进程使用了写时拷贝技术,为inum变量重新申请了内存存放修改后的新值。

上述案例是父子进程间通信,若非血缘关系的进程间通信则只需调用mmap映射同一个文件,这里就不再赘述。

案例二:我们思考一下,若两个进程通过打开同一文件能不能进行通信呢?看下面一个案例:

进程1代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

int main(int argc,char **argv)
{
    int ret = 0;
    char buf[256] = {0};
    char *str = "-----------------lijd 1111---------------------\n";
    int fd = open("mytest.txt", O_CREAT|O_RDWR|O_TRUNC, 0644);
    write(fd, str, strlen(str));
    printf("test1 write mytest.txt finish!\n");

    lseek(fd, 0, SEEK_SET);
    sleep(10);
	
    ret = read(fd, buf, sizeof(buf));

    printf("mytest.txt : %s\n", buf);
    close(fd);
	
    return 0;
}

进程2代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

int main(int argc,char **argv)
{
    int ret = 0;
    char buf[256] = {0};
    char *str = "-----------------lijd 2222---------------------\n";
    int fd = open("mytest.txt", O_RDWR);	
	
    ret = read(fd, buf, sizeof(buf));
    printf("mytest.txt : %s\n", buf);

    write(fd, str, strlen(str));
    printf("test2 write mytest.txt finish!\n");

    close(fd);
	
    return 0;
}

两个进程先执行进程1,然后在10秒内执行进程2;执行结果如下:

分析上述结构可知两个进程打开同一文件是可以实现进程间通信的。我们进一步用strace命令追踪进程的系统调用如下图:

用strace命令追踪可知进程1底层的系统调用也是通过调用mmap函数实现的内存映射进行进程间的数据共享,由此我们更加深入的理解mmap函数的运用。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值