搜档网
当前位置:搜档网 › 内存映射和普通文件访问的区别

内存映射和普通文件访问的区别

内存映射和普通文件访问的区别
内存映射和普通文件访问的区别

在讲述文件映射的概念时, 不可避免的要牵涉到虚存(SVR 4的VM). 实际上, 文件映射是虚存的中心概念, 文件映射一方面给用户提供了一组措施, 好似用户将文件映射到自己地址空间的某个部分, 使用简单的内存访问指令读写文件;另一方面, 它也可以用于内核的基本组织模式, 在这种模式种, 内核将整个地址空间视为诸如文件之类的一组不同对象的映射. 中的传统文件访问方式是, 首先用open系统调用打开文件, 然后使用read, write以及lseek等调用进行顺序或者随即的I/O. 这种方式是非常低效的, 每一次I/O操作都需要一次系统调用. 另外, 如果若干个进程访问同一个文件, 每个进程都要在自己的地址空间维护一个副本, 浪费了内存空间. 而如果能够通过一定的机制将页面映射到进程的地址空间中, 也就是说首先通过简单的产生某些内存管理数据结构完成映射的创建. 当进程访问页面时产生一个缺页中断, 内核将页面读入内存并且更新页表指向该页面. 而且这种方式非常方便于同一副本的共享.

VM是面向对象的方法设计的, 这里的对象是指内存对象: 内存对象是一个软件抽象的概念, 它描述内存区与后备存储之间的映射. 系统可以使用多种类型的后备存储, 比如交换空间, 本地或者远程文件以及帧缓存等等. VM系统对它们统一处理, 采用同一操作集操作, 比如读取页面或者回写页面等. 每种不同的后备存储都可以用不同的方法实现这些操作. 这样, 系统定义了一套统一的接口, 每种后备存储给出自己的实现方法. 这样, 进程的地址空间就被视为一组映射到不同数据对象上的的映射组成. 所有的有效地址就是那些映射到数据对象上的地址. 这些对象为映射它的页面提供了持久性的后备存储. 映射使得用户可以直接寻址这些对象.

值得提出的是, VM体系结构独立于Unix系统, 所有的Unix系统语义, 如正文, 数据及堆栈区都可以建构在基本VM系统之上. 同时, VM体系结构也是独立于存储管理的, 存储管理是由操作系统实施的, 如: 究竟采取什么样的对换和请求调页算法, 究竟是采取分段还是分页机制进行存储管理, 究竟是如何将虚拟地址转换成为物理地址等等(Linux中是一种叫Three Level Page Table的机制), 这些都与内存对象的概念无关.

下面介绍Linux中 VM的实现.

一个进程应该包括一个mm_struct(memory manage struct), 该结构是进程虚拟地址空间的抽象描述, 里面包括了进程虚拟空间的一些管理信息: start_code, end_code, start_data, end_data, start_brk, end_brk等等信息. 另外, 也有一个指向进程虚存区表(vm_area_struct: virtual memory area)的指针, 该链是按照虚拟地址的增长顺序排列的. 在Linux进程的地址空间被分作许多区(vma), 每个区(vma)都对应虚拟地址空间上一段连续的区域, vma是可以被共享和保护的独立实体, 这里的vma就是前面提到的内存对象. 下面是vm_area_struct的结构, 其中, 前半部分是公共的, 与类型无关的一些数据成员, 如: 指向mm_struct的指针, 地址范围等等, 后半部分则是与类型相关的成员, 其中最重要的是一个指向vm_operation_struct向量表的指针

vm_ops, vm_pos向量表是一组虚函数, 定义了与vma类型无关的接口. 每一个特定的子类, 即每种vma类型都必须在向量表中实现这些操作. 这里包括了: open, close, unmap, protect, sync, nopage, wppage, swapout这些操作.

1.struct vm_area_struct {

2./*公共的, 与vma类型无关的 */

3.struct mm_struct * vm_mm;

4.unsigned long vm_start;

5.unsigned long vm_end;

6.struct vm_area_struct *vm_next;

7.pgprot_t vm_page_prot;

8.unsigned long vm_flags;

9.short vm_avl_height;

10.struct vm_area_struct * vm_avl_left;

11.struct vm_area_struct * vm_avl_right;

12.struct vm_area_struct *vm_next_share;

13.struct vm_area_struct **vm_pprev_share;

14.

15./* 与类型相关的 */

16.struct vm_operations_struct * vm_ops;

17.unsigned long vm_pgoff;

18.struct file * vm_file;

19.unsigned long vm_raend;

20.void * vm_private_data;

21.};

vm_ops: open, close, no_page, swapin, swapout……

介绍完VM的基本概念后, 我们可以讲述mmap和munmap系统调用了. mmap调用实际上就是一个内存对象vma的创建过程, mmap的调用格式是:

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

其中start是映射地址, length是映射长度, 如果flags的MAP_FIXED不被置位, 则该参数通常被忽略, 而查找进程地址空间中第一个长度符合的空闲区域;Fd是映射文件的文件句柄, offset是映射文件中的偏移地址;prot是映射保护权限, 可以是PROT_EXEC, PROT_READ, PROT_WRITE, PROT_NONE, flags则是指映射类型, 可以是MAP_FIXED, MAP_PRIVATE, MAP_SHARED, 该参数必须被指定为MAP_PRIVATE和

MAP_SHARED其中之一, MAP_PRIVATE 是创建一个写时拷贝映射(copy-on-write), 也就是说如果有多个进程同时映射到一个文件上, 映射建立时只是共享同样的存储页面, 但是某进程企图修改页面内容, 则复制一个副本给该进程私用, 它的任何修改对其它进程都不可见. 而MAP_SHARED则无论修改与否都使用同一副本, 任何进程对页面的修改对其它进程都是可见的.

mmap系统调用的实现过程是:

1.先通过文件系统定位要映射的文件;

2.权限检查, 映射的权限不会超过文件打开的方式, 也就是说如果文件是以只读方式打开, 那么则不允许建立一个可写映射;

3.创建一个vma对象, 并对之进行初始化;

4.调用映射文件的mmap函数, 其主要工作是给vm_ops向量表赋值;

5.把该vma链入该进程的vma链表中, 如果可以和前后的vma合并则合并;

6.如果是要求VM_LOCKED(映射区不被换出)方式映射, 则发出缺页请求, 把映射

页面读入内存中.

munmap(void * start, size_t length):

该调用可以看作是 mmap的一个逆过程. 它将进程中从start开始length长度的

一段区域的映射关闭, 如果该区域不是恰好对应一个vma, 则有可能会分割几个或几

个vma.

msync(void * start, size_t length, int flags):

把映射区域的修改回写到后备存储中. 因为munmap时并不保证页面回写, 如果不调用msync, 那么有可能在munmap后丢失对映射区的修改. 其中flags可以是MS_SYNC, MS_ASYNC, MS_INVALIDATE, MS_SYNC要求回写完成后才返回, MS_ASYNC发出回写请求后立即返回, MS_INVALIDATE使用回写的内容更新该文件的其它映射. 该系统调用是通过调用映射文件的sync函数来完成工作的.

brk(void * end_data_segement):

将进程的数据段扩展到 end_data_segement指定的地址, 该系统调用和mmap的实现方式十分相似, 同样是产生一个vma, 然后指定其属性. 不过在此之前需要做一些

合法性检查, 比如该地址是否大于mm->end_code, end_data_segement和mm->brk之间是否还存在其它vma等等. 通过brk产生的vma映射的文件为空, 这和匿名映射产生的vma相似, 关于匿名映射不做进一步介绍. 库函数malloc就是通过brk实现的.

Linux提供了内存映射函数mmap, 它把文件内容映射到一段内存上(准确说是虚拟内存上), 通过对这段内存的读取和修改, 实现对文件的读取和修改, 先来看一下mmap的函数声明:

头文件:

原型: void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offsize);

返回值: 成功则返回映射区起始地址, 失败则返回MAP_FAILED(-1).

参数:

addr: 指定映射的起始地址, 通常设为NULL, 由系统指定.

length: 将文件的多大长度映射到内存.

prot: 映射区的保护方式, 可以是:

PROT_EXEC: 映射区可被执行.

PROT_READ: 映射区可被读取.

PROT_WRITE: 映射区可被写入.

PROT_NONE: 映射区不能存取.

flags: 映射区的特性, 可以是:

MAP_SHARED: 对映射区域的写入数据会复制回文件, 且允许其他映射该文件的进程共享.

MAP_PRIVATE: 对映射区域的写入操作会产生一个映射的复制(copy-on-write), 对此区域所做的修改不会写回原文件.

此外还有其他几个flags不很常用, 具体查看linux C函数说明.

fd: 由open返回的文件描述符, 代表要映射的文件.

offset: 以文件开始处的偏移量, 必须是分页大小的整数倍, 通常为0, 表示从文件头开始映射.

下面说一下内存映射的步骤:

用open系统调用打开文件, 并返回描述符fd.

用mmap建立内存映射, 并返回映射首地址指针start.

对映射(文件)进行各种操作, 显示(printf), 修改(sprintf).

用munmap(void *start, size_t lenght)关闭内存映射.

用close系统调用关闭文件fd.

注意事项:

在修改映射的文件时, 只能在原长度上修改, 不能增加文件长度, 因为内存是已经分配好的.

Linux-mmap函数介绍

mmap函数是unix/linux下的系统调用,来看《Unix Netword programming》卷二12.2节对mmap的介绍:

The mmap function maps either a file or a Posix shared memory object into the address space of a process.We use this function for three purposes:

1. with a regular file to provide memory-mapped I/O

2. with special files to provide anonymous memory mappings

3. with shm_open to provide Posix shared memory between unrelated processes

mmap系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而 Posix或系统V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一。

mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。

我们的程序中大量运用了mmap,用到的正是mmap的这种“像访问普通内存一样对文件进行访问”的功能。实践证明,当要对一个文件频繁的进行访问,并且指针来回移动时,调用mmap比用常规的方法快很多。

来看看mmap的定义:

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

参数fd为即将映射到进程空间的文件描述字,一般由open()返回,同时,fd可以指定为-1,此时须指定flags参数中的MAP_ANON,表明进行的是匿名映射(不涉及具体的文件名,避免了文件的创建及打开,很显然只能用于具有亲缘关系的进程间通信)。

len是映射到调用进程地址空间的字节数,它从被映射文件开头offset个字节开始算起。

prot参数指定共享内存的访问权限。可取如下几个值的或:PROT_READ(可

读),PROT_WRITE(可写),PROT_EXEC(可执行),PROT_NONE(不可访问)。

flags由以下几个常值指定:MAP_SHARED, MAP_PRIVATE, MAP_FIXED。其中,MAP_SHARED,MAP_PRIVATE必选其一,而MAP_FIXED则不推荐使用。

如果指定为MAP_SHARED,则对映射的内存所做的修改同样影响到文件。如果是MAP_PRIVATE,则对映射的内存所做的修改仅对该进程可见,对文件没有影响。

offset参数一般设为0,表示从文件头开始映射。

参数addr指定文件应被映射到进程空间的起始地址,一般被指定一个空指针,此时选择起始地址的任务留给内核来完成。函数的返回值为最后文件映射到进程空间的地址,进程可直接操作起始地址为该值的有效地址。

最后,举个例子来结束本节。4.2节说过,Fileinformation数组是以二进制的形式写进一个叫inforindex的文件中。那么,当要访问 Fileinformation数组时,代码类似这样:

1.struct stat st;

2.char buffer=”inforindex”;

3.Fileinformation *_fileinfoIndexptr = NULL;

4.if(stat(buffer,&st)<0)

5.{

6.fprintf(stderr,"error to stat %s\n",buffer);

7.exit(-1);

8.}

9.// mmap the inforindex to _fileinfoIndexptr

10.int fd=open(buffer, O_RDONLY);

11.if(fd<0)

12.{

13.printf("error to open %s\n",buffer);

14.exit(-1);

15.}

16.

17._fileinfoIndexptr=(Fileinformation*)mmap(NULL,st.st_size, PROT_READ

,MAP_SHARED,fd,0);

18.if(MAP_FAILED == _fileinfoIndexptr)

19.{

20.printf("error to mmap %s\n",buffer);

21.close(fd);

22.exit(-1);

23.}

24.close(fd);

下面这个例子显示了把文件映射到内存的方法

源代码是:

1./************关于本文档

********************************************

2.*filename: mmap.c

3.*purpose: 说明调用mmap把文件映射到内存的方法

4.*wrote by: zhoulifa(zhoulifa@https://www.sodocs.net/doc/2b6529880.html,) 周立发

(https://www.sodocs.net/doc/2b6529880.html,)

5.Linux爱好者 Linux知识传播者 SOHO族开发者最擅长C语言

6.*date time:2008-01-27 18:59 上海大雪天,据说是多年不遇

7.*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途

8.* 但请遵循GPL

9.*Thanks to:

10.* Ubuntu 本程序在Ubuntu 7.10系统上测试完全正常

11.* https://www.sodocs.net/doc/2b6529880.html, 我通常通过google搜索发现许多有用的资料

12.*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力

13.* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!

14.*******************************************************************

**/

15.

16.#include /* for mmap and munmap */

17.#include /* for open */

18.#include /* for open */

19.#include /* for open */

20.#include /* for lseek and write */

21.#include

22.

23.int main(int argc, char **argv)

24.{

25.int fd;

26.char *mapped_mem, * p;

27.int flength = 1024;

28.void * start_addr = 0;

29.fd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);

30.flength = lseek(fd, 1, SEEK_END);

31.write(fd, "\0", 1); /* 在文件最后添加一个空字符,以便下面printf正常

工作 */

32.lseek(fd, 0, SEEK_SET);

33.mapped_mem = mmap(start_addr, flength, PROT_READ, //允许读

34. MAP_PRIVATE, //不允许其它进程访问此内存区域

35. fd, 0);

36.

37./* 使用映射区域. */

38.printf("%s\n", mapped_mem); /* 为了保证这里工作正常,参数传递的文件

名最好是一个文本文件 */

39.close(fd);

40.munmap(mapped_mem, flength);

41.return 0;

42.}

编译运行此程序:

gcc -Wall mmap.c

./a.out text_filename

上面的方法因为用了PROT_READ,所以只能读取文件里的内容,不能修改,如果换成PROT_WRITE就可以修改文件的内容了。又由于用了MAAP_PRIVATE所以只能此进程使用此内存区域,如果换成MAP_SHARED,则可以被其它进程访问,比如下面的:

1.#include /* for mmap and munmap */

2.#include /* for open */

3.#include /* for open */

4.#include /* for open */

5.#include /* for lseek and write */

6.#include

7.#include /* for memcpy */

8.

9.int main(int argc, char **argv)

10.{

11.int fd;

12.char *mapped_mem, * p;

13.int flength = 1024;

14.void * start_addr = 0;

15.fd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);

16.flength = lseek(fd, 1, SEEK_END);

17.write(fd, "\0", 1); /* 在文件最后添加一个空字符,以便下面printf正常

工作 */

18.

19.lseek(fd, 0, SEEK_SET);

20.start_addr = 0x80000;

21.mapped_mem = mmap(start_addr, flength, PROT_READ|PROT_WRITE,//允许

写入

22.MAP_SHARED, //允许其它进程访问此内存区域

23.fd, 0);

24.

25./* 使用映射区域. */

26.printf("%s\n", mapped_mem);

27./* 为了保证这里工作正常,参数传递的文件名最好是一个文本文 */

28.while((p = strstr(mapped_mem, "Hello"))) { /* 此处来修改文件内

容 */

29.memcpy(p, "Linux", 5);

30.p += 5;

31.}

32.close(fd);

33.munmap(mapped_mem, flength);

34.return 0;

35.}

关于VB内存映射文件的使用

VB内存映射文件的使用 引言 文件操作是应用程序最为基本的功能之一,Win32 API和MFC均提供有支持文件处理的函数和类,常用的有Win32 API的CreateFile()、WriteFile()、ReadFile()和MFC提供的CFile类等。一般来说,以上这些函数可以满足大多数场合的要求,但是对于某些特殊应用领域所需要的动辄几十GB、几百GB、乃至几TB的海量存储,再以通常的文件处理方法进行处理显然是行不通的。目前,对于上述这种大文件的操作一般是以内存映射文件的方式来加以处理的,本文下面将针对这种Windows核心编程技术展开讨论。 内存映射文件 内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法。 内存映射文件并不是简单的文件I/O操作,实际用到了Windows的核心编程技术--内存管理。所以,如果想对内存映射文件有更深刻的认识,必须对Windows操作系统的内存管理机制有清楚的认识,内存管理的相关知识非常复杂,超出了本文的讨论范畴,在此就不再赘述,感兴趣的读者可以参阅其他相关书籍。 内存映射文件使用方法 1) 首先要通过CreateFile()函数来创建或打开一个文件内核对象,这个对象标识了磁盘上将要用作内 存映射文件的文件。 2)在用CreateFile()将文件映像在物理存储器的位置通告给操作系统后,只指定了映像文件的路径, 映像的长度还没有指定。为了指定文件映射对象需要多大的物理存储空间还需要通过 CreateFileMapping()函数来创建一个文件映射内核对象以告诉系统文件的尺寸以及访问文件的方式。 3)在创建了文件映射对象后,还必须为文件数据保留一个地址空间区域,并把文件数据作为映射到该 区域的物理存储器进行提交。由MapViewOfFile()函数负责通过系统的管理而将文件映射对象的全部或部分映射到进程地址空间。此时,对内存映射文件的使用和处理同通常加载到内存中的文件数据的处理方式基本一样。 4)在完成了对内存映射文件的使用时,还要通过一系列的操作完成对其的清除和使用过资源的释放。 这部分相对比较简单,可以通过UnmapViewOfFile()完成从进程的地址空间撤消文件数据的映像、通过CloseHandle()关闭前面创建的文件映射对象和文件对象。 内存映射文件相关函数 在使用内存映射文件时,所使用的API函数主要就是前面提到过的那几个函数,下面分别对其进行介绍:

主板芯片和内存映射

astrotycoon 大道至简,贵在恒久力行

Diagram for modern motherboard. The northbridge and southbridge make up the chipset.

(补充: 北桥芯片用于与CPU、内存和AGP视频接口,这些接口具有很高的传输速率。北桥芯片还起着存储器控制作用,因此Intel把该芯片标号为MCH(Memory Controller Hub)芯片。南桥芯片用来管理低、中速的组件,例如,PCI总线、IDE硬盘接口、USB端口等,因此南桥芯片的名称为ICH(I/O Controller Hub)) As you look at this, the crucial thing to keep in mind is that the CPU doesn’t really know anything about what it’s connected to. It talks to the outside world through its pins bu t it doesn’t care what that outside world is. It might be a motherboard in a computer but it could be a toaster, network router, brain implant, or CPU test bench. There are thre e main ways by which the CPU and the outside communicate: memory address space, I/O address space, and interrupts. We only worry about motherboards and memory for now. 正如你所看到的,其实CPU是完全不知道自己与哪些外部器件相连接的。 CPU仅仅通过自己的引脚与外界沟通,而它并不关心自己是与什么设备在沟通。或许是另一台计算机的主板,或许是烤面包机,网络路由器,脑植入医疗设备,又或许是CPU测试仪。 CPU主要通过三种方式与外界通信:内存地址空间,IO地址空间,和中断。我们目前只关注主板和内存。 In a motherboard the CPU’s gateway to the world is the front-side bus connecting it to the northbridge. Whenever the CPU needs to read or write memory it does so via this b us. It uses some pins to transmit the physical memory address it wants to write or read, while other pins send the value to be written or receive the value being read. An Intel Core 2 QX6600 has 33 pins to transmit the physical memory address (so there are 233 choices of memory locations) and 64 pins to send or receive data (so data is transmitte d in a 64-bit data path, or 8-byte chunks). This allows the CPU to physically address 64 gigabytes of memory (233 locations * 8 bytes) although most chipsets only handle up to 8 gigs of RAM. CPU通过前端总线与北桥芯片连接,作为与外界通信的桥梁。无论何时,CPU都可以通过前端总线来读写内存。 CPU通过一些引脚来传送想要读写物理内存的地址,同时通过另一些引脚来发送将要写入内存的数据或者接收从内存读取到的数据。 Intel Core 2 QX6600 用33个引脚来传送物理内存地址(因此共有233 个内存地址),并且用64个引脚来发送或接收数据(所以数据在64位通道中传输,也就是8字节的数据块)。因此C PU可以访问64G的物理内存(233*8字节),尽管多数芯片组只能处理8G大小的物理内存。 Now comes the rub. We’re used to thinking of memory only in terms of RAM, the stuff programs read from and write to all the time. And indeed most of the memory requests from the processor are routed to RAM modules by the northbridge. But not all of them. Physical memory addresses are also used for communication with assorted devices on t he motherboard (this communication is called memory-mapped I/O). These devices include video cards, most PCI cards (say, a scanner or SCSI card), and also the flash mem ory that stores the BIOS. 那么现在的问题是,通常一提起内存我们仅仅联想到RAM,以为程序一直读写的就只是RAM。的确,绝大多数来自CPU的内存访问请求都被北桥芯片映射到了RAM。但是,注意,不是全部。物理内存同样可以用来与主板上的各种设备通信(这种通信方式被称为I/O内存映射)。这些设备包括显卡,大多数PCI卡(比如,扫描仪,或者是SCSI卡),也包括存储BIOS的flash存储器。 When the northbridge receives a physical memory request it decides where to route it: should it go to RAM? Video card maybe? This routing is decided via the memory addres s map. For each region of physical memory addresses, the memory map knows the device that owns that region. The bulk of the addresses are mapped to RAM, but when the y aren’t the memory map tells the chipset which device should service requests for those addresses. This mapping of memory addresses away from RAM modules causes the c lassic hole in PC memory between 640KB and 1MB. A bigger hole arises when memory addresses are reserved for video cards and PCI devices. This is why 32-bit OSes have pr oblems using 4 gigs of RAM. In Linux the file /proc/iomem neatly lists these address range mappings. The diagram below shows a typical memory map for the first 4 gigs of p hysical memory addresses in an Intel PC:

内存映射文件

内存映射文件: 内存映射文件有三种,第一种是可执行文件的映射,第二种是数据文件的映射,第三种是借助页面交换文件的内存映射.应用程序本身可以使用后两种内存映射. 1.可执行文件映射: Windows在执行一个Win32应用程序时使用的是内存映射文件技术.系统先在进程地址空间的0x00400000以上保留一个足够大的虚拟地址空间(0x00400000以下是由系统管理的),然后把应用程序所在的磁盘空间作为虚拟内存提交到这个保留的地址空间中去(我的理解也就是说,虚拟内存是由物理内存和磁盘上的页面文件组成的,现在应用程序所在的磁盘空间就成了虚拟地址的页面文件).做好这些准备后,系统开始执行这个应用程序,由于这个应用程序的代码不在内存中(在页面文件中),所以在执行第一条指令的时候会产生一个页面错误(页面错误也就是说,系统所访问的数据不在内存中),系统分配一块内存把它映射到0x00400000处,把实际的代码或数据读入其中(系统分配一块内存区域,把它要访问的在页面文件中的数据读入到这块内存中,需在注意是系统读入代码或数据是一页一页读入的),然后可以继续执行了.当以后要访问的数据不在内存中时,就可以通过前面的机制访问数据.对于Win32DLL的映射也是同样,不过DLL文件应该是被Win32进程共享的(我想应该被映射到x80000000以后,因为0x80000000-0xBFFFFFFF是被共享的空间). 当系统在另一个进程中执行这个应用程序时,系统知道这个程序已经有了一个实例,程序的代码和数据已被读到内存中,所以系统只需把这块内存在映射到新进程的地址空间即可,这样不就实现了在多个进程间共享数据了吗!然而这种共享数据只是针对只读数据,如果进程改写了其中的代码和数据,操作系统就会把修改的数据所在的页面复制一份到改写的进程中(我的理解也就是说共享的数据没有改变,进程改写的数据只是共享数据的一份拷贝,其它进程在需要共享数据时还是共享没有改写的数据),这样就可以避免多个进程之间的相互干扰. 2.数据文件的内存映射: 数据文件的内存映射原理与可执行文件内存映射原理一样.先把数据文件的一部分映射到虚拟地址空间的0x80000000 - 0xBFFFFFFF,但没有提交实际内存(也就是说作为页面文件),当有指令要存取这段内存时同样会产生页面错误异常.操作系统捕获到这个异常后,分配一页内存,映射内存到发生异常的位置,然后把要访问的数据读入到这块内存,继续执行刚才产生异常的指令(这里我理解的意思是把刚才产生异常的指令在执行一次,这次由于数据已经映射到内存中,指令就可以顺利执行过去),由上面的分析可知,应用程序访问虚拟地址空间时由操作系统管理数据在读入等内容,应用程序本身不需要调用文件的I/O函数(这点我觉得很重要,也就是为什么使用内存映射文件技术对内存的访问就象是对磁盘上的文件访问一样). 3.基于页面交换文件的内存映射: 内存映射的第三种情况是基于页面交换文件的.一个Win32进程利用内存映射文件可以在进程共享的地址空间保留一块区域(0x8000000 - 0xBFFFFFFF),这块区域与系统的页面交换文件相联系.我们可以用这块区域来存储临时数据,但更常见的做法是利用这块区域与其他进程通信(因为0x80000000以上是系统空间,进程切换只是私有地址空间,系统空间是所有进程共同使用的),这样多进程间就可以实现通信了.事实上Win32多进程间通信都是使用的内存映射文件技术,如PostMessage(),SentMessage()函数,在内部都使用内存映射文件技术. 使用内存映射文件的方法: 1.利用内存映射文件进行文件I/O操作: CreateFile()-->CreateFileMapping()-->MapViewOfFile()......

内存映射和普通文件访问的区别

在讲述文件映射的概念时, 不可避免的要牵涉到虚存(SVR 4的VM). 实际上, 文件映射是虚存的中心概念, 文件映射一方面给用户提供了一组措施, 好似用户将文件映射到自己地址空间的某个部分, 使用简单的内存访问指令读写文件;另一方面, 它也可以用于内核的基本组织模式, 在这种模式种, 内核将整个地址空间视为诸如文件之类的一组不同对象的映射. 中的传统文件访问方式是, 首先用open系统调用打开文件, 然后使用read, write以及lseek等调用进行顺序或者随即的I/O. 这种方式是非常低效的, 每一次I/O操作都需要一次系统调用. 另外, 如果若干个进程访问同一个文件, 每个进程都要在自己的地址空间维护一个副本, 浪费了内存空间. 而如果能够通过一定的机制将页面映射到进程的地址空间中, 也就是说首先通过简单的产生某些内存管理数据结构完成映射的创建. 当进程访问页面时产生一个缺页中断, 内核将页面读入内存并且更新页表指向该页面. 而且这种方式非常方便于同一副本的共享. VM是面向对象的方法设计的, 这里的对象是指内存对象: 内存对象是一个软件抽象的概念, 它描述内存区与后备存储之间的映射. 系统可以使用多种类型的后备存储, 比如交换空间, 本地或者远程文件以及帧缓存等等. VM系统对它们统一处理, 采用同一操作集操作, 比如读取页面或者回写页面等. 每种不同的后备存储都可以用不同的方法实现这些操作. 这样, 系统定义了一套统一的接口, 每种后备存储给出自己的实现方法. 这样, 进程的地址空间就被视为一组映射到不同数据对象上的的映射组成. 所有的有效地址就是那些映射到数据对象上的地址. 这些对象为映射它的页面提供了持久性的后备存储. 映射使得用户可以直接寻址这些对象. 值得提出的是, VM体系结构独立于Unix系统, 所有的Unix系统语义, 如正文, 数据及堆栈区都可以建构在基本VM系统之上. 同时, VM体系结构也是独立于存储管理的, 存储管理是由操作系统实施的, 如: 究竟采取什么样的对换和请求调页算法, 究竟是采取分段还是分页机制进行存储管理, 究竟是如何将虚拟地址转换成为物理地址等等(Linux中是一种叫Three Level Page Table的机制), 这些都与内存对象的概念无关. 下面介绍Linux中 VM的实现. 一个进程应该包括一个mm_struct(memory manage struct), 该结构是进程虚拟地址空间的抽象描述, 里面包括了进程虚拟空间的一些管理信息: start_code, end_code, start_data, end_data, start_brk, end_brk等等信息. 另外, 也有一个指向进程虚存区表(vm_area_struct: virtual memory area)的指针, 该链是按照虚拟地址的增长顺序排列的. 在Linux进程的地址空间被分作许多区(vma), 每个区(vma)都对应虚拟地址空间上一段连续的区域, vma是可以被共享和保护的独立实体, 这里的vma就是前面提到的内存对象. 下面是vm_area_struct的结构, 其中, 前半部分是公共的, 与类型无关的一些数据成员, 如: 指向mm_struct的指针, 地址范围等等, 后半部分则是与类型相关的成员, 其中最重要的是一个指向vm_operation_struct向量表的指针 vm_ops, vm_pos向量表是一组虚函数, 定义了与vma类型无关的接口. 每一个特定的子类, 即每种vma类型都必须在向量表中实现这些操作. 这里包括了: open, close, unmap, protect, sync, nopage, wppage, swapout这些操作. 1.struct vm_area_struct { 2./*公共的, 与vma类型无关的 */ 3.struct mm_struct * vm_mm;

内存映射文件

内存映射文件 内存映射文件是由一个文件到一块内存的映射。Win32提供了允许应用程序把文件映射到一个进程的函数(CreateFileMapping)。这样,文件内的数据就可以用内存读/写指令来访问,而不是用ReadFile和WriteFile这样的I/O系统函数,从而提高了文件存取速度。 这种函数最适用于需要读取文件并且对文件内包含的信息做语法分析的应用程序,如对输入文件进行语法分析的彩色语法编辑器,编译器等。把文件映射后进行读和分析,能让应用程序使用内存操作来操纵文件,而不必在文件里来回地读、写、移动文件指针。 有些操作,如放弃“读”一个字符,在以前是相当复杂的,用户需要处理缓冲区的刷新问题。在引入了映射文件之后,就简单的多了。应用程序要做的只是使指针减少一个值。 映射文件的另一个重要应用就是用来支持永久命名的共享内存。要在两个应用程序之间共享内存,可以在一个应用程序中创建一个文件并映射之,然后另一个应用程序可以通过打开和映射此文件把它作为共享的内存来使用。 VC++中使用内存映射文件处理大文件(1) 关键词:VC++ 内存映射 阅读提示:本文给出了一种方便实用的解决大文件的读取、存储等处理的方法,并结合相关程序代码对具体的实现过程进行了介绍。 引言 文件操作是应用程序最为基本的功能之一,Win32 API和MFC均提供有支持文件处理的函数和类,常用的有Win32 API的CreateFile()、WriteFile()、ReadFile()和MFC 提供的CFile类等。一般来说,以上这些函数可以满足大多数场合的要求,但是对于某些特殊应用领域所需要的动辄几十GB、几百GB、乃至几TB的海量存储,再以通常的文件处理方法进行处理显然是行不通的。目前,对于上述这种大文件的操作一般是以内存映射文件的方式来加以处理的,本文下面将针对这种Windows核心编程技术展开讨论。 内存映射文件 内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法。 内存映射文件并不是简单的文件I/O操作,实际用到了Windows的核心编程技术--内存管理。所以,如果想对内存映射文件有更深刻的认识,必须对Windows操作系统的内存管理机制有清楚的认识,内存管理的相关知识非常复杂,超出了本文的讨论范畴,

几种常用的经常出现于现有嵌入式应用中的内存映射I-O方法概述

几种常用的经常出现于现有嵌入式应用中的内存映射I/O方法概述Linux 暴风雨般占领了嵌入式系统市场。分析家指出,大约有1/3到1/2的32/64位新的嵌入式系统设计采用了Linux。嵌入式Linux 已经在很多应用领域显示出优势,比如SOHO家庭网络和成像/多功能外设。在(NAS/SAN)存储,家庭数字娱乐(HDTV/PVR/DVR /STB),和手持设备/无线设备,特别是数字移动电话更获得大幅度发展。 嵌入式Linux新应用不会凭空从开发者的头脑中冒出来,大部分项目都是由成千上万行,甚至数百万行的代码组成。成千上百的嵌入式项目已经成功地将现有的其它平台的代码移植到Linux下,比如Wind River VxWorks 和pSOS,VRTX,Nucleus 和其它RTOS。这些移植工作有着重要的价值和现实意义。 到目前为止,大多数关于移植已有的RTOS应用到嵌入式Linux的文献,关注RTOS 接口(API)、任务、调度模式以及怎样将他们映射到相应得用户空间去。同样重要的是,在I/O调用密集的嵌入式程序中如何将RTOS的硬件接口代码移植到更加规范的Linux设备驱动程序中去。 本文将概述几种常用的经常出现于现有嵌入式应用中的内存映射I/O方法。它们涵盖的范围从对中断服务例程的特殊使用及用户线程对硬件访问到出现于有些ROTS中的半规范化驱动程序模型。这对于移植RTOS 代码到规范化的Linux设备启动程序具有一定启发作用,并且介绍了一些移植方法。特别地,本文会重点讨论RTOS和Linux中的内存映射,基于I/O 调度队列的移植,将RTOS I/O重定义到Linux下的驱动程序和守护进程里。RTOS I/O 概念 “不规范”是描述大多数RTOS系统I/O的最佳词语。多数RTOS是针对较早的无MMU 的CPU而设计,所以忽略了内存管理部分,即使当MMU问世后也是这样:不区分物理地址和逻辑地址。大多数RTOS还全部运行在特权模式,虽然表面上看来是增强了性能。全部的RTOS 应用和系统代码都能够访问整个地址空间、内存映射过的设备、以及其他I/O操作。这样,即使存在差别,也是很难把RTOS应用程序代码同驱动程序代码区分开

虚拟内存与物理内存的地址映射解析

在进入正题前先来谈谈操作系统内存管理机制的发展历程,了解这些有利于我们更好的理解目前操作系统的内存管理机制。 一早期的内存分配机制 在早期的计算机中,要运行一个程序,会把这些程序全都装入内存,程序都是直接运行在内存上的,也就是说程序中访问的内存地址都是实际的物理内存地址。当计算机同时运行多个程序时,必须保证这些程序用到的内存总量要小于计算机实际物理内存的大小。那当程序同时运行多个程序时,操作系统是如何为这些程序分配内存的呢?下面通过实例来说明当时的内存分配方法: 某台计算机总的内存大小是128M,现在同时运行两个程序A和B,A需占用内存10M,B需占用内存110。计算机在给程序分配内存时会采取这样的方法:先将内存中的前10M分配给程序A,接着再从内存中剩余的118M中划分出110M分配给程序B。这种分配方法可以保证程序A和程序B都能运行,但是这种简单的内存分配策略问题很多。

图一早期的内存分配方法 问题1:进程地址空间不隔离。由于程序都是直接访问物理内存,所以恶意程序可以随意修改别的进程的内存数据,以达到破坏的目的。有些非恶意的,但是有bug的程序也可能不小心修改了其它程序的内存数据,就会导致其它程序的运行出现异常。这种情况对用户来说是无法容忍的,因为用户希望使用计算机的时候,其中一个任务失败了,至少不能影响其它的任务。 问题2:内存使用效率低。在A和B都运行的情况下,如果用户又运行了程序C,而程序C需要20M大小的内存才能运行,而此时系统只剩下8M的空间可供使用,所以此时系统必须在已运行的程序中选择一个将该程序的数据暂时拷贝到硬盘上,释放出部分空间来

供程序C使用,然后再将程序C的数据全部装入内存中运行。可以想象得到,在这个过程中,有大量的数据在装入装出,导致效率十分低下。 问题3:程序运行的地址不确定。当内存中的剩余空间可以满足程序C的要求后,操作系统会在剩余空间中随机分配一段连续的 20M大小的空间给程序C使用,因为是随机分配的,所以程序运行的地址是不确定的。 二分段 为了解决上述问题,人们想到了一种变通的方法,就是增加一个中间层,利用一种间接的地址访问方法访问物理内存。按照这种方法,程序中访问的内存地址不再是实际的物理内存地址,而是一个虚拟地址,然后由操作系统将这个虚拟地址映射到适当的物理内存地址上。这样,只要操作系统处理好虚拟地址到物理内存地址的映射,就可以保证不同的程序最终访问的内存地址位于不同的区域,彼此没有重叠,就可以达到内存地址空间隔离的效果。 当创建一个进程时,操作系统会为该进程分配一个4GB大小的虚拟进程地址空间。之所以是4GB,是因为在32位的操作系统中,一个指针长度是4字节,而4字节指针的寻址能力是从 0x00000000~0xFFFFFFFF,最大值0xFFFFFFFF表示的即为4GB大小的容量。与虚拟地址空间相对的,还有一个物理地址空间,这个地址

通过VC++内存映射修改大文件方法

通过VC++内存映射修改大文件方法 本文介绍利用VC++内存映射文件修改大文件的方法:在大文件内存前加入一段数据,若要使用内存映射文件,必须执行下列操作步骤: 1.创建或打开一个文件内核对象,该对象用于标识磁盘上你想用作内存 映射文件的文件; 2.创建一个文件映射内核对象,告诉系统该文件的大小和你打算如何访 问该文件; 3.让系统将文件映射对象的全部或一部分映射到你的进程地址空间中;当完成对内存映射文件的使用时,必须执行下面这些步骤将它清除: 1.告诉系统从你的进程的地址空间中撤消文件映射内核对象的映像; 2.关闭文件映射内核对象; 3.关闭文件内核对象; 下面将用一个实例详细介绍这些操作步骤,(本实例的目的就是将一个文件A其内容前面加入一些内容存入文件B,我想大家在程序开发当中会遇到这种情况的)。 一、我们打开关于A文件内核对象,并创建一个关于B文件的内核对象若要创建或打开一个文件内核对象,总是要调用CreateFile函数: HANDLE CreateFile( PCSTR pszFileName, DWORD dwDesiredAccess, DWORD dwShareMode, PSECURITY_ATTRIBUTES psa, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); CreateFile函数拥有好几个参数,这里只重点介绍前3个参数,即szFileName,dwDesiredAccess 和dwShareMode。你可能会猜到,第一个参数pszFileName用于指明要创建或打开的文件的名

linux下的内存映射函数mmap详解及示例代码

自:https://www.sodocs.net/doc/2b6529880.html,/flying5/blog 不错的博客,大家可以去看看 Linux的mmap文件内存映射机制 mmap: memory map 在讲述文件映射的概念时, 不可避免的要牵涉到虚存(SVR 4的VM). 实际上, 文件映射是虚存的中心概念, 文件映射一方面给用户提供了一组措施, 好似用户将文件映射到自己地址空间的某个部分, 使用简单的内存访问指令读写文件;另一方面, 它也可以用于内核的基本组织模式, 在这种模式种, 内核将整个地址空间视为诸如文件之类的一组不同对象的映射. 中的传统文件访问方式是, 首先用open系统调用打开文件, 然后使用read, write以及lseek等调用进行顺序或者随即的I/O. 这种方式是非常低效的, 每一次I/O操作都需要一次系统调用. 另外, 如果若干个进程访问同一个文件, 每个进程都要在自己的地址空间维护一个副本, 浪费了内存空间. 而如果能够通过一定的机制将页面映射到进程的地址空间中, 也就是说首先通过简单的产生某些内存管理数据结构完成映射的创建. 当进程访问页面时产生一个缺页中断, 内核将页面读入内存并且更新页表指向该页面. 而且这种方式非常方便于同一副本的共享. VM是面向对象的方法设计的, 这里的对象是指内存对象: 内存对象是一个软件抽象的概念, 它描述内存区与后备存储之间的映射. 系统可以使用多种类型的后备存储, 比如交换空间, 本地或者远程文件以及帧缓存等等. VM 系统对它们统一处理, 采用同一操作集操作, 比如读取页面或者回写页面等. 每种不同的后备存储都可以用不同的方法实现这些操作. 这样, 系统定义了一套统一的接口, 每种后备存储给出自己的实现方法. 这样, 进程的地址空间就被视为一组映射到不同数据对象上的的映射组成. 所有的有效地址就是那些映射到数据对象上的地址. 这些对象为映射它的页面提供了持久性的后备存储. 映射使得用户可以直接寻址这些对象. 值得提出的是, VM体系结构独立于Unix系统, 所有的Unix系统语义, 如正文, 数据及堆栈区都可以建构在基本VM系统之上. 同时, VM体系结构也是独立于存储管理的, 存储管理是由操作系统实施的, 如: 究竟采取什么样的对换和请求调页算法, 究竟是采取分段还是分页机制进行存储管理, 究竟是如何将虚拟地址转换成为物理地址等等(Linux中是一种叫Three Level Page Table的机制), 这些都与内存对象的概念无关. 下面介绍Linux中VM的实现. 一个进程应该包括一个mm_struct(memory manage struct),该结构是进程虚拟地址空间的抽象描述, 里面包括了进程虚拟空间的一些管理信息: start_code, end_code, start_data, end_data, start_brk, end_brk等等信息. 另外, 也有一个指向进程虚存区表(vm_area_struct: virtual memory area)的指针, 该链是按照虚拟地址的增长顺序排列的. 在Linux进程的地址空间被分作许多区(vma), 每个区(vma)都对应虚拟地址空间上一段连续的区域, vma是可以被共享和保护的独立实体, 这里的vma就是前面提到的内存对象. 下面是vm_area_struct的结构, 其中, 前半部分是公共的, 与类型无关的一些数据成员, 如: 指向mm_struct的指针, 地址范围等等, 后半部分则是与类型相关的成员, 其中最重要的是一个指向vm_operation_struct向量表的指针vm_ops, vm_pos向量表是一组虚函数, 定义了与vma类型无关的接口. 每一个特定的子类, 即每种vma类型都必须在向量表中实现这些操作. 这里包括了: open, close, unmap, protect, sync, nopage, wppage, swapout这些操作. struct vm_area_struct { /*公共的, 与vma类型无关的*/ struct mm_struct * vm_mm; unsigned long vm_start; unsigned long vm_end; struct vm_area_struct *vm_next;

Linux设备驱动程序学习(20)-内存映射和DMA-基本概念

Linux设备驱动程序学习(20)-内存映射和DMA-基本概念 (2011-09-25 15:47) 标签: 虚拟内存设备驱动程序Linux技术分类:Linux设备驱动程序 这部分主要研究 Linux 内存管理的基础知识, 重点在于对设备驱动有用的技术. 因为许多驱动编程需要一些对于虚拟内存(VM)子系统原理的理解。 而这些知识主要分为三个部分: 1、 mmap系统调用的实现原理:它允许设备内存直接映射到一个用户进程地址 空间. 这样做对一些设备来说可显著地提高性能. 2、与mmap的功能相反的应用原理:内核态代码如何跨过边界直接存取用户空间的内存页. 虽然较少驱动需要这个能力. 但是了解如何映射用户空间内存到内 核(使用 get_user_pages)会有用. 3、直接内存存取( DMA ) I/O 操作, 它提供给外设对系统内存的直接存取. 但所有这些技术需要理解 Linux 内存管理的基本原理, 因此我将先学习VM子 系统的基本原理. 一、Linux的内存管理 这里重点是 Linux 内存管理实现的主要特点,而不是描述操作系统的内存管理理论。Linux虚拟内存管理非常的复杂,要写可以写一本书:《深入理解Linux 虚拟内存管理》。学习驱动无须如此深入, 但是对它的工作原理的基本了解是必要的. 解了必要的背景知识后,才可以学习内核管理内存的数据结构. Linux是一个虚拟内存系统(但是在没有MMU的CPU中跑的ucLinux除外), 意味着在内核启动了MMU 之后所有使用的地址不直接对应于硬件使用的物理地址,这些地址(称之为虚拟地址)都经过了MMU转换为物理地址之后再从CPU的内存总线中发出,读取/写入数据. 这样 VM 就引入了一个间接层, 它是许多操作成为可能: 1、系统中运行的程序可以分配远多于物理内存的内存空间,即便单个进程都可拥有一个大于系统的物理内存的虚拟地址空间. 2、虚拟内存也允许程序对进程的地址空间运用多种技巧, 包括映射程序的内存到设备内存.等等~~~ 1、地址类型 Linux 系统处理几种类型的地址, 每个有它自己的含义: 用户虚拟地址:User virtual addresses,用户程序见到的常规地址. 用户地址在长度上是 32 位或者 64 位, 依赖底层的硬件结构, 并且每个进程有它自己 的虚拟地址空间.

内存映射文件使用方法

内存映射文件 要点:MapViewOfFile函数中映射文件的偏移地址必须为系统分配粒度的整数倍,但一次映射的文件长度则不受此限制。 摘要:本文给出了一种方便实用的解决大文件的读取、存储等处理的方法,并结合相关程序代码对具体的实现过程进行了介绍。 引言 文件操作是应用程序最为基本的功能之一,Win32 API和MFC均提供有支持文件处理的函数和类,常用的有Win32 API的CreateFile()、WriteFile()、ReadFile()和MFC提供的CFile类等。一般来说,以上这些函数可以满足大多数场合的要求,但是对于某些特殊应用领域所需要的动辄几十GB、几百GB、乃至几TB的海量存储,再以通常的文件处理方法进行处理显然是行不通的。目前,对于上述这种大文件的操作一般是以内存映射文件的方式来加以处理的,本文下面将针对这种Windows核心编程技术展开讨论。 内存映射文件 内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法。 内存映射文件并不是简单的文件I/O操作,实际用到了Windows的核心编程技术--内存管理。所以,如果想对内存映射文件有更深刻的认识,必须对Windows操作系统的内存管理机制有清楚的认识,内存管理的相关知识非常复杂,超出了本文的讨论范畴,在此就不再赘述,感兴趣的读者可以参阅其他相关书籍。下面给出使用内存映射文件的一般方法: 首先要通过CreateFile()函数来创建或打开一个文件内核对象,这个对象标识了磁盘上将要用作内存映射文件的文件。在用CreateFile()将文件映像在物理存储器的位置通告给操作系统后,只指定了映像文件的路径,映像的长度还没有指定。为了指定文件映射对象需要多大的物理存储空间还需要通过CreateFileMapping()函数来创建一个文件映射内核对象以告诉系统文件的尺寸以及访问文件的方式。在创建了文件映射对象后,还必须为文件数据保留一个地址空间区域,并把文件数据作为映射到该区域的物理存储器进行提交。由MapViewOfFile()函数负责通过系统的管理而将文件映射对象的全部或部分映射到进程地址空间。此时,对内存映射文件的使用和处理同通常加载到内存中的文件数据的处理方式基本一样,在完成了对内存映射文件的使用时,还要通过一系列的操作完成对其的清除和使用过

ARM的内存映射.

几乎每一种外设都是通过读写设备上的寄存器来进行的。外设寄存器也称为“I/O端口”,通常包括:控制寄存器、状态寄存器和数据寄存器三大类,而且一个外设的寄存器通常被连续地编址。CPU对外设IO端口物理地址的编址方式有两种:一种是I/O映射方式(I/O-mapped),另一种是内存映射方式(Memory-mapped)。而具体采用哪一种则取决于CPU 的体系结构。 有些体系结构的CPU(如,PowerPC、m68k等)通常只实现一个物理地址空间(RAM)。在这种情况下,外设I/O端口的物理地址就被映射到CPU的单一物理地址空间中,而成为内存的一部分。此时,CPU可以象访问一个内存单元那样访问外设I/O端口,而不需要设立专门的外设I/O指令。这就是所谓的“内存映射方式”(Memory-mapped)。 而另外一些体系结构的CPU(典型地如X86)则为外设专门实现了一个单独地地址空间,称为“I/O地址空间”或者“I/O端口空间”。这是一个与CPU地RAM物理地址空间不同的地址空间,所有外设的I/O端口均在这一空间中进行编址。CPU通过设立专门的I/O指令(如X86的IN和OUT指令)来访问这一空间中的地址单元(也即I/O端口)。这就是所谓的“I/O 映射方式”(I/O-mapped)。与RAM物理地址空间相比,I/O地址空间通常都比较小,如x86 CPU的I/O空间就只有64KB(0-0xffff)。这是“I/O映射方式”的一个主要缺点。 =========================================== 以上描述中“外设I/O端口的物理地址就被映射到CPU的单一物理地址空间中,而成为内存的一部分”应该如何理解? 这个I/O端口被映射到内存后,是在内存中有一个I/O端口的“镜像”吗(就是说I/O本身的寄存器地址和映射到内存后的端口地址是分开的,只是关系上存在一个对应而已)?还是说I/O端口本身和物理内存一起被编码为连续的“CPU识别的地址”(即CPU将物理内存编码后接着将I/O地址也跟着编码,内存地址和I/O地址并不存在一个对应关系,只是逻辑上被编码为一个连续统一的“物理地址”。) 以上两种理解哪种才是正确的? 另外我想问一下这个“CPU的单一物理地址空间”包括哪些对象,仅仅是物理Memory (RAM\ROM\FLASH)对象吗?是不是一个逻辑上的概念? 回答:外设I/O端口的物理地址就被映射到CPU的单一物理地址空间中,而成为内存的一部分 就是说端口可以用访问内存的方式操纵,比如mov指令,可以把数据直接传送到那个端口 I/O端口本身和物理内存一起被编码为连续的“CPU识别的地址”(即CPU将物理内存编码后接着将I/O地址也跟着编码,内存地址和I/O地址并不存在一个对应关系,只是逻辑上被编码为一个连续统一的“物理地址”。) 这个解释差不多吧

【IT专家】使用.so(共享对象)中的内存映射文件

本文由我司收集整编,推荐下载,如有疑问,请与我司联系 使用.so(共享对象)中的内存映射文件 使用.so(共享对象)中的内存映射文件[英]Using memory-mapped files from within a .so (shared object) I am getting a segfault when accessing shared memory (memory mapped file, using a fixed address of 0x60000000 ) from within a share object (.so). 当从共享对象(.so)中访问共享内存(内存映射文件,使用固定地址 0x60000000)时,我得到一个段错误。 We have many applications accessing this shared memory without difficulty. My app is different in that I create a small .so with it and the .so is the one calling mmap and accessing the shared memory. 我们有很多应用程序毫无困难地访问此共享内存。我的应用程序不同之处在于我 用它创建一个小的.so 而.so 是调用mmap 并访问共享内存的.so。 The mmap() returns just fine. My requested address is returned back properly. But as soon as I try to access the memory from within this .so, it seg faults. mmap()返回正常。我要求的地址已正确退回。但是一旦我尝试从这个.so 中访 问内存,就会出现故障。 I can’t figure out what is unique about a .so that would prevent it from accessing shared memory this way. 我无法弄清楚.so 的独特之处在于它会阻止它以这种方式访问共享内存。 Anyone have thoughts? 有人有想法吗? 2 In general, accessing mmaped memory from a shared library is in no way different from accessing it from a main executable, and you are likely barking up the wrong tree. That said, this: 通常,从共享库访问mmaped 内存与从主可执行文件访问mmaped 内存完全不

相关主题