内存映射的概念与使用

共享内存可以通过mmap()映射普通文件;

使一个磁盘文件与内存中的一个缓冲区相映射,进程可以像访问普通内存一样对文件进行访问,不必再调用read,write。

mmap()

mmap()的优点

实现了用户空间和内核空间的高效交互方式

函数定义:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

功能:创建共享内存映射

函数返回值:成功返回创建的映射区首地址,失败返回MAP_FAILED( ((void *) -1) ),设置errno值

参数说明:

addr:指定要映射的内存地址,一般设置为 NULL, 让操作系统自动选择合适的内存地址。

length:必须>0。映射地址空间的字节数,它从被映射文件开头 offset 个字节开始算起。

prot:指定共享内存的访问权限。可取如下几个值的可选:PROT_READ(可读), PROT_WRITE(可写), PROT_EXEC(可执行), PROT_NONE(不可访问)。

flags:由以下几个常值指定:MAP_SHARED(共享的) MAP_PRIVATE(私有的), MAP_FIXED(表示必须使用 start 参数作为开始地址,如果失败不进行修正),其中,MAP_SHARED , MAP_PRIVATE必选其一,而 MAP_FIXED 则不推荐使用。MAP_ANONYMOUS(匿名映射,用于血缘关系进程间通信)

fd:表示要映射的文件句柄。如果匿名映射写-1。

offset:表示映射文件的偏移量,一般设置为 0 表示从文件头部开始映射。

注意事项:

(1) 创建映射区的过程中,隐含着一次对映射文件的读操作,将文件内容读取到映射区。

(2) 当MAP_SHARED时,要求:映射区的权限应 <=文件打开的权限(出于对映射区的保护),如果不满足报非法参数(Invalid argument)错误。

当MAP_PRIVATE时候,mmap中的权限是对内存的限制,只需要文件有读权限即可,操作只在内存有效,不会写到物理磁盘,且不能在进程间共享。

(3) 映射区的释放与文件关闭无关,只要映射建立成功,文件可以立即关闭。

(4) 用于映射的文件大小(size_t length)必须>0,当映射文件大小为0时,指定非0大小创建映射区,访问映射地址会报总线错误,指定0大小创建映射区,报非法参数错误(Invalid argument)

(5) 文件偏移量必须为0或者4K的整数倍(不是会报非法参数Invalid argument错误).

(6)映射大小可以大于文件大小,但只能访问文件page的内存地址,否则报总线错误 ,超出映射的内存大小报段错误(要的多少给你,要的少多给你)

(7)mmap创建映射区出错概率非常高,一定要检查返回值,确保映射区建立成功再进行后续操作。

内存映射实现进程间通信

示例代码:

写文件:

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

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int main(){
    
    void *addr;
    int fd;
//打开文件
    fd =open("test",O_RDWR);
    if(fd<0){
        perror("open");
        return 0;
    }
//调整文件大小
    int len = lseek(fd,0,SEEK_END);  
//将test映射到共享内存中
    addr = mmap(NULL,2048, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 
    if(addr == MAP_FAILED){
        perror("mmap");
        return 0;
    }
    close(fd);
    int i=0;
    while(i<2048){
        memcpy((addr+i),"a",1);  //写入数据
        i++;
        sleep(1);
    }    
//读出数据
/*
	while(1){
   	 printf("read=%s\n",(char*)(addr));
	sleep(1);
	}
*/
}

读文件:

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

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int main(){
    
    void *addr;
    int fd;
//打开文件
    fd =open("test",O_RDWR);
    if(fd<0){
        perror("open");
        return 0;
    }
//调整文件大小
    int len = lseek(fd,0,SEEK_END);  
//将test映射到共享内存中
    addr = mmap(NULL,2048, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 
    if(addr == MAP_FAILED){
        perror("mmap");
        return 0;
    }
    close(fd);
    int i=0;
/* 
   while(i<2048){
        memcpy((addr+i),"a",1);  //写入数据
        i++;
        sleep(1);
    }
*/    
//读出数据

	while(1){
   	 printf("read=%s\n",(char*)(addr));
	sleep(1);
	}

}

mmap()映射的种类

1基于文件的映射

2 匿名映射

适用于具有亲缘关系的进程之间,不需要文件

示例代码:

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

int main(){

    void *addr;
    
    addr = mmap(NULL,2048, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
    if(addr == MAP_FAILED){
        perror("mmap");
        return 0;
    }
    pid_t pid;
    pid = fork();

    if(pid<0){
        perror("fork");
        return 0;
    }
    else if(pid>0){  //父进程
        memcpy(addr,"1234567890",10);
         
        wait(NULL);
    }else {          //子进程
        sleep(1);    //等待父进程拷贝完成
        printf("read father val=%s\n",(char *)addr);

    }
    
    munmap(addr,2048); //释放内存映射

}

注意:匿名映射(MAP_ANONYMOUS)要加:MAP_SHARED,不然父子进程间无法通讯

释放内存映射

munmap函数

int munmap(void *addr, size_t length);

返回值:成功返回0,失败返回-1,并设置errno值。

函数参数:

addr:调用mmap函数成功返回的映射区首地址

length:映射区大小(即:mmap函数的第二个参数)

System V共享内存

System V 属于System V IPC 的一种

System V IPC
IPC 对象包含: 共享内存、消息队列和信号灯集
每个IPC对象有唯一的ID 用Key关联
IPC对象创建后一直存在,直到被显式地删除
ipcs /ipcrm

共享内存可以读多次,管道只能读一次

IPC 的key

ftok函数->生成key

 key_t  ftok(const char *path,  int id); 

 头文件为#include <sys/ipc.h>

 其中参数path是指定的文件名,这个文件必须是存在的而且可以访问的。

 id是子序号,用于生成key的数字,它是一个8bit的整数。即范围是1~255。

 当函数执行成功,则会返回key_t键值,否则返回-1。

 在一般的UNIX中,通常是将文件的索引节点取出,然后在前面加上子序号就得到key_t的值

system V 共享内存使用步骤:

1.生成key

2.创建/打开共享内存

3.映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问

4.读写共享内存

5.撤销共享内存映射

6.删除共享内存对象

System V lPC-ftok-示例:

#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/shm.h>
#include <string.h>

int main(){
    key_t key;
    int shmid;
    char *buf;
    key = ftok("keytest",100);
    if(key<0){
        perror("ftok");
        return 0;
    }
    printf("key=%x\n",key);

    shmid = shmget(key,512,IPC_CREAT|0666);  //共享内存创建

    if(shmid<0){
        perror("shmget");
        return 0;
    }
    
    printf("shmid=%d\n",shmid);
    
    buf = shmat(shmid,NULL,0);
    if(buf<0){
        perror("shmat");
        return 0;
    }    
    strcpy(buf,"hello world");
    
}

查看共享内存命令ipcs

共享内存创建 – shmget

#include <sys/ipc.h>

#include <sys/shm.h>
int shmget(key_t key, int size, int shmflg);

>成功时返回共享内存的id,失败时返回EOF

>key 和共享内存关联的key,IPC PRIVATE 或 ftok生成

>shmflg 共享内存标志位 IPC_CREAT|0666

共享内存映射

#include <sys/ipc.h>

#include <sys/shm.h>

void  *shmat(int shmid, const void *shmaddr, int shmflg);

成功时返回映射后的地址,失败时返回(void*)-1

shmid     :要映射的共享内存id

shmaddr :映射后的地址,NULL表示由系统自动映射

shmflg    :标志位 0表示可读写;SHMRDONLY表示只读

第二个参数一般写NULL,表示自动映射

第三参数一般写0 ,表示可读写

共享内存撤销

 int  shmdt(void *shmaddr);

撤销后,内存地址不可再访问。

成功返回0 ,失败返回EOF;

不使用共享内存时硬撤销映射

进程结束后自动撤销 

共享内存控制

int  shmctl(int shmid, int cmd, struct shmid_ds *buf);

shmctl(shmid, IPC_RMID, NULL);删除共享内存

成功时返回0,失败时返回EOF

shmid 要操作的共享内存的id

cmd要执行的操作IPC_STAT IPC_SET IPC_RMID

buf 保存或设置共享内存属性的地址

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值