mmap、munmap

一、函数声明

#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
/*
参数:
addr:	建立映射区的首地址,由linux内核指定。使用时,直接传递NULL
length:	欲创建映射区的大小
prot:	映射区权限PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
flags:	标志位参数
		MAP_SHARED:会将映射区所做的操作反映到物理磁盘上。
		MAP_PRIVATE:映射区所做的修改不会反映到物理设备。
fd:		用来建立映射区的文件描述符
offset:	映射文件的偏移(4K的整数倍,不可传4095、4098等)
*/

/*
返回值:
成功:返回创建的映射区首地址
失败:MAP_FAILED宏
*/


int munmap(void *addr, size_t length);
/*
参数:
addr:	mmap返回的地址
length:	mmap创建映射区的大小
*/

/*
返回值:
成功: 0
失败: -1, and errno is set
*/

二、借助共享内存访问磁盘文件

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h> //mmap munmap
#include <fcntl.h>
#include <stdlib.h>

int main()
{
    	// 打开文件
        int fd = open("./my_file.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
        if (fd == -1)
        {
                perror("open file failed.");
                exit(EXIT_FAILURE);
        }

    	// 设定文件大小
        unsigned fileSize = 64;
        int ret = ftruncate(fd, fileSize);
        if (ret == -1)
        {
                perror("open file failed.");
                exit(EXIT_FAILURE);
        }

    	// 建立内存映射
        char *p = (char *)mmap(NULL, fileSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if (p == MAP_FAILED)
        {
                perror("mmap failed.");
                exit(EXIT_FAILURE);
        }
    
   	    // 往文件中写入信息
        strcpy(p, "hello, world!\n");
        munmap(p, fileSize);
        close(fd);

        return 0;
}

三、mmap使用注意事项

1、open文件的时候,可以O_CREAT一个新文件来创建映射区吗?
  可以,但是需要设定文件的大小,不然新创建的文件大小为0,mmap创建映射区的大小为能0。

2、如果参返回的地址进行addr++操作,mumap可否成功?
  不能,释放映射区的时候,需要传入创建映射区的返回地址,映射区大小,必须保持一致。

3、如果open时用O_RDONLY,而mmap时用了PROT_READ|PROT_WRITE会怎样?
  当MAP_SHARED时,要求创建映射区的权限要小于等于打开文件的权限(出于对映射区的保护)。
  而MAP_PRIVATE则没有要求,因为mmap中的权限是对内存进行限制。
  映射区的创建过程当中隐含一次对文件的读操作。

4、如果文件偏移量设置成1000会怎样?
  最后一个offset的大小必须是4K的整数倍(一内存页大小), mmu在映射的过程当中,单位是4K

5、如果不检测mmap的返回值,会怎样?
  mmap创建映射区出错的概率非常高,一定要检查返回值,确保映射区建立成功再进行后续操作。

6、mmap什么情况下会调用失败?
  映射区大小不能大于文件大小。

7、对addr进程越界操作会怎样?
  会core。

8、文件描述符先关闭,对mmap映射有没有影响?
  不影响。所谓的文件描述符只是用来操作文件的句柄,mmap操作文件的方式是通过地址来操作文件,一旦映射区创建成功,文件描述符已经没有作用了,可以关闭。


四、mmap用于父子进程间通信

  父子等有血缘关系的进程之间也可以通过mmap建立的映射区来完成数据通信。但相应的要创建映射区的时候指定对应的标志位参数flags:

  MAP_PRIVATE:(私有映射) 父子进程各自独占映射区;
  MAP_SHARED:(共享映射) 父子进程共享映射区;

#include <sys/mman.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>


int main()
{
        int v = 100;
		
    	// 创建临时文件
        int fd = open("./tmp", O_RDWR | O_CREAT |O_TRUNC, 0644);
        if (fd == -1)
        {
                perror("open file failed.");
                exit(EXIT_FAILURE);
        }

        unlink("./tmp");      // 删除临时文件目录项,使之具备被释放的条件。当进程不再占用就删除文件。
        ftruncate(fd, 4);

        int *p = (int *)mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    
    	//父子进程不共享映射区
    	//int *p = (int *)mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); 
    	
        if (p == MAP_FAILED)
        {
                perror("mmap failed.");
                exit(EXIT_FAILURE);
        }
        close(fd);

        pid_t pid = fork();
        if (pid == 0)
        {
                v = 1000;
                *p = 1000;
                printf("child v=%d, *p=%d\n", v, *p);
        }
        else if (pid > 0)
        {
                sleep(1);
                printf("parent v=%d, *p=%d\n", v, *p);
                wait(NULL); 

                int ret = munmap(p, 4);
                if (ret == -1)
                {
                        perror("munmap failed.");
                        exit(EXIT_FAILURE);
                }
        }

        return 0;
}

结论:

父子进程共享:
  1、打开的文件
  2、mmap建立的映射区(但必须要使用MAP_SHARED)


五、匿名映射区

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


使用 MAP_ANONYMOUS(或MAP_ANON),如:

int *p = (int *)mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
// 4是随意举例,表示大小
// fd == -1, 不需要传文件描述符
// flags == MAP_SHARED|MAP_ANONYMOUS

  需要注意的是,MAP_ANONYMOUSMAP_ANON这两个宏是linux操作系统特有的宏。在类Unix系统中如无该宏定义,可以使用如下两步来完成匿名映射区的建立。

fd = open("/dev/zero", O_RDWR);

P = mmap(NULL, size, PROT_READ|PROT_WRITE, MMAP_SHARED, fd, 0);


示例:

#include <sys/mman.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
        int v = 100;

        int *p = (int *)mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED |MAP_ANONYMOUS, -1, 0);
        if (p == MAP_FAILED)
        {
                perror("mmap failed.");
                exit(EXIT_FAILURE);
        }

        pid_t pid = fork();
        if (pid == 0)
        {
                v = 1000;
                *p = 1000;
                printf("child v=%d, *p=%d\n", v, *p);
        }
        else if (pid > 0)
        {
                sleep(1);
                printf("parent v=%d, *p=%d\n", v, *p);
                wait(NULL); 

                int ret = munmap(p, 4);
                if (ret == -1)
                {
                        perror("munmap failed.");
                        exit(EXIT_FAILURE);
                }
        }

        return 0;
}

六、mmap用于非血缘关系进程间通信

mmap_write.c

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


struct STU {
        int id;
        char name[20];
        char sex;
};

void sys_err(const char *str)
{
        perror(str);
        exit(EXIT_FAILURE);
}

int main(int argc, char **argv)
{
        int fd;
        struct STU stu = {10, "xiaoming", 'm'};
        struct STU *p;

        if (argc < 2) sys_err("./a.out file_shared\n");

        fd = open(argv[1], O_RDWR|O_CREAT, 0644);
        if (fd == -1) sys_err("open error");
    
    	//	创建文件成功后,一定要记得设置文件大小,否则会报Bus error
        ftruncate(fd, sizeof(stu));

        p = (struct STU *)mmap(NULL, sizeof(stu), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        if (p == MAP_FAILED) sys_err("mmap error");

        close(fd);

    	// 第隔一秒,更新mmap中的内容。
        while (1)
        {
                memcpy(p, &stu, sizeof(stu));
                stu.id++;
                sleep(1);
        }

        munmap(p, sizeof(stu));

        return 0;
}

mmap_read.c

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


struct STU {
        int id;
        char name[20];
        char sex;
};

void sys_err(const char *str)
{
        perror(str);
        exit(EXIT_FAILURE);
}

int main(int argc, char **argv)
{
        int fd;
        struct STU stu;
        struct STU *p;

        if (argc < 2) sys_err("./a.out file_shared\n");

        fd = open(argv[1], O_RDONLY);
        if (fd == -1) sys_err("open error");

        p = (struct STU *)mmap(NULL, sizeof(stu), PROT_READ, MAP_SHARED, fd, 0);
        if (p == MAP_FAILED) sys_err("mmap error");

        close(fd);

    	// 每隔两秒,读取mmap中的内容。
        while (1)
        {
                printf("stu.id = %d\t stu.name=%s\t stu.sex = %c\n", p->id, p->name, p->sex);
                sleep(2);
        }

        munmap(p, sizeof(stu));

        return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值