UNIXC001 文件操作 之 内存的映射 mmap

物理地址映射到虚拟地址空间

在这里插入图片描述

  • mmp 会在物理地址里找一块页框,然后在用虚拟地址空间选一块虚拟(线性地址)地址,然后将这两个内容分别填到页表里,就建立起来里他们之间的映射关系。
  • 用户态先通过虚拟地址找到页表,然后再找到对应的物理地址,然后在访问其中的内存,mmp这这个系统调用就是来建立这种关系的

文件映射到虚拟地址空间

在这里插入图片描述

  • 除了将物理地址映射到线性(虚拟)地址空间,还可以将文件映射到虚拟地址空间,那么用户在虚拟地址空间里(也就相当于用户在内存里面),直接对文件的操作可以同步到文件当中。这样就可以一次映射一大块文件,不用再使用read和write这样的系统调用。节省了系统的开销。
  • 建立mmp映射时候,如果不对建立的这块虚拟内存进访问,是不会把磁盘的内容加载到物理地址空间。只要访问时,才会把文件块的内容加载到物理地址的页框当中。
  • 包括加载共享库,也是用的mmp机制。

mmp

在这里插入图片描述

  • mmp 返回一个地址,void 的意思就是不对返回地址对应的地址空间进行访问,需要访问时再用不同的访问方式去访问这块内存。(将void*改成int *, char *…再使用)
  • port 指定映射区域的访问权限,虚拟地址映射到物理地址是以页(Page)为单位的,通常一页为4K。前三个可以进行或运算,PORT_NONE只能单独使用(对页不进行任何访问)

在这里插入图片描述

  • MAP_SHARED: 可以把一块物理内存映射到两个进程的不同的虚拟地址空间当中,这时一个进程对映射的虚拟地址空间中的内容进行修改,那在另一个进程中也可见。文件映射时,修改也会同步到文件当中。
  • MAP_ANONYMOUS 把物理地址映射到虚拟地址空间,但是这个物理地址是没有名字的。
  • offset 是4k的整数倍

munmap

在这里插入图片描述

代码示例

1. 将一块物理地址映射到进程的虚拟地址空间

t_stdio.h

#ifndef T_STDIO_H_
#define T_STDIO_H_
#include <stdio.h>
#define E_MSG(STRING, VAL) do{perror(STRING); return(VAL);}while(0)
#endif

mmap.c

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

int main(void){
    // 权限是可读可写
    int prot = PROT_READ | PROT_WRITE;
    // 不需要同步到底层文件, 所以采用私有。
    // 并且是物理地址映射到虚拟地址空间, 所以采用匿名
    int flags = MAP_PRIVATE | MAP_ANONYMOUS;
    // 将物理地址映射到进程的虚拟地址空间
    // NULL让内核去选择一块虚拟空间
    // 指定映射区域长度1024, 那么munmap的映射区域长度也就是1024
    // 映射区域可读可写
    // flags 映射方式 私有加匿名
    // 因为是匿名映射fd=-1, offset=0
    void *p  = mmap(NULL, 1024, prot, flags, -1, 0);
    if (p == MAP_FAILED) E_MSG("mmap", -1);
    // 到这里已经将物理地址映射到了进程的虚拟地址空间
    // 再这里去使用p就不会出现段错误了,因为*p这个虚拟地址有对应的物理地址
    // strcpy 可以给void* 类型的地址空间里拷贝数据吗? 但是void类型的指针是不能直接使用的。好好想想
    strcpy(p, "hello beijing");
    printf("%s\n", (char *)p);

    // 解除映射
    munmap(p, 1024);
    return 0;
}


```shell
$ gcc mmap.c 
$ ./a.out 
hello beijing

2. 将一个文件映射到进程的虚拟地址空间(内存), 对内存中的内容进行修改,直接反映到文件中

t_file.h

#ifndef T_FILE_H
#define T_FILE_H

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

#endif

mmap_file.c

#include "t_file.h"
#include "t_stdio.h"
#include <sys/mman.h>
int main(int argc, char *argv[]) {
    // 以读写的方式打开文件
    int fd=open(argv[1], O_RDWR);
    if(fd==-1)E_MSG("open", -1);

    // 权限和上面的文件打开方式保持一致
    int prot = PROT_READ | PROT_WRITE;
    // 映射方式选择MAP_SHARED
    int flags = MAP_SHARED;
    // 建立文件到虚拟地址的映射
    // 映射时不要超过文件的长度, 不然会错误
    void *p = mmap(NULL, 6, prot, flags, fd, 0);
    if (p==MAP_FAILED)E_MSG("mmap", -1);
    
    // 关闭文件描述符, 不影响已经建立的映射区域
    close(fd);
    //映射已经建立
    // void * 类型指针不能直接使用,转为int * 类型再使用
    // 前面的0x前缀表示十六进制。十六进制下每2位恰好为二进制下的8位即8bit。我们把8bit定义为1byte,所以十六进制数每2位即是1byte。
    // 0x30313233 是16进制的整数,4个字节, 以整型的方式去修改进程虚拟地址空间
    // 0x31 -> 0011 0001 -> 是整数49 -> ASSIC中 字符'1' 对应 整数49 
    // 字符在内存中以整数的方式保存
    *((int *)p) = 0x30313233;

    // 解除映射
    munmap(p,6);
    return 0;
}

// 查看文件内容
// 上面是accic码
$ touch hello; echo hello > hello
$ gcc mmap_file.c 
 
## od -tx1 -tc 查看文件内容
## 下面是字符, 上面是assic码
## 实际上 hello 字符会被转换成assic码( 68  65  6c  6c  6f ) 存放在文件当中
$ od -tx1 -tc hello
0000000  68  65  6c  6c  6f  0a
          h   e   l   l   o  \n
0000006
$ a.out hello
## 以整型的方式去修改, 一次修改4个字节
$ od -tx1 -tc hello
0000000  33  32  31  30  6f  0a
          3   2   1   0   o  \n
0000006
## 如果 *((int *)p) = 0x3031;
## 还是修改4个字节
$ od -tx1 -tc hello
0000000  31  30  00  00  6f  0a
          1   0  \0  \0   o  \n
0000006
# 30 在高地址,属于高位字节放在高地址中,是小端

补充知识(字节序)

在这里插入图片描述

  • short val = 0x0001; 十六进制数每2位即是1byte, 短整型val占两个字节, 00是高位字节, 01是低位字节, 内存增长方向为: 左边是低地址,右边是高地址。
  • 大端:如果把00高位字节放到低地址当中, 01低位字节就必须放到高地址
  • 小端:如果把00高位字节放到高地址中,01低位字节就必须放到低地址中

代码示例

s_b.c

#include <stdio.h>
//short类型占两个字节, char类型占一个字节
// 所以这个联合类型的变量占两个字节,  c 和 v的地址是一样的
typedef union {
    short v; 
    char c; 
}u_t;

int main(void){
    // 把0x0001存到v里,查看c里是00还是01, 如果01说明是小端(低位字节放低地址)
    u_t t;
    t.v = 0x0001;
    if(t.c)
        printf("small\n");
    else
        printf("big\n");
    return 0;
}
$ gcc s_m.c 
$ ./a.out 
small
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值