文章目录
前言
基础不牢, 地动山摇
一、内存
包含以下几个内容:
- 1、零散的知识总结
- 2、虚拟内存
- 3、物理内存
- 4、内存映射
- 5、虚拟内存映射
- 6、共享内存
- 7、匿名共享内存
- 8、分页
1.1 零散知识总结
这里的零散的知识很可能过不了多久就忘记了, 忘记了就再来多看两遍
- 1、内存的一个地址里面"住"的是一个字节的数据
- 2、32位OS物理地址有2^32个, 因而只能使用4GB的内存
- 3、任何一个32位的程序可操作的逻辑地址是2^32个即4GB (2^32 / 1024 / 1024 / 1024 = 4GB)
- 4、针对1,2,3条会出现多个程序使用内存总和大于物理内存, 此时会借助磁盘, 将并不着急使用的内存先放到磁盘, PT对应的帧号只显示是磁盘.
1.2 虚拟内存
- 1、局部性原理
- 2、虚拟内存概念
- 3、虚拟内存工作流程
- 4、虚拟内存特征
1.2.1 局部性原理
1.2.1.1 时间局部性
如果执行了程序中的某条指令, 那么不久后这条指令很有可能再次执行, 如果某个数据被访问过, 不久之后该数据很可能再次被访问.(因为程序中存在大量的循环)
1.2.1.2 空间局限性
一旦程序访问了某个存储单元, 在不久之后, 其附近的存储单元也很可能被访问.(因为很多数据在内存中都是连续存放的, 并且程序的指令也是顺序地在内存中存放的)
1.2.2 虚拟内存
1.2.2.1 虚拟内存三个特征
- 1、
多次性:
无需在程序运行时一次性全部装入内存, 而是允许被分成多次调入内存 - 2、
对换性:
在程序运行时无需一直常驻内存, 而是允许在程序运行过程中, 将程序换入、换出 - 3、
虚拟性:
从逻辑上扩充了内存的容量, 使用户看到的内存容量, 远大于实际的容量
1.2.3 虚拟内存概念
虚拟内存是计算机系统内存管理的一种技术, 它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间), 而实际上, 它通常是被分隔成多个物理内存碎片, 还有部分暂时存储在外部磁盘存储器上, 在需要时进行数据交换. 目前大多数操作系统都使用了虚拟内存, 如Windows家族的"虚拟内存", Linux的"交换空间"
一个进程运行时都会得到4G的虚拟内存, 这个虚拟内存可以认为每个进程都认为自己拥有4G的空间, 这只是每个进程认为的, 但是实际上在虚拟内存对应的物理内存上, 可能只对应一点点的物理内存, 实际用了多少内存, 就会对应多少物理内存.
进程得到的这4G虚拟内存是一个连续的地址空间(这也只是进程认为), 而实际上, 它通常是被分隔成多个物理内存碎片, 还有一部分存储在外部磁盘存储器上, 在需要时进行数据交换.
进程开始要访问一个地址, 它可能会经历下面的过程:
- 1、每次我要访问地址空间上的某一个地址, 都需要把地址翻译为实际物理内存地址
- 2、所有进程共享这整一块物理内存, 每个进程只把自己目前需要的虚拟地址空间映射到物理内存上
- 3、进程需要知道哪些地址空间上的数据在物理内存中, 哪些不在(可能这部分存储在磁盘上), 还有在物理内存上的哪里, 这就需要通过页表来记录
- 4、页表的每一个表项分为两部分, 第一部分记录此页是否在物理内存上, 第二部分记录物理内存页的地址.
- 5、当进程访问某个虚拟地址的时候, 就会先去看页表, 如果发现对应的数据不在物理内存上, 就会发生缺页异常
- 6、缺页异常的处理过程, 操作系统立即阻塞该进程, 并将硬盘里对应的页换入内存, 然后使该进程就绪, 如果内存已经满了, 没有空地方了, 那就找一个页覆盖, 至于具体覆盖的哪个页, 就需要看操作系统的页面置换算法是怎么设计的了.
1.2.4 虚拟内存工作
- 1、当每个进程创建的时候, 内核会为进程分配4G的虚拟内存, 当进程还没有开始运行时, 这只是一个内存布局. 实际上并不立即就把虚拟内存对应位置的程序数据和代码拷贝到物理内存中, 只是建立好虚拟内存和磁盘文件之间的映射. 这个时候数据和代码还是在磁盘上. 当运行到对应的程序时, 进程去寻找页表, 发现页表中地址没有存放在物理内存上, 而是在磁盘上, 于是发生缺页异常, 于是将磁盘上的数据拷贝到物理内存中.
- 2、在进程运行过程中, 要通过malloc来动态分配内存时, 也只是分配了虚拟内存, 即为这块虚拟内存对于的页表项做相应设置, 当进程真正访问到此数据时, 才引发缺页异常.
- 3、可以认为虚拟内存都被映射到了磁盘空间中(事实上也是按需要映射到磁盘空间上, 通过mmap, mmap是用来建立虚拟空间和磁盘空间的映射关系的)
1.3 物理内存
物理内存 = 运行内存, 指的是计算机的安装内存 通俗的讲就是内存条的大小
1.4 共享内存
- 1、共享内存的概念
- 2、共享内存的有点
- 3、共享内存的缺点
1.4.1 共享内存概念
共享内存就是允许两个或多个进程共享一定的存储区, 让两个进程地址通过页表映射到同一片物理地址以便于通信. 就如同malloc()函数向不同进程返回了指向同一个物理内存区域的指针. 当一个进程改变了这块地址中的内容的时候, 其它进程都会察觉到这个更改. 因为数据不需要在客户机和服务器之间复制, 数据直接写到内存, 不用若干数据拷贝, 所以这是最快的一种IPC.
1.4.2 共享内存优点
你可以给一个区域里面写入数据, 理所当然就可以从中拿取数据, 这也就构成了进程间的双向通信, 而且共享内存是IPC通信当中 传输速度最快的通信方式之一
, 理由很简单, 客户进程和服务进程传递的数据直接从内存里存取、存放, 数据不需要在两进程间复制, 没有什么操作比这简单了. 两者用共享内存进行数据通信, 它对数据也没啥限制.
1.4.3 共享内存缺点
共享内存并不完美, 共享内存并未提供同步机制, 也就是说, 在一个服务进程结束对共享内存写操作之前, 并没有自动机制可以阻止另一个进程(客户进程)开始对它进行读取
1.5 匿名共享内存
Android可以使用Linux的一切IPC通信方式, 包括共享内存, 不过Android主要使用的方式是匿名共享内存Ashmem(Anonymous Shared Memory), 跟原生的不太一样, 比如它在自己的驱动中添加了互斥锁, 另外通过fd的传递来实现共享内存的传递.
二、Linux函数
2.1 mmap
- 1、mmap函数
- 2、原理
- 3、与shmget的区别
2.1.1 mmap函数原型
/**
* 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
*/
void* mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset)
2.1.2 mmap机制
在磁盘上建立一个文件, 每个进程存储器里面, 单独开辟一个空间来进行映射
, 如果多进程的话, 那么不会对实际的物理存储器(主存)消耗太大
2.1.3 mmap与shmget
mmap: 保存到实际硬盘, 实际存储并没有反映到主存上, 优点: 存储量可以很大(多于主存), 缺点: 进程间读取和写入速度要比主存的要慢
shmget: 保存到物理存储器(主存), 实际的存储量直接反映到主存上, 优点: 进程间访问速度比磁盘要快, 缺点, 存储量不能非常大(多于主存)
使用上: 如果分配的存储量不大, 使用shmget, 如果存储量大, 使用mmap
2.2 malloc
// 函数原型:
// void*: 未确定类型的指针,c,c++规定void*可以强转为任何其他类型的指针.
void *malloc(unsigned int num_bytes);
全称是memory allocation(动态内存分配), 用于申请一块连续的指定大小的内存区域, 如果申请成功, 返回指向被分配内存空间的指针, 反之返回指针NULL
2.2.1 malloc与new的不同
在使用上, malloc和new至少有两个不同: new返回指定类型的指针, 并且可以自动计算所需要大小, 而malloc需要调用者计算字节数, 并且在返回后强行转换为实际类型的指针.
另外还有一点不能直接看出的区别是malloc只管分配内存, 并不能对所得的内存进行初始化, 所以得到的一片新内存中, 其值将是随机的.