mmap函数参数讲解

1.mmap-创建内存映射

    作用:将磁盘文件的数据映射到内存,用户通过内存就能修改磁盘文件

    函数原型:

void *mmap{
	void *addr;	//映射区首地址,传NULL
	size_t length;	//映射区的大小
		//会自动调为4k的整数倍
		//不能为0
		//一般文件多大,length就指定多大
	int prot;	//映射区权限
		//PROT_READ 映射区比必须要有读权限
		//PROT_WRITE
		//PROT_READ | PROT_WRITE
	int flags;	//标志位参数
		//MAP_SHARED 修改了内存数据会同步到磁盘
		//MAP_PRIVATE 修改了内存数据不会同步到磁盘
	int fd;	//要映射的文件对应的fd
	off_t offset;  //映射文件的偏移量,从文件的哪里开始操作
		//映射的时候文件指针的偏移量
		//必须是4k的整数倍
		//一般设置为0

}

返回值:

映射区的首地址-调用成功

调用失败:MAP_FALED

2.munmap-释放内存映射区

函数原型:int munmap(void *addr,size_t length);

    addr-mmap的返回值

    length-mmap的第二个参数

3.注意事项

问:如果对mmap的返回值(ptr)做++操作(ptr++), munmap是否能够成功?
答:不能
问:如果open时O_RDONLY, mmap时prot参数指定PROT_READ | PROT_WRITE会怎样?
答mmap调用失败
open文件指定权限应该大于等于mmap第三个参数prot指定的权限
问:如果文件偏移量为1000会怎样?
答:必须是4096的整数倍
问:mmap什么情况下会调用失败?
第二个参数length = 0
第三个参数必须指定PROT_READ
fd对应的打开权限必须大于等于port权限
偏移量:必须是4096的整数倍
问:可以open的时候O_CREAT一个新文件来创建映射区吗?
答:可以,需要做文件拓展
lseek
truncate(path,length)
问:mmap后关闭文件描述符,对mmap映射有没有影响?
答:没有
问:对ptr越界操作会怎样?
答:段错误

4.mmap使用

(1)基本代码

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

int main(int argc, const char* argv[])
{
        //打开一个文件
        int fd=open("english.txt",O_RDWR);
        int len=lseek(fd,0,SEEK_END);

        //创建内存映射区
        void *ptr=mmap(NULL,len,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
        if(ptr==MAP_FAILED)
        {
                perror("mmap error");
                exit(1);
        }

        printf("%s",(char*)ptr);

        //释放内存映射区
        munmap(ptr,len);
        close(fd);
        return 0;
}

运行结果:

wangkai@wangkai-HP-242-G1-Notebook-PC:~/0110/6Day$ ./mmap 
sadaskdkasj
sdasdasd
asdasdsadasds
dasdas

(2)使用mmap进行有血缘关系的进程间通信

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

/*
利用mmap进行父子间通信,效率比文件I/O高,
数据不是从磁盘上读,而是从内存上读写

不阻塞,所以读之前一定要写好
*/
int main(int argc, const char* argv[])
{
        //打开一个文件
        int fd=open("english.txt",O_RDWR);
        int len=lseek(fd,0,SEEK_END);

        //创建内存映射区
        void *ptr=mmap(NULL,len,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
        if(ptr==MAP_FAILED)
        {
                perror("mmap error");
                exit(1);
        }
    
        close(fd);
        //创建子进程
        pid_t pid=fork();
        if(pid==-1)
        {
                perror("fork error");
                exit(1);
        }
        if(pid>0)
        {
                //写数据
                strcpy((char*)ptr,"我是你爸爸");
                //回收子进程
                wait(NULL);
        }
        else if(pid==0)
        {
                //读数据
                printf("%s\n",(char*)ptr);
        }
        printf("%s",(char*)ptr);

        //释放内存映射区
        munmap(ptr,len);

        return 0;
}

(3)创建匿名映射区

        int len=4096;
        //创建匿名内存映射区,不指定文件
        void *ptr=mmap(NULL,len,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANON,-1,0);

效果和(2)一样。

(4)没有血缘关系的进程间通信

写代码

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


int main(int argc, char *argv[])
{
	int fd = open("temp", O_RDWR | O_CREAT, 0664);

	void* ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if(ptr == MAP_FAILED)
	{
		perror("mmap");
		exit(1);
	}

	while(1)
	{
        char*p = (char*)ptr;
        p += 1024;
		strcpy(p, "hello parent, i am your 朋友!!!\n");
		sleep(2);
	}

	// 释放
	int ret = munmap(ptr, 8192);
	if(ret == -1)
	{
		perror("munmap");
		exit(1);
	}

	return 0;
}


读代码

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


int main(int argc, char *argv[])
{
	int fd = open("temp", O_RDWR | O_CREAT, 0664);
    ftruncate(fd, 4096);
    int len = lseek(fd, 0, SEEK_END);

	void* ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if(ptr == MAP_FAILED)
	{
		perror("mmap");
		exit(1);
	}

	while(1)
	{
		sleep(1);
		printf("%s\n", (char*)ptr+1024);
	}

	// 释放
	int ret = munmap(ptr, len);
	if(ret == -1)
	{
		perror("munmap");
		exit(1);
	}

	return 0;
}


小结

1. 进程间通信
    a. 有血缘关系的
父子进程共享内存映射区
    b. 没有血缘关系的进程间通信
i. 如何通信?
不能使用匿名映射的方式
只能借助磁盘文件创建映射区 - hello
不阻塞
ii. a(a.c)  b(b.c)
a.c
int fd = open("hello");
void* ptr = mmap(,,,,,fd, 0);
对映射区进行读写操作

b.c
int fd1 = open("hello");
void* ptr1 = mmap(,,,,fd1, 0);

对映射区做读写操作

2. mmap 实现内存映射
a. 必须有一个文件
b. 文件数据什么时候有用:
i. 单纯文件映射
ii. 进程间通信:
1) 文件数据是没有用的
3. 如果创建匿名映射区
mmap的时候:
    第二个参数:指定映射区大小
    第四个参数:需要添加MAP_ANON宏
    第五个参数:-1
4. 父子进程永远共享的东西?
文件描述符
内存映射区


猜你喜欢

转载自blog.csdn.net/wk_bjut_edu_cn/article/details/80467749